# Szállítási szolgáltatások

A szállítási szolgáltatások API-ja lehetővé teszi, hogy egy ügyfélfiók lefoglalja azokat a szállítási szolgáltatásokat, amelyeket a logisztikai szolgáltatója beállított és hozzárendelt. Az ügyfél saját rendszere listázza a használható szolgáltatásokat, betölti egy szolgáltatás szabályait, beárazza a küldeményt, létrehozza a szállítási rendelést, kifizeti a fiók egyenlegéből, és a kézbesítésig követi a küldeményt. Ez az útmutató azoknak a fejlesztőknek szól, akik egy importőr, kereskedő vagy nagykereskedő rendszerét kapcsolják össze az őt kiszolgáló logisztikai szolgáltatóval.

## 1. Mit készíthet vele

Az útmutató minden példája egyetlen forgatókönyvet használ. A **Harbourline Imports Inc.**, egy torontói teaimportőr, ügyfélfiókkal rendelkezik a logisztikai szolgáltatójánál. A szolgáltató az `intl_express` (International Express) szolgáltatást kínálja a Toronto Hub raktárából (raktárazonosító: `7`). A Harbourline két karton mintateát ad le a Toronto Hubban egy seattle-i forgalmazó részére, a `HLI-PO-1058` beszerzési rendelése alapján.

- **Foglalás a beszerzésirendelés-kezelő rendszerből.** Egy beszerzési rendelés kiadásakor a Harbourline rendszere beárazza a küldeményt az `intl_express` szolgáltatáson, létrehozza a szállítási rendelést a beszerzési rendelés számával mint hivatkozással, és kifizeti az előre feltöltött fiókegyenlegből anélkül, hogy bárki megnyitná a szolgáltató portálját.
- **Árellenőrzés a kötelezettségvállalás előtt.** A Harbourline beszerzője a küldemény lefoglalása előtt látja a két karton fuvardíját, pótdíjait, adóját és végösszegét, és az a küldemény, amelyet a szolgáltatás nem tud beárazni, még a rendelés létrejötte előtt leáll.
- **Küldeményállapot az ERP-ben.** Minden karton követési száma a beszerzési rendeléshez rendelve tárolódik; a webhookok a rendelés állapotát és a követési idővonalat az ERP-be juttatják, egy éjszakai feladat pedig a rendeléslistával egyezteti az adatokat.
- **Ellenőrzött módosítások.** A ki nem fizetett foglalás helyben javítható, a már nem szükséges foglalás pedig lemondható, a kifizetett összeg visszakerül a fiók jóváírásai közé.

## 2. Mire terjed ki ez az útmutató

Ezt a végpontcsaládot akkor használja, ha a hívó a logisztikai vállalkozás **ügyfele**, és a vállalkozás saját szállítási szolgáltatásainak egyikét foglalja le: a vállalkozás állítja be az árazási tervet, a raktárakat, a pótdíjakat és a csomagolást, és rendeli hozzá a szolgáltatásokat az ügyfélhez. Az ügyfél csak a hozzá rendelt szolgáltatásokat látja és foglalhatja le.

A következő esetekben másik végpontcsaládot használjon:

- A hívó maga a logisztikai vállalkozás (vállalkozói fiók), és saját flottájával foglal aznapi vagy helyi felvételeket és kézbesítéseket: olvassa el a **Felvétel és kézbesítés (saját flotta)** útmutatót.
- A hívó fuvarozói címkéket vásárol (például UPS vagy FedEx) a fiók kedvezményes díjain: olvassa el a **Fuvarozói címkék** útmutatót.
- Az ügyfél a szolgáltató raktárában tárolja az árut, és készletből szállítja ki: olvassa el a **Tárolás és kiszállítás** útmutatót.

Az **Uniorder: egy API minden küldeményhez** (`/api/v1/uniorder/...`) az ajánlott egységes belépési pont a helyi kiszállítás és a fuvarozói címkék új integrációihoz. Az Uniorder nem terjed ki a szállítási szolgáltatásokra: a szállítási szolgáltatások rendelései kizárólag az itt leírt `/api/v1/customer/shipping-orders/...` végpontokon hozhatók létre és kezelhetők.

## 3. Mielőtt elkezdené

