# Transportne usluge

Koristite sopstvene transportne usluge logističke kompanije: lista usluga → učitavanje konfiguracije jedne usluge → procena → kreiranje porudžbine → plaćanje → ponovno čitanje → praćenje → otkazivanje test porudžbine.

Ovaj vodič dokumentuje isključivo sledeće operacije. Izvršite ih redom. Autentifikacija koristi nalog **kupca**.

Zamenite `YOUR_HOST` i `ACCESS_TOKEN` vrednostima iz vašeg okruženja.

## 1. Prijava (kupac)

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

Koristite `access_token` ovako:

```
Authorization: Bearer ACCESS_TOKEN
```

GraphQL koristi isto zaglavlje na `POST /api/graphql`.

**Verifikacija:** prijava vraća `access_token`. Kasniji poziv bez njega vraća `401`.

## 2. Lista usluga

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

Svaki red ima `service_code`, `name`, `offer_pickup`, `allow_warehouse_delivery`. Prazna lista znači da ovom kupcu nije dodeljena nijedna usluga.

**Verifikacija:** sačuvali ste jedan `service_code` (primer ispod: `intl_express`).

## 3. Učitavanje konfiguracije te usluge

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

**Verifikacija:** imate bar jedan `id` skladišta ako ta usluga dozvoljava predaju u skladište. `403` znači da ovom kupcu ta usluga nije dozvoljena.

## 4. Procena

**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` (predaja u skladište) ili `pickup` (kompanija preuzima). Uskladite sa onim što je korak 3 rekao da usluga dozvoljava.

**Verifikacija:** `result` je true i imate cenu (ili oznaku „potrebna ponuda“ za ručno određivanje cene). Još ne kreirajte ako su odredište/paket odbijeni.

## 5. Kreiranje porudžbine

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

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

**Verifikacija:** odgovor ima `id` porudžbine. Sačuvajte `id`, `tracking_number` / `reference_number` kada postoje.

## 6. Plaćanje

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

Opciono: prvo `GET /api/v1/customer/shipping-orders/{id}/payment-info`. `402` znači da novčanik ne pokriva iznos — dopunite, pa pokušajte ponovo.

**Verifikacija:** porudžbina više nije za plaćanje, ili je `remaining_balance` `0`.

## 7. Pregled porudžbine i praćenje

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

Kada porudžbina ima `tracking_number`, javno praćenje (bez tokena):

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

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

**Verifikacija:** detalj je porudžbina ovog kupca. Javno praćenje je pronalazi čim broj postoji.

## 8. Konfiguracija obaveštenja o događajima

| Podešavanje | Događaj | Kada |
|---|---|---|
| `order_create_webhook_url` | `order.created` | Sačuvati `id` i brojeve praćenja |
| `tracking_event_webhook_url` | `tracking.event` | Vremenska linija |
| `order_status_change_webhook_url` | `order.status_change` | Status u vašem sistemu |

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

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

Proverite **v2** nad sirovim telom: `HMAC_SHA256(timestamp + "." + raw_body, secret)` protiv `X-Webhook-Signature-V2`. Deduplikujte po `X-Webhook-Event-Id`. Odgovorite **2xx za manje od 3 sekunde**.

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

**Verifikacija:** jedno test kreiranje proizvodi `order.created` sa tim `id`.

## 9. Otkazivanje test porudžbine

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

**Verifikacija:** drugo otkazivanje je bezbedno, ili API kaže da je porudžbina već otkazana. `422` znači da se ovaj status ne može otkazati.

## Lista provera

Koristite jednokratni `reference` kao `DEV-SHIP-001`:

- [ ] Lista usluga nije prazna; sačuvali ste jedan `service_code`.
- [ ] Konfiguracija vraća skladišta / pakovanja za tu uslugu.
- [ ] Procena vraća cenu (ili jasnu oznaku da je potrebna ponuda).
- [ ] Kreiranje vraća `id`; isti `Idempotency-Key` ne kreira drugu porudžbinu.
- [ ] Plaćanje uspeva, **ili** ste potvrdili da novčanik mora da se dopuni (`402`).
- [ ] Detalj pokazuje porudžbinu ovog kupca.
- [ ] Javno praćenje pronalazi pošiljku čim postoji broj za praćenje.
- [ ] Otkazivanje uspeva, **ili** ste potvrdili da se ovaj status ne može otkazati.
