6.5 KiB
Packeta (Zásilkovna) integration
Packeta delivers mainly to pickup points and Z-BOX lockers (plus home delivery in some regions). It's the most common choice for SK/CZ eshops. This repo is already scaffolded for Packeta's pickup-point picker — you mostly need an API key to switch it on. Shipment creation via API is extra, optional work.
1. Get a Packeta account & keys
- Register a client account at https://client.packeta.com (Zásilkovna / Packeta). For SK: https://www.packeta.sk.
- In the client portal open Client support → API / Nastavenia API (or
"Integrations"). You get two different secrets — don't mix them up:
- Web/Widget API key — public-ish key used by the browser pickup-point
widget (
Packeta.Widget.pick). This is the one this repo already uses. - API password (REST/SOAP) — secret server key used to create packets (shipments). Never expose this to the browser.
- Web/Widget API key — public-ish key used by the browser pickup-point
widget (
- (For real shipping) configure your sender/pickup address and label format in the portal.
2. Activate the pickup-point picker (already built)
The checkout template already loads the widget and wires the chosen point into
the order whenever packeta_api_key is non-empty
(assets/views/shop/checkout.html):
- loads
https://widget.packeta.com/v6/www/js/library.js Packeta.Widget.pick(packetaKey, point => …)fills hiddenpickup_point_id+pickup_point_name- if the key is empty it falls back to a plain text field
So to turn it on:
a) Set the Web/Widget API key
Set the env var (read by config/development.yaml / production.yaml →
settings.packeta_api_key, exposed via src/shared/settings.rs):
# .env (development) or your production environment
PACKETA_API_KEY=your_web_widget_api_key
config/development.yaml already contains:
settings:
packeta_api_key: {{ get_env(name="PACKETA_API_KEY", default="") }}
For production, add the same line under settings: in config/production.yaml
(it isn't there yet).
b) Create a Packeta delivery option in the admin
Go to /admin/shipping → "Add delivery option":
- Name: e.g.
Packeta – pickup point - Price: your fee (e.g.
2.90) - ✅ Requires pickup point ← this makes the picker appear at checkout
- ✅ Active
The auto-generated code will be packeta-pickup-point (or similar). Customers
now see the option, click "Choose pickup point", pick on the map, and the order
stores pickup_point_id + pickup_point_name.
At this point you have a working Packeta flow — you read the pickup point on
the order in /admin/orders and create the parcel manually in the Packeta
portal. Many small shops stop here.
3. (Optional) Create shipments via API
Automate "register the parcel + get tracking + print label". Do the
shared groundwork
first (HTTP client, integrations module, carrier column, tracking columns).
Endpoint & auth
- Packeta REST API base:
https://www.zasilkovna.cz/api/rest(SOAP also available athttp://www.zasilkovna.cz/api/soap.wsdl). - Auth = your API password (the server secret from step 1), sent in the request body, not the widget key.
- Key operation:
createPacket. You send sender id, recipient name/email/phone, the chosen pickup point id (addressId), value, weight, and COD amount; you receive a packet id + barcode (tracking). A separatepacketLabelPdfcall returns the label PDF.
Store the secret
PACKETA_API_PASSWORD=your_secret_api_password
Add to config/*.yaml under settings::
packeta_api_password: {{ get_env(name="PACKETA_API_PASSWORD", default="") }}
Client sketch (src/integrations/packeta.rs)
use loco_rs::prelude::*;
use crate::shared::settings;
// createPacket accepts XML; serde_json works for the JSON REST variant.
pub async fn create_shipment(ctx: &AppContext, req: super::ShipmentRequest<'_>)
-> Result<super::ShipmentResult>
{
let api_password = settings::get(ctx, "packeta_api_password")
.ok_or_else(|| Error::string("packeta_api_password not configured"))?;
// Packeta's createPacket is XML/SOAP-ish; build the body per their docs.
// number = your order_number
// name/surname = recipient
// addressId = req.pickup_point_id (the chosen point)
// cod = req.cod_cents / 100 (0 if not COD)
// value = goods value
// eshop = your sender label/id from the portal
let body = format!(r#"<createPacket>
<apiPassword>{api_password}</apiPassword>
<packetAttributes>
<number>{}</number>
<name>{}</name>
<email>{}</email>
<addressId>{}</addressId>
<cod>{}</cod>
<value>{}</value>
<weight>{}</weight>
<eshop>YOUR_SENDER_LABEL</eshop>
</packetAttributes>
</createPacket>"#,
req.order_number, req.recipient_name, req.email,
req.pickup_point_id.unwrap_or(""),
req.cod_cents as f64 / 100.0,
req.cod_cents as f64 / 100.0,
req.weight_grams);
let resp = reqwest::Client::new()
.post("https://www.zasilkovna.cz/api/rest")
.body(body)
.send().await.map_err(|e| Error::string(&e.to_string()))?
.text().await.map_err(|e| Error::string(&e.to_string()))?;
// Parse <id> (packet id) and <barcode> (tracking) out of the XML response.
// Then optionally call packetLabelPdf with that id to fetch the label.
todo!("parse resp into ShipmentResult")
}
Then call it from your admin "Create shipment" action for orders whose
shipping_methods.carrier == "packeta", and save tracking_number /
shipment_id back on the order.
4. Testing
- Use the Packeta sandbox/staging portal if your account offers one, or a
test API password. Verify
createPacketreturns a packet id before going live. - Track the parcel at
https://tracking.packeta.com/...using the returned barcode.
5. Go-live checklist
PACKETA_API_KEY(widget) set in production envpacketa_api_keyline added undersettings:inconfig/production.yaml- Packeta delivery option created in
/admin/shippingwith Requires pickup point ✅ - (If using API)
PACKETA_API_PASSWORDset +src/integrations/packeta.rsimplemented - Sender address & label format configured in the Packeta portal
- Test order → pickup point saved on order → (API) tracking number stored