Skip to main content
Version: 2.0.0

Developer / API

DPD API client

Yui\Dpd\services\DpdClient wraps the DPD REST API (SK/CZ region) over Basic Auth, switching between production (https://api.dpd.sk/v1) and sandbox (https://api.test.dpd.sk/v1) — or a custom URL override from settings — based on the Sandbox mode switch:

MethodEndpointPurpose
createParcel(array $payload)POST /parcelsRegister a new shipment.
downloadLabel(string $parcelId, string $format = 'pdf')GET /parcels/{id}/labelFetch the raw label PDF.
getTracking(string $parcelId)GET /parcels/{id}/trackingFetch tracking events for a parcel.
getParcel(string $parcelId)GET /parcels/{id}Fetch parcel details.
getPickupPoints(array $query)GET /parcelshopsList ParcelShop pickup points.
testConnection()GET /parcelshops (limit 1)Used by Test Connection in Settings.

Every request/response error is normalized into an InvalidConfigException with a human-readable message (DPD's errors/error/message/detail fields are extracted when present) and logged through StorePlugin::error() under dpd:client:*.

Shipment creation and order automation

Yui\Dpd\services\ShipmentService::syncOrder() builds the DPD parcel payload (buildParcelPayload() — recipient from the order's shipping address, sender from the configured pickup address, weight from the order or the Default parcel weight fallback, and a cash-on-delivery amount when the order isn't paid yet), calls DpdClient::createParcel(), and persists the result as a Yui\Dpd\records\ShipmentRecord (table {{%dpd_shipments}}). It then fetches and caches the label via LabelService::fetchAndCacheLabel() and, if the storefront plugin is present, adds an order comment noting the parcel number.

Automatic sync is wired in Plugin::init(): on OrdersService::EVENT_AFTER_ORDER_STATUS_CHANGE, if the order's new status matches Auto-create shipment on status and ShipmentService::shouldSyncOrder() (order uses the dpd shipping method) and hasReachedLabelLimit() allow it, syncOrder() runs and the result is logged under dpd:order-sync.

Labels are stored on disk under storage/dpd/labels/ (path recorded on the ShipmentRecord as label_file_path), so getLabelForShipment() serves from cache before hitting the API again. TrackingService::refreshStatusForShipment() re-pulls the DPD tracking response and updates the stored status; getTimelineForShipment() normalizes DPD's tracking events (or falls back to the current status alone) into a display-ready timeline.

Checkout pickup-point widget

When Enable ParcelShop pickup points is on and DPD passes checkRestrictions(), Plugin::init() renders src/templates/shipping-code.twig into the storefront's shared remote-checkout-scripts view hook. The template defines an Alpine component, initDpdPickupPoints(), which:

  1. Fetches pickup points from the anonymous site-facing endpoint on init and on ZIP-code search, using the plugin handle (dpd) as the country query parameter — the template sets DPD_COUNTRY = handle is defined ? handle : 'SK', and Plugin::init() always renders it with handle => self::PLUGIN_HANDLE. The Default country for pickup points setting (Settings::$pickupPointCountry) is read only by PickupPointService::getPickupPointsForCheckout(), which nothing in the plugin currently calls, so it has no effect on this request.
  2. Renders the list, letting the customer pick one.
  3. On selection, dispatches two window CustomEvents: dpd-shipping-selected (detail: the raw point) and shipping-option-data (detail: { method: 'dpd', pickupPoint: point }), which the checkout's shipping-option handling consumes.

Plugin::shippingDetails() also exposes an action block (trigger: 'action-dpd', init: 'initDpdPickupPoints()') so the shipping-methods module knows to render the same component inline in the method's own action area, not only via the checkout-scripts hook.

Site-facing endpoint

dpd/api/pickup-points (anonymous, GET) is the only endpoint DPD exposes to the storefront:

GET /index.php?p=dpd/api/pickup-points&country=SK&zipCode=83101
{ "success": true, "points": [
{ "id": "12345", "name": "ParcelShop Example", "street": "...", "city": "...",
"zipCode": "83101", "country": "SK", "latitude": 48.15, "longitude": 17.11,
"openingHours": "...", "type": "PARCELSHOP", "raw": { "...": "..." } }
] }

Results are cached for one hour per country/ZIP combination (PickupPointService::CACHE_DURATION) via Craft's application cache. On an API failure, the endpoint returns { "success": false, "points": [] } rather than surfacing the exception to anonymous callers.

The endpoint itself accepts any country value, but the checkout widget documented above always calls it with country=dpd (the plugin handle), not a real ISO country code — this looks like an unintended bug rather than by design, since a pickupPointCountry setting exists specifically to configure this and is simply never wired up to the checkout call.

Shipping method registration

Like every yStore carrier plugin, Plugin registers itself with the shared ShippingMethodRegistry on Plugins::EVENT_AFTER_LOAD_PLUGINS. Plugin::shippingDetails() is the method metadata consumed by the shipping-methods module (price, restrictions, the pickup-point action config described above); Plugin::checkRestrictions() is the single source of truth for whether the method is currently offered (enabled flag + visibility window).

CP routes worth knowing about

RoutePurpose
dpd/dashboardShipment statistics dashboard
dpd/dashboard/save-layoutPersists dashboard widget layout
dpd/settingsPlugin/shipping method settings form
dpd/api/test-connectionTests the configured API credentials
dpd/shipmentsShipment list (status/search filters, CSV export)
dpd/shipments/{id}Shipment detail: parcel info, delivery address, tracking timeline, raw payload/response
dpd/shipments/create/{orderId}Create a shipment for an order
dpd/shipments/refresh/{id}Refresh tracking status from DPD
dpd/shipments/resync/{id}Create a new shipment/label for the same order
dpd/shipments/{id}/labelView or download (?download=1) the stored label PDF
dpd/shipments/delete-label/{id}Delete the stored label file (keeps the DPD parcel)
dpd/licenseLicense management panel (activate/redeem/revoke/copy token)
dpd/api/pickup-pointsAnonymous site-facing endpoint used by the checkout widget

All routes above except dpd/api/pickup-points require a CP request; dpd/api/pickup-points is the only one reachable from the storefront.