# 错误与恢复 发布 0.4.0 / API v1。HTTP 错误包含 `code`、`message`、`request_id`、`retryable`。按 `code` 判断,`message` 是给人看的一句英文说明,措辞可能调整,不要据它分支。保留业务引用和请求 ID,报告中不要包含密钥、原始终端用户数据或上游响应。 | 情况 | 接入方行为 | | -------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 401 unauthorized | 核对所属产品、密钥是否已撤销与私有配置;管理凭据不能替代产品 Key | | 403 forbidden / csrf_failed | 核对动作授权;后台产品调用使用产品 Key,Cookie 只用于同源管理界面 | | 404 not_found | 核对当前用户与对象归属、所用服务入口;不通过跨用户遍历寻找对象 | | 400 invalid_path | 路径编码或长度不合法,属请求错误,不重试 | | 409 event_conflict | 原 `event_id` 对应原内容;从本地持久记录恢复原内容重发,不能为绕过冲突而换 `event_id` | | 409 conflict / product_name_taken / config_version_conflict | 名称或版本号已被不同内容占用,原请求重试不会成功;核对是否已创建,或换一个名称/版本号 | | 409 account_closed | 用量上报与下单(checkout)都拒绝:停止新收费工作;已记的用量不变;迟到付款不重新开通 | | 409 product_archived | 产品已归档,不再收款:checkout 与 `purchase-sessions` 都拒绝,读取与已有订单不受影响;请维护者确认产品状态 | | 422 unsupported_sku / amount_out_of_range / amount_not_accepted / unsupported_payment_method | 下单参数不被当前在售目录接受(sku 不在售;自定金额超出 min/max 或不合 step;固定额度包不接受 `amount`;收款方式未配置)。原样重试不会成功:重读 `catalog` 修正参数后再下单 | | 409 subscription_active | 用户还有会续费的订阅(或一个未结束的订阅收银台):先 `POST …/subscription/cancel` 设为到期取消,再关闭账户;关闭后到期前不会再发放额度 | | 409 subscription_exists / subscription_checkout_open | 用户已有订阅:换档用 `POST …/plan`;另一个订阅收银台刚创建、还不能作废时,稍后重试 | | 409 subscription_required | 用户没有可操作的有效订阅(或从未开过订阅收银台,没有客户管理页):引导去开通 | | 409 meter_changed | 负责人修改了计量,这笔用量没有记账:重读 `catalog.config`,按新单位换算后重报(可沿用原 `event_id`),之后带新的 `meter_version` | | 409 subscription_ending | 订阅已设为到期取消,不能换档;先撤销取消(`POST …/subscription/resume`)再换 | | 409 plan_unchanged / plan_change_in_progress | 已是这个档位;或同一用户的另一次换档还在处理,稍后重读 `GET …/plan` 再决定 | | 409 plan_change_payment_failed | 升级的差价没扣成功(拒卡,或需要 3DS 验证),档位未变:请用户在客户管理页换一张卡后重试 | | 409 billing_portal_not_configured | 负责人还没在 Stripe 后台保存过客户管理页设置 | | 422 unsupported_plan / free_plan_not_subscribable | 档位不存在、已下架或是免费档:重读 `catalog` 的 `plans` 后再选 | | 422 return_origin_not_configured / invalid_return_path | 产品未配置 `return_origin` 时不能传 `return_path`;`return_path` 须是以单个 `/` 开头、不含 `#` 与控制字符的站内路径。省略 `return_path` 或请维护者配置回跳地址 | | `allowed: false`(200) | 余额已用完或有欠额:停止当前工作,展示充值入口;下一笔充值先抵扣欠额 | | 429 try_again_later | 托管充值页:同一会话 15 分钟内下单超过 20 次,`Retry-After` 给出本窗口剩余的秒数,由付款人到时在页面重试,产品后端不代为重发。任何接口:同一来源地址每分钟请求过多(带产品 Key 的请求与 Stripe 通知超过 6000 个,其余超过 1200 个,按 Cloudflare 节点计),`Retry-After: 60`,按原请求稍后重试 | | 410 topup_session_expired | 链接已过期,不能继续使用;回产品查原订单,需要时重新签发 | | 503 stripe_not_configured | 联系 Billing 维护者配置 Stripe;用量上报不依赖 Stripe | | 503 stripe_unavailable | 建单时 Stripe 暂时不可用,订单保持 `creating`:用同一个 `purchase_ref` 重试,得到同一个订单与收银台 | | timeout / service_unavailable | 不推断零消费或付款失败;读取原记录,用原 `event_id`/`purchase_ref` 有限重试相同请求(不需要也不读取 `Idempotency-Key`) | 用量上报失败时,保留这一段用量和它的 `event_id`,下一轮用原内容重发;SDK 的 `createMeter` 已这样做。进程崩溃最多丢一个上报间隔的用量(对商户是少收,不是多收)。不要为了"保险"换一个 `event_id` 重报同一段用量。 关闭账户前,产品先停止该用户的新工作、报完剩余用量,并把会续费的订阅设为到期取消。关闭后上报返回 `account_closed`,已记的用量和欠额保持不变。 Billing 实例的恢复由维护者操作(Cloudflare Durable Object 按时间点恢复);恢复期间不接受新用量。客户端不能通过赠金、改余额或换引用来绕过。