# Felvétel és kézbesítés

A saját flottával végzett felvétel és last mile ugyanazokat az API-kat használja. Felvételi vagy kézbesítési rendelés létrehozása → helyi címke nyomtatása → követés → feliratkozás az eseményértesítésekre → tesztrendelés törlése.

A `type` választja ki a megállót: `D` kézbesítés, `P` felvétel. Hajtsa végre a lépéseket sorrendben. Az árajánlat nem kötelező, és a létrehozás előtt nem szükséges.

Cserélje a `YOUR_HOST` és `ACCESS_TOKEN` értékeket a saját környezetének értékeire.

## 1. Belépés

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

A visszaadott `access_token` értéket helyezze a kérés fejlécébe:

```
Authorization: Bearer ACCESS_TOKEN
```

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

[REST kézikönyv](/api/documentation#/paths/v1-user-login/post) · [GraphQL kézikönyv](/api/graphql/documentation#/user/userLogin)

**Ellenőrzés:** a belépés `access_token`-t ad. Az e token nélküli későbbi kérések `401`-et adnak.

## 2. Árajánlat (opcionális)

Ez a lépés opcionális. Csak árajánlatot ad vissza; rendelés nem jön létre. A létrehozáshoz nem kell előzetes árajánlat. A `type` értéke legyen `D` (kézbesítés) vagy `P` (felvétel).

**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": "H9S2H9",
    "from_country": "CA",
    "to_postcode": "H4B2T5",
    "to_country": "CA",
    "packages": [{
      "weight": 1,
      "weight_unit": 2,
      "length": 30,
      "width": 20,
      "height": 10,
      "dimension_unit": 2
    }]
  }'
```

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

```json
{
  "result": true,
  "shipping_price": "6.99",
  "currency": "CAD",
  "price_details": {
    "shipping_fee": "6.99",
    "tax_details": [{ "tax_name": "HST", "tax_rate": "13.00", "tax": "0.91" }]
  }
}
```

**GraphQL** (JSON skalár — nincs selection set) (`ordersRate` ([GraphQL kézikönyv](/api/graphql/documentation#/orders/ordersRate))):

```graphql
mutation {
  ordersRate(
    type: "D"
    from_postcode: "H9S2H9"
    from_country: "CA"
    to_postcode: "H4B2T5"
    to_country: "CA"
    packages: [{ weight: 1, weight_unit: 2, length: 30, width: 20, height: 10, dimension_unit: 2 }]
  )
}
```

**Ellenőrzés:** ha futtatja ezt a hívást, a `result` true, a `shipping_price` pedig szám. Ismételje meg `type` `P` értékkel a felvétel árajánlatához. Üres ár azt jelenti, hogy az irányítószám nincs aktív zónában. A létrehozás nem függ ettől a lépéstől.

## 3. Rendelés létrehozása

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

A kérésnek tartalmaznia kell az `Idempotency-Key` fejlécet, hogy az újrapróbálás ne hozhasson létre második rendelést.

### Kézbesítés (`type` `D`)

```bash
curl -X POST https://YOUR_HOST/api/v1/client/orderCreate \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: dev-local-001" \
  -d '{
    "type": "D",
    "need_pick_up": 0,
    "ref": "DEV-LOCAL-001",
    "name": "Jane Recipient",
    "telephone": "5555555555",
    "email": "jane@example.com",
    "address_1": "2667 boul des sources",
    "city": "Pointe-Claire",
    "province": "QC",
    "postcode": "H9S2H9",
    "country": "Canada",
    "packages": 1,
    "packagesDetail": [{
      "ref": "PKG-001",
      "weight": 1,
      "weight_unit": 2,
      "length": 30,
      "width": 20,
      "height": 10,
      "dimension_unit": 2
    }],
    "delivery_instruction": "Leave at the side door"
  }'
```

### Felvétel (`type` `P`)

Ugyanaz a végpont. A cím a felvételi megálló.

```bash
curl -X POST https://YOUR_HOST/api/v1/client/orderCreate \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: dev-pickup-001" \
  -d '{
    "type": "P",
    "need_pick_up": 1,
    "ref": "DEV-PICKUP-001",
    "name": "Jane Sender",
    "telephone": "5555555555",
    "email": "jane@example.com",
    "address_1": "2667 boul des sources",
    "city": "Pointe-Claire",
    "province": "QC",
    "postcode": "H9S2H9",
    "country": "Canada",
    "packages": 1,
    "packagesDetail": [{
      "ref": "PKG-P-001",
      "weight": 1,
      "weight_unit": 2,
      "length": 30,
      "width": 20,
      "height": 10,
      "dimension_unit": 2
    }],
    "pickup_instruction": "Ring the side bell"
  }'
