PHP client library for the DPD Cloud Service Webservice (DPD Deutschland).
An open-source PHP client for DPD Germany's Cloud Service: create shipments and download their labels, pre-flight validate orders server-side, track parcels via two different tracking models, find pickup ParcelShops, and fetch account pickup rules. SOAP by default, with an optional REST transport.
- PHP 8.2+
ext-curlext-domext-json
composer require very-code-com/dpd-de-phpuse VeryCodeCom\DpdDe\DpdCloudClient;
use VeryCodeCom\DpdDe\Dto\{Address, OrderItem, Parcel};
use VeryCodeCom\DpdDe\Enum\ShipService;
$client = DpdCloudClient::sandbox('DPD Cloud Service Alpha2', 'partner-token', 123456, 'user-token');
$result = $client->createShipment(new OrderItem(
shipAddress: new Address(
name: 'Max Mustermann', street: 'Musterstr.', houseNo: '1',
zipCode: '12345', city: 'Berlin', country: 'DE',
),
parcelShopId: 0,
parcel: new Parcel(
ShipService::Classic,
weightKg: 2.5,
content: 'Testware', // required by DPD (1-35 chars)
yourInternalId: 'ORDER-1', // required by DPD (1-35 chars)
reference1: 'ORDER-1', // required by DPD (1-35 chars)
),
));
echo $result->firstParcelNo(); // e.g. 01234567890123
file_put_contents('label.pdf', $result->labelPdf); // already Base64-decodedSee examples/ for complete, runnable scripts.
// Named constructors
$client = DpdCloudClient::sandbox($partnerName, $partnerToken, $userId, $userToken);
$client = DpdCloudClient::production($partnerName, $partnerToken, $userId, $userToken);
// From environment variables (recommended)
$config = DpdCloudConfig::fromEnv();
$client = new DpdCloudClient($config);
// From array (framework config)
$config = DpdCloudConfig::fromArray([
'partner_name' => '...', 'partner_token' => '...',
'user_id' => 123456, 'user_token' => '...', 'env' => 'production',
]);DPD Cloud Service uses two credential pairs, both issued by DPD:
- PartnerCredentials (
Name+Token): identifies the software/integration (your "Partner" slot, e.g.DPD Cloud Service Alpha2). - UserCredentials (
cloudUserID+Token): identifies the DPD customer account (your own account).
| Env variable | Required | Default | Description |
|---|---|---|---|
DPD_CLOUD_PARTNER_NAME |
yes | - | PartnerCredentials.Name |
DPD_CLOUD_PARTNER_TOKEN |
yes | - | PartnerCredentials.Token |
DPD_CLOUD_USER_ID |
yes | - | UserCredentials.cloudUserID (integer) |
DPD_CLOUD_USER_TOKEN |
yes | - | UserCredentials.Token |
DPD_CLOUD_ENV |
no | production |
sandbox or production |
DPD_CLOUD_LANGUAGE |
no | de_DE |
Response language, e.g. de_DE, en_US |
DPD_CLOUD_TIMEOUT |
no | 30 |
Request timeout (seconds) |
DPD_CLOUD_CONNECT_TIMEOUT |
no | 10 |
Connection timeout (seconds) |
DPD_CLOUD_DEBUG |
no | 0 |
1/true to enable verbose debug output (see below) |
Sandbox vs. production endpoints:
| Environment | SOAP endpoint | REST base |
|---|---|---|
| Sandbox (Testsystem) | https://cloud-stage.dpd.com/services/v1/DPDCloudService.asmx |
https://cloud-stage.dpd.com/api/v1 |
| Production | https://cloud.dpd.com/services/v1/DPDCloudService.asmx |
https://cloud.dpd.com/api/v1 |
DPD issues separate credentials per environment: your production PartnerCredentials/UserCredentials will not work against the sandbox and vice versa. Using live credentials against cloud-stage returns ErrorID 2000 / CLOUD_API_PARTNERCREDENTIALS, and vice versa.
Register a free developer account at esolutions.dpd.com/entwickler/registrieren.aspx, then log in.
Do not use the portal's "Benutzerdaten ändern" / "Sandbox Zugangsdaten" page. Despite what
the official PDF (page 2) and DPD support both tell you, that page shows credentials for a
different API: DPD Web Connect (public-ws-stage.dpd.com, DelisID + password and a
getAuth call returning an authToken, i.e. sandboxdpd / xMm...). Those do not work with the Cloud
Service and are not convertible into Cloud credentials: the formats are incompatible
(PartnerCredentials.Token is 10-25 chars, cloudUserID is an integer).
The DPD Cloud sandbox credentials are pre-filled inside the portal's code examples:
Entwickler / DPD Cloud Webservice / Code Beispiele / SOAP (or REST) / Microsoft .NET, Java or PHP
https://esolutions.dpd.com/entwickler/dpdcloudwebservice/code-beispiele-soap-microsoft.aspx?allowcloud=true
Note the ?allowcloud=true query parameter: the entire Cloud section of the portal is
unreachable through the normal logged-in navigation without it.
The snippets contain a shared, generic sandbox account: the same values for every developer, not personalised to you. Copy the four values out of the snippet into your environment (only their leading characters are shown here):
export DPD_CLOUD_ENV=sandbox
export DPD_CLOUD_PARTNER_NAME="DPD Sandbox"
export DPD_CLOUD_PARTNER_TOKEN="064..." # 20 characters
export DPD_CLOUD_USER_ID=279... # 7 digits
export DPD_CLOUD_USER_TOKEN="635..." # 20 charactersThat account covers all five operations over both SOAP and REST, including setOrder with
startOrder, which returns a real PDF label. Its own pickup address resolves to ZIP 63110,
depot 0163.
For production credentials, contact DPD via the eSolutions portal or cit@dpd.de; they
are issued by e-mail in exactly the same four-value shape.
Set the debug flag (constructor arg, DPD_CLOUD_DEBUG=1, or 'debug' => true in
fromArray) to make the client attach the raw DPD response to every thrown
exception and log a full debug report (message + raw XML/JSON + stack trace) at
error level via the injected PSR-3 logger:
$config = DpdCloudConfig::fromArray([...], debug: true);
$client = new DpdCloudClient($config, logger: $myPsrLogger);
try {
$client->createShipment($item);
} catch (\VeryCodeCom\DpdDe\Exception\DpdCloudException $e) {
echo $e->getRawResponse(); // exact XML/JSON DPD returned (or null)
echo $e->getDebugReport(); // class + message + raw response + stack trace
}Leave debug off in production to keep exceptions and logs concise.
| Document | Contents |
|---|---|
| docs/API.md | Every client method, the DTOs they take and return, the local validation rules, and the exception hierarchy |
| docs/DPD-NOTES.md | DPD's own quirks: the two tracking models, SOAP/REST transport details, fields that are mandatory despite being optional in the WSDL |
| examples/ | Runnable scripts covering shipments, batching, tracking, ParcelShops, REST, business rules and error handling |
The client accepts a custom TransportInterface and PSR-3 logger:
new DpdCloudClient(
config: DpdCloudConfig,
transportMode: TransportMode = TransportMode::Soap,
transport: TransportInterface = new CurlTransport(),
logger: ?Psr\Log\LoggerInterface = null,
)Implement TransportInterface::send(TransportRequest): TransportResponse to swap in a
PSR-18 HTTP client adapter, or a scripted fake for tests, see
tests/Unit/DpdCloudClientTest.php for the pattern
used by this library's own test suite (no real network calls).
composer install
# Unit tests (no network required)
vendor/bin/phpunit --testsuite unit
# Integration tests against the real DPD Cloud Service sandbox (Testsystem).
# Credentials: see "Where to get sandbox (Testsystem) credentials" above.
DPD_CLOUD_SANDBOX=1 \
DPD_CLOUD_PARTNER_NAME="DPD Sandbox" \
DPD_CLOUD_PARTNER_TOKEN=064... \
DPD_CLOUD_USER_ID=279... \
DPD_CLOUD_USER_TOKEN=635... \
vendor/bin/phpunit --testsuite integration
# Static analysis (PHPStan level 8)
vendor/bin/phpstan analyse --memory-limit=512MThe integration suite exercises all five operations over both transports against
cloud-stage.dpd.com, including a real setOrder/startOrder that issues a label. Add
DPD_CLOUD_ALLOW_LABELS=0 to skip the two label-issuing tests and keep the run read-only.
| Workflow | Runs | What it does |
|---|---|---|
ci.yml |
every push and pull request | Unit tests on PHP 8.2/8.3/8.4, PHPStan level 8, composer validate, lints every example |
integration.yml |
pushes to master, nightly, manual |
The sandbox integration suite |
The integration workflow is deliberately kept out of the pull-request gate: forks cannot read
repository secrets, and DPD enforces a per-account call limit (2027 CLOUD_API_USERCALLLIMIT,
10-minute cool-down) that a per-push, per-PHP-version run would walk straight into. It is
serialised through a concurrency group for the same reason.
To enable it, add the four sandbox values (DPD_CLOUD_PARTNER_NAME,
DPD_CLOUD_PARTNER_TOKEN, DPD_CLOUD_USER_ID, DPD_CLOUD_USER_TOKEN) to a repository
environment named dpd-sandbox, restricted to the master branch. Environment secrets
are only readable by jobs that opt into that environment, so a workflow added on a side
branch cannot reach them. Without them the job reports "not configured" and stops rather
than failing the build. Running it manually (workflow_dispatch) offers a checkbox to skip
the label-issuing tests.
Apache License 2.0, see NOTICE for attribution requirements.
You may use, distribute, and modify this library freely. You must retain the NOTICE file and copyright notices in any redistribution or derivative work.
Built by Very Code. Contributions welcome, open an issue or PR.