错误契约
统一信封。按 code 分支,不要按 message 分支 —— Accept-Language
会改变 message,不会改变 code。
{
"type": "invalid_request_error",
"code": "insufficient_balance",
"message": "The member's available balance is not enough.",
"request_id": "01J8X4K9…"
}
每个响应都带 X-Request-Id,错误体里也带一份。工单第一句话就是它。
type → HTTP → 你该怎么办
| type | HTTP | 语义 | 处置 |
|---|---|---|---|
| invalid_request_error | 400 | 入参不合法或业务前置条件不满足 | 改正后重发,换新幂等键 |
| authentication_error | 401 | 令牌无效 / 过期 / 签名不符 | 重新换令牌;连续失败查签名串拼法 |
| permission_error | 403 | scope 不足 / IP 不在白名单 / 商户被停用 | 后台改配置,不要重试 |
| not_found | 404 | 对象不存在或不属于本商户 | 不要重试 |
| idempotency_error | 409 | 同键异体 / 首次仍在处理 | 见「幂等」 |
| rate_limit_error | 429 | 超配额 | 指数退避 + 随机 jitter |
| api_error | 500 | 我方内部错误 | 同键重试;持续则报工单带 request_id |
| upstream_error | 502 | 上游不可用 | 同键重试 |
| upstream_timeout | 504 | 结果不明(可能已执行) | 同键重试,或查单确认。绝不换新键 |
常见 code
| code | 含义 |
|---|---|
| member_not_found | 会员不存在或不属于本商户或已停用 —— 三种情况同一响应 |
| member_context_required | 会员作用域端点缺 x-on-behalf-of |
| kyc_required | 该业务要求的 KYC 层级未达到,附 required_level |
| insufficient_balance | 会员可用余额不足 |
| merchant_insufficient_funds | 你的资金账户余额不足 —— 去商户后台充值 |
| product_not_available | 产品未对本商户授权、已停售或不满足条件 |
| limit_exceeded | 附 limit_type(single/daily/monthly)与 limit_scope(member/merchant) |
| request_rejected | 风控拒绝的唯一对外码。 不带原因、不带规则名、不带评分 |
| step_up_required | 需强认证,附 hosted_url 或 challenge_id |
| order_not_cancellable | 订单已进入不可撤销阶段 |
| asset_not_allowed | 该资产不在你的白名单内 |
| environment_mismatch | 沙盒凭据打了 live 域(或反过来) |
两件我方刻意不告诉你的事
风控判据永不出境。 命中原因、规则名、阈值、评分一律不下发。被风控拒绝
时你只会看到 request_rejected。人工申诉走商户后台工单。
跨商户信息不经由错误码泄露。 「该邮箱已注册」「该证件号已用于开卡」
这类响应会暴露另一个商户的会员存在性 —— 一律收敛成同一个不区分的响应。
所以你用一个已属于别处的邮箱建户时,拿到的不是 409。