# Fuvarozói címkék

A címkeszolgáltatás a fiókhoz kapcsolt fuvarozóktól (például UPS és Canada Post) vásárol szállítási címkéket, és minden címkét rendelésként tárol. Az integráció listázza a fiók szállítási módjait, árajánlatot kér egy csomagra, létrehozza a címkerendelést, megvásárolja a címkét a kiválasztott fuvarozói szolgáltatással, kinyomtatja a PDF-et, követi a csomagot, és törli a fel nem használt címkéket. Olyan webáruházaknak, raktárrendszereknek és rendeléskezelő rendszereknek szól, amelyek a csomagokat fuvarozókkal szállíttatják, nem saját sofőrökkel.

## 1. Mit építhet

Az útmutató példái egyetlen vállalkozást követnek: a **Northbound Outfitters** kültéri felszereléseket forgalmazó webáruház, amely a torontói 1200 Eglinton Ave E címen lévő raktárából szállít. A fiókjában van egy Canada Post és egy UPS szállítási mód. Egy tipikus rendelés egy 4,2 kg-os, 60 × 30 × 25 cm-es sátordoboz Calgaryba; az Egyesült Államokba szóló rendelések UPS-szel mennek.

- **Fuvarozóválasztás a pénztárnál.** Az áruház a vásárló kosarára árajánlatot kér a Canada Posttól, megjeleníti a szolgáltatásokat árral és tranzitnapokkal, és azzal a szolgáltatással szállít, amelyet a vásárló kifizetett.
- **Címkenyomtatás egy kattintással a raktárban.** A csomagolóállomás a doboz becsomagolásakor létrehozza a címkerendelést, megvásárolja a címkét a kiválasztott szolgáltatással, és hőnyomtatón kinyomtatja a fuvarozói PDF-et.
- **Határon átnyúló küldemények vámadatokkal.** Az Egyesült Államokba szóló rendelések tételsorokat (leírás, mennyiség, érték, HS-kód) tartalmaznak, így a UPS-címke a kereskedelmi adatokkal együtt készül el.
- **Automatikus állapotfrissítés a vásárlónak.** Az áruház tárolja a fuvarozói követési számot, a rendelés oldalán megjeleníti a nyilvános követési idővonalat, és frissíti a rendelést, amikor egy `tracking.event` webhook jelzi, hogy a csomagot kézbesítették.

## 2. Mit tartalmaz ez az útmutató

Ez az útmutató a v1 címkeszolgáltatást (`/api/v1/labelservice/...`) ismerteti: hívásonként egy szállítási módot (egy fuvarozói fiókot). Akkor használja, ha az integráció már tudja, melyik szállítási móddal szállít, vagy ha egy meglévő címkeszolgáltatás-integrációt tart karban.

Új integrációkhoz a Uniorder (`/api/v1/uniorder/...`) az ajánlott egységes belépési pont. A Uniorder útmutatói, az „Uniorder: egy API minden küldeményhez” és az „Árajánlat és rendelés egy folyamatban”, egyszerre kérnek árajánlatot a fiók összes fuvarozói szolgáltatására (ahol alkalmazható, a vállalkozás saját kiszállításával együtt), és a címkét a kiválasztott szolgáltatással vásárolják meg annak `rate_id` értékének visszaküldésével. Ugyanezek a hívások nyomtatják, követik és törlik ezután az összes rendelést.

A többi küldeménytípust más útmutatók ismertetik:

- Kiszállítás a vállalkozás saját sofőreivel: „Felvétel és kézbesítés (saját flotta)”.
- Ügyfélfiók, amely a vállalkozása által kínált szolgáltatásokkal szállít: „Szállítási szolgáltatások”.
- Raktárban tárolt áru, amelyet kérésre kiszállítanak: „Tárolás és kiszállítás”.

## 3. Mielőtt elkezdi

