# Verzenddiensten

Gebruik de eigen verzenddiensten van het logistieke bedrijf: diensten weergeven → de configuratie van één dienst laden → schatten → de order aanmaken → betalen → teruglezen → volgen → een testorder annuleren.

Dit handboek documenteert uitsluitend de volgende bewerkingen. Voer ze in volgorde uit. Authenticatie gebruikt een **klantaccount**.

Vervang `YOUR_HOST` en `ACCESS_TOKEN` door de waarden van uw omgeving.

## 1. Aanmelden (klant)

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

Gebruik `access_token` zo:

```
Authorization: Bearer ACCESS_TOKEN
```

GraphQL gebruikt dezelfde header op `POST /api/graphql`.

**Verificatie:** login geeft `access_token` terug. Een latere aanroep zonder token geeft `401`.

## 2. Diensten weergeven

**REST:** `GET /api/v1/customer/shipping-orders/services` — [REST-handboek](/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` ([GraphQL-handboek](/api/graphql/documentation#/customer/customerShippingOrderServices))

Elke rij heeft `service_code`, `name`, `offer_pickup`, `allow_warehouse_delivery`. Een lege lijst betekent dat aan deze klant geen dienst is toegewezen.

**Verificatie:** u hebt één `service_code` vastgelegd (voorbeeld hieronder: `intl_express`).

## 3. De configuratie van die dienst laden

**REST:** `GET /api/v1/customer/shipping-orders/services/{serviceCode}/config` — [REST-handboek](/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` ([GraphQL-handboek](/api/graphql/documentation#/customer/customerShippingOrderServiceConfig))

**Verificatie:** u hebt minstens één magazijn-`id` als deze dienst magazijnafgifte toestaat. `403` betekent dat deze klant die dienst niet mag gebruiken.

## 4. Schatten

**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` (afgeven in een magazijn) of `pickup` (het bedrijf haalt op). Stem af op wat stap 3 zei dat de dienst toestaat.

**Verificatie:** `result` is true en u hebt een prijs (of een vlag «offerte nodig» voor handmatige prijs). Nog niet aanmaken als bestemming/pakket wordt geweigerd.

## 5. De order aanmaken

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

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

**Verificatie:** het antwoord heeft een order-`id`. Bewaar `id`, `tracking_number` / `reference_number` wanneer aanwezig.

## 6. Betalen

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

Optioneel: eerst `GET /api/v1/customer/shipping-orders/{id}/payment-info`. Een `402` betekent dat de portemonnee het bedrag niet dekt — opwaarderen, daarna opnieuw.

**Verificatie:** de order is niet meer te betalen, of `remaining_balance` is `0`.

## 7. De order ophalen en volgen

**REST:** `GET /api/v1/customer/shipping-orders/{id}` — [REST-handboek](/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` ([GraphQL-handboek](/api/graphql/documentation#/customer/customerShippingOrderShow))

Wanneer er een `tracking_number` op de order staat, publieke tracking (geen token):

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

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

**Verificatie:** het detail is de order van deze klant. Publieke tracking vindt hem zodra er een nummer is.

## 8. Gebeurtenismeldingen configureren

| Instelling | Gebeurtenis | Wanneer |
|---|---|---|
| `order_create_webhook_url` | `order.created` | `id` en trackingnummers bewaren |
| `tracking_event_webhook_url` | `tracking.event` | Tijdlijn |
| `order_status_change_webhook_url` | `order.status_change` | Status in uw systeem |

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

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

Verifieer **v2** over de ruwe body: `HMAC_SHA256(timestamp + "." + raw_body, secret)` tegen `X-Webhook-Signature-V2`. Dedupliceer op `X-Webhook-Event-Id`. Antwoord **2xx binnen 3 seconden**.

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

**Verificatie:** één test-aanmaak produceert `order.created` met die `id`.

## 9. Een testorder annuleren

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

**Verificatie:** een tweede annulering is veilig, of de API zegt dat de order al is geannuleerd. `422` betekent dat deze status niet kan worden geannuleerd.

## Testlijst

Gebruik een test-`reference` zoals `DEV-SHIP-001`:

- [ ] De dienstenlijst is niet leeg; u hebt één `service_code` vastgelegd.
- [ ] Configuratie geeft magazijnen / verpakkingen voor die dienst terug.
- [ ] Schatting geeft een prijs terug (of een duidelijke vlag dat een offerte nodig is).
- [ ] Aanmaken geeft een `id`; dezelfde `Idempotency-Key` maakt geen tweede order.
- [ ] Betalen slaagt, **of** u hebt bevestigd dat de portemonnee moet worden opgewaardeerd (`402`).
- [ ] Detail toont de order van deze klant.
- [ ] Publieke tracking vindt de zending zodra er een trackingnummer is.
- [ ] Annuleren slaagt, **of** u hebt bevestigd dat deze status niet kan worden geannuleerd.