- **Fióktípus.** A logisztikai vállalkozás **ügyfélfiókja**, amelyen a vállalkozás engedélyezte az **API-jogosultságot**. Vállalkozói vagy alkalmazotti fiók nem tud belépni az alábbi ügyfélbelépéssel.
- **Szolgáltatás-hozzárendelés.** A vállalkozásnak legalább egy aktív szállítási szolgáltatást hozzá kell rendelnie az ügyfélhez. Hozzárendelt szolgáltatás nélküli ügyfél üres szolgáltatáslistát kap.
- **Tesztadatok.** Egyeztessen a vállalkozással egy tesztszolgáltatás-kódról, egy tesztraktárról és kis előre feltöltött egyenlegről a tesztfiókon. Használjon olyan hivatkozást, mint `HLI-PO-1058` vagy `DEV-SHIP-001`, hogy a tesztrendelések könnyen megtalálhatók és lemondhatók legyenek.
- **Tokenkezelés.** Az API-t csak a szerveréről hívja. A jelszót és a hozzáférési tokent ne tegye elérhetővé böngészőkben és mobilkliensekben. A token a belépés után egy héttel lejár (`expires_at`); a lejárat előtt lépjen be újra.
- **Helyőrzők.** Cserélje a `YOUR_HOST` értéket a logisztikai vállalkozás gazdagépnevére, az `ACCESS_TOKEN` értéket pedig a belépési lépésben kapott tokenre.
- **JSON-hibák.** Minden kérésben küldjön `Accept: application/json` fejlécet, hogy az érvényesítési hibák átirányítás helyett JSON-t adjanak vissza.

## 4. Belépés ügyfélként

A belépés az ügyfél e-mail-címét és jelszavát bearer tokenre cseréli. Az útmutató minden későbbi hívása ezt a tokent küldi.

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

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

```json
{
  "result": true,
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOi...",
  "token_type": "Bearer",
  "expires_at": "2026-10-05 09:15:00",
  "expires_timestamp": 1791206100,
  "name": "Harbourline Imports Inc."
}
```

- `access_token`: minden kérésben küldje `Authorization: Bearer ACCESS_TOKEN` formában. A GraphQL ugyanazt a fejlécet használja a `POST /api/graphql` híváson.
- `expires_at` / `expires_timestamp`: ütemezzen új belépést ezen időpont előttre.

**Ellenőrzés:** a válaszban `result: true` és `access_token` szerepel. A token nélküli kérés `401`-et ad; a nem ügyfélfiókkal vagy API-jogosultság nélküli fiókkal történő belépés szintén `401`-et ad.

## 5. Az ügyfélhez rendelt szolgáltatások listázása

A szolgáltatáslista megmutatja az integrációnak, mely szolgáltatáskódokat foglalhatja le, és hogy az egyes szolgáltatások raktári leadást, felvételt vagy mindkettőt fogadnak-e. Tárolja a `service_code` értéket; minden későbbi szolgáltatáshívás ezt használja.

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

```bash
curl https://YOUR_HOST/api/v1/customer/shipping-orders/services \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Accept: application/json"
```

```json
{
  "result": true,
  "data": {
    "services": [
      {
        "id": 12,
        "service_code": "intl_express",
        "name": { "en": "International Express" },
        "offer_pickup": true,
        "allow_warehouse_delivery": true,
        "support_multi_package": true,
        "allow_special_requirements": false,
        "allow_purchase_supplies": true,
        "send_confirmation_email": true,
        "warehouses": [{ "id": 7, "name": "Toronto Hub" }]
      }
    ]
  }
}
```

- `service_code`: minden későbbi szolgáltatáshívás útvonalparamétere.
- `offer_pickup` / `allow_warehouse_delivery`: az `origin_type` megengedett értékei (`pickup` / `warehouse`).
- `support_multi_package`: egy rendelés tartalmazhat-e egynél több csomagsort.
- Az üres `services` tömb azt jelenti, hogy ehhez az ügyfélhez nincs szolgáltatás rendelve.

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

```graphql
query CustomerShippingOrderServices {
  customerShippingOrderServices
}
```

**Ellenőrzés:** a lista legalább egy szolgáltatást tartalmaz, és Ön eltárolta annak `service_code` értékét (ebben az útmutatóban: `intl_express`).

## 6. A szolgáltatás konfigurációjának betöltése

A konfiguráció mindent visszaad, amire egy szolgáltatás rendelési űrlapjának szüksége van: a leadást fogadó raktárakat, a választható pótdíjakat, a csomagolási és kellékkatalógust, a mértékegységeket, valamint azokat az országokat, ahonnan a szolgáltatás felvehet és ahová kézbesíthet. Az árazás vagy bármi létrehozása előtt ellenőrizze ehhez a rendelési adatait.

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