- **Fiók.** Használjon vállalkozási (ügyfél-) fiókot, annak egy alkalmazottját vagy egy vállalkozás ügyfélfiókját. A vállalkozási fiók a saját szállítási módjait látja. Az ügyfélfiók csak azokat a módokat látja, amelyeket a vállalkozása hozzárendelt, és minden megvásárolt címkéje az egyenlegét terheli; ha a vállalkozás az adott ügyfélnél bekapcsolta a Címke szolgáltatás automatikus szüneteltetése funkciót, a címkét a rendszer elutasítja, amíg az egyenleg és a hitelkeret együtt nem fedezi.
- **API-jogosultság.** A fiókon engedélyezni kell az API-hozzáférést. Enélkül minden címkeszolgáltatás-hívás `401` választ ad `Unauthorized` üzenettel.
- **Szállítási módok.** A fiókon legalább egy szállítási módnak aktívnak kell lennie (ügyfeleknél: az ügyfélhez rendelve). A módazonosítók fiókonként különböznek, és nem szabad beégetni őket a kódba; olvassa ki őket az 5. lépésben.
- **Tesztadatok.** Használjon tesztszállítási módot vagy fuvarozói sandboxot, ahol ilyen be van állítva (a díjak ekkor `test_mode: true` értéket tartalmaznak), és olyan célcímet, amely az Ön ellenőrzése alatt áll. Minden éles módon vásárolt tesztcímkét töröljön.
- **Tokenkezelés.** A szerveréről lépjen be, és ott tárolja a tokent. Ne helyezze el a tokent vagy a jelszót böngészőben vagy mobilalkalmazásban.
- **Helyőrzők.** Cserélje a `YOUR_HOST` értéket a platform hosztjára, az `ACCESS_TOKEN` értéket pedig a 4. lépésben kapott tokenre. A `shipping_method` értékeket cserélje a fiókja azonosítóira.

## 4. Belépés

Minden címkeszolgáltatás-hívás bearer tokent igényel. Az integráció egyszer belép, a szerveren tárolja az `access_token` és az `expires_at` értéket, és a token lejárata előtt újra belép.

**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":"shipping@northbound-outfitters.ca","password":"your_password"}'
```

```json
{
  "result": true,
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOi...",
  "token_type": "Bearer",
  "expires_at": "2027-09-28 10:15:00"
}
```

- `access_token`: minden későbbi hívásban küldje az alábbi fejlécként.
- `expires_at`: ezen időpont előtt lépjen be újra.

```
Authorization: Bearer ACCESS_TOKEN
```

A GraphQL ugyanazt 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 belépés `access_token` értéket ad. Az e token nélküli későbbi kérések `401` választ adnak.

## 5. Szállítási módok listázása

A módlista megmutatja az integrációnak, mely fuvarozói fiókokkal szállíthat, és melyik mód milyen beállításokat fogad el. Tárolja minden használt mód `id` értékét; ez minden későbbi hívás `shipping_method` értéke.

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

```bash
curl -X POST https://YOUR_HOST/api/v1/labelservice/getShippingMethodList \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"detail": true}'
```

```json
[
  {
    "id": 59,
    "name": "Canada Post",
    "unique_identifier": "CPC-TOR",
    "options": {
      "signature_option": true,
      "insurance_option": true,
      "insurance_value": true,
      "multi_package": true
    },
    "package_type": {
      "parcel": {
        "name": "Parcel",
        "options": { "weight_options": true, "dimension_options": true }
      }
    },
    "from_contry_limit": ["CA"],
    "isUploadMethod": false
  },
  {
    "id": 61,
    "name": "UPS",
    "unique_identifier": "UPS-TOR",
    "options": {
      "signature_option": true,
      "insurance_option": true,
      "insurance_value": true,
      "multi_package": true
    }
  }
]
```

Minden sor tartalmazza:

| Mező | Használat |
|---|---|
| `id` | `shipping_method` minden későbbi hívásban |
| `name` | Megjelenített név |
| `unique_identifier` | Stabil kód |
| `options.signature_option` | Aláírás elérhető |
| `options.insurance_option` | Biztosítás elérhető |
| `options.multi_package` | Több mint egy darab |
| `package_type` | Elfogadott `package_type` kódok, és hogy mindegyikhez kell-e súly és méret |
| `from_contry_limit` | Országok, amelyekben a feladó címe lehet |
| `services` | A mód mögötti fuvarozók és szolgáltatások; a kódokkal az árajánlat a `carriers` / `services` mezővel szűkíthető |

Küldjön `"id": 59` értéket egyetlen mód lekérdezéséhez, vagy `"detail": false` értéket, ha csak az `id`, `name` és `unique_identifier` mezőket kéri.

**GraphQL:** `labelserviceGetShippingMethodList` ([GraphQL kézikönyv](/api/graphql/documentation#/labelservice/labelserviceGetShippingMethodList)) (JSON skalár).

**Ellenőrzés:** a lista nem üres. Kiválasztott egy `id` értéket, és tudja, hogy az a mód lehetővé teszi-e az aláírást, a biztosítást és a több csomagot. Az üres lista azt jelenti, hogy a fiókon nincs engedélyezett mód.

## 6. Árajánlat

Az árajánlat úgy kér árakat a fuvarozótól, hogy semmit nem hoz létre: a kéréshez használt ideiglenes rendelés törlődik, és semmilyen terhelés nem történik. A Northbound Outfitters a pénztárnál hívja meg, hogy megjelenítse a kosárra vonatkozó Canada Post-szolgáltatásokat. A törzs ugyanolyan szerkezetű, mint a 7. lépésben. A `shipping_method` kötelező.

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

```bash
curl -X POST https://YOUR_HOST/api/v1/labelservice/rate \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "shipping_method": 59,
    "name": "Emily Tremblay",
    "telephone": "4035550182",
    "email": "emily.tremblay@example.com",
    "address_1": "1415 17 Ave SW",
    "city": "Calgary",
    "province": "AB",
    "postcode": "T2T0C8",
    "country": "CA",
    "weight": 4.2,
    "length": 60,
    "width": 30,
    "height": 25,
    "dimension_unit": 2,
    "weight_unit": 2,
    "signature_option": 0,
    "insurance_option": 0,
    "package_type": "parcel",
    "ref": "NB-10482",
    "sender_name": "Northbound Outfitters",
    "sender_telephone": "4165550140",
    "sender_address_1": "1200 Eglinton Ave E",
    "sender_city": "Toronto",
    "sender_province": "ON",
    "sender_postcode": "M3C1H9",
    "sender_country": "CA"
  }'
