# Uniorder: un'API per ogni spedizione

Uniorder è un unico insieme di endpoint con cui un’integrazione quota, crea, stampa, traccia e annulla ogni spedizione dell’account, indipendentemente dal modo in cui la spedizione viene eseguita. Un solo preventivo restituisce la consegna effettuata dall’azienda stessa e, su richiesta, ogni servizio di etichette corriere dell’account, ciascuno con un `rate_id`. L’ordine viene creato restituendo il `rate_id` scelto; nessun altro elemento della richiesta seleziona il servizio.

## 1. Cosa potete realizzare

- **Un checkout che offre tutte le opzioni di spedizione in una volta.** Il cliente inserisce un indirizzo, il checkout chiama un solo endpoint e la pagina elenca la consegna locale accanto a UPS, Canada Post e a ogni altro corriere utilizzato dall’account, ciascuno con il proprio prezzo.
- **Un connettore per la gestione degli ordini o per l’ERP con un unico percorso di codice.** Gli ordini di tutti i canali passano attraverso le stesse chiamate di creazione, lettura, etichetta, tracking e annullamento. Il connettore non necessita di una logica separata per la consegna locale e per le etichette corriere.
- **Elaborazione massiva notturna.** Fino a 500 spedizioni vengono quotate o create in un unico job in coda, e i risultati vengono letti tramite l’id del job.
- **Una schermata di assistenza clienti.** Un operatore cerca un ordine, ristampa la sua etichetta, legge la cronologia di tracking e lo annulla, con le stesse quattro chiamate per ogni ordine.

## 2. Cosa fa Uniorder per voi

| Senza Uniorder | Con Uniorder |
|---|---|
| Un’API per gli ordini di consegna locale e un’altra per le etichette corriere, ciascuna con i propri campi e le proprie risposte | Un’unica struttura di richiesta (`from_*`, `to_*`, `packages`) e un’unica struttura di risposta per ogni servizio |
| L’integrazione decide quale API del corriere chiamare | Il preventivo elenca ogni servizio; decide il `rate_id` della tariffa scelta |
| Endpoint di etichetta, tracking e annullamento separati per ogni servizio | `GET /label`, `GET /tracking` e `POST /cancel` funzionano per ogni ordine |
| Creazione in lotto disponibile solo per la consegna locale | Quotazione in lotto e creazione in lotto per ogni servizio, sincrone o in coda |

Uniorder non sostituisce gli endpoint esistenti, che restano disponibili e invariati. È il punto di ingresso consigliato per una nuova integrazione.

## 3. Mappa degli endpoint, nell’ordine in cui un’integrazione li utilizza

| Passo | Scopo | REST | GraphQL |
|---|---|---|---|
| 1 | Ottenere un token di accesso | `POST /api/v1/user/login` | `userLogin` |
| 2 | Quotare tutti i servizi | `POST /api/v1/uniorder/rate` | `uniorderRate` |
| 3 | Creare l’ordine alla tariffa scelta | `POST /api/v1/uniorder` | `uniorderCreate` |
| 4 | Stampare l’etichetta | `GET /api/v1/uniorder/{orderId}/label` | `uniorderLabel` |
| 5 | Consultare l’ordine | `GET /api/v1/uniorder/{orderId}` | `uniorder` |
| 6 | Tracciare l’ordine | `GET /api/v1/uniorder/{orderId}/tracking` | `uniorderTracking` |
| 7 | Annullare l’ordine | `POST /api/v1/uniorder/{orderId}/cancel` | `uniorderCancel` |
| — | Acquistare un’etichetta che non è stato possibile acquistare alla creazione | `POST /api/v1/uniorder/{orderId}/label` | `uniorderPurchaseLabel` |
| — | Quotare o creare fino a 20 righe alla volta | `POST /api/v1/uniorder/rate/batch`, `POST /api/v1/uniorder/batch` | `uniorderRateBatch`, `uniorderCreateBatch` |
| — | Mettere in coda fino a 500 righe | `POST /api/v1/uniorder/rate/batch-async`, `POST /api/v1/uniorder/batch-async`, `GET /api/v1/uniorder/jobs/{jobId}` | `uniorderRateBatchAsync`, `uniorderCreateBatchAsync`, `uniorderJob` |

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