```bash
curl https://YOUR_HOST/api/v1/customer/shipping-orders/services/intl_express/config \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Accept: application/json"
```

```json
{
  "result": true,
  "data": {
    "service": {
      "service_code": "intl_express",
      "offer_pickup": true,
      "allow_warehouse_delivery": true,
      "allow_special_requirements": false,
      "allow_purchase_supplies": true,
      "weight_mode": 2,
      "volumetric_factor": 5000
    },
    "warehouses": [
      {
        "id": 7,
        "name": "Toronto Hub",
        "address_1": "250 Dundas St W",
        "city": "Toronto",
        "province": "ON",
        "country": "CA",
        "postcode": "M5T 2Z5",
        "telephone": "4165550100"
      }
    ],
    "surcharges": [],
    "has_surcharges": false,
    "packagings": [],
    "products": [],
    "weight_units": { "2": { "name": "weight_kg", "accuracy": 3 } },
    "dimension_units": { "2": { "name": "dimension_cm", "accuracy": 1 } },
    "delivery_allowed_countries": ["CA", "US"],
    "pickup_allowed_countries": ["CA"]
  }
}
```

- `warehouses[].id`: a küldendő `warehouse_id`, ha az `origin_type` értéke `warehouse`. A listában nem szereplő azonosítót a létrehozás elutasítja.
- `service.weight_mode`: az árazási terv által megkövetelt csomagmezők: `0` tényleges súly (weight), `1` térfogatsúly (hosszúság, szélesség és magasság), `2` díjköteles súly (mindkettő). A `null` azt jelenti, hogy a szolgáltatást kézzel árazzák. Küldje el a súlyt és mindhárom méretet, hogy minden módnak megfeleljen.
- `delivery_allowed_countries` / `pickup_allowed_countries`: a becslés meghívása előtt utasítsa el az e listákon kívül eső cél- vagy felvételi országot.
- `surcharges[].id`, `packagings[].id`, `products[].id`: az opcionális pótdíjakhoz, csomagoláshoz és kellékvásárláshoz használandó azonosítók.
- `weight_units` / `dimension_units`: a csomag mértékegységeit számként kell küldeni. Küldjön `weight_unit: 2` (kg) és `dimension_unit: 2` (cm) értéket, ahogy az útmutató minden példája; a mezők elhagyásakor is ezek az alapértékek.

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

```graphql
query CustomerShippingOrderServiceConfig {
  customerShippingOrderServiceConfig(serviceCode: "intl_express")
}
```

**Ellenőrzés:** a `result` értéke `true`, és raktári leadás esetén a `warehouses` tartalmazza a használni kívánt raktárat. A `403` azt jelenti, hogy a szolgáltatás nincs ehhez az ügyfélhez rendelve; a `404` azt, hogy a szolgáltatáskód nem létezik vagy inaktív.

## 7. Az ár becslése

A becslés a szolgáltatás árazási tervével árazza be a küldeményt, anélkül hogy bármit rögzítene. Mutassa meg a végösszeget a beszerzőnek, és ne hozza létre a rendelést, ha a becslés elutasítást jelez.

