# Carrier integrations This eshop manages **delivery options** as plain rows in the `shipping_methods` table (admin UI at `/admin/shipping` — add / edit price + toggle / remove). A delivery option is just a name, a price, and two flags. **None of that talks to a carrier yet** — it only decides what the customer can pick and how much they pay. Integrating a real carrier (Packeta, DPD, DHL) means wiring two *separate* concerns on top of an existing delivery option: 1. **Pickup-point selection** (checkout, browser-side) — only for carriers that deliver to pickup points / lockers. The customer picks a point via the carrier's JS map widget; the chosen id + name land in the order. 2. **Shipment creation** (server-side, after the order is placed) — you call the carrier's HTTP API to register the parcel, then store the returned tracking number and print the label. These are independent: you can ship to a Packeta pickup point manually (no API) just by enabling the pickup widget, and you can create DHL labels via API for a home-delivery option that has no pickup point at all. > ❗ This is **not** a many-to-many / database relationship between your tables. > A carrier is an **external HTTP API** you call from the server. The only > schema you add is a few columns (which carrier a method maps to; a tracking > number on the order) — see "Shared groundwork" below. ## What already exists in the codebase | Piece | Where | Status | |---|---|---| | Delivery option CRUD | `src/controllers/admin_shipping.rs`, `assets/views/admin/shipping/index.html` | ✅ done | | `shipping_methods` table (`code`, `name`, `price_cents`, `requires_pickup_point`, `enabled`, `position`) | `migration/.../m20260616_150755_shipping_methods.rs` | ✅ done | | Carrier choice + pickup fields on checkout | `assets/views/shop/checkout.html` (`carrier_code`, `pickup_point_id`, `pickup_point_name`) | ✅ done | | Order stores carrier + pickup point | `orders` table (`carrier_code`, `carrier_name`, `pickup_point_id`, `pickup_point_name`, `shipping_cents`) | ✅ done | | Settings lookup | `src/shared/settings.rs` → reads `settings.*` from `config/*.yaml` | ✅ done | | Packeta pickup-point widget | `assets/views/shop/checkout.html` (loads when `packeta_api_key` set) | ✅ scaffolded | | `shipping_methods.carrier` (which API a method maps to) | `migration/.../m20260617_000001_*` + admin add-form dropdown | ✅ done | | Tracking / shipment id / label on order | `migration/.../m20260617_000002_*` (`orders.tracking_number`, `shipment_id`, `label_url`) | ✅ done | | Manual "Send to carrier" admin action | `src/controllers/admin_orders.rs` (`ship`), order detail page | ✅ done | | Carrier client dispatch | `src/integrations/` (`create_shipment`) | ✅ done | | Packeta shipment client | `src/integrations/packeta.rs` (real `createPacket`) | ✅ done | | DPD / DHL shipment clients | `src/integrations/dpd.rs`, `dhl.rs` | 🟡 credential-guarded stub — fill in HTTP call per contract | **Shipments are created only when an admin clicks "Send to carrier" on the order page** — never automatically at checkout. Packeta is wired end-to-end (needs just the API password + sender label). DPD/DHL run through the same flow but their HTTP body must be finalised against your contract (clearly marked TODOs in each file). ## Shared groundwork (do this once, before any carrier's API step) The pickup-widget half needs nothing new. The **shipment-creation** half needs: 1. **An HTTP client dependency.** Add to `Cargo.toml`: ```toml reqwest = { version = "0.12", features = ["json"] } ``` (Loco already pulls `tokio`/`serde`/`serde_json`.) 2. **A place for carrier clients.** Create `src/integrations/mod.rs` and a file per carrier (`packeta.rs`, `dpd.rs`, `dhl.rs`). Register `pub mod integrations;` in `src/lib.rs` (next to `pub mod controllers;` etc.). 3. **Map a delivery option to a carrier.** Add a `carrier` column to `shipping_methods` so each admin-created option knows which API (if any) to call. Generate the migration: ```bash cargo loco generate migration add_carrier_to_shipping_methods carrier:string ``` Values: `none` (manual, the default), `packeta`, `dpd`, `dhl`. Then add a `