6.2 KiB
DHL integration
DHL is best for home delivery and international/express shipments. Like DPD, nothing DHL-specific is scaffolded here. DHL is mostly an address (home) delivery carrier — pickup points exist (DHL ServicePoint / Packstation, mostly DE) but most shops use DHL for door-to-door, so you can usually skip the pickup widget entirely.
DHL has several separate APIs behind one developer portal (https://developer.dhl.com). Pick the one that matches your service:
- DHL Parcel DE (Post & Parcel Germany) — Shipping API for German domestic parcels / Packstation.
- DHL eCommerce (Parcel) APIs for various countries.
- DHL Express — MyDHL API for international express. Confirm which your contract covers before coding.
1. Get DHL API access
- Create an account on the DHL Developer Portal: https://developer.dhl.com.
- Create an app and subscribe it to the specific API you need (e.g. "Shipping API" or "MyDHL API"). You receive an API key (client id) + secret.
- Separately you need a DHL business/customer account (EKP / account number, billing number) — the developer key alone can't bill shipments. Link your business account credentials to the app.
- Most DHL APIs use OAuth2 client-credentials: you exchange key+secret for a short-lived Bearer token, then call the shipping endpoints with it. (Some older endpoints use Basic auth — check your API's docs.)
2. Create the delivery option
At /admin/shipping → "Add delivery option":
- Name: e.g.
DHLorDHL Express (international) - Price: your fee
- Requires pickup point: ❌ off for normal home delivery (turn ✅ on only if you specifically offer DHL Packstation/ServicePoint and build a picker — see section 4)
- ✅ Active
With the option active, customers can already choose DHL and you can create the label manually in DHL Business Customer Portal. The API (section 3) automates that.
3. Create shipments via the DHL API
Do the shared groundwork
first. Set shipping_methods.carrier = "dhl" for your DHL options.
Credentials
DHL_API_KEY=your_client_id
DHL_API_SECRET=your_client_secret
DHL_ACCOUNT_NUMBER=your_ekp_or_billing_number
DHL_API_BASE=https://api-eu.dhl.com # depends on the specific API
Add matching lines under settings: in config/*.yaml:
dhl_api_key: {{ get_env(name="DHL_API_KEY", default="") }}
dhl_api_secret: {{ get_env(name="DHL_API_SECRET", default="") }}
dhl_account_number: {{ get_env(name="DHL_ACCOUNT_NUMBER", default="") }}
dhl_api_base: {{ get_env(name="DHL_API_BASE", default="") }}
Flow (OAuth2 + create shipment)
- Token →
POST {base}/.../tokenwithgrant_type=client_credentials+ key/secret →access_token(Bearer; cache until it expires). - Create shipment →
POSTthe shipment-orders endpoint with the Bearer token: shipper (your account/EKP), consignee (recipient from the order), product code (domestic vs international/express), weight, customs data for non-EU, and references (order_number). COD is a value-added service if you offer it. - Label → the response includes a tracking/shipment number and a label (PDF/base64). Store/print it.
Client sketch (src/integrations/dhl.rs)
use loco_rs::prelude::*;
use crate::shared::settings;
async fn bearer(ctx: &AppContext) -> Result<String> {
let base = settings::get(ctx, "dhl_api_base").unwrap_or_default();
let key = settings::get(ctx, "dhl_api_key").unwrap_or_default();
let secret = settings::get(ctx, "dhl_api_secret").unwrap_or_default();
// POST client_credentials → access_token; cache with expiry.
todo!()
}
pub async fn create_shipment(ctx: &AppContext, req: super::ShipmentRequest<'_>)
-> Result<super::ShipmentResult>
{
let token = bearer(ctx).await?;
let account = settings::get(ctx, "dhl_account_number").unwrap_or_default();
// Build shipment JSON:
// - shipper: your account address (account = EKP/billing number)
// - consignee: req.recipient_name / address / city / zip / country
// - details: weight, product code (domestic / express), currency
// - refs: req.order_number
// - for international: customs (HS codes, declared value, contents)
// POST {base}/.../shipments with Authorization: Bearer {token}
todo!("parse tracking number + label into ShipmentResult")
}
Wire into the admin "Create shipment" action for carrier == "dhl" orders.
🌍 International note: for shipments outside the EU customs union you must send customs/commodity data (HS codes, declared value, item descriptions). Your
order_itemsonly store name + price today — if you ship internationally you'll likely add a customs description/HS-code field to products.
4. (Optional) DHL pickup points
If you offer Packstation / ServicePoint, set "Requires pickup point" ✅ on
that delivery option and render DHL's Location Finder (a separate DHL API)
in the checkout pickup block (the x-show="requiresPoint" section of
assets/views/shop/checkout.html), writing the chosen locker id into the
existing hidden pickup_point_id / pickup_point_name fields. For Packstation
you also need the recipient's DHL post number — an extra field most shops
avoid unless targeting Germany.
5. Testing
- DHL provides a sandbox environment per API (separate base URL + test credentials) on the developer portal. Get a token and create one test shipment there before production.
- Validate the tracking number on https://www.dhl.com/track.
6. Go-live checklist
- DHL developer app created + subscribed to the right API
- DHL business account (EKP/billing number) linked
DHL_*env vars set; matchingsettings:lines added toconfig/production.yaml- Delivery option created in
/admin/shipping;carrier = "dhl"set src/integrations/dhl.rsimplemented; OAuth token caching working- (International) customs data available on products/items
- Test shipment in DHL sandbox → tracking number stored on order
- Switched from sandbox to production base URL/credentials