Files
kompress_eshop/docs/integrations/dhl.md
2026-06-17 16:15:22 +02:00

151 lines
6.2 KiB
Markdown

# 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
1. Create an account on the **DHL Developer Portal**: <https://developer.dhl.com>.
2. 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**.
3. 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.
4. 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. `DHL` or `DHL 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](README.md#shared-groundwork-do-this-once-before-any-carriers-api-step)
first. Set `shipping_methods.carrier = "dhl"` for your DHL options.
### Credentials
```bash
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`:
```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)
1. **Token**`POST {base}/.../token` with `grant_type=client_credentials` +
key/secret → `access_token` (Bearer; cache until it expires).
2. **Create shipment**`POST` the 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.
3. **Label** → the response includes a **tracking/shipment number** and a
**label** (PDF/base64). Store/print it.
### Client sketch (`src/integrations/dhl.rs`)
```rust
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_items` only 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; matching `settings:` lines added to `config/production.yaml`
- [ ] Delivery option created in `/admin/shipping`; `carrier = "dhl"` set
- [ ] `src/integrations/dhl.rs` implemented; 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