```

```json
{
  "result": true,
  "shipping_price": "24.86",
  "price_details": { "shipping_fee": "24.86" },
  "rates": [
    {
      "carrier_name": "canadapost",
      "currency": "CAD",
      "service_code": "canadapost_expedited_parcel",
      "service_name": "CANADAPOST EXPEDITED PARCEL",
      "price": 24.86,
      "transit_days": 3,
      "test_mode": false
    },
    {
      "carrier_name": "canadapost",
      "currency": "CAD",
      "service_code": "canadapost_xpresspost",
      "service_name": "CANADAPOST XPRESSPOST",
      "price": 38.12,
      "transit_days": 2,
      "test_mode": false
    }
  ],
  "best_rate": {
    "service_code": "canadapost_expedited_parcel",
    "price": 24.86,
    "transit_days": 3
  }
}
```

- `rates[]`: fuvarozói szolgáltatásonként egy bejegyzés `price`, `currency`, `transit_days` és `price_detail` (alapdíj, üzemanyag-pótdíj, adók) mezőkkel. Ezeket mutassa meg a vásárlónak.
- `best_rate` / `shipping_price`: a mód által visszaadott első díj.
- Az árajánlat nem tartalmaz `rate_id` értéket és rendelés-`id` értéket. A címkék a 7. lépésben létrehozott rendelés díjai alapján vásárolhatók meg.

`weight_unit`: `1` g, `2` kg, `3` oz, `4` lb. `dimension_unit`: `1` mm, `2` cm, `3` m, `4` in.

Több darabhoz küldje: `packages: [{ ref, weight, length, width, height, weight_unit, dimension_unit }]`. A `shipping_from` (címjegyzék-azonosító) vagy a `shipping_from_code` helyettesítheti a `sender_*` blokkot. A `carriers` és a `services` az árajánlatot a felsorolt kódokra szűkíti.

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

**Ellenőrzés:** a `result` true, és van ár (valamint tranzitnapok, ha a fuvarozó küldi őket). Ha nincs díj, javítsa a célcímet / csomagot / módot, **mielőtt** létrehozza a rendelést.

## 7. A címkerendelés létrehozása

Ez a hívás létrehozza a címkerendelést, és lekéri a fuvarozótól az adott küldemény díjait. Visszaadja a rendelés `id` értékét és szolgáltatásonként egy `rate_id` értéket. A címke ekkor még nincs megvásárolva, és terhelés sem történik; a vásárlást a 8. lépés végzi. A Northbound Outfitters a doboz becsomagolásakor hívja meg, és az `id` értéket a saját `NB-10482` rendeléséhez tárolja.

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

Ugyanaz a törzs, mint a 6. lépésben. Küldjön `Idempotency-Key` fejlécet: az azonos kulccsal és azonos törzzsel ismételt kérés az első választ adja vissza, és nem hoz létre második rendelést.

```bash
curl -X POST https://YOUR_HOST/api/v1/labelservice/submitOrder \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: nb-10482-label" \
  -d '{
    "shipping_method": 59,
    "name": "Emily Tremblay",
    "telephone": "4035550182",
    "email": "emily.tremblay@example.com",
    "address_1": "1415 17 Ave SW",
    "city": "Calgary",
    "province": "AB",
    "postcode": "T2T0C8",
    "country": "CA",
    "weight": 4.2,
    "length": 60,
    "width": 30,
    "height": 25,
    "dimension_unit": 2,
    "weight_unit": 2,
    "signature_option": 0,
    "insurance_option": 0,
    "package_type": "parcel",
    "ref": "NB-10482",
    "sender_name": "Northbound Outfitters",
    "sender_telephone": "4165550140",
    "sender_address_1": "1200 Eglinton Ave E",
    "sender_city": "Toronto",
    "sender_province": "ON",
    "sender_postcode": "M3C1H9",
    "sender_country": "CA"
  }'