**REST:** `POST /api/v1/customer/shipping-orders/services/{serviceCode}/estimate-price` — [REST kézikönyv](/api/documentation#/paths/v1-customer-shipping-orders-services-serviceCode--estimate-price/post)

```bash
curl -X POST https://YOUR_HOST/api/v1/customer/shipping-orders/services/intl_express/estimate-price \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "origin_type": "warehouse",
    "warehouse_id": 7,
    "delivery_postcode": "98104",
    "delivery_country": "US",
    "packages": [{
      "weight": 12,
      "length": 50,
      "width": 40,
      "height": 35,
      "weight_unit": 2,
      "dimension_unit": 2,
      "quantity": 2
    }]
  }'
```

```json
{
  "result": true,
  "data": {
    "needs_manual_quote": false,
    "shipping_fee": 118.40,
    "shipping_fee_total": 131.20,
    "fuel_surcharge": 12.80,
    "pickup_fee": null,
    "surcharges_total": 0,
    "sub_total": 131.20,
    "tax": { "total_tax": 17.06 },
    "total": 148.26,
    "currency": "CAD",
    "all_fees_calculated": true,
    "has_items_needing_quote": false,
    "unpriced_items": [],
    "refused": false,
    "refusal_message": null
  }
}
```

- `origin_type`: `warehouse` (az ügyfél egy raktárban adja le az árut; küldje a `warehouse_id` értéket) vagy `pickup` (a szolgáltató veszi fel; küldje a `pickup_postcode` és `pickup_country` értéket). Csak olyan értéket használjon, amelyet az 5. lépés megenged.
- `packages`: azonos csomagok csoportjánként egy sor; a `quantity` megsokszorozza a sort.
- `total` és `currency`: a megjelenítendő összeg. A `total` értéke `null`, amíg bármely díj nincs kiszámítva.
- `needs_manual_quote` / `has_items_needing_quote`: a vállalkozás kézzel árazza a rendelést; a rendelés létrehozható, és a kifizetés azután történik, hogy a vállalkozás megadta az árat.
- `refused` / `refusal_message`: a szolgáltatás elutasítja azokat a küldeményeket, amelyeket nem tud beárazni. Ne hozza létre a rendelést; helyette jelenítse meg a `refusal_message` értéket.
- Opcionális bemenetek: `surcharges`, `products` (termékazonosítók és mennyiségek leképezése, csak akkor érvényesül, ha az `allow_purchase_supplies` értéke igaz), `has_special_requirements`, `coupon_code`.

**Ellenőrzés:** a `result` értéke `true`, a `refused` értéke `false`, és vagy a `total` tartalmaz értéket, vagy a `needs_manual_quote` értéke `true`.

## 8. A szállítási rendelés létrehozása

A létrehozási hívás lefoglalja a küldeményt a szolgáltatáson. Az integráció a visszaadott `id` értéket a saját beszerzési rendeléséhez rendelve tárolja; minden későbbi hívás ezt az azonosítót használja.

**REST:** `POST /api/v1/customer/shipping-orders/services/{serviceCode}/orders` — [REST kézikönyv](/api/documentation#/paths/v1-customer-shipping-orders-services-serviceCode--orders/post)

Küldjön a saját stabil azonosítójából (itt a beszerzési rendelés számából) képzett `Idempotency-Key` fejlécet. Az azonos kulccsal és azonos törzzsel ismételt kérés az első választ adja vissza `"replayed": true` értékkel és `Idempotency-Replayed: true` fejléccel, és nem hoz létre második rendelést.

```bash
curl -X POST https://YOUR_HOST/api/v1/customer/shipping-orders/services/intl_express/orders \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -H "Idempotency-Key: hli-po-1058" \
  -d '{
    "origin_type": "warehouse",
    "warehouse_id": 7,
    "reference": "HLI-PO-1058",
    "delivery_name": "Cascade Tea Distributors",
    "delivery_telephone": "2065550143",
    "delivery_email": "receiving@cascadetea.example",
    "delivery_address_1": "300 5th Ave S",
    "delivery_city": "Seattle",
    "delivery_province": "WA",
    "delivery_country": "US",
    "delivery_postcode": "98104",
    "note": "Two cartons of sample tea, dock door B",
    "package": [{
      "description": "Sample tea carton",
      "weight": 12,
      "length": 50,
      "width": 40,
      "height": 35,
      "weight_unit": 2,
      "dimension_unit": 2,
      "value": 380,
      "quantity": 2
    }]
  }'
```

```json
{
  "result": true,
  "data": {
    "id": 9001,
    "status": 0,
    "total_price": 148.26,
    "price_breakdown": { "total": 148.26, "currency": "CAD" },
    "promotion_id": null,
    "tracking_number": null
  }
}
```

- A csomagok törzskulcsa létrehozáskor `package` (a becslésnél `packages`). Minden N `quantity` értékű sorból N csomag lesz, és minden csomag saját követési számot kap.
- Kötelező mezők: `delivery_name`, `delivery_telephone`, `delivery_address_1`, `delivery_city`, `delivery_province`, `delivery_country`, `delivery_postcode`, `origin_type`, `package[].weight`; továbbá `warehouse` esetén `warehouse_id`, illetve `pickup` esetén `pickup_name`, `pickup_telephone`, `pickup_address_1`, `pickup_city`, `pickup_province`, `pickup_country`, `pickup_postcode`.
- Opcionális mezők: `reference` (a rendelés `reference_number` mezőjében tárolódik), `delivery_email`, `delivery_address_2`, `scheduled_date`, `time_window`, `note`, `special_requirements` (szövegsorok tömbje, csak akkor érvényesül, ha a szolgáltatás engedi), `products`, `surcharges`, `coupon_code`.
- `id`: tárolja el. A `status` `0` értéke Pending (fizetésre vár).
- `total_price`: a 9. lépésben felszámított összeg. Értéke `0`, amíg a rendelés kézi árajánlatra vár.
- A rendelésszintű `tracking_number` értéke `null`; a követési számok a csomagokon vannak, és a 10. lépésben olvashatók ki.
- Új rendelés esetén a végpont HTTP `201` választ ad.

**Ellenőrzés:** a válaszban `result: true` és `id` szerepel. Ugyanannak a kérésnek ugyanazzal az `Idempotency-Key` értékkel történő megismétlése ugyanazt az `id` értéket adja vissza `"replayed": true` értékkel.

## 9. A rendelés kifizetése a fiók egyenlegéből

A szállítási rendeléseket teljes egészében az ügyfél fiókegyenlegéből kell kifizetni. A kifizetett rendelés Pending állapotból Confirmed állapotba kerül, és a szolgáltató megkezdi a feldolgozását.

Először kérdezze le az összeget:

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

```bash
curl https://YOUR_HOST/api/v1/customer/shipping-orders/9001/payment-info \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Accept: application/json"
```

```json
{
  "result": true,
  "data": {
    "order_id": 9001,
    "currency": "CAD",
    "total_price": "148.26",
    "paid_amount": "0.00",
    "remaining_balance": "148.26",
    "user_balance": "500.00",
    "has_sufficient_balance": true,
    "shortfall": 0,
    "payment_options": [
      { "type": "remaining_balance", "amount": 148.26 }
    ]
  }
}
```

Ezután fizessen:

**REST:** `POST /api/v1/customer/shipping-orders/{id}/pay` — [REST kézikönyv](/api/documentation#/paths/v1-customer-shipping-orders-id--pay/post)

```bash
curl -X POST https://YOUR_HOST/api/v1/customer/shipping-orders/9001/pay \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"payment_type":"remaining_balance"}'
```

```json
{
  "result": true,
  "message": "Payment of $148.26 processed successfully.",
  "data": {
    "order_id": 9001,
    "status": 1,
    "amount_paid": "148.26"
  }
}
```

- `has_sufficient_balance` / `shortfall`: ha az egyenleg nem fedezi a `remaining_balance` összeget, a fizetés előtt töltse fel a fiókot.
- `payment_type`: csak a `remaining_balance` támogatott; mindig a teljes fennmaradó összeget számítja fel.
- A `data.status` `1` értéke Confirmed.

**Ellenőrzés:** a fizetési hívás `result: true` és `status` `1` értéket ad, egy második `payment-info` hívás pedig `400`-at ad, mert a rendelés teljesen ki van fizetve. Elégtelen egyenleggel a fizetési hívás `422`-t ad, és semmit nem számít fel.

## 10. A rendelés lekérdezése és a csomagok követése

A részletező hívás visszaadja az aktuális állapotot és minden csomag követési számát. Tárolja a csomagok követési számait a beszerzési rendeléshez rendelve; a nyilvános követés mindegyiket elfogadja.

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

```bash
curl https://YOUR_HOST/api/v1/customer/shipping-orders/9001 \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Accept: application/json"
```

```json
{
  "result": true,
  "data": {
    "id": 9001,
    "status": 1,
    "status_name": "Confirmed",
    "can_edit": false,
    "can_cancel": true,
    "shipping_code": "K7RW2Q",
    "tracking_number": null,
    "reference_number": "HLI-PO-1058",
    "origin_type": "warehouse",
    "warehouse_id": 7,
    "delivery_address": { "name": "Cascade Tea Distributors", "city": "Seattle", "country": "US" },
    "package_count": 2,
    "packages": [
      { "id": 55101, "description": "Sample tea carton", "tracking_number": "SR123456789012", "weight": 12, "weight_unit": 2 },
      { "id": 55102, "description": "Sample tea carton", "tracking_number": "SR123456789013", "weight": 12, "weight_unit": 2 }
    ],
    "total_price": 148.26
  }
}
```

- `status`: `0` Pending, `1` Confirmed, `2` In Transit, `3` Shipped, `4` Cancelled, `5` Failed, `6` Partially Picked Up, `7` Picked Up, `8` Processing.
- `can_edit` / `can_cancel`: a 12. lépés jelenleg engedélyezett-e.
- `packages[].tracking_number`: a tárolandó és követendő számok.
- `shipping_code`: a raktári leadóképernyők által elfogadott kód; nyomtassa rá a leadási dokumentumokra.

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

```graphql
query CustomerShippingOrderShow {
  customerShippingOrderShow(id: 9001) {
    result
    message
    data {
      id
      status
      status_name
      can_cancel
      reference_number
      packages {
        id
        tracking_number
        weight
      }
      total_price
    }
  }
}
```

Egy szolgáltatás összes rendelésének egyeztetéséhez, például egy éjszakai feladatban, listázza őket szűrővel. Az `id` szűrő a rendelésazonosítóra, egy követési számra vagy a hivatkozásra illeszkedik.

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

```bash
curl "https://YOUR_HOST/api/v1/customer/shipping-orders/services/intl_express/orders?id=HLI-PO-1058&created_at_from=2026-09-01&per_page=20" \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Accept: application/json"
```

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

A nyilvános követéshez nem kell token, és egy csomag eseményidővonalát adja vissza:

**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 \
  -H "Accept: application/json"
```

```json
{
  "result": true,
  "deliveried": false,
  "data": [
    {
      "tracking_event_status_id": 1,
      "otep_status": "received",
      "description": "Received at warehouse",
      "location_city": "Toronto",
      "updated_at": "2026-09-29 10:42:00"
    }
  ]
}
```

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

```graphql
query TrackingPublic {
  trackingPublic(trackingNumber: "SR123456789012") {
    result
    message
    deliveried
    data {
      tracking_event_status_id
      otep_status
      description
      location_city
      updated_at
    }
  }
}
```

**Ellenőrzés:** a részletek ennek az ügyfélnek a rendelését adják vissza csomagonként egy követési számmal, a nyilvános követés pedig csomagkövetési számra `result: true` értéket ad. Egy másik ügyfél rendelésazonosítója `404`-et ad.

## 11. Webhookok fogadása

A webhookok a rendelés létrehozását, az állapotváltozásokat és a követési eseményeket a szerverére juttatják, így az integrációnak nem kell lekérdezésekkel figyelnie. Az ügyfélfiók maga állítja be a webhook URL-jeit és az aláírási titkot.

Szállítási rendelés létrehozásakor a szolgáltató a diszpécsercsapata számára egy kapcsolódó felvételi rendelést is létrehoz. A webhookok erre a kapcsolódó rendelésre érkeznek: a `ref` értéke `Shipping-Pickup-{shipping order id}` (például `Shipping-Pickup-9001`), és minden csomagja az `external_tracking_number` mezőben hordozza a szállítási csomag követési számát. A beérkező eseményeket e két mező alapján párosítsa.

| Beállítás | Esemény | Mit tesz az integráció |
|---|---|---|
| `order_create_webhook_url` | `order.created` | Az eseményt a `ref` és a `packages[].external_tracking_number` alapján a szállítási rendeléshez kapcsolja |
| `tracking_event_webhook_url` | `tracking.event` | Az eseményt hozzáfűzi a csomag idővonalához |
| `order_status_change_webhook_url` | `order.status_change` | Frissíti a rendszerében megjelenített állapotot |

**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" \
  -H "Accept: application/json" \
  -d '{
    "order_create_webhook_url": "https://erp.harbourline-imports.example/hooks/order-created",
    "tracking_event_webhook_url": "https://erp.harbourline-imports.example/hooks/tracking",
    "order_status_change_webhook_url": "https://erp.harbourline-imports.example/hooks/status",
    "webhook_sign_secret": "hli-webhook-secret-7f2c9a1e5b"
  }'
```

```json
{
  "result": true,
  "changed_keys": [
    "order_create_webhook_url",
    "tracking_event_webhook_url",
    "order_status_change_webhook_url",
    "webhook_sign_secret"
  ],
  "recipient_type": "customer",
  "settings": {
    "webhook_sign_secret": "*************************1e5b",
    "tracking_event_webhook_url": "https://erp.harbourline-imports.example/hooks/tracking"
  }
}
```

- Csak a beküldött kulcsok változnak; az üres karakterlánc törli az URL-t. A `webhook_sign_secret` hossza 16–255 karakter lehet, és amíg a titok üres, nem küldődik webhook.
- A `recipient_type` értéke ügyfélfiók esetén `customer`.

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

```graphql
mutation WebhookSettingsUpdate {
  webhookSettingsUpdate(
    order_status_change_webhook_url: "https://erp.harbourline-imports.example/hooks/status"
    tracking_event_webhook_url: "https://erp.harbourline-imports.example/hooks/tracking"
  )
}
```

Ellenőrizze a **v2** aláírást a nyers törzsön: `HMAC_SHA256(timestamp + "." + raw_body, secret)`, összevetve az `X-Webhook-Signature-V2` fejléccel. Az ismétlődéseket az `X-Webhook-Event-Id` alapján szűrje ki. **3 másodpercen belül 2xx** választ adjon, és az eseményt ezután 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:** a beállítási hívás után egy tesztlétrehozás olyan `order.created` eseményt eredményez, amelynek `ref` értéke `Shipping-Pickup-{id}` az új szállítási rendelés azonosítójával, és az aláírás-ellenőrzés sikeres.

## 12. Rendelés módosítása vagy lemondása

A rendelés Pending állapotban (fizetés előtt) javítható, Pending vagy Confirmed állapotban pedig lemondható. A kifizetett rendelés lemondásakor a kifizetett összeg visszakerül a fiók jóváírásai közé.

Módosításhoz küldje el újra a teljes rendelést ugyanazokkal a mezőkkel, mint a 8. lépésben. Az ár újraszámolódik.

**REST:** `PUT /api/v1/customer/shipping-orders/{id}` — [REST kézikönyv](/api/documentation#/paths/v1-customer-shipping-orders-id/put)

```bash
curl -X PUT https://YOUR_HOST/api/v1/customer/shipping-orders/9001 \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "origin_type": "warehouse",
    "warehouse_id": 7,
    "reference": "HLI-PO-1058",
    "delivery_name": "Cascade Tea Distributors",
    "delivery_telephone": "2065550143",
    "delivery_email": "receiving@cascadetea.example",
    "delivery_address_1": "300 5th Ave S",
    "delivery_address_2": "Suite 210",
    "delivery_city": "Seattle",
    "delivery_province": "WA",
    "delivery_country": "US",
    "delivery_postcode": "98104",
    "package": [{
      "description": "Sample tea carton",
      "weight": 12,
      "length": 50,
      "width": 40,
      "height": 35,
      "weight_unit": 2,
      "dimension_unit": 2,
      "value": 380,
      "quantity": 2
    }]
  }'
```

```json
{
  "result": true,
  "data": {
    "id": 9001,
    "status": 0,
    "total_price": 148.26,
    "promotion_id": null,
    "coupon_code": null,
    "promotion_discount": null
  }
}
```

Lemondáshoz:

**REST:** `POST /api/v1/customer/shipping-orders/{id}/cancel` — [REST kézikönyv](/api/documentation#/paths/v1-customer-shipping-orders-id--cancel/post)

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

```json
{
  "result": true,
  "data": {
    "id": 9001,
    "status": 4,
    "refund_amount": 148.26
  },
  "message": "Order cancelled. $148.26 refunded to your credit."
}
```

- A `status` `4` értéke Cancelled. A kapcsolódó felvételi rendelés eltávolításra kerül.
- `refund_amount`: a fiók jóváírásai közé visszakerülő összeg; ki nem fizetett rendelésnél `0`.
- Mielőtt ezeket a műveleteket felkínálná a felhasználóknak, olvassa ki a `can_edit` és a `can_cancel` értékét a 10. lépésből.

**Ellenőrzés:** a lemondás `status` `4` értéket ad, a részletekben pedig a `status_name` értéke `Cancelled`. Egy második lemondás, vagy egy In Transit vagy későbbi állapotú rendelés lemondása `403`-at ad a `This order can no longer be cancelled.` üzenettel; kifizetett rendelés módosítása `403`-at ad.

## 13. Hibakezelés

| Helyzet | HTTP-állapot | Kód | Mit tesz az integráció |
|---|---|---|---|
| Hiányzó, lejárt vagy érvénytelen token; belépés nem ügyfélfiókkal vagy API-jogosultság nélkül | `401` | — | Lépjen be újra; ha maga a belépés sikertelen, kérje meg a vállalkozást a fióktípus és az API-jogosultság ellenőrzésére |
| A szolgáltatás nincs ehhez az ügyfélhez rendelve | `403` | — | Olvassa be újra a szolgáltatáslistát (5. lépés), és csak hozzárendelt szolgáltatásokat foglaljon |
| Az API-t olyan platformalkalmazás-munkamenetből hívják, amelynek alkalmazásában a szállítási rendelések le vannak tiltva | `403` | `APP_CAPABILITY_DISABLED` | Kérje meg a vállalkozást, hogy engedélyezze a szállítási rendeléseket az alkalmazás számára |
| Ismeretlen vagy inaktív szolgáltatáskód; a rendelésazonosító nem található ennél az ügyfélnél | `404` | — | Frissítse a szolgáltatáslistát; ellenőrizze a tárolt rendelésazonosítót |
| Hiányzó vagy érvénytelen kötelező mező | `422` | — | Olvassa ki a törzsben az `errors` értéket, javítsa a mezőket, és küldje el újra |
| A szolgáltatás nem kínálja a kiindulási típust, vagy a raktár nem szerepel a szolgáltatás listájában | `422` | — | Az 5. és 6. lépésből származó `origin_type` és `warehouse_id` értéket használjon |
| A szolgáltatás nem tudja beárazni a küldeményt, és elutasítja a be nem árazott küldeményeket | `422` | `unpriced_refused` | Semmi nem jött létre; jelenítse meg a `message` értéket, és változtatás nélkül ne próbálja újra |
| Készleten nem lévő kellékek rendelése | `422` | — | Olvassa ki a `stock_shortages` értéket, csökkentse a mennyiségeket, és küldje el újra |
| Ugyanaz az `Idempotency-Key` eltérő törzzsel | `409` | `IDEMPOTENCY_CONFLICT` | Új rendeléshez új kulcsot használjon; eltérő tartalomhoz soha ne használjon újra kulcsot |
| Ismételt kérés, miközben az adott kulcsú első kérés feldolgozása még folyamatban van | `409` | `IDEMPOTENCY_IN_PROGRESS` | Várjon `Retry-After` másodpercig, majd próbálja újra ugyanazzal a kulccsal és törzzsel |
| Fizetés elégtelen egyenleggel | `422` | — | Töltse fel a fiókot, majd fizessen újra |
| Fizetési információ vagy fizetés teljesen kifizetett rendelésen | `400` | — | Kezelje a rendelést kifizetettként; kérdezze le a részleteit |
| Lemondás azután, hogy a rendelés kilépett a Pending vagy Confirmed állapotból | `403` | — | Jelezze, hogy a rendelés már nem mondható le; lépjen kapcsolatba a vállalkozással |
| Módosítás fizetés után | `403` | — | Mondja le, és hozzon létre új rendelést, vagy lépjen kapcsolatba a vállalkozással |
| Szerverhiba becslés, létrehozás, fizetés vagy lemondás közben | `500` | — | Próbálja újra egyszer; létrehozásnál ugyanazzal az `Idempotency-Key` értékkel |

## Tesztlista

Használjon olyan teszthivatkozást, mint `DEV-SHIP-001` vagy `HLI-PO-1058`:

- [ ] Az ügyfélbelépés `access_token` értéket ad; a token nélküli kérés `401`-et ad.
- [ ] A szolgáltatáslista nem üres, és Ön eltárolt egy `service_code` értéket.
- [ ] A konfiguráció visszaadja az adott szolgáltatás raktárait, mértékegységeit és engedélyezett országait, és az űrlapja ezeket használja.
- [ ] A becslés `total` értéket ad (vagy `needs_manual_quote: true` értéket), és az elutasított küldemény nem jön létre.
- [ ] A létrehozás `id` értéket ad; ugyanaz az `Idempotency-Key` ugyanazzal a törzzsel ugyanazt az `id` értéket adja `"replayed": true` értékkel.
- [ ] A fizetés sikeres, és az állapot Confirmed lesz, vagy meggyőződött arról, hogy elégtelen egyenleg esetén `422` érkezik, és semmi nem kerül felszámításra.
- [ ] A részletek ennek az ügyfélnek a rendelését mutatják csomagonként egy követési számmal, és a nyilvános követés minden csomagot megtalál.
- [ ] A webhookok aláírási titokkal vannak beállítva; egy tesztlétrehozás `order.created` eseményt eredményez `Shipping-Pickup-{id}` `ref` értékkel, és az aláírás-ellenőrzés sikeres.
- [ ] A tesztrendelés lemondása `status` `4` értéket és a várt `refund_amount` értéket adja; egy második lemondás `403`-at ad.
