# 集成更新指南

以下所有变更均严格采用增量方式：现有端点、响应字段、HTTP 状态码、Webhook 载荷字节以及旧版 `Signature` 头均保持不变。不做任何改动的集成将完全按原样继续工作。各项功能可独立采用，顺序不限。

实时发布于 https://api.superlabel.ca/api/documentation/integration-updates?lang=chs（附加 `?download=1` 可保存）。配套文档：OpenAPI（https://api.superlabel.ca/api/documentation?lang=chs）、Postman 集合（https://api.superlabel.ca/api/documentation/postman-collection）、状态字典与流转（https://api.superlabel.ca/api/documentation/order-status-flow?lang=chs）。

---

## 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 令牌作用域与 IP 白名单（可选）

单个令牌可被限制为 `orders:read`、`orders:write` 和/或 `webhooks:manage`，并可限定允许的 IP 列表。不受限制的令牌（默认情况，以及所有既有令牌）的行为与之前完全一致。受限令牌在其作用域/IP 之外发起调用将收到 `403`。如需限制令牌，请联系支持团队。

## 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` 头公告。这些保证由每次发布时运行的字节级自动化兼容性测试强制执行。