```

```json
{
  "result": true,
  "id": 128455,
  "shipping_price": "24.86",
  "price_details": { "shipping_fee": "24.86" },
  "rates": [
    {
      "rate_id": "rat_5f0c2a9e41d84b7c9a3e6d21b8f47c10",
      "carrier_name": "canadapost",
      "service_code": "canadapost_expedited_parcel",
      "service_name": "CANADAPOST EXPEDITED PARCEL",
      "price": 24.86,
      "currency": "CAD",
      "transit_days": 3
    },
    {
      "rate_id": "rat_5f0c2a9e41d84b7c9a3e6d21b8f47c11",
      "carrier_name": "canadapost",
      "service_code": "canadapost_xpresspost",
      "service_name": "CANADAPOST XPRESSPOST",
      "price": 38.12,
      "currency": "CAD",
      "transit_days": 2
    }
  ],
  "best_rate": {
    "rate_id": "rat_5f0c2a9e41d84b7c9a3e6d21b8f47c10",
    "service_code": "canadapost_expedited_parcel",
    "price": 24.86
  }
}
```

| Mező | Használat |
|---|---|
| `id` | Superroute rendelésazonosító — vásárlás, letöltés és törlés |
| `rates[].rate_id` | A 8. lépésben megvásárolandó szolgáltatás; csak ehhez a rendeléshez érvényes |
| `rates[].price` | Az adott szolgáltatás ára |
| `shipping_price` | A `best_rate` ára |

Az Egyesült Államokba szóló küldemény a UPS-módon megy, a vámkezeléshez szükséges tételsorokkal. Ez a példa a `packages` formát használja, amely dobozonként tartalmazza a tételeket:

```bash
curl -X POST https://YOUR_HOST/api/v1/labelservice/submitOrder \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: nb-10497-label" \
  -d '{
    "shipping_method": 61,
    "name": "Daniel Price",
    "telephone": "2065550117",
    "email": "daniel.price@example.com",
    "address_1": "500 Mercer St",
    "city": "Seattle",
    "province": "WA",
    "postcode": "98109",
    "country": "US",
    "package_type": "parcel",
    "paid_by": 1,
    "ref": "NB-10497",
    "sender_name": "Northbound Outfitters",
    "sender_telephone": "4165550140",
    "sender_address_1": "1200 Eglinton Ave E",
    "sender_city": "Toronto",
    "sender_province": "ON",
    "sender_postcode": "M3C1H9",
    "sender_country": "CA",
    "packages": [
      {
        "ref": "NB-10497-1",
        "weight": 2.6,
        "length": 45,
        "width": 30,
        "height": 20,
        "weight_unit": 2,
        "dimension_unit": 2,
        "items": [
          {
            "name": "Down sleeping bag",
            "description": "Down-filled sleeping bag, -7 C rating",
            "quantity": 1,
            "unit_price": 289.00,
            "currency": "CAD",
            "weight": 1.6,
            "hscode": "9404400000",
            "sku": "NB-SB-7C",
            "unit": "PCS"
          },
          {
            "name": "Camp stove",
            "description": "Canister camp stove",
            "quantity": 1,
            "unit_price": 79.00,
            "currency": "CAD",
            "weight": 1.0,
            "hscode": "7321111000",
            "sku": "NB-ST-01",
            "unit": "PCS"
          }
        ]
      }
    ]
  }'
