API v2 幂等、重试与错误处理
使用 order_code 安全重试创建订单,并按 HTTP 状态码和机器可读错误 code 处理冲突、限流与依赖故障。
Darkroom API v2 把同一商家的 order_code 作为创建订单的自然幂等键。客户端可以在网络超时后重试,但必须保持业务请求内容不变。
订单号怎么取:随机,别用连号
order_code 由你指定,Darkroom 原样保存,并会展示给顾客、写进通知。它除了做幂等键之外,还是顾客自助验证身份的因子之一:顾客凭「手机号 + 订单号」可以把微信绑到自己的顾客档案、在公众号里找到订单、找回密码。所以订单号必须不可预测。
- 建议至少 8 位随机字母数字,可以加固定前缀便于对账,例如
crm-20260727-K7Q2XM。 - ⛔ 不要用自增序号、日期加流水号、CRM 主键这类能推算出来的编号(如
20260904001)。知道某位顾客手机号的人,配合可预测的订单号,就能通过自助入口接管这位顾客的档案。 - Darkroom 自动生成的订单号是 6 位随机大写字母数字,约 10 亿种组合;自助入口都有限流,同一来源每分钟只能试几十次,被猜中的概率接近零。这是民用行业的普遍做法,自定义连号会让这层保护失效。
- 如果你的系统只能提供连号,把连号放在自己这边做关联(或写进
team_remark),把随机串作为order_code传给 Darkroom。
创建订单的三种结果
| 场景 | HTTP | idempotent_replay |
客户端动作 |
|---|---|---|---|
| 第一次成功创建 | 201 | false |
保存返回的订单 |
相同 order_code、相同规范化内容 |
200 | true |
按成功处理,不创建重复订单 |
相同 order_code、不同规范化内容 |
409 | — | 停止自动重试,检查来源数据或使用正确的新编号 |
服务端会对验证后的业务字段做规范化再计算 hash,因此 JSON 的空格和 object key 顺序变化不会制造冲突;服务数组顺序、金额、日期、顾客字段等业务差异会参与判断。
安全重试规则
| 状态 | 是否自动重试 | 建议 |
|---|---|---|
| 网络超时、连接中断 | 是 | 使用相同的 order_code 和业务内容重新发送请求 |
429 rate_limit_exceeded |
是 | 等到下一分钟窗口(Retry-After),加随机抖动 |
429 burst_limit_exceeded |
是 | 按 Retry-After 等待,默认约 1 秒,无需等待到下一分钟窗口 |
| 500 / 502 / 503 / 504 | 有限重试 | 指数退避,设重试上限 |
401 invalid_token |
更新 token 后重试一次 | token 可能已过期或被撤销;如果仍返回 401,请停止自动重试并检查凭据状态 |
| 400 / 403 / 404 / 422 | 否 | 修正请求、scope 或业务数据 |
409 order_code_conflict |
否 | 人工或业务逻辑处理编号冲突 |
标准错误结构
{
"error": {
"code": "invalid_request",
"message": "The request contains invalid fields.",
"details": [
{
"field": "services.0.unit_price_amount",
"reason": "must_be_non_negative_decimal_string"
}
]
},
"request_id": "req_01K1A8M6WDQW8DM8QSCZ6N4G7Y"
}
| HTTP | 常见 error.code |
含义 |
|---|---|---|
| 400 | malformed_json, invalid_request |
body 不是合法 JSON 或基本格式错误 |
| 401 | missing_token, invalid_token |
缺少 Authorization: Bearer,或 token 已过期 / 被撤销 |
| 403 | insufficient_scope, credential_disabled |
token 的 scope 不满足端点要求,或凭据已停用 |
| 404 | order_not_found |
当前 merchant tenant 中找不到订单 |
| 409 | order_code_conflict |
同编号被内容不同的订单占用 |
| 422 | store_not_found, service_not_found, invalid_customer_mobile, coupon_invalid, prepayment_method_invalid |
格式正确但业务数据不可接受 |
| 429 | rate_limit_exceeded, burst_limit_exceeded |
已达到分钟配额或秒级突发限制;两者采用不同的等待时间,见下节 |
| 503 | api_temporarily_unavailable |
API gate、凭据服务或订单依赖暂不可用 |
错误 message 面向人阅读,集成逻辑只应依赖 HTTP status 与稳定的 error.code。details 只在适用时出现。
/oauth/token 端点另有一组认证错误码(invalid_client、credential_expired、invalid_scope、unsupported_grant_type),见认证与访问令牌。
分别处理分钟配额和秒级突发限制
| 层 | 阈值 | 性质 | error.code |
Retry-After |
|---|---|---|---|---|
| 分钟配额 | 20 req/min/key | 限制每分钟的请求总量 | rate_limit_exceeded |
距下一分钟窗口的秒数 |
| 突发限制 | 2 req/s/key | 限制短时间内的请求峰值 | burst_limit_exceeded |
1 |
收到 burst_limit_exceeded 时,应按 Retry-After 等待约 1 秒;收到 rate_limit_exceeded 时,应等待到下一分钟窗口。所有 2xx 和 429 响应均包含以下限流响应头,调用方可以据此调整请求速率:
RateLimit-Limit: 20
RateLimit-Remaining: 7
RateLimit-Reset: 34
Retry-After: 34 # 仅 429
分钟配额按凭据配置,默认值为 20 req/min/key。批量导入历史订单等场景可以申请调整,无需重新部署集成代码。/oauth/token 使用独立配额:10 req/min/client_id。
request_id 与日志
请保存每次调用返回的 request_id,并在服务端日志中关联外部订单号和 HTTP 状态码。日志中不得记录:
client_secret或完整access_token;- 顾客姓名、手机号、Email、儿童资料或地址;
- 完整 request / response body。
联系 Darkroom 支持团队时,请提供 request_id、发生时间、HTTP 状态码、error.code 和经过脱敏处理的 order_code 哈希值。