```

| Mező | Jelentés |
|---|---|
| `type` | `D` kézbesítés, vagy `P` felvétel |
| `need_pick_up` | `0` — a csomag már a raktárban van. `1` — a 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ó |
| `auto_deduplication` | `1` elutasít egy második csomagot ugyanazzal a csomag-`ref`-fel |

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

```json
{ "result": true, "id": 12345, "tracking_number": "SR123456789012", "orders_status_id": 2 }
```

Az `orders_status_id` `2` Új. Tárolja az `id`, `tracking_number` és `ref` értékeket.

**Ellenőrzés:** küldje el **ugyanazt** a törzset ugyanazzal az `Idempotency-Key`-vel újra. Ugyanazt az `id`-t kell kapnia, és nem hozhat létre második rendelést.

A rendelés **nem** jön létre, ha:

| `code` | Teendő |
|---|---|
| `INSUFFICIENT_BALANCE` | Az `insufficient_balance` tartalmazza: required / available / shortfall. Töltse fel, majd próbálja újra. |
| `OUT_OF_DELIVERY_AREA` | A cím a szolgáltatási zónán kívül van, és a cég törli ezeket a rendeléseket. Küldjön be zónán belüli címet. |
| `IDEMPOTENCY_CONFLICT` | Ugyanazt az idempotencia-kulcsot más kéréstörzzsel használták újra. Használjon új kulcsot. |

Egy megtartott zónán kívüli rendelés mégis adhat `result: true` értéket `shipping_price: null` és `warning` mellett. Olvassa ezt a mezőt.

## 4. Rendelés lekérdezése

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

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

**REST:** `GET /api/v1/orders/list` — a fiók összes rendelése, a legújabbak elöl, mindegyik a csomagjaival és azok tételsoraival. A `page` és a `per_page` együttes megadásával lapozhat (a `per_page` legfeljebb 1000); ezek nélkül a legújabb 1000 rendelést kapja meg egy `truncated` jelzéssel. [REST kézikönyv](/api/documentation#/paths/v1-orders-list/get)

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

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

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

**Ellenőrzés:** a rendelés a hitelesített fiókéhoz tartozik. A `ref` egyezik a létrehozott értékkel. A `tracking_number` egyezik a 3. lépéssel.

## 5. Helyi címke nyomtatása

**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
  }'
```

`type`: `TRACKING_NUMBER`, `ORDER_ID` vagy `REF`. A `base64: 0` (alapértelmezett) PDF-et streamel. A `base64: 1` esetén a teljes válasz törzse egy legfelső szintű JSON-szöveg, amely a base64 PDF-et tartalmazza, nem pedig `pdf_data` mezővel rendelkező objektum. Hívja inkább a `POST /api/v2/shipping/getShippingLabel` végpontot, ha a címkét szokásos JSON-objektumban szeretné megkapni.

**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 (`pdf_data`).

**Ellenőrzés:** a PDF megnyílik. A kézbesítési címke a címzettet mutatja; a felvételi címke a felvételi címet. A rejtett címmező a címkén üresen jelenik meg.

## 6. Követés

Nyilvános követési végpont; hozzáférési token nem szükséges.

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