```

**GraphQL:** `labelserviceSubmitOrder` ([GraphQL kézikönyv](/api/graphql/documentation#/labelservice/labelserviceSubmitOrder)). A REST-törzs az `input` argumentumba kerül:

```graphql
mutation {
  labelserviceSubmitOrder(input: {
    shipping_method: 59
    name: "Emily Tremblay"
    telephone: "4035550182"
    address_1: "1415 17 Ave SW"
    city: "Calgary"
    province: "AB"
    postcode: "T2T0C8"
    country: "CA"
    weight: 4.2
    length: 60
    width: 30
    height: 25
    dimension_unit: 2
    weight_unit: 2
    package_type: "parcel"
    ref: "NB-10482"
    sender_name: "Northbound Outfitters"
    sender_telephone: "4165550140"
    sender_address_1: "1200 Eglinton Ave E"
    sender_city: "Toronto"
    sender_province: "ON"
    sender_postcode: "M3C1H9"
    sender_country: "CA"
  })
}
```

**Ellenőrzés:** a válasz tartalmaz egy `id` értéket és legalább egy `rates[].rate_id` értéket. Tárolja mindkettőt. Ugyanaz az `Idempotency-Key` ugyanazzal a törzzsel ugyanazt az `id` értéket adja vissza, és nem hoz létre második rendelést.

## 8. A címke megvásárlása és a küldemény részleteinek lekérdezése

Ez a hívás megvásárolja a címkét a kiválasztott szolgáltatással, terheli az összeget, és visszaadja a fuvarozói követési számokat. Ha a címke már meg van vásárolva, csak a részleteket kérdezi le, így az ismételt hívás soha nem vásárol kétszer. A Northbound Outfitters annak a szolgáltatásnak a `rate_id` értékét küldi, amelyet a vásárló kifizetett.

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

```bash
curl -X POST https://YOUR_HOST/api/v1/labelservice/getShippingDetail \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "128455",
    "type": "ORDER_ID",
    "rate_id": "rat_5f0c2a9e41d84b7c9a3e6d21b8f47c10"
  }'
```

```json
{
  "id": 128455,
  "shippingPrice": "24.86",
  "mainTrackingNumber": "7023210039414604",
  "trackingNumber": "7023210039414604",
  "needSubmitShippingInformation": false,
  "rate": {
    "carrier_name": "canadapost",
    "price": 24.86,
    "price_detail": [
      { "name": "Base charge", "amount": 18.40 },
      { "name": "Fuel surcharge", "amount": 3.60 },
      { "name": "GST", "amount": 1.10 }
    ],
    "tax_items": ["HST", "GST", "PST", "QST"]
  },
  "labelStatus": "ready",
  "shippingLabel": "JVBERi0xLjQKMS... (base64 encoded)"
}
```

| Mező | Használat |
|---|---|
| `mainTrackingNumber` | Az első csomag fuvarozói követési száma; adja meg a vásárlónak |
| `trackingNumber` | Az összes csomag fuvarozói követési száma, vesszővel elválasztva |
| `shippingPrice` | Felszámított összeg |
| `labelStatus` | `ready`: a `shippingLabel` tartalmazza a PDF-et. `pending`: megvásárolva és terhelve, a fuvarozó még nem készítette el a fájlt; hívja meg később újra. `failed`: a háttérben futó lekérés leállt; az újabb hívás újraindítja |
| `needSubmitShippingInformation` | `true`, ha ennél a módnál be kell küldeni a szállítási információkat (13. lépés) |

A `type` lehet `ORDER_ID` (alapértelmezett), `TRACKING_NUMBER` (a Superroute csomagszáma) vagy `THIRD_PARTY_TRACKING_NUMBER` (a fuvarozói szám). Küldje a `rate_id` értéket, hogy a címkét a kiválasztott szolgáltatással vásárolja meg; enélkül a mód az alapértelmezett díjjal vásárol.

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

```graphql
mutation {
  labelserviceGetShippingDetail(
    id: "128455"
    type: "ORDER_ID"
    rate_id: "rat_5f0c2a9e41d84b7c9a3e6d21b8f47c10"
  )
}
```

**Ellenőrzés:** a `mainTrackingNumber` nem üres, a `labelStatus` pedig `ready` (vagy `pending`, amely egy későbbi hívásnál `ready` lesz). A második hívás ugyanazt a követési számot és ugyanazt a `shippingPrice` értéket adja vissza.

## 9. A PDF letöltése

A raktár ebből a hívásból nyomtatja ki a fuvarozó címkéjét. Ha a címke még nincs megvásárolva, az első hívás az alapértelmezett díjjal vásárolja meg, a 8. lépéshez hasonlóan; a szolgáltatás rögzítéséhez először a 8. lépést hívja meg.

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

```bash
curl -X POST https://YOUR_HOST/api/v1/labelservice/getShippingLabel \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "128455",
    "type": "ORDER_ID",
    "base64": 1
  }'
