148 lines
6.1 KiB
Markdown
148 lines
6.1 KiB
Markdown
# DPD integration
|
|
|
|
DPD offers **home/business delivery** and **DPD Pickup parcelshops & lockers**.
|
|
Unlike Packeta, **nothing DPD-specific is scaffolded yet** in this repo, so this
|
|
is a full integration: an optional pickup widget plus the shipment-creation API.
|
|
|
|
---
|
|
|
|
## 1. Get a DPD account & API access
|
|
|
|
1. You need a **business contract** with DPD in your country (e.g. DPD SK:
|
|
<https://www.dpd.com/sk>). Ask your account manager for **API access**.
|
|
2. DPD exposes a few different APIs depending on country/era — confirm which one
|
|
your contract uses:
|
|
- **REST Shipping API** (modern; JSON) — most new integrations.
|
|
- **SOAP "Login/Shipment" web services** (older; still common in CEE).
|
|
- Some markets use the **DPD Geodata / Shop Finder API** for parcelshops.
|
|
3. You'll receive: an **API base URL**, a **delisId / login**, and a
|
|
**password** (the SOAP `login` call returns a short-lived **auth token** you
|
|
reuse on subsequent calls). REST variants use an API key/token directly.
|
|
4. Note your **sender address** and **DPD customer number** — required on every
|
|
shipment.
|
|
|
|
---
|
|
|
|
## 2. Decide which DPD services you offer
|
|
|
|
Create one delivery option per service at **`/admin/shipping`** → "Add delivery
|
|
option":
|
|
|
|
| Option | "Requires pickup point" | Notes |
|
|
|---|---|---|
|
|
| `DPD Home` (classic) | ❌ off | delivered to the address on the order |
|
|
| `DPD Pickup` (parcelshop/locker) | ✅ on | customer must choose a shop/locker |
|
|
|
|
For `DPD Pickup` you need a **point picker** (section 3). For `DPD Home` you can
|
|
skip straight to the API (section 4).
|
|
|
|
---
|
|
|
|
## 3. (Pickup only) Add the DPD parcelshop picker at checkout
|
|
|
|
The checkout already has the generic pickup machinery: when the selected method
|
|
has `requires_pickup_point = true`, the block with hidden `pickup_point_id` /
|
|
`pickup_point_name` shows (`assets/views/shop/checkout.html`, the `x-show=
|
|
"requiresPoint"` block). Today that block only renders the **Packeta** widget
|
|
(guarded by `{% if packeta_api_key %}`) or a text fallback.
|
|
|
|
To support DPD you make that block carrier-aware:
|
|
|
|
1. Pass a `dpd_enabled` / map-widget key flag into the checkout context from
|
|
`src/controllers/checkout.rs` (like `packeta_api_key` is passed today).
|
|
2. In the pickup block, branch on the chosen `carrier` (the Alpine `carrier`
|
|
variable already holds the method `code`) and render DPD's parcelshop map
|
|
widget when a DPD pickup method is selected. DPD provides an embeddable
|
|
**map/widget** (or you query their **Shop Finder API** and render your own
|
|
list); on selection, write the shop id into `pointId` and a human label into
|
|
`pointName` — exactly what the existing hidden inputs expect.
|
|
|
|
No new order fields are needed — `pickup_point_id` / `pickup_point_name` already
|
|
carry the DPD shop id + name.
|
|
|
|
---
|
|
|
|
## 4. Create shipments via the DPD API
|
|
|
|
Do the [shared groundwork](README.md#shared-groundwork-do-this-once-before-any-carriers-api-step)
|
|
first. Set `shipping_methods.carrier = "dpd"` for your DPD options.
|
|
|
|
### Auth (SOAP-style, common in CEE)
|
|
|
|
```bash
|
|
DPD_API_BASE=https://api.dpd.sk # from your account manager
|
|
DPD_LOGIN=your_delis_login
|
|
DPD_PASSWORD=your_password
|
|
DPD_CUSTOMER_NUMBER=your_customer_no
|
|
```
|
|
Add matching lines under `settings:` in `config/*.yaml`:
|
|
```yaml
|
|
dpd_api_base: {{ get_env(name="DPD_API_BASE", default="") }}
|
|
dpd_login: {{ get_env(name="DPD_LOGIN", default="") }}
|
|
dpd_password: {{ get_env(name="DPD_PASSWORD", default="") }}
|
|
dpd_customer_number: {{ get_env(name="DPD_CUSTOMER_NUMBER", default="") }}
|
|
```
|
|
|
|
### Flow
|
|
|
|
1. **Login** → `LoginService.getAuth(delisId, password)` returns an **auth
|
|
token** (valid for a while; cache it).
|
|
2. **Create shipment** → `ShipmentService.storeOrders(...)` with the auth token,
|
|
recipient address (or parcelshop id for Pickup), parcel weight, references
|
|
(your `order_number`), and COD amount if cash-on-delivery. Returns a
|
|
**parcel number (MPS id)** = your tracking number, plus label data.
|
|
3. **Label** → the same call (or `getParcelLabels`) returns a **PDF/ZPL label**;
|
|
store or print it.
|
|
|
|
### Client sketch (`src/integrations/dpd.rs`)
|
|
|
|
```rust
|
|
use loco_rs::prelude::*;
|
|
use crate::shared::settings;
|
|
|
|
async fn auth_token(ctx: &AppContext) -> Result<String> {
|
|
let base = settings::get(ctx, "dpd_api_base").unwrap_or_default();
|
|
let login = settings::get(ctx, "dpd_login").unwrap_or_default();
|
|
let pass = settings::get(ctx, "dpd_password").unwrap_or_default();
|
|
// POST login → parse token from response. Cache it (e.g. in-memory w/ expiry).
|
|
todo!()
|
|
}
|
|
|
|
pub async fn create_shipment(ctx: &AppContext, req: super::ShipmentRequest<'_>)
|
|
-> Result<super::ShipmentResult>
|
|
{
|
|
let token = auth_token(ctx).await?;
|
|
let customer = settings::get(ctx, "dpd_customer_number").unwrap_or_default();
|
|
// Build storeOrders payload:
|
|
// - product: "CL" (classic/home) or "Pickup" + parcelShopId = req.pickup_point_id
|
|
// - recipient: req.recipient_name / address / city / zip / country / phone
|
|
// - cod: req.cod_cents (set cash-on-delivery service if > 0)
|
|
// - reference: req.order_number
|
|
// POST to {base}/shipment ... with `token`.
|
|
todo!("parse parcel number + label into ShipmentResult")
|
|
}
|
|
```
|
|
|
|
Wire it into the admin "Create shipment" action for `carrier == "dpd"` orders.
|
|
|
|
---
|
|
|
|
## 5. Testing
|
|
|
|
- DPD provides a **test/integration environment** (separate base URL +
|
|
credentials) — get it from your account manager. Validate login + one
|
|
shipment there first.
|
|
- Confirm the returned parcel number tracks on
|
|
`https://tracking.dpd.de/...` / your local DPD tracking site.
|
|
|
|
## 6. Go-live checklist
|
|
|
|
- [ ] DPD business contract + API credentials obtained
|
|
- [ ] `DPD_*` env vars set; matching `settings:` lines added to `config/production.yaml`
|
|
- [ ] Delivery option(s) created in `/admin/shipping` (`DPD Home` and/or `DPD Pickup`)
|
|
- [ ] `carrier = "dpd"` set on those methods (via the shared `carrier` column)
|
|
- [ ] (Pickup) parcelshop picker rendered in checkout for DPD methods
|
|
- [ ] `src/integrations/dpd.rs` implemented; login token caching working
|
|
- [ ] Test shipment in DPD test env → tracking number stored on order
|
|
- [ ] Switched base URL/credentials from test to production
|