# Felvétel és kézbesítés (saját flotta)

Ez az útmutató egy vállalkozói fiók helyi kézbesítési API-ját írja le: olyan rendeléseket, amelyeket a vállalkozás saját sofőrjei kézbesítenek egy címzettnek (`type` `D`), vagy vesznek fel egy feladótól (`type` `P`). Egyetlen végpontkészlet kér árajánlatot, hoz létre rendelést, nyomtat címkét, követ és mond le mindkét megállótípusnál, a webhookok pedig minden változást jelentenek az Ön rendszerének. Rendeléskezelő rendszerek, ERP-k és webáruházak fejlesztőinek szól, amelyek a vállalkozás saját flottájának adnak ki munkát.

## 1. Mit építhet

Az alábbi példák egyetlen vállalkozást követnek: a **Farine & Fils** pékáru-nagykereskedőt, amelynek raktára a 2200 Rue Cohen, Saint-Laurent, QC (H4R 2N6) címen található, nagykereskedelmi rendeléseket szállít Montreal szigetének egész területén, és összegyűjti a vevők által visszaküldött üres kenyeresládákat. Egy tipikus kézbesítés egy 12 kg-os, 60 × 40 × 30 cm-es ládaköteg a Café Lumière részére (5400 Avenue du Parc, Montréal, H2V 4G7), `WHS-20931` nagykereskedelmi rendelésszámmal. Egy tipikus felvétel egy 4 kg-os üres ládaköteg az Épicerie Wellington címéről (4100 Rue Wellington, Verdun, H4G 1V5), `CRT-20931` hivatkozással.

- **Az ERP-ből diszpécserhez küldött nagykereskedelmi rendelések.** Minden visszaigazolt nagykereskedelmi rendelésből kézbesítési rendelés lesz a kávézó reggeli kézbesítési időablakával, és az ERP a visszaadott követési számot a rendelési sorhoz tárolja.
- **Ládavisszagyűjtési felvételek.** Amikor egy vevő üres ládákat jelez, az ERP felvételi rendelést hoz létre a vevő címére, és egy sofőr a következő útvonalon összegyűjti a ládákat.
- **Címkenyomtatás a raktárban.** Az ERP letölti minden rendelés címke-PDF-jét, és a rakodórámpánál kinyomtatja, így minden ládakötegen ott van a követési vonalkód.
- **Vevői portál élő állapottal.** Minden kávézó látja kézbesítéseinek és felvételeinek állapotát a kézbesítési igazolással együtt, amelyet lekérdezés helyett webhookok frissítenek.

## 2. Mire vonatkozik ez az útmutató

Akkor használja ezt az útmutatót, ha a rendelést a vállalkozás saját sofőrjei szállítják: kézbesítések a raktárból és felvételek a vevő címéről, egyenként vagy kötegekben létrehozva a `/api/v1/client/...` és `/api/v1/orders/...` végpontokon keresztül.

Új integrációkhoz az Uniorder (`/api/v1/uniorder/...`) az ajánlott egyetlen belépési pont: ugyanezeket a saját flottás kézbesítéseket kínálja egyetlen API-n keresztül, a fuvarozói címkékkel együtt, egyetlen árajánlatból. Az áttekintést az **Uniorder: egy API minden küldeményhez**, a lépésenkénti kéréseket az **Árajánlat és rendelés egy folyamatban** útmutató tartalmazza. Az ebben az útmutatóban szereplő végpontok változatlanul elérhetők maradnak az ezekre épülő integrációk számára.

A **Fuvarozói címkék** útmutatót akkor használja, ha a csomagot külső fuvarozó szállítja a platformon keresztül vásárolt címkével. A **Szállítási szolgáltatások** útmutatót az ügyfélfiók által egy vállalkozás szolgáltatásaira foglalt rendelésekhez, a **Tárolás és kiszállítás** útmutatót pedig a raktárban tárolt és kérésre kiszállított árukhoz használja; az Uniorder erre a kettőre nem vonatkozik.

## 3. Mielőtt elkezdené

- **Fiók.** Használjon API-jogosultsággal rendelkező vállalkozói (ügyfél-) fiókot vagy a vállalkozás munkavállalói fiókját. A rendelések létrehozásához ezenfelül rendelésleadási jogosultság is szükséges; enélkül a `POST /api/v1/client/orderCreate` `401`-et ad vissza.
- **Szolgáltatási terület.** A kézbesítési vagy felvételi címnek a vállalkozás egy aktív régióján belül kell lennie. Tesztekhez használjon területen belüli címeket, például az ebben az útmutatóban szereplőket.
- **Tesztadatok.** Használjon teszthivatkozásokat, például `WHS-20931` és `CRT-20931`, és a végén mondja le a tesztrendeléseket (12. lépés).
- **Tokenek.** A hozzáférési tokent a szerveréről kérje le, és ott is tárolja. Soha ne küldje el böngészőnek vagy mobilalkalmazásnak.
- **Helyőrzők.** Cserélje a `YOUR_HOST` értéket a környezete API-gazdagépére, az `ACCESS_TOKEN` értéket pedig a 4. lépésben kapott tokenre.
- **Mértékegységek.** `weight_unit`: `1` g, `2` kg, `3` oz, `4` lb. `dimension_unit`: `1` mm, `2` cm, `3` m, `4` in. Mindkettő alapértéke `1`.