```

```json
"JVBERi0xLjQKJcOkw7zDtsOfCjIgMCBvYmoKPDwvTGVuZ3RoIDMgMCBSL0ZpbHRlci9GbGF0ZURlY29kZT4+..."
```

- `base64: 1` esetén a törzs a PDF egyetlen base64 karakterláncként; dekódolja, és küldje a nyomtatóra.
- `base64: 0` esetén a válasz maga a PDF-fájl (`application/pdf`).

A `type` lehet `ORDER_ID` (alapértelmezett), `TRACKING_NUMBER` vagy `THIRD_PARTY_TRACKING_NUMBER` (a fuvarozói szám). Ez a **fuvarozó hivatalos címkéje**. A darabszámot a foglalás rögzíti.

**GraphQL:** `labelserviceGetShippingLabel` ([GraphQL kézikönyv](/api/graphql/documentation#/labelservice/labelserviceGetShippingLabel)). A GraphQL mindig a base64 karakterláncot adja vissza.

**Ellenőrzés:** a PDF megnyílik, és a 8. lépés fuvarozói vonalkódját / követési számát mutatja. Nyomtasson egy tesztpéldányt, majd semmisítse meg — tesztcímkét ne adjon át fuvarozónak.

## 10. Követés

Az áruház a vásárló rendelési oldalán mutatja a csomag haladását. A nyilvános követési végpont nem igényel tokent, és elfogadja a 8. lépésből származó fuvarozói számot.

**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/7023210039414604
```

```json
{
  "result": true,
  "is_third_party_tracking": true,
  "deliveried": false,
  "data": [
    {
      "tracking_event_status_id": 430,
      "otep_status": "in_transit",
      "description": "Item in transit",
      "location_city": "Mississauga",
      "updated_at_localized": "2026-09-29 18:42"
    }
  ]
}
```

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

```graphql
query {
  trackingPublic(trackingNumber: "7023210039414604") {
    result
    deliveried
    is_third_party_tracking
    data {
      tracking_event_status_id
      otep_status
      description
      updated_at_localized
    }
    third_party_info { tracking_number status carrier_tracking_link }
    proofs { file_id type full_url signed_url }
  }
}
```

- Az `is_third_party_tracking` true, ha az események a fuvarozótól származnak.
- `data`: a legújabb esemény az első. A `tracking_event_status_id` / `otep_status` alapján ágazzon el, ne a `description` alapján. A korai események még „információ elküldve” állapotúak lehetnek, amíg a fuvarozó be nem olvassa a csomagot.
- A `deliveried` true, és a `500` jelentése kézbesítve; a `proofs[]` ekkor tartalmazhat aláírást (`type` `1`) vagy fényképet (`type` `2`).

**Ellenőrzés:** a keresés a most létrehozott küldeményt adja vissza. Ismeretlen vagy törölt szám esetén a válasz `404` `result: false` értékkel.

## 11. Eseményértesítések beállítása

A webhookok kiváltják a lekérdezést: az áruház szervere megkap minden fuvarozói beolvasást, és frissíti a rendelést anélkül, hogy a 10. lépést ütemezetten hívná.

| Beállítás | Esemény | Mikor |
|---|---|---|
| `tracking_event_webhook_url` | `tracking.event` | Fuvarozói beolvasások, kézbesítés alatt, kézbesítve |
| `order_status_change_webhook_url` | `order.status_change` | Állapot az Ön rendszerében |
| `order_create_webhook_url` | `order.created` | Címkerendelés jött létre (7. lépés); címkerendeléseknél csak akkor érkezik, ha az `order_created_webhook_all_types` értéke `1` |

**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 '{
    "tracking_event_webhook_url": "https://shop.northbound-outfitters.ca/hooks/superroute",
    "order_create_webhook_url": "https://shop.northbound-outfitters.ca/hooks/superroute",
    "order_created_webhook_all_types": 1,
    "webhook_sign_secret": "YOUR_LONG_RANDOM_SECRET",
    "webhook_verify_ssl": 1
  }'
