# Serviços de envio

Use os serviços de envio da própria empresa de logística: listar serviços → carregar a configuração de um serviço → estimar → criar o pedido → pagar → relê-lo → rastrear → cancelar um pedido de teste.

Este guia documenta apenas as operações seguintes. Execute-as em sequência. A autenticação usa uma conta de **cliente**.

Substitua `YOUR_HOST` e `ACCESS_TOKEN` pelos valores do seu ambiente.

## 1. Autenticação (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"}'
```

Coloque o `access_token` devolvido no cabeçalho do pedido:

```
Authorization: Bearer ACCESS_TOKEN
```

O GraphQL usa o mesmo cabeçalho em `POST /api/graphql`.

**Verificação:** o login devolve `access_token`. Uma chamada posterior sem ele devolve `401`.

## 2. Listar serviços

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

Cada linha tem `service_code`, `name`, `offer_pickup`, `allow_warehouse_delivery`. Uma lista vazia significa que nenhum serviço está atribuído a este cliente.

**Verificação:** registou um `service_code` (exemplo abaixo: `intl_express`).

## 3. Carregar a configuração desse serviço

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

**Verificação:** tem pelo menos um `id` de armazém se este serviço permitir entrega no armazém. `403` significa que este cliente não tem permissão para esse serviço.

## 4. Estimar

**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` (entregar num armazém) ou `pickup` (a empresa recolhe). Corresponda ao que o passo 3 disse que o serviço permite.

**Verificação:** `result` é true e tem um preço (ou um sinalizador «precisa de cotação» para preço manual). Ainda não crie se o destino/volume for recusado.

## 5. Criar o pedido

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

Envie `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ção:** a resposta tem um `id` de pedido. Guarde `id`, `tracking_number` / `reference_number` quando existirem.

## 6. Pagar

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

Opcional: primeiro `GET /api/v1/customer/shipping-orders/{id}/payment-info`. Um `402` significa que a carteira não cobre o montante — carregue saldo e tente de novo.

**Verificação:** o pedido já não é pagável, ou `remaining_balance` é `0`.

## 7. Consultar o pedido e rastrear

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

Quando o pedido tem um `tracking_number`, rastreio público (sem token):

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

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

**Verificação:** o detalhe é o pedido deste cliente. O rastreio público encontra-o assim que existir um número.

## 8. Configurar notificações de eventos

| Definição | Evento | Quando |
|---|---|---|
| `order_create_webhook_url` | `order.created` | Persistir `id` e os números de rastreio |
| `tracking_event_webhook_url` | `tracking.event` | Linha temporal |
| `order_status_change_webhook_url` | `order.status_change` | Estado no seu sistema |

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

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

Verifique **v2** sobre o corpo em bruto: `HMAC_SHA256(timestamp + "." + raw_body, secret)` contra `X-Webhook-Signature-V2`. Deduplique em `X-Webhook-Event-Id`. Responda **2xx em menos de 3 segundos**.

```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ção:** uma criação de teste produz `order.created` com esse `id`.

## 9. Cancelar um pedido de teste

**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ção:** um segundo cancelamento é seguro, ou a API diz que o pedido já está cancelado. `422` significa que este estado não pode ser cancelado.

## Lista de testes

Use um `reference` de teste como `DEV-SHIP-001`:

- [ ] A lista de serviços não está vazia; registou um `service_code`.
- [ ] A configuração devolve armazéns / embalagens para esse serviço.
- [ ] A estimativa devolve um preço (ou um sinalizador claro de cotação necessária).
- [ ] Criar devolve um `id`; a mesma `Idempotency-Key` não cria um segundo pedido.
- [ ] O pagamento é bem-sucedido, **ou** confirmou que a carteira precisa de ser carregada (`402`).
- [ ] O detalhe mostra o pedido deste cliente.
- [ ] O rastreio público encontra o envio assim que existir um número de rastreio.
- [ ] O cancelamento é bem-sucedido, **ou** confirmou que este estado não pode ser cancelado.
