接入中台时会遇到的全部错误码。按接口分组,每个码写清楚 HTTP 状态、含义和你该怎么处理。
#1. 两种错误格式
| 接口 |
错误体 |
SDK 抛出 |
/api/center/*(收款、通知、文件) |
{"error":{"code":"…","message":"…"}} |
CenterApiError(code / message / status) |
/api/oauth/site/*(OIDC 标准端点:authorize、token、userinfo、jwks、logout)和 discovery |
{"error":"…","error_description":"…"},OAuth 2.0 标准格式 |
CenterOAuthError(error / description / status) |
/api/oauth/site/request、consent、approve |
{"error":{"code":"…","message":"…"}} |
灵策自己的授权页在用,接入方不直接调用 |
- 按
code(或 error)分支,message 是给人看的中文,文案可能调整,不要拿它做判断。
/api/v1/* 模型接口的错误见 模型调用,不在这里。
- 校验错误多一个
details 字段:{"error":{"code":"validation_error","message":"…","details":[{"path":"amountFen","message":"…"}]}}。
- 请求体不是合法 JSON 时,错误在进入业务接口前由 HTTP 框架返回,是
400 且格式不同({"statusCode":400,"code":"FST_ERR_CTP_INVALID_JSON_BODY","message":"…"})。SDK 会按 http_400 抛出;出现它说明你的请求拼错了。
#2. 重试的通用规则
| 状态 |
怎么做 |
| 400、403、404、409 |
不要原样重试,先修请求或配置。例外见下表(order_creating) |
| 401 |
检查 client_id / client_secret;刚轮换过密钥就换上新的 |
| 429 |
退避后重试,建议从几秒起指数退避 |
| 500、503 |
退避后重试。带幂等键的请求(outTradeNo、outRefundNo、eventKey)用同一个键 |
| 502 |
支付方出错。下单要换 outTradeNo;退款要用同一个 outRefundNo 重试,见下表 |
#3. 所有 /api/center/* 都可能返回
| code |
状态 |
含义 |
你该做什么 |
invalid_client |
401 |
凭证错误、应用不存在、应用停用或没有生成过密钥,不区分原因 |
核对 client_id、client_secret,确认应用在后台是启用状态 |
rate_limited |
429 |
同一 IP 10 分钟失败 20 次、同一 IP 每分钟超过 1200 次、单个应用每分钟超过 600 次;通知接口里还表示应用整体发送过快 |
退避后重试 |
validation_error |
400 |
请求参数不符合约定,details 里有具体字段 |
修参数 |
internal_error |
500 |
服务内部错误 |
退避后重试,幂等键不变;持续出现联系灵策 |
http_<状态码> |
其它 |
上面没覆盖的框架错误,如 404 接口路径写错 |
核对路径和方法 |
#4. 收款 /api/center/pay/*
见 收款与 Webhook。
| code |
状态 |
含义 |
你该做什么 |
center_pay_disabled |
403 |
应用没开通收款 |
找运营在后台打开 |
payment_provider_not_allowed |
403 |
应用没开通这个支付方 |
找运营勾选,或换支付方 |
payment_provider_unavailable |
503 |
支付方未配置或已停用,也用于查单、退款时支付方不可用 |
换支付方或稍后再试 |
payment_scene_unsupported |
400 |
这个支付方不支持这个 scene |
对照 center-pay.md 3.1 改 scene |
amount_exceeds_limit |
400 |
金额超过单笔上限 |
拆单或找运营调整上限 |
metadata_too_large |
400 |
metadata 超过 4KB |
精简 |
return_url_not_allowed |
400 |
returnUrl 不是 https,或与应用首页、回调不同源 |
改成同源地址 |
client_ip_required |
400 |
h5 场景没传 clientIp |
传用户的公网 IP |
openid_required |
400 |
jsapi 场景没传 openid |
传灵策服务号下的 openid |
out_trade_no_conflict |
409 |
同一个 outTradeNo 用过了,但金额、标题、支付方、场景或 userId 不同 |
换 outTradeNo,或恢复原参数 |
order_creating |
409 |
同号的订单正在创建 |
稍等后用同号重试,这是唯一可以原样重试的 409 |
payment_create_failed |
502 |
支付方下单失败,这张单已记为 closed |
换一个 outTradeNo 重新下单 |
#查单、关单
| code |
状态 |
含义 |
你该做什么 |
order_not_found |
404 |
单号不存在,或不是你的应用的单 |
核对 orderNo(CTR 开头的中台单号) |
order_already_paid |
409 |
订单已付款或状态已变化,不能关单 |
重新查单,按已付款处理发货或退款 |
payment_query_failed |
502 |
向支付方查单失败或状态未知 |
退避后重试,不要假设没付 |
| code |
状态 |
含义 |
你该做什么 |
order_not_paid |
409 |
只有已付款的单能退 |
先查单 |
order_fully_refunded |
409 |
已经退满 |
不用再退 |
refund_amount_exceeded |
409 |
退款金额超过可退余额 |
查 refundedFen,算出余额再退 |
refund_conflict |
409 |
同一 outRefundNo 金额不同 |
换 outRefundNo,或恢复原金额 |
refund_failed |
409 |
这笔退款已确定失败,额度已释放 |
换一个新的 outRefundNo 重新发起 |
payment_refund_rejected |
422 |
支付方明确拒绝退款(如商户余额不足、交易状态不允许),钱没动,额度已释放 |
解决原因后换一个新的 outRefundNo 重新发起 |
payment_refund_failed |
502 |
支付方退款结果未知,额度仍占着 |
用同一个 outRefundNo 重试,或等 Webhook |
#支付方透传的错误
支付方适配器的错误码会原样带出来,5xx 归成 502。退款时状态码是 4xx 表示支付方明确拒绝,5xx 表示结果未知,要用同一个 outRefundNo 重试。
| code |
状态 |
含义 |
你该做什么 |
alipay_not_configured |
502 |
支付宝未配置 |
换支付方或联系灵策 |
alipay_scene_disabled |
409 |
灵策没开通这个支付宝场景 |
换 scene |
alipay_request_failed |
502 |
请求支付宝失败 |
下单换 outTradeNo,退款用同号重试 |
alipay_precreate_invalid |
502 |
支付宝返回的二维码无效 |
换 outTradeNo 重新下单 |
payment_input_missing |
400 |
缺支付方要求的参数 |
补齐后换 outTradeNo 重试 |
wechat_pay_refund_abnormal |
502 |
微信退款异常 |
用同号重试,持续失败联系灵策 |
其它未列出的支付方错误码按「状态码 + 上面的重试规则」处理。
#只在内部出现
下面几个码来自支付方回调、对账和运营后台的人工处理,接入方的接口不会收到,写在这里是为了排查日志时能查到。
| code |
状态 |
含义 |
payment_provider_mismatch |
409 |
回调的支付方与订单不一致,不改订单状态 |
amount_mismatch |
409 |
回调金额与订单不一致,不改订单状态 |
refund_release_mismatch |
409 |
释放退款额度时发现累计退款小于本笔金额,需要人工核对 |
refund_not_found |
404 |
后台操作的退款记录不存在,或不属于这张单 |
refund_state_changed |
409 |
后台操作时退款状态已经变了,刷新后再看 |
refund_attempt_in_flight |
409 |
刚调用过支付方退款,结果没回来前不能人工判失败 |
refund_provider_conflict |
409 |
支付方查到的状态和人工标记相反,不允许标记 |
order_state_changed |
409 |
后台操作时订单状态已经变了,刷新后再看 |
order_not_refunded_upstream |
409 |
支付方没有确认已退款,不能按已退款处理 |
#5. 通知 /api/center/notify/messages
见 通知与文件。
| code |
状态 |
含义 |
你该做什么 |
center_notify_disabled |
403 |
应用没开通通知 |
找运营在后台打开 |
user_not_authorized |
403 |
用户没授权过你的应用。未知的 userId 也是这个码 |
用户登录过你的应用才能发,不要重试 |
user_not_found |
404 |
授权过,但账号已停用或注销 |
不要重试,在你那边标记用户不可达 |
event_key_conflict |
409 |
同一 eventKey 发给过另一个用户 |
换 eventKey,检查是否把用户串了 |
user_rate_limited |
429 |
这个用户 24 小时内收到你的应用的消息已达上限 |
过一天再发,或合并通知 |
rate_limited |
429 |
应用整体发送过快 |
退避后重试,eventKey 不变 |
#6. 文件 /api/center/files/*
见 通知与文件。
| code |
状态 |
含义 |
你该做什么 |
center_files_disabled |
403 |
应用没开通文件 |
找运营在后台打开 |
file_type_not_allowed |
400 |
不支持的文件类型 |
对照 center-open.md 第 3 节的类型表 |
file_too_large |
400 |
超过这个类型的单文件上限 |
压缩或拆分 |
file_not_found |
404 |
文件不存在,或不是你的应用上传的 |
核对 key |
storage_unavailable |
503 |
灵策的文件存储未配置或暂不可用 |
退避后重试,持续失败联系灵策 |
#7. 登录 /api/oauth/site/*
见 统一登录。
/authorize 只有两种错误不跳转,直接回 400:client_id 未登记、redirect_uri 不在白名单。其余错误带 error 和 state 跳回你的回调地址,CenterClient.handleCallback 会抛成 CenterOAuthError,此时 status 为 0。
| error |
状态 |
出现在 |
含义 |
你该做什么 |
invalid_request |
400 |
authorize、token、logout |
缺参数或参数不对:state、PKCE、nonce、grant_type、redirect_uri;登出时 id_token_hint 无效或回跳地址不在应用域名内 |
修请求,看 error_description |
invalid_client |
400 |
authorize、token、logout |
client_id 没登记,或应用已停用 |
核对 client_id,确认应用启用 |
unsupported_response_type |
回调 |
authorize |
response_type 不是 code |
修请求 |
invalid_scope |
回调 |
authorize |
带了 openid,但 OIDC 还没开通 |
去掉 openid 或联系灵策 |
access_denied |
回调 |
authorize |
用户点了取消 |
回到登录页,不算错误 |
invalid_grant |
400 |
token |
授权码无效、已用、过期;redirect_uri 或 PKCE 不符;账号不可用;续期令牌已用过、过期、被「退出所有设备」或单点登出吊销;用户被移出应用名单 |
重新发起登录,不要重试同一个码 |
invalid_token |
401 |
userinfo |
访问令牌无效、过期、账号已退出,或用户被移出名单 |
续期或重新登录 |
slow_down |
429 |
authorize、token、userinfo、logout |
请求过于频繁 |
退避后重试 |
temporarily_unavailable |
503 |
token、logout |
登录服务暂时不可用(授权码或吊销标记存储) |
退避后重试。登出时要重试,否则续期令牌没有吊销 |
server_error |
500 |
token、userinfo、logout |
服务内部错误 |
退避后重试 |
not_found |
404 |
discovery、jwks、logout |
OIDC 还没开通 |
联系灵策 |
#灵策授权页内部接口
接入方不直接调用,用户在授权页上看到的提示来自这里。
| code |
状态 |
含义 |
site_sso_request_invalid |
400 |
登录请求无效或过期,用户要回到你的网站重新点登录 |
site_sso_rate_limited |
429 |
操作太频繁 |
site_sso_access_denied |
403 |
用户不在应用名单里 |
site_signup_disabled |
403 |
应用不开放注册,这次新注册的账号不能登录 |
site_sso_account_unavailable |
403 |
当前账号不可用 |
site_sso_unavailable |
503 |
服务暂时不可用 |
site_sso_failed |
500 |
其它错误 |
#8. SDK 自身的错误
| 类型 |
code |
含义 |
CenterSdkError |
network_error |
连不上中台 |
CenterSdkError |
invalid_config |
SDK 配置不对,比如缺 clientId |
CenterSdkError |
state_mismatch |
回调的 state 与发起时不一致,不要继续登录 |
CenterSdkError |
missing_code、subject_mismatch |
回调或令牌响应不完整、不一致 |
CenterOAuthError |
invalid_response |
中台返回的不是预期的 JSON |
IdTokenError |
见 统一登录 第 7 节 |
id_token 校验失败,按未登录处理 |
WebhookSignatureError |
见 收款与 Webhook 4.3 |
Webhook 验签失败,回 400 |