```

```json
{
  "result": true,
  "changed_keys": [
    "tracking_event_webhook_url",
    "order_create_webhook_url",
    "order_created_webhook_all_types",
    "webhook_sign_secret",
    "webhook_verify_ssl"
  ],
  "recipient_type": "business",
  "settings": {
    "webhook_sign_secret": "************CRET",
    "tracking_event_webhook_url": "https://shop.northbound-outfitters.ca/hooks/superroute",
    "order_create_webhook_url": "https://shop.northbound-outfitters.ca/hooks/superroute",
    "order_created_webhook_all_types": 1,
    "webhook_verify_ssl": 1
  }
}
```

- Csak az elküldött kulcsok módosulnak; ismeretlen kulcs esetén a válasz `400`.
- A `changed_keys` felsorolja, mi lett tárolva. A titkos kulcsot a válasz mindig maszkolva adja vissza.

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

Minden `tracking.event` tartalmazza az `order_id`, `tracking_event_status_id`, `tracking_event_key`, `tracking_number` és `external_tracking_number` mezőt; az `order_id` (a 7. lépésből származó `id`) alapján párosítsa a saját rendeléséhez.

A **v2** ellenőrzést a nyers törzsön végezze: `HMAC_SHA256(timestamp + "." + raw_body, secret)` a `X-Webhook-Signature-V2` értékével összevetve. Deduplikáljon az `X-Webhook-Event-Id` alapján. Válaszoljon **2xx kóddal 3 másodpercen belül**.

```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;
}
```

Az `order_cancel_failed_webhook_url` (`order.cancel_failed`) a 12. lépés címketörléseinél nem kerül elküldésre; az elutasított címketörlést az adott hívás válasza jelzi.

**Ellenőrzés:** egy teszt `submitOrder` hívás `order.created` eseményt küld a rendelés `id` értékével, az első fuvarozói beolvasás pedig `tracking.event` eseményt. Érvénytelen aláírást a fogadónak `401` kóddal kell elutasítania.

## 12. Törlés

A fel nem adott címkét törölni kell, hogy a fuvarozó ne számlázza ki; a terhelt összeg visszakerül a fiókra. A törlés csak addig lehetséges, amíg a fuvarozó még engedi (általában az átvétel előtt).

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

```bash
curl -X POST https://YOUR_HOST/api/v1/labelservice/cancelShippingLabel \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: nb-10482-cancel" \
  -d '{"id": 128455}'
```

```json
{
  "result": true,
  "message": "Shipping Label cancelled successfully"
}
```

- Pontosan egyet küldjön a következők közül: `id` (a rendelésazonosító) vagy `tracking_number` (a Superroute vagy a fuvarozói követési szám). Mindkettő elküldése esetén a válasz `400`.
- `result: true`: a fuvarozó elfogadta a törlést, és a címke díja visszatérítésre került.

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

Ha a csomag már a fuvarozónál van, a fuvarozó elutasítja a törlést: a válasz `400` `result: false` értékkel és a fuvarozó üzenetével. Olyan rendelés, amelynek címkéjét soha nem vásárolták meg, ezzel a hívással nem törölhető.

**Ellenőrzés:** a válasz `result: true`, és a szám nyilvános követése `404` választ ad. Az azonos `Idempotency-Key` kulccsal ismételt kérés a tárolt választ adja vissza; ugyanarra a rendelésre küldött új törlési kérés `400` választ ad `This order already cancelled` üzenettel.

## 13. Szállítási információk beküldése és napzárás (csak ha ez a mód megköveteli)

Egyes fuvarozóknak az átvétel előtt meg kell kapniuk a nap küldeményeit (manifest). A 8. lépés ezt rendelésenként a `needSubmitShippingInformation` mezőben jelzi. Gyűjtse ezeket a rendelésazonosítókat a nap folyamán, és az utolsó címke után küldje be őket.

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

```bash
curl -X POST https://YOUR_HOST/api/v1/labelservice/submitShippingInformation \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ids": [128455, 128461, 128470]}'
```

```json
{
  "result": true,
  "message": "Processed 3 orders. Success: 3, Failed: 0",
  "data": {
    "total_processed": 3,
    "success_count": 3,
    "failure_count": 0,
    "details": [
      { "order_id": 128455, "result": true, "message": "Successful" },
      { "order_id": 128461, "result": true, "message": "Successful" },
      { "order_id": 128470, "result": true, "message": "Successful" }
    ]
  }
}
```

- `details[]`: rendelésenként egy sor; a `result: false` értékű rendeléseket a `message` mezőben megadott ok kijavítása után küldje be újra.
- `404` `No eligible orders found for shipping information submission`: egyik azonosítóhoz sem tartozik olyan megvásárolt címke, amelyet még be kell küldeni.

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

Ezután zárja le a napot. A hívásnak nincs törzse, és a hívó összes címkerendelésére vonatkozik.

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

```bash
curl -X POST https://YOUR_HOST/api/v1/labelservice/endofday \
  -H "Authorization: Bearer ACCESS_TOKEN"