[REST kézikönyv](/api/documentation#/operations/getPublicTracking) · [GraphQL kézikönyv](/api/graphql/documentation#/tracking/trackingPublic)

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

**GraphQL** (típusos — selection set kell):

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

A `tracking_event_status_id` szerint ágazzon, ne a `description` szerint (ez a szöveg az `Accept-Language`-t 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ésre úton |
| `500` | kézbesítés | Kézbesítve |
| `501` | kézbesítés | Kézbesítés sikertelen, új terv kell |
| `460` | felvétel | Felvételre úton |
| `510` | felvétel | Felvéve |
| `512` | felvétel | Felvétel sikertelen, próbálja később |
| `513` | felvétel | Felvételi probléma |

A `data` a legújabbal kezdődik. Az első sort tekintse aktuálisnak. `deliveried: true` a `500` után.

`500` vagy `510` esetén a `proofs[]` tartalmazhat `type` `1` (aláírás) vagy `2` (fotó) értéket, plusz `file_id` és `signed_url`. Az esemény után feltöltött fotó nincs abban a payloadban — a 7. lépésben iratkozzon fel a `pod.files_updated` eseményre.

**Ellenőrzés:** létrehozás után a legújabb esemény `100`, a `deliveried` pedig false. Ismeretlen szám: `result: false` / 404 — mutasson nem található állapotot; ne találjon ki követési eseményeket.

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

Állítsa be az e folyamathoz szükséges visszahívási URL-eket:

| Beállítás | Esemény | Mikor |
|---|---|---|
| `order_create_webhook_url` | `order.created` | `id` és `tracking_number` mentése |
| `order_status_change_webhook_url` | `order.status_change` | Ügyfélnek látható állapot |
| `tracking_event_webhook_url` | `tracking.event` | Felvételi vagy kézbesítési idővonal |
| `pod_files_webhook_url` | `pod.files_updated` | Fotó/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 törlést elutasították |

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

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

```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.example.com/hooks/superroute",
    "order_status_change_webhook_url": "https://erp.example.com/hooks/superroute",
    "tracking_event_webhook_url": "https://erp.example.com/hooks/superroute",
    "webhook_sign_secret": "YOUR_LONG_RANDOM_SECRET",
    "webhook_verify_ssl": 1
  }'
```

A **v2** ellenőrzés a nyers törzsön: `HMAC_SHA256(timestamp + "." + raw_body, secret)` a `X-Webhook-Signature-V2` ellen. Deduplikáljon `X-Webhook-Event-Id` szerint. Válaszoljon **2xx-szel 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;
}
```

**Ellenőrzés:** hozzon létre egy tesztrendelést, és lássa az `order.created` eseményt ugyanazzal az `id` / `tracking_number` értékkel. Érvénytelen aláírást a fogadónak `401`-gyel kell elutasítania. Ugyanazon `X-Webhook-Event-Id` második kézbesítése nem dolgozható fel kétszer.

## 8. Tesztrendelés törlése

**REST:** `POST /api/v1/orders/cancel` — [REST kézikönyv](/api/documentation#/paths/v1-orders-cancel/post) — pontosan egy 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"}'
```

Már törölve: `200` `already_cancelled: true` értékkel. **GraphQL:** `ordersCancel` ([GraphQL kézikönyv](/api/graphql/documentation#/orders/ordersCancel)).

**Ellenőrzés:** a nyilvános követés már nem kezeli a küldeményt aktívként. Ha a törlést elutasítják (`409`, pl. `ORDER_ALREADY_IN_DELIVERY`), az `order.cancel_failed` esemény indul.

## 9. Kötegelt létrehozás (opcionális)

- `POST /api/v1/client/batchOrderCreate` — [REST kézikönyv](/api/documentation#/paths/v1-client-batchOrderCreate/post) — megvárja, amíg minden sor feldolgozásra kerül.
- `POST /api/v1/client/batchOrderCreateAsync` — [REST kézikönyv](/api/documentation#/paths/v1-client-batchOrderCreateAsync/post) — azonnal visszaad egy feladat-azonosítót; kérdezze a `GET /api/v1/client/async/{id}` — [REST kézikönyv](/api/documentation#/paths/v1-client-async-id/get) végpontot, vagy vegye az `order.create_async` eseményt.

Ugyanazok a mezők, mint a 3. lépésben, rendelések tömbjeként. Minden sor lehet `type` `D` vagy `P`. **GraphQL:** `clientBatchOrderCreate` ([GraphQL kézikönyv](/api/graphql/documentation#/client/clientBatchOrderCreate)) / `clientBatchOrderCreateAsync` ([GraphQL kézikönyv](/api/graphql/documentation#/client/clientBatchOrderCreateAsync)).

**Ellenőrzés:** minden sornak saját `result` értéke van. Az aszinkron végpontot akkor használja, ha körülbelül 100 sornál többet küld be.

## Teszlista

Használjon teszt `ref` értéket, pl. `DEV-LOCAL-001` / `DEV-PICKUP-001`:

- [ ] (Opcionális) Az árajánlat árat ad zónán belüli irányítószámra `type` `D` értékkel.
- [ ] (Opcionális) Az árajánlat árat ad zónán 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; ugyanaz az `Idempotency-Key` nem hoz létre második rendelést.
- [ ] A felvétel létrehozása `id` + `tracking_number` értéket ad; a `need_pick_up` értéke `1`.
- [ ] A lista / részlet ezen a fiókon mutatja a rendelést.
- [ ] A helyi címke PDF-je megnyílik, és a címzettet vagy a felvételi címet mutatja.
- [ ] A nyilvános követés token nélkül adja az idővonalat; a legújabb esemény `100`.
- [ ] Megérkezik az `order.created`; a v2 aláírás ellenőrizhető.
- [ ] A törlés `result: true` (vagy újrapróbáláskor `already_cancelled: true`).
