tietge / silverstripe-shipping-dpd
DPD-Paketdienst für tietge/silverstripe-shipping — Etiketten über die SOAP-Webdienste von DPD Deutschland.
Package info
git.innomedia.de/Tietge/silverstripe-shipping-dpd
Type:silverstripe-vendormodule
pkg:composer/tietge/silverstripe-shipping-dpd
Requires
- php: ^8.3
- ext-iconv: *
- ext-mbstring: *
- ext-soap: *
- psr/simple-cache: ^2 || ^3
- silverstripe/framework: ^6
- tietge/silverstripe-shipping: ^1.1
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-14 11:26:59 UTC
README
DPD Deutschland für tietge/silverstripe-shipping — Etiketten über die SOAP-Webdienste
(Web Connect), ShipmentService/V4_5 und LoginService/V2_0.
Handgeschrieben: Es gibt kein offizielles SDK, die brauchbaren Community-Pakete sind winzig oder
aufgegeben. Der Client deckt genau zwei Aufrufe ab — getAuth und storeOrders.
Status in diesem Projekt: vollständig gebaut und gegen die Stage-Umgebung verifiziert (Anmeldung, Depot, echtes Etikett mit
!DEMO!und korrektem Gewicht). Produktiv fehlt allein die Kundennummer. Da der Versand auf UPS umgestellt wurde, ruht das Modul — es meldet sich nur an, wenn Zugangsdaten gepflegt sind, und steht sonst nirgends im Weg.
Installation
composer require tietge/silverstripe-shipping-dpd
vendor/bin/sake db:build --flush
Braucht ext-soap und ext-iconv.
Zugangsdaten
Tietge\Shipping\Dpd\DpdCarrier:
sandbox: true
delis_id: '' # 8–10 Zeichen
password: ''
customer_number: '' # Versender-Kundennummer, numerisch
products: ['CL']
message_language: 'de_DE'
- DelisID und Passwort für die Sandbox stehen im Entwicklerportal
(
esolutions.dpd.com/entwickler/dpdwebservices.aspx) unter „Benutzerdaten ändern". Kein Antrag nötig. - Produktive Zugangsdaten über
cit@dpd.de— und das ist keine Datenanfrage, sondern eine Abnahme: „Each development using the DPD Web Connect service must be validated and approved by DPD." Erst gegen Stage fertig werden, dann beantragen. - Kundennummer ≠ DelisID. Wer hier die DelisID einträgt, bekommt keinen Anmeldefehler — der Auftrag wird erst beim ersten Etikett abgelehnt.
- Die Depotnummer wird nicht konfiguriert. Sie kommt aus der Anmeldeantwort und taucht als Präfix der Paketscheinnummer wieder auf.
Wer die Zugangsdaten aus der YAML heraushalten will, legt sie in die .env und verweist darauf —
wahlweise mit Backticks oder einem _env-Schlüssel:
Tietge\Shipping\Dpd\DpdCarrier:
delis_id: '`SS_DPD_DELIS_ID`'
password: '`SS_DPD_PASSWORD`'
customer_number_env: 'SS_DPD_CUSTOMER_NUMBER'
SS_DPD_DELIS_ID="…"
SS_DPD_PASSWORD="…"
SS_DPD_CUSTOMER_NUMBER="…"
Das gilt für delis_id, password und customer_number; ein <name>_env-Schlüssel hat Vorrang.
Aufgelöst wird über EnvironmentConfigTrait des Basismoduls (ab 1.1) mit Environment::getEnv()
— Silverstripe selbst ersetzt Backticks nur in Injector-Definitionen, nicht in der Config-API.
Vorher ging die Zeichenkette samt Backticks deshalb wörtlich als Passwort an DPD. Ist die
Variable nicht gesetzt, gilt die Einstellung als fehlend; der Unterreiter „DPD" nennt dann die
Variable.
Ausführlich: docs/versandmodul-dpd-einrichtung.md.
Test und Produktion haben getrennte Nutzerverwaltungen: public-ws-stage.dpd.com gegenüber
public-ws.dpd.com. Testetiketten tragen einen !DEMO!-Wasserdruck.
Betriebsregeln, die im Code stecken
DPDs API Developer Guidelines sind mit Sanktion formuliert: „Failure to follow these guidelines may cause an application ban." Drei Regeln schlagen unmittelbar durch:
| Regel | Grenze | Wie das Modul damit umgeht |
|---|---|---|
| Anmeldungen | 10 pro DelisID und Tag, „calling the login service in combination with each shipment service … is specifically prohibited" | Sitzung im PSR-16-Cache; das Modul zählt mit und verweigert die elfte, statt eine Sperre zu riskieren |
| Neuanmeldung | nur bei LOGIN_5 / LOGIN_6 | genau diese beiden Codes lösen einen einmaligen zweiten Versuch aus |
| Token-Laufzeit | ein Geschäftstag, endet gegen 3 Uhr | Cache läuft passend zum Geschäftstag ab; der Tageszähler ebenfalls, damit er nicht um Mitternacht zehn zusätzliche Anmeldungen freigibt |
| Versandaufrufe | 1/Sekunde, 60/Minute, 3.600/Stunde, immer nacheinander | Drossel im Client, damit eine spätere Sammelaktion sie nicht umgehen kann |
Praktische Folge: Nie „Verbindung testen" in Serie drücken. Jeder Test verbraucht eine Anmeldung.
Fallstricke, die den Code prägen
Gewicht in 10-Gramm-Einheiten. 300 bedeutet 3 kg. Aus der Schemadoku: „Parcel weight in
grams rounded in 10 gram units without decimal point." Ein naives „Gramm einsetzen" bucht das
Zehnfache — und das fällt erst auf der Frachtrechnung auf. Am gedruckten Etikett gegengeprüft:
2450 g gesendet als weight: 245, gedruckt „2,45 kg".
HTTP 200 bedeutet nicht Erfolg. DPD legt Beanstandungen als faults in eine formal
erfolgreiche Antwort — bis zu drei je Auftrag. Es kann dabei eine Paketscheinnummer ohne PDF
zurückkommen; die Nummer ist dann verbraucht. Der Adapter prüft deshalb beides: keine faults
und ein vorhandenes Dokument. Die verbrauchte Nummer steht in der Fehlermeldung, damit
niemand später eine Sendung sucht, die es nie gab.
Elementreihenfolge ist bindend. Alles ist xsd:sequence; ein Feld an falscher Stelle liefert
keinen Fachfehler, sondern einen rohen cvc-complex-type-Fault. Die Reihenfolge ist im Adapter
fest kodiert, nicht aus einer Wertetabelle erzeugt.
Feldlängen: Schema ≠ Fachregel. name1, street und city erlauben im XSD 50 Zeichen, die
Fachregel nennt 35 — die Schemadoku sagt es selbst. Gekürzt wird auf 35, sonst COMMON_3.
Nur ISO 8859-1 für Adressdaten. „Łódź" wird zu „Lódz", bevor es ins Envelope geht; sonst
lehnt DPD mit COMMON_4 ab. Umlaute und nordische Zeichen bleiben, Latin-1 kennt sie — das ó
überlebt deshalb, nur ł und ź werden umgeschrieben. Daten, die gar kein gültiges UTF-8 sind,
werden vorher als Latin-1 gelesen; sonst fiele das Feld leer aus und DPD meldete einen
Schemafehler statt eines Adressfehlers.
splitByParcel=true, damit das PDF immer unter parcelInformation[].output steht — ohne den
Schalter liegt es je nach Fall an zwei verschiedenen Stellen im Antwortbaum.
Was DPD nicht kann
- Kein Storno. Die deutsche
ShipmentServicehat in allen Fassungen (V3_2, V4_4, V4_5) genau eine Operation:storeOrders. Aus der WSDL ausgelesen, nicht vermutet. Ein Etikett verfällt, wenn das Paket nie eingeliefert wird —supportsCancellation()meldet deshalbfalse, und der Storno-Knopf erscheint bei DPD-Sendungen gar nicht erst. - Kein REST für den Versand. „The shipment service is not available via the REST interface."
Kaufmännisch offen: ob DPD je erzeugtem Etikett oder je eingeliefertem Paket abrechnet. Üblich ist Letzteres, belegt ist es nicht — und weil es kein Storno gibt, ist das der Unterschied zwischen „Fehldruck kostet nichts" und „Fehldruck kostet".
Produktkatalog
CL (DPD CLASSIC) ist der Standardfall. Gültig in V4_5: CL, E830, E12, E18, IE2, PL,
MAIL, MAX. E10 und PM4 gibt es nicht mehr — die standen nur bis V4_4 im Enum.
Etikettenformat (A4 oder A6) im CMS unter Website-Einstellungen → Shop → Versand → DPD.