```

```json
{
  "result": true,
  "success": 0,
  "failed": 0,
  "success_ids": [],
  "failed_ids": []
}
```

- `400` `There are orders need to submit shipping information` üzenettel: egyes megvásárolt címkéket még be kell küldeni; küldje be őket a `submitShippingInformation` hívással, majd hívja meg újra.

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

Hagyja ki ezt a lépést, ha a nap egyetlen rendelése sem jelzett `needSubmitShippingInformation: true` értéket.

**Ellenőrzés:** a `submitShippingInformation` `failure_count: 0` értéket jelez, az `endofday` pedig `200` választ ad. Először tesztmódon futtassa.

## 14. Hibakezelés

A címkeszolgáltatás hibái `message` mezőt tartalmaznak; `code` csak ott szerepel, ahol a táblázat feltünteti.

| Helyzet | HTTP-állapot | Kód | Az integráció teendője |
|---|---|---|---|
| Hiányzó vagy lejárt token, vagy nincs engedélyezve az API-hozzáférés | `401` | — (`Unauthorized`) | Lépjen be újra; ha a hiba továbbra is fennáll, kérje a vállalkozást az API-hozzáférés engedélyezésére |
| A `shipping_method` hiányzik, vagy a hívó számára nem elérhető | `400` | — | Töltse be újra a módlistát (5. lépés), és abból használjon `id` értéket |
| A mód nem kínálja a `package_type` típust | `400` | — | Használjon kulcsot az 5. lépés `package_type` mezőjéből |
| Érvénytelen cím vagy csomag, vagy a fuvarozó nem ad díjat | `400` | — (a fuvarozó üzenete) | Jelenítse meg az üzenetet, javítsa az adatokat, és kérjen újra árajánlatot |
| Az `auto_deduplication` értéke `1`, és a `ref` már létezik | `400` | — (`exist_order_ids`) | Új rendelés létrehozása helyett használja az `exist_order_ids` szerinti meglévő rendelést |
| Ugyanaz az `Idempotency-Key` eltérő törzzsel | `409` | `IDEMPOTENCY_CONFLICT` | Eltérő kéréshez használjon új kulcsot |
| Ugyanaz az `Idempotency-Key`, amíg az első kérés még fut | `409` | `IDEMPOTENCY_IN_PROGRESS` | Várjon `Retry-After` másodpercet, majd ismételje meg ugyanazzal a kulccsal és törzzsel |
| Az ügyfél egyenlege és hitelkerete nem fedezi a címkét | `400` | `INSUFFICIENT_BALANCE` | Töltse fel az egyenleget az `insufficient_balance` részletek (`shortfall`, `add_funds_url`) alapján, majd hívja meg újra a 8. lépést |
| A címke megvásárolva, a fuvarozói fájl még nem kész | `400` az első vásárláskor, később `200` | `shipment_label_not_ready` | Várjon, amíg a `labelStatus` értéke `pending`; ha `failed`, hívja meg újra a 8. lépést |
| A rendelésazonosító vagy szám nem a hívóé | `401` | — (`Not Auth`) | Ellenőrizze az azonosítót és a `type` értéket; használja azt a fiókot, amely a rendelést létrehozta |
| A fuvarozó elutasította a törlést, vagy a rendelés már törölve van | `400` | — | Kezelje a címkét feladottként (vagy már töröltként); ne ismételje meg |
| A követési szám ismeretlen vagy törölt | `404` | — | Ne jelenítse meg tovább az idővonalat ehhez a számhoz |
| `endofday` még be nem küldött küldeményekkel | `400` | — | Futtassa a `submitShippingInformation` hívást ezekre a rendelésekre, majd hívja meg újra |

## Tesztlista

Használjon olyan célcímet, amely az Ön ellenőrzése alatt áll, és törölhető módot:

- [ ] A módlista nem üres; rögzített egy `id` értéket.
- [ ] A díjlekérés árat ad az adott módra és célcímre.
- [ ] A Submit rendelés-`id` és `rates[].rate_id` értéket ad; ugyanaz az `Idempotency-Key` nem hoz létre második rendelést.
- [ ] A `getShippingDetail` a kiválasztott `rate_id` értékkel `mainTrackingNumber` értéket ad; a második hívás nem terhel újra.
- [ ] A címke PDF-je megnyílik, és a fuvarozói követési számot mutatja.
- [ ] A nyilvános követés megtalálja a küldeményt azon a számon.
- [ ] Megérkezik a `tracking.event` (és engedélyezés esetén az `order.created`); a v2 aláírás ellenőrizhető.
- [ ] A fuvarozó elfogad egy `items` mezőt tartalmazó, határon átnyúló tesztküldeményt.
- [ ] A törlés sikerül, **vagy** megerősítette, hogy ez a mód foglalás után nem törölhető.
- [ ] Ha a mód napzárást igényel, egy tesztfutás hiba nélkül lefut.
