# Servizi di spedizione

Usate i servizi di spedizione della società di logistica: elencare i servizi → caricare la configurazione di un servizio → stimare → creare l’ordine → pagare → rileggerlo → tracciare → annullare un ordine di prova.

Questa guida documenta esclusivamente le operazioni seguenti. Eseguitele in sequenza. L’autenticazione usa un account **cliente**.

Sostituite `YOUR_HOST` e `ACCESS_TOKEN` con i valori del vostro ambiente.

## 1. Accesso (cliente)

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

Usate `access_token` così:

```
Authorization: Bearer ACCESS_TOKEN
```

GraphQL usa la stessa intestazione su `POST /api/graphql`.

**Verifica:** il login restituisce `access_token`. Una chiamata successiva senza di esso restituisce `401`.

## 2. Elencare i servizi

**REST:** `GET /api/v1/customer/shipping-orders/services` — [Manuale REST](/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"
```

**GraphQL:** `customerShippingOrderServices` ([Manuale GraphQL](/api/graphql/documentation#/customer/customerShippingOrderServices))

Ogni riga ha `service_code`, `name`, `offer_pickup`, `allow_warehouse_delivery`. Un elenco vuoto significa che a questo cliente non è assegnato alcun servizio.

**Verifica:** avete annotato un `service_code` (esempio sotto: `intl_express`).

## 3. Caricare la configurazione di quel servizio

**REST:** `GET /api/v1/customer/shipping-orders/services/{serviceCode}/config` — [Manuale REST](/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"
```

**GraphQL:** `customerShippingOrderServiceConfig` ([Manuale GraphQL](/api/graphql/documentation#/customer/customerShippingOrderServiceConfig))

**Verifica:** avete almeno un `id` di magazzino se questo servizio consente il deposito in magazzino. `403` significa che questo cliente non è autorizzato a quel servizio.

## 4. Stimare

**REST:** `POST /api/v1/customer/shipping-orders/services/{serviceCode}/estimate-price`

```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" \
  -d '{
    "origin_type": "warehouse",
    "warehouse_id": 7,
    "delivery_postcode": "M5V2H1",
    "delivery_country": "CA",
    "packages": [{
      "weight": 2,
      "length": 30,
      "width": 20,
      "height": 15,
      "weight_unit": 2,
      "dimension_unit": 2
    }]
  }'
```

`origin_type`: `warehouse` (consegna in magazzino) o `pickup` (la società ritira). Allineatevi a quanto il passo 3 ha detto che il servizio consente.

**Verifica:** `result` è true e avete un prezzo (o un indicatore «serve quotazione» per prezzo manuale). Non create ancora se destinazione/collo sono rifiutati.

## 5. Creare l’ordine

**REST:** `POST /api/v1/customer/shipping-orders/services/{serviceCode}/orders`

Inviate `Idempotency-Key`.

```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 "Idempotency-Key: dev-ship-001" \
  -d '{
    "origin_type": "warehouse",
    "warehouse_id": 7,
    "reference": "DEV-SHIP-001",
    "delivery_name": "Jane Recipient",
    "delivery_telephone": "5555555555",
    "delivery_email": "jane@example.com",
    "delivery_address_1": "123 King St W",
    "delivery_city": "Toronto",
    "delivery_province": "ON",
    "delivery_country": "CA",
    "delivery_postcode": "M5V2H1",
    "package": [{
      "weight": 2,
      "length": 30,
      "width": 20,
      "height": 15,
      "weight_unit": 2,
      "dimension_unit": 2,
      "quantity": 1
    }]
  }'
```

**Verifica:** la risposta ha un `id` dell’ordine. Conservate `id`, `tracking_number` / `reference_number` quando presenti.

## 6. Pagare

**REST:** `POST /api/v1/customer/shipping-orders/{id}/pay`

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

Facoltativo: prima `GET /api/v1/customer/shipping-orders/{id}/payment-info`. Un `402` significa che il portafoglio non copre l’importo — ricaricate, poi ritentate.

**Verifica:** l’ordine non è più pagabile, oppure `remaining_balance` è `0`.

## 7. Consultare l’ordine e tracciare

**REST:** `GET /api/v1/customer/shipping-orders/{id}` — [Manuale REST](/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"
```

**GraphQL:** `customerShippingOrderShow` ([Manuale GraphQL](/api/graphql/documentation#/customer/customerShippingOrderShow))

Quando sull’ordine c’è un `tracking_number`, tracking pubblico (nessun token):

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

[Manuale REST](/api/documentation#/operations/getPublicTracking) · [Manuale GraphQL](/api/graphql/documentation#/tracking/trackingPublic)

**Verifica:** il dettaglio è l’ordine di questo cliente. Il tracking pubblico lo trova una volta che esiste un numero.

## 8. Configurare le notifiche degli eventi

| Impostazione | Evento | Quando |
|---|---|---|
| `order_create_webhook_url` | `order.created` | Persistere `id` e i numeri di tracking |
| `tracking_event_webhook_url` | `tracking.event` | Cronologia |
| `order_status_change_webhook_url` | `order.status_change` | Stato nel vostro sistema |

**REST:** `PUT /api/v1/webhook-settings` — [Manuale REST](/api/documentation#/paths/v1-webhook-settings/put)

**GraphQL:** `webhookSettingsUpdate` ([Manuale GraphQL](/api/graphql/documentation#/webhooks/webhookSettingsUpdate)).

Verificate **v2** sul corpo grezzo: `HMAC_SHA256(timestamp + "." + raw_body, secret)` contro `X-Webhook-Signature-V2`. Rimuovete i duplicati su `X-Webhook-Event-Id`. Rispondete **2xx in meno di 3 secondi**.

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

**Verifica:** una creazione di prova produce `order.created` con quell’`id`.

## 9. Annullare un ordine di prova

**REST:** `POST /api/v1/customer/shipping-orders/{id}/cancel`

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

**Verifica:** un secondo annullamento è sicuro, oppure l’API indica che l’ordine è già annullato. `422` significa che questo stato non può essere annullato.

## Elenco di verifica

Usate un `reference` di prova come `DEV-SHIP-001`:

- [ ] L’elenco servizi non è vuoto; avete annotato un `service_code`.
- [ ] La configurazione restituisce magazzini / imballaggi per quel servizio.
- [ ] La stima restituisce un prezzo (o un indicatore chiaro di quotazione necessaria).
- [ ] La creazione restituisce un `id`; lo stesso `Idempotency-Key` non crea un secondo ordine.
- [ ] Il pagamento va a buon fine, **oppure** avete confermato che il portafoglio va ricaricato (`402`).
- [ ] Il dettaglio mostra l’ordine di questo cliente.
- [ ] Il tracking pubblico trova la spedizione una volta che esiste un numero di tracking.
- [ ] L’annullamento va a buon fine, **oppure** avete confermato che questo stato non può essere annullato.