## 4. Hitelesítés

Az útmutató minden hívása – a nyilvános követés kivételével – a vállalkozói fiók nevében történik. Jelentkezzen be egyszer a szerveréről, tárolja a visszaadott tokent, és küldje el minden kérésben.

**REST:** `POST /api/v1/user/login` — [REST kézikönyv](/api/documentation#/paths/v1-user-login/post)

```bash
curl -X POST https://YOUR_HOST/api/v1/user/login \
  -H "Content-Type: application/json" \
  -d '{"email":"dispatch@farineetfils.example","password":"your_password"}'
```

```json
{
  "result": true,
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9..."
}
```

- `access_token`: helyezze el minden további kérés fejlécében:

```
Authorization: Bearer ACCESS_TOKEN
```

A GraphQL ugyanezt a fejlécet használja a `POST /api/graphql` híváson.

**GraphQL:** `userLogin` ([GraphQL kézikönyv](/api/graphql/documentation#/user/userLogin))

**Ellenőrzés:** a bejelentkezés `access_token` értéket ad vissza. Az e token nélküli további kérések `401`-et adnak vissza.

## 5. Árajánlat kézbesítésre vagy felvételre (opcionális)

Az árajánlat a rendelés létrejötte előtt mutatja meg egy megálló árát, például a kézbesítési díj nagykereskedelmi számlán való feltüntetéséhez. Semmit nem hoz létre, és a rendelés létrehozásához nem szükséges előzetes árajánlat. A `type` értéke legyen `D` (kézbesítés) vagy `P` (felvétel); a `to_postcode` a megálló irányítószáma.

**REST:** `POST /api/v1/orders/rate` — [REST kézikönyv](/api/documentation#/paths/v1-orders-rate/post)

```bash
curl -X POST https://YOUR_HOST/api/v1/orders/rate \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "D",
    "from_postcode": "H4R2N6",
    "from_country": "CA",
    "to_postcode": "H2V4G7",
    "to_country": "CA",
    "packages": [{
      "weight": 12,
      "weight_unit": 2,
      "length": 60,
      "width": 40,
      "height": 30,
      "dimension_unit": 2
    }]
  }'
```

```json
{
  "result": true,
  "shipping_price": "18.50",
  "currency": "CAD",
  "price_details": {
    "shipping_fee": "18.50",
    "tax_details": [
      { "tax_name": "GST", "tax_rate": "5.00", "tax": "0.93" },
      { "tax_name": "QST", "tax_rate": "9.975", "tax": "1.85" }
    ]
  }
}
```

- `shipping_price`: a megálló adó nélküli ára. Az üres ár azt jelenti, hogy az irányítószám nincs aktív régióban, vagy a díjtáblázatban nincs rá vonatkozó sor.
- `price_details.tax_details`: a rendelést terhelő adók; tüntesse fel őket a számlasoron.
- `currency`: a válaszban szereplő összes összeg pénzneme.

A ládafelvétel árajánlatához küldje el ugyanezt a kérést `"type": "P"`, `"to_postcode": "H4G1V5"` értékkel, valamint a ládaköteg súlyával és méretével.

**GraphQL:** `ordersRate` ([GraphQL kézikönyv](/api/graphql/documentation#/orders/ordersRate)). Az eredmény JSON skalár, és nem fogad selection setet.

```graphql
mutation {
  ordersRate(
    type: "P"
    from_postcode: "H4R2N6"
    from_country: "CA"
    to_postcode: "H4G1V5"
    to_country: "CA"
    packages: [{ weight: 4, weight_unit: 2, length: 60, width: 40, height: 30, dimension_unit: 2 }]
  )
}
```

**Ellenőrzés:** a `result` értéke `true`, a `shipping_price` pedig szám mind `type` `D`, mind `type` `P` esetén. A rendelés létrehozása nem függ ettől a lépéstől.

## 6. Kézbesítési rendelés létrehozása

Minden visszaigazolt nagykereskedelmi rendelésből egy kézbesítési rendelés lesz. Az ERP a visszaadott `id` és `tracking_number` értéket a rendelési sorához tárolja; minden későbbi hívás ezek egyikét használja.

**REST:** `POST /api/v1/client/orderCreate` — [REST kézikönyv](/api/documentation#/paths/v1-client-orderCreate/post)

Küldjön nagykereskedelmi rendelésenként egyedi `Idempotency-Key` fejlécet, hogy egy időtúllépés utáni újrapróbálkozás ne hozhasson létre második rendelést.

```bash
curl -X POST https://YOUR_HOST/api/v1/client/orderCreate \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: whs-20931-delivery" \
  -d '{
    "type": "D",
    "need_pick_up": 0,
    "ref": "WHS-20931",
    "name": "Marie Tremblay",
    "company_name": "Café Lumière",
    "telephone": "5145550142",
    "email": "commandes@cafelumiere.example",
    "address_1": "5400 Avenue du Parc",
    "city": "Montréal",
    "province": "QC",
    "postcode": "H2V4G7",
    "country": "Canada",
    "schedule_date": "2026-10-02",
    "time_window_start": "2026-10-02 06:00:00",
    "time_window_end": "2026-10-02 09:00:00",
    "packages": 1,
    "packagesDetail": [{
      "ref": "WHS-20931-1",
      "weight": 12,
      "weight_unit": 2,
      "length": 60,
      "width": 40,
      "height": 30,
      "dimension_unit": 2
    }],
    "delivery_instruction": "Deliver to the back door on Rue Saint-Viateur"
  }'
```

```json
{
  "result": true,
  "id": 12345,
  "ref": "WHS-20931",
  "shipping_price": "18.50",
  "currency": "CAD",
  "price_details": { "shipping_fee": "18.50" },
  "tracking_number": ["SR123456789012"],
  "packages": [
    { "id": 67890, "ref": "WHS-20931-1", "tracking_number": "SR123456789012", "external_tracking_number": "" }
  ]
}
```

| Mező | Jelentés |
|---|---|
| `type` | `D` kézbesítés vagy `P` felvétel |
| `need_pick_up` | `0` — az áru már a raktárban van. `1` — egy sofőrnek fel kell vennie a csomagot |
| `ref` | Külső hivatkozás kereséshez és egyeztetéshez |
| `name` / cím | Kézbesítés: címzett. Felvétel: felvételi megálló |
| `schedule_date`, `time_window_start`, `time_window_end` | Kézbesítési dátum (`Y-m-d`) és az időablak, amelyben a megállót ki kell szolgálni (`Y-m-d H:i:s`) |
| `packagesDetail` | Csomagonként egy bejegyzés; a `ref` azonosítja a csomagot az Ön rendszerében |
| `auto_deduplication` | `1` elutasít egy második csomagot ugyanazzal a csomag-`ref` értékkel |

A válaszban:

- `id`: a rendelés azonosítója; tárolja a rendelés részleteihez és a lemondási híváshoz.
- `tracking_number`: csomagonként egy követési szám; ezekkel nyomtasson és kövessen.
- `warning`: akkor szerepel, ha a rendelés figyelmeztetéssel jött létre, például a kézbesítési területen kívüli cím esetén, amelyet a vállalkozás megtart vagy visszatart. Egy megtartott, területen kívüli rendelés `shipping_price: null` értéket adhat vissza.

**GraphQL:** `clientOrderCreate` ([GraphQL kézikönyv](/api/graphql/documentation#/client/clientOrderCreate)). Az eredmény JSON skalár, a REST-válasszal azonos törzzsel.

```graphql
mutation {
  clientOrderCreate(
    type: "D"
    need_pick_up: 0
    ref: "WHS-20931"
    name: "Marie Tremblay"
    company_name: "Café Lumière"
    telephone: "5145550142"
    address_1: "5400 Avenue du Parc"
    city: "Montréal"
    province: "QC"
    postcode: "H2V4G7"
    country: "Canada"
    packages: 1
    packagesDetail: [{ ref: "WHS-20931-1", weight: 12, weight_unit: 2, length: 60, width: 40, height: 30, dimension_unit: 2 }]
  )
}
```

**Ellenőrzés:** küldje el újra ugyanazt a törzset ugyanazzal az `Idempotency-Key` értékkel. A válasz ugyanazt az `id` értéket tartalmazza, és nem jön létre második rendelés.

## 7. Felvételi rendelés létrehozása

A felvételi rendelés egy sofőrt küld egy címre áru felvételére; itt az Épicerie Wellington üres ládáiért. Ugyanazt a végpontot használja, mint a kézbesítés: a cím a felvételi megálló, a `type` értéke `P`, a `need_pick_up` értéke `1`.

**REST:** `POST /api/v1/client/orderCreate` — [REST kézikönyv](/api/documentation#/paths/v1-client-orderCreate/post)

```bash
curl -X POST https://YOUR_HOST/api/v1/client/orderCreate \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: crt-20931-pickup" \
  -d '{
    "type": "P",
    "need_pick_up": 1,
    "ref": "CRT-20931",
    "name": "Luc Gagnon",
    "company_name": "Épicerie Wellington",
    "telephone": "5145550187",
    "email": "luc@epiceriewellington.example",
    "address_1": "4100 Rue Wellington",
    "city": "Verdun",
    "province": "QC",
    "postcode": "H4G1V5",
    "country": "Canada",
    "packages": 1,
    "packagesDetail": [{
      "ref": "CRT-20931-1",
      "weight": 4,
      "weight_unit": 2,
      "length": 60,
      "width": 40,
      "height": 30,
      "dimension_unit": 2
    }],
    "pickup_instruction": "Empty crates are stacked at the loading door"
  }'
```

```json
{
  "result": true,
  "id": 12346,
  "ref": "CRT-20931",
  "shipping_price": "12.00",
  "currency": "CAD",
  "tracking_number": ["SR123456789029"],
  "packages": [
    { "id": 67891, "ref": "CRT-20931-1", "tracking_number": "SR123456789029", "external_tracking_number": "" }
  ]
}
```

- `id` és `tracking_number`: tárolja őket a ládavisszagyűjtéshez, ugyanúgy, mint kézbesítésnél.
- `pickup_instruction`: a felvételi megállóban jelenik meg a sofőrnek; kézbesítésnél a `delivery_instruction` a megfelelője.

**Ellenőrzés:** a rendelés részletei (8. lépés) ennél a rendelésnél `type` `P` és `need_pickup` `1` értéket mutatnak.

## 8. A rendelés lekérdezése

A rendelés részletei megerősítik a tárolt adatokat, és visszaadják az aktuális állapotot; a listavégpont lehetővé teszi, hogy az ERP egyeztesse saját nyilvántartását a platformmal.

**REST:** `GET /api/v1/orders/{orderId}` — [REST kézikönyv](/api/documentation#/paths/v1-orders-orderId/get)

```bash
curl https://YOUR_HOST/api/v1/orders/12345 \
  -H "Authorization: Bearer ACCESS_TOKEN"
```

```json
{
  "business_name": "Farine & Fils",
  "order": {
    "id": 12345,
    "ref": "WHS-20931",
    "type": "D",
    "need_pickup": 0,
    "orders_status_id": 2,
    "name": "Marie Tremblay",
    "address_1": "5400 Avenue du Parc",
    "postcode": "H2V4G7",
    "time_window_start": "2026-10-02 06:00:00",
    "time_window_end": "2026-10-02 09:00:00",
    "shipping_price": "18.50"
  },
  "tracking_numbers": ["SR123456789012"],
  "packages": [
    { "id": 67890, "ref": "WHS-20931-1", "tracking_number": "SR123456789012" }
  ]
}
```

- `order.orders_status_id`: a rendelés állapota; `2` az Új, `12` a Lemondva.
- `order.type` és `order.need_pickup`: megerősítik, hogy a megálló kézbesítésként vagy felvételként lett tárolva.
- `tracking_numbers`: a rendelés csomagjainak követési számai.

**REST:** `GET /api/v1/orders/list` — [REST kézikönyv](/api/documentation#/paths/v1-orders-list/get)

```bash
curl "https://YOUR_HOST/api/v1/orders/list?page=1&per_page=50" \
  -H "Authorization: Bearer ACCESS_TOKEN"
```

A lista a fiók összes rendelését visszaadja, a legújabbal kezdve, mindegyiket a csomagjaival és tételsoraival. A lapozáshoz adja meg együtt a `page` és a `per_page` paramétert (a `per_page` legfeljebb 1000); ezek nélkül a legújabb 1000 rendelés érkezik egy `truncated` jelzővel.

**GraphQL:** `orders` ([GraphQL kézikönyv](/api/graphql/documentation#/orders/orders)) egy rendeléshez és `ordersList` ([GraphQL kézikönyv](/api/graphql/documentation#/orders/ordersList)) a listához. Mindkettő JSON skalárt ad vissza.

```graphql
query {
  orders(orderId: "12345")
}
```

**Ellenőrzés:** a rendelés a hitelesített fiókhoz tartozik, a `ref` megegyezik a létrehozáskor küldött értékkel, a `tracking_numbers` pedig megegyezik a létrehozási válasszal.

## 9. A helyi címke nyomtatása

A címkén szerepel a követési vonalkód, amelyet a sofőr a raktárban és a megállóban beolvas. Csomagonként egy címkét nyomtasson, és rögzítse a ládakötegre.

**REST:** `POST /api/v1/shipping/getShippingLabel` — [REST kézikönyv](/api/documentation#/paths/v1-shipping-getShippingLabel/post)

```bash
curl -X POST https://YOUR_HOST/api/v1/shipping/getShippingLabel \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "SR123456789012",
    "type": "TRACKING_NUMBER",
    "base64": 1,
    "hide_sender_address": 0,
    "hide_receiver_address": 0
  }'
```

```json
"JVBERi0xLjcKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZwo..."
```

- `type`: az `id` értelmezése: `TRACKING_NUMBER` (alapértelmezett), `ORDER_ID` vagy `REF`.
- `base64`: `0` (alapértelmezett) a PDF-et streameli. `1` esetén a teljes választörzs egy legfelső szintű JSON-karakterlánc, amely a base64 PDF-et tartalmazza, nem pedig egy `pdf_data` mezőt tartalmazó objektum. Ha a címkét szabályos JSON-objektumban szeretné megkapni, hívja helyette a `POST /api/v2/shipping/getShippingLabel` — [REST kézikönyv](/api/documentation#/paths/v2-shipping-getShippingLabel/post) végpontot.
- `packages`: opcionális; a nyomtatandó címkék száma. A rendelés csomagszámától eltérő érték frissíti a rendelést.
- `hide_sender_address` / `hide_receiver_address`: `1` esetén az adott cím üresen marad a címkén.

**GraphQL:** `shippingGetShippingLabel` ([GraphQL kézikönyv](/api/graphql/documentation#/shipping/shippingGetShippingLabel)). A `shippingGetShippingLabelV2` ([GraphQL kézikönyv](/api/graphql/documentation#/shipping/shippingGetShippingLabelV2)) mindig JSON-t ad vissza (`pdf_data`).

**Ellenőrzés:** a dekódolt PDF megnyílik. A kézbesítési címke a Café Lumière címét, a felvételi címke az Épicerie Wellington címét mutatja. Az elrejtett cím üres a címkén.

## 10. A rendelés követése

A nyilvános követés egy csomag eseményidővonalát adja vissza. Nem igényel hozzáférési tokent, így egy vevői portál közvetlenül megjelenítheti; a kézbesítési vagy felvételi igazolás is vele együtt érkezik.

**REST:** `GET /api/v1/tracking/{trackingNumber}` — [REST kézikönyv](/api/documentation#/paths/v1-tracking-trackingNumber/get)

```bash
curl https://YOUR_HOST/api/v1/tracking/SR123456789012
```

```json
{
  "result": true,
  "postcode": "H2V4G7",
  "deliveried": false,
  "returntosender": false,
  "rejectedbyrecipient": false,
  "data": [
    {
      "tracking_event_status_id": 100,
      "description": "Order information submitted",
      "updated_at_localized": "2026-10-01 16:42:10"
    }
  ],
  "proofs": []
}
```

Ugyanez az URL az Ön `ref` értékét is elfogadja, ha azt külső számként tárolták.

A `tracking_event_status_id` alapján ágaztasson el, ne a `description` alapján; az a karakterlánc az `Accept-Language` fejlécet követi.

| `tracking_event_status_id` | Oldal | Jelentés |
|---|---|---|
| `100` | mindkettő | Rendelés beérkezett |
| `300` / `301` | kézbesítés | Telephelyen |
| `450` | kézbesítés | Kézbesítés alatt |
| `500` | kézbesítés | Kézbesítve |
| `501` | kézbesítés | Sikertelen kézbesítés, új tervezés szükséges |
| `460` | felvétel | Felvétel alatt |
| `510` | felvétel | Felvéve |
| `512` | felvétel | Sikertelen felvétel, később újra |
| `513` | felvétel | Felvételi probléma |

- `data`: a legújabbal kezdve; az első sor az aktuális állapot.
- `deliveried`: `true` a `500` után.
- `proofs[]`: `500` vagy `510` esetén tartalmazhat `type` `1` (aláírás) vagy `2` (fénykép) elemet `file_id` és `signed_url` értékkel. Az esemény után feltöltött fénykép nem szerepel ebben a válaszban; iratkozzon fel a `pod.files_updated` eseményre (11. lépés).

**GraphQL:** `trackingPublic` ([GraphQL kézikönyv](/api/graphql/documentation#/tracking/trackingPublic)). Az eredmény típusos, és selection setet igényel.

```graphql
query {
  trackingPublic(trackingNumber: "SR123456789012") {
    result
    deliveried
    returntosender
    rejectedbyrecipient
    postcode
    data {
      tracking_event_status_id
      tracking_event_key
      description
      updated_at_localized
    }
    proofs { file_id type full_url signed_url }
  }
}
```

**Ellenőrzés:** közvetlenül a létrehozás után a legújabb esemény `100`, a `deliveried` pedig `false`. Ismeretlen szám esetén `result: false` és `404` a válasz; jelenítsen meg „nem található” állapotot, és ne állítson elő követési eseményeket.

## 11. Webhookok fogadása

A webhookok minden változást az Ön szerverére küldenek, így az ERP és a vevői portál lekérdezés nélkül naprakész marad. Regisztrálja az ehhez a folyamathoz szükséges visszahívási URL-eket:

| Beállítás | Esemény | Felhasználás |
|---|---|---|
| `order_create_webhook_url` | `order.created` | Az `id` és a `tracking_number` tárolása |
| `order_status_change_webhook_url` | `order.status_change` | A vevőnek megjelenített állapot |
| `tracking_event_webhook_url` | `tracking.event` | Felvételi vagy kézbesítési idővonal |
| `pod_files_webhook_url` | `pod.files_updated` | Fénykép vagy aláírás felvétel vagy kézbesítés után |
| `order_cancel_failed_webhook_url` | `order.cancel_failed` | Az Ön által küldött lemondást elutasították |
| `order_create_async_postback_url` | `order.create_async` | Egy aszinkron köteg eredménye (13. lépés) |

**REST:** `PUT /api/v1/webhook-settings` — [REST kézikönyv](/api/documentation#/paths/v1-webhook-settings/put)

```bash
curl -X PUT https://YOUR_HOST/api/v1/webhook-settings \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "order_create_webhook_url": "https://erp.farineetfils.example/hooks/superroute",
    "order_status_change_webhook_url": "https://erp.farineetfils.example/hooks/superroute",
    "tracking_event_webhook_url": "https://erp.farineetfils.example/hooks/superroute",
    "pod_files_webhook_url": "https://erp.farineetfils.example/hooks/superroute",
    "order_cancel_failed_webhook_url": "https://erp.farineetfils.example/hooks/superroute",
    "webhook_sign_secret": "YOUR_LONG_RANDOM_SECRET",
    "webhook_verify_ssl": 1
  }'
```

```json
{
  "result": true,
  "changed_keys": [
    "order_create_webhook_url",
    "order_status_change_webhook_url",
    "tracking_event_webhook_url",
    "pod_files_webhook_url",
    "order_cancel_failed_webhook_url",
    "webhook_sign_secret"
  ],
  "settings": {
    "webhook_sign_secret": "************CRET",
    "tracking_event_webhook_url": "https://erp.farineetfils.example/hooks/superroute",
    "webhook_verify_ssl": 1
  }
}
```

- `changed_keys`: a hívás által módosított beállítások.
- `settings.webhook_sign_secret`: maszkolva érkezik vissza; a teljes értéket csak a szerverén tárolja.

**GraphQL:** `webhookSettingsUpdate` ([GraphQL kézikönyv](/api/graphql/documentation#/webhooks/webhookSettingsUpdate)).

A fogadó oldalon ellenőrizze a **v2** aláírást a nyers törzsön: `HMAC_SHA256(timestamp + "." + raw_body, secret)`, összevetve az `X-Webhook-Signature-V2` értékkel, ahol az időbélyeg az `X-Webhook-Timestamp`. A duplikátumokat az `X-Webhook-Event-Id` alapján szűrje. **3 másodpercen belül 2xx** választ adjon, és az eseményt ezt követően dolgozza fel.

```php
$raw = file_get_contents('php://input');
$ts  = $_SERVER['HTTP_X_WEBHOOK_TIMESTAMP'] ?? '';
$sig = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE_V2'] ?? '';
$expected = hash_hmac('sha256', $ts . '.' . $raw, $sharedSecret);
if (!hash_equals($expected, $sig)) {
    http_response_code(401);
    exit;
}
```

**Ellenőrzés:** hozzon létre egy tesztrendelést, és fogadja az `order.created` eseményt ugyanazzal az `id` és `tracking_number` értékkel. A fogadó `401`-gyel utasítja el az érvénytelen aláírást, és ugyanazon `X-Webhook-Event-Id` második kézbesítését nem dolgozza fel kétszer.

## 12. Rendelés lemondása

Mondja le a rendelést, ha a nagykereskedelmi rendelést visszavonták, vagy a ládafelvételre már nincs szükség. A hívás idempotens: egy már lemondott rendelés lemondása ismét sikeres.

**REST:** `POST /api/v1/orders/cancel` — [REST kézikönyv](/api/documentation#/paths/v1-orders-cancel/post) — pontosan egyet küldjön a következők közül: `order_id`, `tracking_number`, `external_tracking_number`.

```bash
curl -X POST https://YOUR_HOST/api/v1/orders/cancel \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"tracking_number":"SR123456789012"}'
```

```json
{
  "result": true,
  "id": 12345,
  "message": "Order 12345 has been cancelled successful.",
  "already_cancelled": false
}
```

- `result`: `true`, ha a rendelés le van mondva.
- `already_cancelled`: `true`, ha a rendelést e hívás előtt már lemondták; kezelje sikerként.
- `code`: akkor szerepel, ha a lemondást elutasították; lásd a 14. lépést.

**GraphQL:** `ordersCancel` ([GraphQL kézikönyv](/api/graphql/documentation#/orders/ordersCancel)). Az eredmény típusos, és selection setet igényel.

```graphql
query {
  ordersCancel(tracking_number: "SR123456789012") {
    result
    id
    message
    already_cancelled
    code
  }
}
```

**Ellenőrzés:** a rendelés részletei `orders_status_id` `12` értéket mutatnak, és ugyanaz a lemondás `already_cancelled: true` értéket ad vissza. Ha egy lemondást elutasítanak, az `order.cancel_failed` esemény az `order_cancel_failed_webhook_url` címre érkezik.

## 13. Rendelések kötegelt létrehozása (opcionális)

Az ERP egyetlen kérésben elküldheti a nap nagykereskedelmi rendeléseit és ládafelvételeit. Minden sor ugyanazokat a mezőket fogadja, mint a 6. és 7. lépés, és lehet `type` `D` vagy `P`.

**REST:** `POST /api/v1/client/batchOrderCreate` — [REST kézikönyv](/api/documentation#/paths/v1-client-batchOrderCreate/post) — akkor válaszol, amikor minden sor feldolgozása befejeződött.

```bash
curl -X POST https://YOUR_HOST/api/v1/client/batchOrderCreate \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: batch-2026-10-01" \
  -d '{
    "per_order_transaction": 1,
    "orders": [
      {
        "type": "D",
        "need_pick_up": 0,
        "ref": "WHS-20932",
        "name": "Sophie Roy",
        "company_name": "Boulangerie du Marché",
        "telephone": "5145550163",
        "address_1": "7070 Avenue Henri-Julien",
        "city": "Montréal",
        "province": "QC",
        "postcode": "H2S3S3",
        "country": "Canada",
        "packages": 1,
        "packagesDetail": [{ "ref": "WHS-20932-1", "weight": 10, "weight_unit": 2, "length": 60, "width": 40, "height": 30, "dimension_unit": 2 }]
      },
      {
        "type": "P",
        "need_pick_up": 1,
        "ref": "CRT-20932",
        "name": "Luc Gagnon",
        "company_name": "Épicerie Wellington",
        "telephone": "5145550187",
        "address_1": "4100 Rue Wellington",
        "city": "Verdun",
        "province": "QC",
        "postcode": "H4G1V5",
        "country": "Canada",
        "packages": 1,
        "packagesDetail": [{ "ref": "CRT-20932-1", "weight": 4, "weight_unit": 2, "length": 60, "width": 40, "height": 30, "dimension_unit": 2 }]
      }
    ]
  }'
```

```json
[
  { "result": true, "id": 12347, "ref": "WHS-20932", "tracking_number": ["SR123456789036"], "packages": [{ "id": 67892, "ref": "WHS-20932-1", "tracking_number": "SR123456789036", "external_tracking_number": "" }] },
  { "result": true, "id": 12348, "ref": "CRT-20932", "tracking_number": ["SR123456789043"], "packages": [{ "id": 67893, "ref": "CRT-20932-1", "tracking_number": "SR123456789043", "external_tracking_number": "" }] }
]
```

- Minden sornak saját `result` értéke van; a `ref` alapján párosítsa a rendelési sorához. Az elutasított sor `message` és `skipped_ref` értéket tartalmaz, és tartalmazhat `code` értéket is (például `INSUFFICIENT_BALANCE` vagy `OUT_OF_DELIVERY_AREA`).
- `per_order_transaction`: `1` esetén minden sor külön kerül véglegesítésre, így egy sikertelen sor nem vonhatja vissza a többit.
- A 100 rendelésnél nagyobb kötegek `X-Batch-Size-Warning` válaszfejlécet kapnak; ezeket az aszinkron végpontra küldje.

**REST:** `POST /api/v1/client/batchOrderCreateAsync` — [REST kézikönyv](/api/documentation#/paths/v1-client-batchOrderCreateAsync/post) — ugyanazt a törzset fogadja, és azonnal feladatazonosítót ad vissza:

```json
{ "message": "Order batch created in async,please check later.", "asyncId": 28 }
```

Kérdezze le a `GET /api/v1/client/async/{id}` — [REST kézikönyv](/api/documentation#/paths/v1-client-async-id/get) — végpontot az `asyncId` értékkel, vagy fogadja az `order.create_async` eseményt az `order_create_async_postback_url` címen. A feladat eredménye ugyanaz a soronkénti lista, mint a szinkron végpontnál.

```bash
curl https://YOUR_HOST/api/v1/client/async/28 \
  -H "Authorization: Bearer ACCESS_TOKEN"
```

**GraphQL:** `clientBatchOrderCreate` ([GraphQL kézikönyv](/api/graphql/documentation#/client/clientBatchOrderCreate)), `clientBatchOrderCreateAsync` ([GraphQL kézikönyv](/api/graphql/documentation#/client/clientBatchOrderCreateAsync)) és `clientAsync` ([GraphQL kézikönyv](/api/graphql/documentation#/client/clientAsync)).

**Ellenőrzés:** egy kétsoros köteg két eredményt ad vissza, mindegyiket a saját `ref` értékével. Az aszinkron feladat a lefutása után ugyanezeket a sorokat adja vissza.

## 14. Hibakezelés

| Helyzet | HTTP-állapot | Kód | Mit tesz az integráció |
|---|---|---|---|
| Egy kötelező mező hiányzik vagy hibás (létrehozás) | 400 | `VALIDATION_FAILED` | Javítsa a `message` értékben megnevezett mezőt, és küldje el újra a kérést. |
| A fiók egyenlege nem fedezi a rendelést | 400 | `INSUFFICIENT_BALANCE` | Olvassa ki az `insufficient_balance` értéket (szükséges, rendelkezésre álló, hiányzó összeg); töltse fel az egyenleget, majd próbálja újra. Rendelés nem jött létre. |
| A cím a szolgáltatási területen kívül esik, és a vállalkozás törli az ilyen rendeléseket | 400 | `OUT_OF_DELIVERY_AREA` | Adjon meg a szolgáltatási területen belüli címet. Rendelés nem jött létre. |
| Egy csomag-`ref` vagy külső követési szám már létezik (bekapcsolt duplikációszűrés mellett) | 200 (`result` `false`), vagy 409 `strict_duplicate_check` `1` esetén | `DUPLICATE_TRACKING_NUMBER` | Olvassa ki az `exist_package_ref` értéket, és új rendelés létrehozása helyett a meglévőt kapcsolja össze. |
| Egy `Idempotency-Key` eltérő törzzsel kerül újrafelhasználásra | 409 | `IDEMPOTENCY_CONFLICT` | Eltérő kéréshez új kulcsot használjon. |
| Egy ugyanazzal az `Idempotency-Key` értékkel küldött kérés még fut | 409 | `IDEMPOTENCY_IN_PROGRESS` | Várjon, majd próbálja újra ugyanazzal a kulccsal. |
| Lemondás rendelésazonosító nélkül | 400 | `MISSING_IDENTIFIER` | Küldje el a következők egyikét: `order_id`, `tracking_number`, `external_tracking_number`. |
| Nem létező rendelés lemondása | 400 | `ORDER_NOT_FOUND` | Ellenőrizze a tárolt `id` értéket vagy követési számot. |
| A szám egynél több aktív rendeléssel egyezik | 409 | `MULTIPLE_ORDERS_MATCHED` | Mondja le `order_id` alapján, a `matched_order_ids` egyikét használva. |
| A rendelés egy másik fiókhoz tartozik | 401 | `ORDER_CANCEL_UNAUTHORIZED` | A rendelést létrehozó fiókkal mondja le. |
| A rendelés állapota már nem teszi lehetővé a lemondást | 401 | `ORDER_STATUS_NOT_CANCELLABLE` | Hagyja a rendelést változatlanul; a visszaküldést külön kezelje. |
| A rendelés egy harmadik fél fuvarozónál van, amely nem tudja lemondani | 409 | `ORDER_CANCEL_BLOCKED_THIRD_PARTY`, `THIRD_PARTY_CANCEL_NOT_SUPPORTED` vagy `THIRD_PARTY_CANCEL_FAILED` | A rendelés változatlan; forduljon a vállalkozáshoz. |
| A token hiányzik vagy lejárt, vagy a fiók nem adhat le rendelést | 401 | — | Jelentkezzen be újra; ellenőrizze a fiók jogosultságait. |

## Tesztlista

Használjon teszthivatkozásokat, például `WHS-20931` és `CRT-20931`:

- [ ] (Opcionális) Az árajánlat árat ad vissza egy területen belüli irányítószámra `type` `D` értékkel.
- [ ] (Opcionális) Az árajánlat árat ad vissza egy területen belüli irányítószámra `type` `P` értékkel.
- [ ] A kézbesítés létrehozása `id` + `tracking_number` értéket ad vissza; ugyanaz az `Idempotency-Key` nem hoz létre második rendelést.
- [ ] A felvétel létrehozása `id` + `tracking_number` értéket ad vissza; a rendelés részletei `type` `P` és `need_pickup` `1` értéket mutatnak.
- [ ] A rendelés részletei és a lista mindkét rendelést ezen a fiókon mutatják.
- [ ] A helyi címke PDF-je megnyílik, és a címzett vagy a felvételi cím látható rajta.
- [ ] A nyilvános követés token nélkül visszaadja az idővonalat; a legújabb esemény `100`.
- [ ] Megérkezik az `order.created`, és a v2 aláírása ellenőrizhető.
- [ ] A lemondás `result: true` értéket ad vissza, a második lemondás pedig `already_cancelled: true` értéket.
- [ ] Egy kézbesítésből és egy felvételből álló köteg két eredményt ad vissza, mindegyiket a saját `ref` értékével.
