# 整合更新指南

以下每項變更都是嚴格的新增式變更：既有的端點、回應欄位、HTTP 狀態碼、Webhook 資料位元組以及舊版 `Signature` 標頭均維持不變。不做任何調整的整合仍會完全照舊運作。各項功能可獨立採用，順序不拘。

於 https://api.superlabel.ca/api/documentation/integration-updates?lang=cht 即時提供（加上 `?download=1` 可儲存）。相關文件：OpenAPI（https://api.superlabel.ca/api/documentation?lang=cht）、Postman 集合（https://api.superlabel.ca/api/documentation/postman-collection）、狀態字典與流程（https://api.superlabel.ca/api/documentation/order-status-flow?lang=cht）。

---

## 1. 依追蹤號碼取消（冪等）

`POST https://api.superlabel.ca/api/v1/orders/cancel` — 請在 `order_id`、`tracking_number`、`external_tracking_number` 中僅提供其中一項。

```json
POST /api/v1/orders/cancel
Authorization: Bearer <token>
{ "external_tracking_number": "WP1234567890" }
```

- 成功：`200 {"result":true,"id":123456,"already_cancelled":false,...}`
- 已經取消（可安全重試）：`200`，且 `"already_cancelled": true`
- 號碼符合多筆進行中的訂單：`409`，並附 `"matched_order_ids": [...]` — 請改用 `order_id` 重試。
- 舊版 `GET /v1/orders/{orderId}/cancel` 維持不變。

## 2. 增量對帳資料源

掃描指定時間窗內所有變更過的資料；透過游標分頁，絕不會遺漏或重複計算任何記錄。

- `GET https://api.superlabel.ca/api/v1/orders-reconciliation?updated_from=&updated_to=&per_page=&cursor=`
- `GET https://api.superlabel.ca/api/v1/tracking-events/reconciliation?...`

`updated_from` / `updated_to` 接受 unix 時間戳記或平台時區的 `Y-m-d H:i:s` 字串。持續依 `next_cursor` 翻頁，直到 `has_more` 為 false；比較時請使用 `*_timestamp` unix 欄位，切勿使用當地時間字串。追蹤事件項目包含 `occurred_at` / `occurred_timestamp`（業務實際發生時間；歷史資料則等於建立時間）。

## 3. 機器可讀的錯誤代碼與請求追蹤

高頻率的建立/取消錯誤回應現在會在維持不變的 `message` 旁附帶一個穩定的 `code` 欄位：
`VALIDATION_FAILED`, `MISSING_IDENTIFIER`, `ORDER_NOT_FOUND`,
`ORDER_CANCEL_UNAUTHORIZED`, `ORDER_STATUS_NOT_CANCELLABLE`,
`ORDER_CANCEL_BLOCKED_THIRD_PARTY`, `LABEL_CANCEL_FAILED`,
`DUPLICATE_TRACKING_NUMBER`, `MULTIPLE_ORDERS_MATCHED`.
代碼是穩定的識別碼 — 可能會新增，但既有代碼的含義絕不會改變。每個 API 回應也會回傳 `X-Request-ID`；回報問題時請一併提供。

## 4. 重複偵測升級

啟用 `auto_deduplication=1` 時，被攔截的建立請求之 `exist_package_ref` / `exist_external_tracking_number` 項目現在會包含既有的 `order_id` 與 `order_ref`，且外層資料會帶有 `"duplicate": true`。可選的嚴格模式：傳送 `strict_duplicate_check=1` 以收到 HTTP `409`，取代舊版的 `200` + `result:false`（省略此旗標則維持舊版行為）。

## 5. 批次建立選項

- `per_order_transaction: 1`（置於批次內容的頂層）：每筆訂單獨立提交 — 單筆失敗不再連帶回滾其他訂單。若處理提前中止，您仍會收到所有已完成項目的結果列，外加最後一列 `{"result":false,"batch_aborted":true}`。
- 超過 100 筆訂單的批次會收到提示性的 `X-Batch-Size-Warning` 回應標頭；大量訂單建議改用 `POST /v1/client/batchOrderCreateAsync`。

## 6. 簽收證明（POD）檔案同步

追蹤回應中的 `proofs[]` 項目現在還會額外包含：

```json
{ "url": "...", "full_url": "...", "type": 2,
  "file_id": 234567, "uploaded_at": "2026-07-20 14:30:05",
  "uploaded_timestamp": 1784745005,
  "signed_url": "https://.../files/pod/234567?expires=...&signature=...",
  "signed_url_expires_at": 1784745005 }
```

請以 `file_id` 作為去重 / 增量同步的鍵值。`signed_url` 是有時效的連結（7 天）— 過期後請重新查詢追蹤以取得新連結。永久的 `url` / `full_url` 連結維持不變。

## 7. Webhook：v2 簽章標頭（所有投遞）

每個 Webhook 現在除了維持不變的舊版 `Signature` 標頭外，還會一併傳送：

