Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Dhlparcel Php Api Laravel Package

mvdnbrk/dhlparcel-php-api

A PHP client for the DHL Parcel API that provides a fluent interface for creating shipments, generating labels, tracking parcels, and managing DHL Parcel services. Works with Laravel and standalone PHP applications.

View on GitHub
Deep Wiki
Context7

Getting Started

Install with Composer:

composer require mvdnbrk/dhlparcel-php-api

The package is an HTTPlug/PSR-18 client, so it does not ship an HTTP adapter. You need one installed (usually already present in a Laravel app):

composer require php-http/guzzle7-adapter

If you are in Laravel, also pull the wrapper rather than wiring the raw client by hand:

composer require mvdnbrk/laravel-dhlparcel
php artisan vendor:publish --tag=config

Then put your credentials in .env. DHL Parcel issues two values from the DHL Parcel developer portal / Mijn DHL Parcel — a user id and an API key — plus a separate account id used for shipment booking. Do not conflate them; the client needs the user id + key for authentication and the account id on the shipment payload.

Minimal bootstrap outside Laravel:

use Mvdnbrk\DhlParcel\Client;

$client = new Client();
$client->setUserId(config('services.dhl.user_id'));
$client->setApiKey(config('services.dhl.api_key'));
$client->setSandbox(true); // omit for production

Look first at the src/Endpoints (or resource) directory — that is the actual API surface: shipments, labels, tracks, rates, parcelshops, and address validation. Read one endpoint class and the matching test to understand the response objects before writing anything.

The first use case worth building is the happy path: create a shipment → request a label → store the PDF → track the barcode. That loop covers 80% of what a DHL integration actually does in a shop backend, and it exercises authentication, error handling, and binary responses all at once.

Implementation Patterns

Wrap the client in your own service. Do not let Mvdnbrk\DhlParcel\Client leak into controllers. Create an App\Services\DhlParcel\ShipmentService that takes the client via constructor injection, builds the shipment payload from your own Order model, and returns a small DTO. This isolates you from the package (which was last released in 2021) and makes testing trivial.

Shipment creation is a two-step flow. First $client->shipments()->create($payload), which returns a shipment object carrying a shipmentId / trackerCode. Then $client->labels()->create(...) (or the label helper hanging off the shipment) to fetch the actual PDF/ZPL. Treat these as two independent failure points — a shipment can exist in DHL's system with no label, and you will need to re-request the label rather than re-book the shipment.

Store the label, not the request. Label responses are binary (PDF, PNG, ZPL). Write them to disk (Storage::put("labels/{$order->id}.pdf", $response->pdf)) and keep the barcode plus shipmentId in the database. Never re-call the label endpoint on every page view; it is slow and rate-limited.

Queue label generation. Shipment booking is a network call to DHL with a real timeout. Fire it from a queued job triggered by an order state transition ("packed"), with $tries = 3 and backoff. Surface a "retry label" button for support staff.

Cache rates and parcel shops. $client->rates() and $client->parcelShops() are read-only lookups that change slowly. Cache rate responses per (postcode, weight, product) for an hour or so, and cache the parcelshop list for a day. This is the single biggest latency win in a checkout flow.

Polling, not webhooks. DHL Parcel's tracking is pull-based here — $client->tracks()->get($barcode). Run a scheduled command (every thirtyMinutes) over open shipments and update status locally, stopping once delivered/returned. Keep the raw response in a JSON column so you can re-derive statuses if DHL changes event naming.

Test against a fake HTTP client. Because the client is PSR-18, you can inject any client — php-http/mock-client or Laravel's Http::fake() adapter — and assert on the outgoing request body without touching DHL. Do this for payload shape; DHL's validation errors are opaque enough that catching a malformed address in CI is worth it.

Gotchas and Tips

It is a 2021 package. Pin it and audit before adopting. It targets PHP 7.x-era tooling; on PHP 8.2+ you may hit deprecation notices or a psr/http-client version conflict. Run composer why-not and check the adapter constraint first — most install failures are adapter-version mismatches, not API problems.

No HTTP adapter means a confusing first error. If you see a "no client found" / discovery exception, that is the package's fault only indirectly — install php-http/guzzle7-adapter or similar.

Sandbox labels are not real labels. setSandbox(true) points at the DHL test environment; barcodes generated there will not scan in production and are not billable. Make the flag strictly environment-driven (app()->isProduction()), and add a guard so a production deploy with DHL_SANDBOX=true fails loudly rather than silently shipping untracked parcels.

Weight and dimension units trip everyone up. DHL's shipment payload expects grams and millimetres. Your shop almost certainly stores kilograms and centimetres. Put the conversion in exactly one place in your service class and unit-test it; a 1000× weight error is a real billing dispute.

Address data must be pre-normalised. Dutch postcodes need the 1234 AB form, country codes are ISO 3166-1 alpha-2, and the house number is frequently a separate field DHL expects on its own. Build a small address value object that formats itself into DHL's shape, rather than sprinkling strtoupper() through the payload builder. The package does not normalise for you.

Errors are not strongly typed. Most failures surface as a generic exception carrying DHL's error code and message. Wrap every call in a try/catch that logs the request payload and response body — DHL's validation messages reference field names, and without the payload in the log you cannot tell which of your twelve shipment fields was rejected.

Duplicate shipments are easy to create. A retried job after a timeout may book twice. Persist the shipmentId the moment the response returns, and check for an existing one before booking. DHL has no idempotency key in this client.

Do not use the client directly for label re-prints. Fetch once, store, serve from your own storage. DHL label endpoints are rate-limited and re-requesting can, depending on product, return a label with a different barcode.

Extension point: if an endpoint you need is missing (the API is broader than the package's surface), subclass Client and add a resource class following the pattern in src/. The internal HTTP plumbing is thin — a missing endpoint is usually 40 lines, not a fork.

Testing seam: if you need to inspect outgoing requests in feature tests, bind a recording PSR-18 client into the container and let the wrapper resolve it; this is far cleaner than mocking the Client class itself, which has many fluent setters.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle