# Uniorder: jedno API dla każdej przesyłki

Uniorder to jeden zestaw endpointów, przez który integracja wycenia, tworzy, drukuje, śledzi i anuluje każdą przesyłkę konta, niezależnie od sposobu jej realizacji. Jedna wycena zwraca dostawę realizowaną przez samą firmę oraz, na żądanie, wszystkie usługi etykiet przewoźników dostępne na koncie, każdą z `rate_id`. Zamówienie tworzy się przez odesłanie wybranego `rate_id`; nic innego w żądaniu nie wybiera usługi.

## 1. Co można zbudować

- **Kasę, która oferuje wszystkie opcje wysyłki jednocześnie.** Klient wpisuje adres, kasa wywołuje jeden endpoint, a strona wyświetla dostawę lokalną obok UPS, Canada Post i każdego innego przewoźnika używanego przez konto, każdą z ceną.
- **Konektor systemu zarządzania zamówieniami lub ERP z jedną ścieżką kodu.** Zamówienia ze wszystkich kanałów przechodzą przez te same wywołania tworzenia, odczytu, etykiety, śledzenia i anulowania. Konektor nie potrzebuje osobnej logiki dla dostawy lokalnej i dla etykiet przewoźników.
- **Nocne przetwarzanie masowe.** Do 500 przesyłek jest wycenianych lub tworzonych w jednym zadaniu w kolejce, a wyniki odczytuje się według identyfikatora zadania.
- **Ekran obsługi klienta.** Pracownik wyszukuje zamówienie, ponownie drukuje jego etykietę, odczytuje oś czasu śledzenia i anuluje je, używając tych samych czterech wywołań dla każdego zamówienia.

## 2. Co Uniorder robi za Państwa

| Bez Uniorder | Z Uniorder |
|---|---|
| Jedno API dla zamówień dostawy lokalnej i drugie dla etykiet przewoźników, każde z własnymi polami i odpowiedziami | Jeden format żądania (`from_*`, `to_*`, `packages`) i jeden format odpowiedzi dla każdej usługi |
| Integracja decyduje, które API przewoźnika wywołać | Wycena wymienia każdą usługę; decyduje `rate_id` wybranej stawki |
| Osobne endpointy etykiety, śledzenia i anulowania dla każdej usługi | `GET /label`, `GET /tracking` i `POST /cancel` działają dla każdego zamówienia |
| Tworzenie partii dostępne tylko dla dostawy lokalnej | Wycena partii i tworzenie partii dla każdej usługi, synchronicznie lub w kolejce |

Uniorder nie zastępuje istniejących endpointów; pozostają one dostępne i niezmienione. Jest zalecanym punktem wejścia dla nowej integracji.

## 3. Mapa endpointów w kolejności użycia przez integrację

| Krok | Cel | REST | GraphQL |
|---|---|---|---|
| 1 | Uzyskanie tokenu dostępu | `POST /api/v1/user/login` | `userLogin` |
| 2 | Wycena każdej usługi | `POST /api/v1/uniorder/rate` | `uniorderRate` |
| 3 | Utworzenie zamówienia po wybranej stawce | `POST /api/v1/uniorder` | `uniorderCreate` |
| 4 | Druk etykiety | `GET /api/v1/uniorder/{orderId}/label` | `uniorderLabel` |
| 5 | Pobranie zamówienia | `GET /api/v1/uniorder/{orderId}` | `uniorder` |
| 6 | Śledzenie zamówienia | `GET /api/v1/uniorder/{orderId}/tracking` | `uniorderTracking` |
| 7 | Anulowanie zamówienia | `POST /api/v1/uniorder/{orderId}/cancel` | `uniorderCancel` |
| — | Zakup etykiety, której nie udało się kupić przy tworzeniu | `POST /api/v1/uniorder/{orderId}/label` | `uniorderPurchaseLabel` |
| — | Wycena lub utworzenie do 20 wierszy naraz | `POST /api/v1/uniorder/rate/batch`, `POST /api/v1/uniorder/batch` | `uniorderRateBatch`, `uniorderCreateBatch` |
| — | Umieszczenie w kolejce do 500 wierszy | `POST /api/v1/uniorder/rate/batch-async`, `POST /api/v1/uniorder/batch-async`, `GET /api/v1/uniorder/jobs/{jobId}` | `uniorderRateBatchAsync`, `uniorderCreateBatchAsync`, `uniorderJob` |