Le richieste, le risposte e le verifiche passo per passo si trovano nella guida **Preventivo e ordine in un unico flusso**.

## 4. Esempio: un checkout che offre tutte le opzioni

Un fiorista di Montreal vende online. Al checkout quota il pacco una sola volta, includendo i corrieri delle etichette:

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

La risposta elenca una tariffa `self_delivery` e una tariffa `label_service` per ogni servizio del corriere. Il checkout le mostra come opzioni; il cliente sceglie la consegna locale in giornata. L’ordine viene creato con il `rate_id` di quella tariffa e con gli stessi indirizzi e colli:

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

La risposta restituisce l’`id` dell’ordine e i suoi numeri di tracking. Il negozio stampa l’etichetta con `GET /api/v1/uniorder/{orderId}/label` e mostra la cronologia di `GET /api/v1/uniorder/{orderId}/tracking` nella pagina dell’ordine del cliente.

## 5. Esempio: un ERP che spedisce ogni notte

Un ERP esporta gli ordini del giorno alle 22:00. Invia gli indirizzi a `POST /api/v1/uniorder/rate/batch-async`, legge il job finché `status` è `done`, seleziona una tariffa per ogni riga secondo le proprie regole e invia le righe scelte a `POST /api/v1/uniorder/batch-async`. Ogni risultato riporta la `reference` della riga, così l’ERP associa ogni risultato alla propria riga d’ordine. Un `rate_id` è valido per 30 minuti, quindi il job di creazione viene inviato poco dopo il completamento del job di quotazione.

## 6. Esempio: una schermata di assistenza clienti

Durante la chiamata di un cliente, la schermata dell’operatore chiama `GET /api/v1/uniorder/{orderId}` per lo stato e gli indirizzi, `GET /api/v1/uniorder/{orderId}/tracking` per la cronologia e la prova di consegna, e `POST /api/v1/uniorder/{orderId}/cancel` quando il cliente annulla. Le stesse chiamate valgono per un ordine di consegna e per un ordine di etichetta; il campo `type` li distingue.

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

Lo stesso ordine tramite GraphQL ([Manuale GraphQL](/api/graphql/documentation#/orders/uniorder)):

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

## 7. Regole da considerare nella progettazione

- **Il `rate_id` decide il servizio.** È valido per 30 minuti e solo per l’account che ha richiesto il preventivo.
- **L’ordine viene tariffato al momento della creazione.** `shipping_price` è il prezzo addebitato; `quoted_price` è il prezzo del preventivo. I due possono differire.
- **Un ordine di etichetta non va mai perso.** Quando l’etichetta non può essere acquistata alla creazione, l’ordine viene conservato e la risposta è `LABEL_PURCHASE_FAILED` con l’`id` dell’ordine; l’etichetta viene acquistata in seguito con `POST /api/v1/uniorder/{orderId}/label`.
- **I nuovi tentativi sono sicuri.** Inviate un’intestazione `Idempotency-Key` a ogni chiamata di creazione; l’annullamento di un ordine già annullato restituisce `already_cancelled` `true`.
- **Gli stati sono uniformi.** Un ordine di consegna riporta `pending`, `in_transit`, `out_for_delivery`, `delivered`, `exception` o `cancelled`; un ordine di etichetta riporta `label_pending`, `label_purchased` o `cancelled`, e il tracking del suo corriere indica dove si trova il pacco.

## 8. Checklist per la messa in produzione

- [ ] L’account dispone dell’autorizzazione API e il token è conservato sul server, non in un browser.
- [ ] I preventivi vengono richiesti con gli indirizzi completi del mittente e del destinatario.
- [ ] Gli ordini vengono creati entro 30 minuti dal preventivo, con un `Idempotency-Key`.
- [ ] `LABEL_PURCHASE_FAILED` viene gestito acquistando l’etichetta in seguito, mai creando di nuovo l’ordine.
- [ ] Il tracking viene letto da `GET /api/v1/uniorder/{orderId}/tracking` o ricevuto tramite webhook.
- [ ] L’annullamento gestisce `ORDER_STATUS_NOT_CANCELLABLE` e `ORDER_CANCEL_REFUSED` lasciando l’ordine invariato.
