**概述**
- **目标**: 让快递合作方（例如韵达）能安全、可靠地向系统的 `/webhook/express/status` 发送回调，用于运单状态同步与推送触发。
- **范围**: 包含鉴权/验签约定、幂等策略、测试样例、部署与运维建议。

**接入概览**
- **Endpoint**: `https://<your-domain>/webhook/express/status`
- **协议**: HTTPS（TLS 必需）
- **数据格式**: `application/json`，body 为第三方原始回调 JSON。

**鉴权与验签**
- **方式**: 每个合作方分配一份 HMAC secret（UTF-8 文本），服务端按 `client_id` 查秘钥（建议把 `client_id` 放在请求头 `X-Client-Id`）。
- **签名头**: `X-Timestamp`（UTC 秒）与 `X-Signature`（hex 小写）。
- **签名算法**: HMAC-SHA256(secret, rawBodyText + timestamp) → hex。
- **时窗**: 接收端按 ±300 秒（可配置）判定时间窗，超时拒绝（防重放）。
- **严格模式**: `WEBHOOK_REJECT_INVALID_SIGNATURE=true` 时签名不合将直接 401（上线初期可先设 false，观测后切为 true）。

**幂等与去重**
- **推荐字段**: 合作方可在 body 中提供 `dedupe_key`（优先使用）；若无，服务端基于稳定字段计算哈希（建议用 `tracking_no|carrier|event_time|event_code` 组合）。
- **数据库约束**: `platform_express_tracking_events` 应有唯一约束 `(waybill_id, dedupe_key)`，重复回调不再写入事件表。
- **返回语义**: 对重复回调可返回 200 并在 body 标注 `duplicate: true`，以便合作方不再重试。

**请求/响应与重试策略**
- **接收端快速 ACK**: 推荐返回 HTTP 202 Accepted（或 200）表示“已入队/接收”，不代表处理完成。
- **错误返回**: 4xx 表示请求问题（签名/格式），5xx 表示服务端异常（合作方可按重试策略重试）。
- **建议重试规则**（对方遵守）: 3–5 次，指数退避（例如 1m, 2m, 4m, ...），总重试时长不超过 1 小时。

**示例：签名与发送（bash）**
```bash
TS=$(date -u +%s)
BODY='{"tracking_no":"YT123","event_code":"DELIVERED","event_time":"2026-03-12T10:00:00Z"}'
SECRET='YOUR_PARTNER_SECRET'
SIG=$(printf '%s%s' "$BODY" "$TS" | openssl dgst -sha256 -hmac "$SECRET" -binary | xxd -p -c 256)
curl -v -X POST https://your-domain/webhook/express/status \
  -H 'Content-Type: application/json' \
  -H "X-Timestamp: $TS" \
  -H "X-Signature: $SIG" \
  -H "X-Client-Id: partner-yd" \
  -d "$BODY"
```

**示例：PowerShell（Windows）**
```powershell
$ts = [int][double]::Parse((Get-Date).ToUniversalTime().Subtract([datetime]'1970-01-01').TotalSeconds)
$body = '{"tracking_no":"YT123","event_code":"DELIVERED","event_time":"2026-03-12T10:00:00Z"}'
$secret = 'YOUR_PARTNER_SECRET'
$hmac = New-Object System.Security.Cryptography.HMACSHA256([Text.Encoding]::UTF8.GetBytes($secret))
$hash = $hmac.ComputeHash([Text.Encoding]::UTF8.GetBytes($body + $ts))
$sig = ($hash | ForEach-Object { $_.ToString("x2") }) -join ''
Invoke-RestMethod -Uri 'https://your-domain/webhook/express/status' -Method Post -Body $body -ContentType 'application/json' -Headers @{ 'X-Timestamp'=$ts; 'X-Signature'=$sig; 'X-Client-Id'='partner-yd' }
```

**测试与沙箱**
- **临时隧道**: 联调期可使用 `ngrok` 或 `cloudflared` 将本地服务映射到公网供合作方发送测试回调。
- **测试密钥**: 为每个合作方发放 `test_secret` 与 `prod_secret`，并在系统中分离管理。
- **回放工具**: 提供一个简单 `test-send.js`（仓库已有）并在 README 中示例化如何运行。

**部署与网关建议**
- **域名与 TLS**: 使用 `webhook.<your-domain>`，证书推荐用 Let's Encrypt 自动续期。
- **反向代理 / 网关**: 推荐放在 Nginx / Kong / Cloud Load Balancer 前做：
  - TLS 终端、IP 白名单、速率限制（rate-limiting）、日志落盘与审计。
  - 如使用 Kong，可在网关做 `key-auth` 或 IP 白名单做第一道防护。
- **WAF/防护**: 对外公开时开启基础 WAF（过滤常见攻击）与 DDOS 保护。

**监控与告警**
- **关键 Metric**: 接收量、验签失败率、重复率（duplicate percent）、处理延时、no-targets 率。
- **告警阈值**: 验签失败率 >1% 或 no-targets 突增 触发 PagerDuty/邮件告警。

**上线流程（最小安全步骤）**
1. 在测试环境配置 partner test_secret 与 `X-Client-Id`，提供示例脚本。  
2. 使用 `ngrok` 联调，通过 2–3 次真实回调验证 `dedupe_key`、waybill 匹配和入队。  
3. 把 partner 的 prod_secret 写入生产密钥存储（避免明文在 repo）。  
4. 在生产网关启用 IP 白名单或 key-auth，并在低流量窗口把 `WEBHOOK_REJECT_INVALID_SIGNATURE` 改为 `true`。  

**常见问题与排查**
- **验签失败**: 检查是否对方用了 stringify 后的 body 或时间戳单位不一致（秒 vs 毫秒）；建议双方按示例脚本校验。  
- **重复回调仍写入**: 检查 `dedupe_key` 的字段组合与 DB 唯一约束 `(waybill_id,dedupe_key)` 是否生效。  
- **no-targets**: 确保 `push_devices` 已按 `merchant_id` 或 `user_id` 正确写入（见 `GET /api/v1/push/devices` 验证）。

**联系人与上游联调清单**
- **提供方需给出**: 回调样例（真实 JSON）、回调频率预估、回调 IP 段、是否支持 `dedupe_key` 字段、联系人邮箱/电话。  
- **我方需要提供**: `test_secret`、测试域名、示例脚本、接入说明（本文件）。

---
作者：自动生成；可根据合作方反馈补充示例与节流策略。