[Podręcznik REST](/api/documentation#/paths/v1-uniorder-rate/post) · [Podręcznik GraphQL](/api/graphql/documentation#/orders/uniorderRate)

Żądania, odpowiedzi i kontrole krok po kroku znajdują się w przewodniku **Wycena i zamówienie w jednym przepływie**.

## 4. Przykład: kasa oferująca wszystkie opcje

Kwiaciarnia w Montrealu sprzedaje online. Przy kasie wycenia paczkę jeden raz, z uwzględnieniem przewoźników etykiet:

```bash
curl -X POST https://YOUR_HOST/api/v1/uniorder/rate \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "from_name": "Fleurs du Plateau", "from_telephone": "5145550100",
    "from_address": "4500 Rue Saint-Denis", "from_city": "Montreal", "from_province": "QC", "from_country": "CA", "from_postcode": "H2J2L3",
    "to_name": "Jane Recipient", "to_telephone": "5145550199",
    "to_address": "6841 Rue Saint-Denis", "to_city": "Montreal", "to_province": "QC", "to_country": "CA", "to_postcode": "H2S2S3",
    "quote_labels": true,
    "packages": [{ "weight": 1.2, "weight_unit": 2, "length": 40, "width": 25, "height": 25, "dimension_unit": 2 }]
  }'
```

Odpowiedź wymienia jedną stawkę `self_delivery` i jedną stawkę `label_service` na każdą usługę przewoźnika. Kasa pokazuje je jako opcje; klient wybiera lokalną dostawę tego samego dnia. Zamówienie tworzy się z `rate_id` tej stawki oraz z tymi samymi adresami i paczkami:

```bash
curl -X POST https://YOUR_HOST/api/v1/uniorder \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: checkout-10045" \
  -d '{
    "rate_id": "eyJpdiI6Ik1rT2Z...",
    "ref": "WEB-10045",
    "from_name": "Fleurs du Plateau", "from_telephone": "5145550100",
    "from_address": "4500 Rue Saint-Denis", "from_city": "Montreal", "from_province": "QC", "from_country": "CA", "from_postcode": "H2J2L3",
    "to_name": "Jane Recipient", "to_telephone": "5145550199",
    "to_address": "6841 Rue Saint-Denis", "to_city": "Montreal", "to_province": "QC", "to_country": "CA", "to_postcode": "H2S2S3",
    "packages": [{ "weight": 1.2, "weight_unit": 2, "length": 40, "width": 25, "height": 25, "dimension_unit": 2 }]
  }'
```

Odpowiedź zwraca `id` zamówienia i jego numery śledzenia. Sklep drukuje etykietę przez `GET /api/v1/uniorder/{orderId}/label` i pokazuje oś czasu z `GET /api/v1/uniorder/{orderId}/tracking` na stronie zamówienia klienta.

## 5. Przykład: ERP wysyłający co noc

ERP eksportuje zamówienia z danego dnia o 22:00. Wysyła adresy do `POST /api/v1/uniorder/rate/batch-async`, odczytuje zadanie, dopóki `status` nie przyjmie wartości `done`, wybiera stawkę dla każdego wiersza według własnych reguł i wysyła wybrane wiersze do `POST /api/v1/uniorder/batch-async`. Każdy wynik zawiera `reference` wiersza, dzięki czemu ERP przypisuje każdy wynik do własnej pozycji zamówienia. `rate_id` jest ważny przez 30 minut, dlatego zadanie tworzenia wysyła się wkrótce po zakończeniu zadania wyceny.

## 6. Przykład: ekran obsługi klienta

Podczas rozmowy z klientem ekran pracownika wywołuje `GET /api/v1/uniorder/{orderId}` w celu pobrania statusu i adresów, `GET /api/v1/uniorder/{orderId}/tracking` w celu pobrania osi czasu i potwierdzenia doręczenia oraz `POST /api/v1/uniorder/{orderId}/cancel`, gdy klient anuluje zamówienie. Te same wywołania dotyczą zamówienia dostawy i zamówienia etykiety; rozróżnia je pole `type`.

```bash
curl https://YOUR_HOST/api/v1/uniorder/123456/tracking \
  -H "Authorization: Bearer ACCESS_TOKEN"
```

To samo zamówienie przez GraphQL ([Podręcznik GraphQL](/api/graphql/documentation#/orders/uniorder)):

```graphql
query {
  uniorder(order_id: 123456)
}
```

## 7. Zasady projektowe

- **`rate_id` decyduje o usłudze.** Jest ważny przez 30 minut i tylko dla konta, które zażądało wyceny.
- **Zamówienie jest wyceniane w chwili utworzenia.** `shipping_price` to naliczona cena; `quoted_price` to cena z wyceny. Mogą się one różnić.
- **Zamówienie etykiety nigdy nie ginie.** Jeśli etykiety nie można kupić przy tworzeniu, zamówienie zostaje zachowane, a odpowiedzią jest `LABEL_PURCHASE_FAILED` z `id` zamówienia; etykietę kupuje się później przez `POST /api/v1/uniorder/{orderId}/label`.
- **Ponowienia są bezpieczne.** Przy każdym wywołaniu tworzenia należy wysyłać nagłówek `Idempotency-Key`; anulowanie już anulowanego zamówienia zwraca `already_cancelled` `true`.
- **Statusy są jednolite.** Zamówienie dostawy zgłasza `pending`, `in_transit`, `out_for_delivery`, `delivered`, `exception` lub `cancelled`; zamówienie etykiety zgłasza `label_pending`, `label_purchased` lub `cancelled`, a położenie paczki podaje śledzenie przewoźnika.

## 8. Lista kontrolna przed uruchomieniem

- [ ] Konto ma uprawnienie API, a token jest przechowywany na serwerze, nie w przeglądarce.
- [ ] Wyceny są wykonywane z pełnymi adresami nadawcy i odbiorcy.
- [ ] Zamówienia są tworzone w ciągu 30 minut od wyceny, z `Idempotency-Key`.
- [ ] `LABEL_PURCHASE_FAILED` jest obsługiwany przez późniejszy zakup etykiety, nigdy przez ponowne utworzenie zamówienia.
- [ ] Śledzenie jest odczytywane z `GET /api/v1/uniorder/{orderId}/tracking` lub odbierane przez webhooki.
- [ ] Anulowanie obsługuje `ORDER_STATUS_NOT_CANCELLABLE` i `ORDER_CANCEL_REFUSED`, pozostawiając zamówienie bez zmian.
