Files
kompress_eshop/docs/integrations/README.md
2026-06-17 17:27:19 +02:00

6.2 KiB

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:

    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:

    cargo loco generate migration add_carrier_to_shipping_methods carrier:string
    

    Values: none (manual, the default), packeta, dpd, dhl. Then add a <select name="carrier"> to the add-form in assets/views/admin/shipping/index.html and persist it in admin_shipping::create.

  4. Store the tracking number / label on the order. Generate:

    cargo loco generate migration add_tracking_to_orders \
      tracking_number:string shipment_id:string label_url:string
    
  5. A "Create shipment" admin action. In the admin order detail (src/controllers/admin_orders.rs), add a button/handler that: looks up the order's carrier_code → finds the shipping_methods.carrier → calls the matching integrations::<carrier>::create_shipment(...) → saves tracking_number + label_url back onto the order. Optionally do this automatically in orders::place, but a manual admin trigger is safer to start (you can review the order first).

After the groundwork, each carrier file implements one async function roughly like:

pub struct ShipmentRequest<'a> {
    pub order_number: &'a str,
    pub recipient_name: &'a str,
    pub email: &'a str,
    pub phone: Option<&'a str>,
    pub address: Option<&'a str>,
    pub city: Option<&'a str>,
    pub zip: Option<&'a str>,
    pub country: Option<&'a str>,
    pub pickup_point_id: Option<&'a str>,
    pub cod_cents: i64,         // 0 unless cash-on-delivery
    pub currency: &'a str,
    pub weight_grams: i32,
}

pub struct ShipmentResult {
    pub shipment_id: String,
    pub tracking_number: String,
    pub label_url: Option<String>,
}

pub async fn create_shipment(ctx: &AppContext, req: ShipmentRequest<'_>) -> loco_rs::Result<ShipmentResult> { ... }
  • packeta.md — Packeta / Zásilkovna (pickup points + home, SK/CZ-centric)
  • dpd.md — DPD (home delivery + Pickup parcelshops)
  • dhl.md — DHL (international, Parcel/Express)

⚠️ Carrier APIs change. Treat the endpoint names, field names, and auth details here as a map of the moving parts, and confirm exact request formats against each carrier's current developer portal before coding.