| 標頭 | 含義 |
|---|---|
| `X-Webhook-Event-Id` | UUID，重試時保持不變 — 可用於去重 |
| `X-Webhook-Timestamp` | 首次發送時的 unix 秒數 |
| `X-Webhook-Signature-V2` | 對 `"<timestamp>.<raw body>"` 的 HMAC-SHA256 |

驗證方式（任何語言均適用）：`expected = HMAC_SHA256(timestamp + "." + raw_body, your_secret)`；再與標頭做恆定時間比較。時間戳記在首次發送時即固定，重試時沿用（重試最長可持續約 3 小時），因此請搭配較寬鬆的容許時間窗與 `X-Webhook-Event-Id` 去重，而非嚴格的重放截止時間。您既有的舊版 `Signature` 驗證仍照常運作 — 可在任何時候採用 v2。可在 Webhook 指南頁面（`/webhooks-guide`）對您的端點測試兩種簽章。

## 8. Webhook：選用的資料封套

在 Webhook 設定中將 `webhook_payload_envelope = 1`，即可在物件型事件的 JSON 內容中收到：`event_id`、`event_time`（含時區偏移的 ISO8601）、`event_timestamp`（unix），以及（適用時）`is_correction: true`（先前已送達/已交接的訂單重新進入流程）與追蹤事件上的 `occurred_at` / `occurred_timestamp`。未啟用此旗標時，您的資料與先前逐位元組完全相同。清單型資料（`order.create_async`）永遠不會被修改。

## 9. 新的 Webhook 事件（選用 URL）

- `pod.files_updated` — 配送照片/簽名在事後被新增或移除。請設定 `pod_files_webhook_url`。 資料內容：
  `{"action":"added|removed","order_id":...,"order_ref":...,
  "tracking_number":[...],"external_tracking_number":[...],
  "file":{"file_id":...,"type":1|2,"note":null}, "event_id":..., ...}`
- `order.deleted` — 訂單已被永久刪除。請設定 `order_deleted_webhook_url`。 資料內容： `{"action":"deleted","order_id":...,
  "order_ref":...,"tracking_number":[...],"external_tracking_number":[...],
  "event_id":..., ...}`

這兩個事件一律包含封套欄位，且僅在設定了對應 URL 時才會發送。

## 10. Webhook：投遞選項

- **每個事件可設定多個端點**：每個 Webhook URL 設定可接受單一 URL（如同以往）、逗號分隔的清單或 JSON 陣列。每個端點都有各自獨立的投遞與重試週期。
- **TLS 驗證**：設定 `webhook_verify_ssl = 1` 可讓對外呼叫驗證您的憑證。預設維持關閉（沿襲歷史行為）。
- **所有訂單類型的 order.created**：設定 `order_created_webhook_all_types = 1` 可將訂單建立事件的範圍擴展至本地配送以外的訂單。預設維持歷史範圍。
- **簽章密鑰**：首次設定時會產生高強度的隨機密鑰；既有密鑰絕不會被自動輪換。

## 11. API Token 權限範圍與 IP 允許清單（選用）

個別 Token 可限制為 `orders:read`、`orders:write` 及/或 `webhooks:manage`，並可限制允許的 IP 清單。未受限的 Token（預設值，以及所有既有 Token）行為完全照舊。受限 Token 若在其權限範圍/IP 之外呼叫，將收到 `403`。如需限制 Token，請聯絡客服。

## 12. 運營商包裹提交（智能櫃）

外部運營商透過一次批次呼叫把包裹提交到倉庫暫存區。可選附上收件人電話/電郵，包裹入櫃後即以取件碼通知收件人。收件人欄位為可選，介面保持向後相容。

```json
POST https://api.superlabel.ca/api/v1/inventories/carrier-staging-stockin
Authorization: Bearer <token>
{ "carrier_id": 1, "warehouse_id": 10, "data": [
  { "carrier_reference_number": "TRK1", "ref": "REF1",
    "recipient_phone": "+14165550123", "recipient_email": "r@example.com" }
] }
```

```graphql
mutation($carrierId: Int!, $warehouseId: Int, $packages: Json) {
  submitCarrierPackages(carrierId: $carrierId, warehouseId: $warehouseId, packages: $packages)
}
```

- 驗證：照常使用 Bearer 權杖。customer 帳號需具備運營商上傳授權（運營商 + 倉庫）；client、employee、partner 帳號使用各自的倉庫範圍。
- 每個包裹：依運營商設定提供 member_number 和/或 carrier_reference_number；可選 carrier_order_id、batch、ref、grid_code（僅員工）、recipient_phone、recipient_email。
- 回應每列回傳 pickup_code；同一取件碼會隨取件通知傳送給收件人。

收件人聯絡方式加密儲存，絕不回傳顯示。電話與電郵都提供時兩路都通知；電話/電郵優先於會員綁定的站內通知。

## 相容性承諾

請參閱變更管理政策：回應僅做新增式變更、新端點與舊端點並存、Webhook 內容預設凍結、預設值絕不更改、棄用至少提前 30 天以 `Deprecation` / `Sunset` 標頭公告。這些保證由每次發佈時的自動化位元組層級相容性測試強制執行。