客户中心下载 OpenAPI 定义错误码与处理说明公开接口 v1 · 错误契约 2026-09-21

Plus API 完整接入手册

版本 v1 · 错误契约增补日期 2026-09-21。本手册只描述客户接口,不公开内部执行实现。

1. 地址、范围与接入准备

接入顺序

管理员开户并发放 API Key → 兑换积分 → 查询账户
用户输入 Session / 卡资料 → 创建预检 → 保存预检 ID 和到期时间
用户明确点击确认付款 → 提交原预检 → 保存订单 ID
查询原单 / 接收签名回调 → 显示明确结果 → 成功后另取账单

预检不授权付款;只有用户明确确认后的订单提交才授权执行。预检 queued/preparing 时也能提交原预检,不要求顾客重复输入或重新创建。

2. 认证与公共约定

所有 /v1/ 接口均需服务端请求头:

Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

Key 是本服务密钥,不是 Session accessToken,也不是支付平台公钥,即使其前缀是 pk_live_ 也不能混用。URL 中的 api_key 不被接受;自助页 Cookie 不能代替 Bearer。不要把 Key 放进公开网页、客户端源码、截图、Git 或日志。

项目 契约
编码 UTF-8 JSON;写请求体必须是对象,不接受未知字段
成功包装 直接返回对象,无统一 data/ok 包装
成功 HTTP 创建、确认、兑换均为 200,不是 201/202
错误 {"error":{"code":"invalid_api_key"}};网络边缘错误可能返回非 JSON
时间 Unix 秒,可有小数;前端日期转换乘 1000
尚未记录时间 null,不是耗时 0 秒
request_id 8–128 字符,只允许字母、数字、下划线、点、冒号、短横线
幂等范围 同一客户;预检和订单分别计算。建议 pf-UUID / order-UUID
普通请求体上限 8,192 字节
预检请求体上限 98,304 字节;其中 Session 字符串 20–65,536 UTF-8 字节
压缩请求体 不支持 gzip 等压缩格式
跨域调用 推荐客户服务端调用,不把 Bearer 暴露给浏览器

HTTP 200 仅表示请求成功,必须查看订单 status 才能判断充值结果。

3. 全部接口

方法 路径 用途
GET /v1/account 账户与积分
GET /v1/cards 当前 Key 的私人卡池
POST /v1/cards/import 批量导入自己的银行卡
POST /v1/cards/{card_id} 调整次数、暂停或删除私人卡
POST /v1/credits/redeem 兑换积分卡密
GET /v1/credits/ledger 积分流水
POST /v1/preflights 提前准备,不付款
GET /v1/preflights/{preflight_id} 预检状态及期限
POST /v1/orders 顾客确认后正式提交
GET /v1/orders 最近 50 条订单
GET /v1/orders/{order_id} 原单状态与进度
GET /v1/orders/{order_id}/invoice 成功后的账单
GET /v1/webhook 查询回调配置
POST /v1/webhook 设置回调并生成/轮换签名密钥
DELETE /v1/webhook 关闭回调

没有客户生成积分、付款撤销、预检续期/取消、强制释放未知结果、手工重放回调接口。私人卡池不提供自动开卡或平台卡借用。

4. 账户与积分

GET /v1/account

curl --fail-with-body "https://api.666666.homes/v1/account" \
  -H "Authorization: Bearer $PLUS_API_KEY"
{"id":"cus_example","name":"示例客户","balance":10,"frozen":2,"available":8,"concurrency":4}
字段 类型 含义
id string 客户 ID,不是 Key ID
name string 管理员登记的名称
balance integer 总积分,包含冻结
frozen integer 在途和待核对订单的冻结积分
available integer 可用积分,balance - frozen
concurrency integer 客户并发上限,非实时保证可用名额

多个 Key 共用该客户的积分、订单和限流额度,不会增加并发。但银行卡池严格按具体 Key 隔离,同一客户另一个 Key 也不能读取或选取这些卡。

结算规则

预检要求 available 至少为 1,但不扣、不冻结积分。正式订单接受后冻结 1 积分;充值成功只扣 1 积分;明确失败释放冻结;review 继续冻结,不能理解成已扣积分或未扣款。

充值成功后,账单和取消自动续费处理异常不改变成功,不额外扣积分。积分是服务次数,不是美元或银行卡金额。

当前 Plus 路线付款前要求 PHP 982.14(最小单位整数 98214);公开请求不接受覆盖金额、币种或产品的字段,不能关闭金额校验。

POST /v1/credits/redeem

请求:

{"code":"PC-YOUR-CREDIT-VOUCHER"}

首次返回:

{"credits":10,"already_redeemed":false}

同一客户重复兑换返回 already_redeemed=true,不重复入账;其他客户已兑换返回 409 voucher_already_redeemed。code 为 10–128 字符,服务端会去首尾空白并统一大写。兑换响应没有 balance,请另查账户。

GET /v1/credits/ledger

首次无参数,后续 ?before=上一页最后一条id。每页最多 50 条,id 倒序,空数组表示查完。没有 page/limit 参数。

{"entries":[{"id":12,"kind":"consume","reference":"ord_example","balance_delta":-1,"frozen_delta":-1,"balance_after":9,"frozen_after":0,"created_at":1789700030.5}]}
kind balance_delta frozen_delta 含义
redeem +N 0 兑换入账
freeze 0 +1 正式接单冻结
consume -1 -1 成功结算
release 0 -1 明确失败释放

reference 为兑换记录或订单标识,不返回卡密明文。流水不可由客户修改。

4.1 私人银行卡池

用当前 API Key 登录客户中心,在“我的银行卡”点击“导入银行卡”。输入、粘贴或修改资料后自动识别,无需点击识别按钮;核对尾号、有效期与成功次数上限后,只需点击“确认导入”。网页支持整段复制的“卡号、过期时间、CVV”等分行标签,以及全角数字、分组卡号和多张卡;也支持 卡号----月份----年份----安全码、卡号 月份/年份 安全码。年份可为两位或四位。网页识别后会先转为下述标准接口格式再提交;直接调用导入API仍应遵循其行格式。缺失或歧义资料必须修正,不会从下一张卡借用有效期或安全码。

卡片按尾号或有效期搜索、按状态筛选,默认每页 10 张,可切换为 20 或 50 张。列表独立滚动,具体次数调整、暂停、启用和删除均在该卡的“管理”弹窗完成;使用中或待核对卡不可修改。订单与积分兑换在独立导航页,不挤占卡片列表。退出时清空页面中的卡片输入和列表,不写入浏览器本地缓存。

POST /v1/cards/import

{"text":"YOUR_CARD_NUMBER 12/2029 YOUR_CVC","use_limit":5}

text 为字符串,每行一张,最多100张/32768字符,请求体最多65536字节。use_limit 可选,默认5,允许1–1000整数,表示每张卡总成功次数上限,不是金额额度。过期或非法行导致整个批次不导入。一个 Key 最多保存500张未删除卡。

{"added":2,"duplicates":1}

同一个 Key 重复导入同一卡号不会覆盖材料、上限、计数或占用;超时后可查询卡池或重复原导入,不会重复加卡。删除后再导入仍保留历史成功次数。只导入你有权使用的银行卡,不要把资料写入 URL、日志或公开截图。

GET /v1/cards

{"cards":[{"id":"pcard_0123456789abcdef0123456789abcdef","last4":"1234","expiry":"2029-12","use_limit":5,"success_count":1,"version":3,"remaining":4,"status":"available"}]}

不会返回完整卡号或安全码。状态:available可用;occupied使用中或待核对/清理;paused已暂停;expired已过期;exhausted次数已用完。remaining为次数余额,不代表当前卡一定空闲。

POST /v1/cards/{card_id}

传最新列表中的 version 防止旧页面覆盖新状态。use_limit、enabled、delete按需传入;occupied卡禁止修改、删除。

{"version":3,"use_limit":8}
{"version":3,"enabled":false}
{"version":3,"delete":true}

成功均返回 {"ok":true}。上限不得小于已经成功的次数。成功只计一次;取消续费/账单失败不退次数;明确未付款失败不扣;结果未知保持占用。没有强制解锁接口。

常见错误:no_available_private_card(409,当前Key没有可用卡,请补卡/等待占用结束);private_card_not_found(404,不属于当前Key或已删除);private_card_in_use(409,暂不可修改);card_version_conflict(409,刷新后再操作);card_limit_below_used(409,上限小于已用次数);expired_card(400);invalid_card_line_N(400,第N行格式有误)。禁止因这些错误换用平台卡或其他人的卡。

5. 预检

POST /v1/preflights

以下是结构模板;占位银行卡不可直接运行:

{
  "request_id":"pf-your-unique-uuid",
  "session":"{\"accessToken\":\"AUTHORIZED_TOKEN\",\"account\":{\"id\":\"PERSONAL_ACCOUNT_ID\"}}",
  "card":{"number":"YOUR_CARD_NUMBER","exp_month":"09","exp_year":"2029","cvc":"YOUR_CVC"}
}
字段 必填 类型及约束
request_id 是 string,固定的一次逻辑预检标识
session 是 完整 Session JSON 的字符串,不是对象,也不是仅传令牌
card 否 兼容原有逐单自带卡,object,仅下列四项;与card_id互斥
card_id 否 当前Key私人卡池中指定的卡;不能传其他Key或平台卡标识
card.number 是 string,12–19 位数字,通过卡号校验;不要加空格
card.exp_month 传 card 时必填 string,1–12,建议两位
card.exp_year 传 card 时必填 string,四位 20xx,不是两位年份
card.cvc 传 card 时必填 string,3–4 位数字,保留前导0

Session 需包含有效非空 accessToken 与账号标识(account.id 或现有资料中的 user.id)。请使用用户授权的原始完整 Session,不要根据邮箱自行编造/替换 ID,也不必删除原资料的其他字段。

使用私人卡池时,省略 card 和 card_id 即由当前 Key 的卡池选取一张空闲且剩余次数大于0的卡;也可仅传 card_id 指定自己的卡。缺卡、读取异常或卡在占用时不会借用平台卡,不会自动开卡。原有显式 card 模式仍只使用请求提供的卡,不会从平台取卡。需要卡池次数管理时请使用 card_id 或自动选卡模式。

{"request_id":"pf-your-pool-uuid","session":"AUTHORIZED_COMPLETE_SESSION_JSON_STRING"}

提交、查询这个预检必须继续使用创建时的 Key。同一 request_id 重试返回原分配,不会另外拿卡。

返回:

{"id":"pfapi_example","created_at":1789700000.0,"expires_at":1789700120.0,"order_id":"","status":"queued","error_code":"","customer_message":"","stage":"preflight","retryable":false,"next_action":"wait","cleanup_acknowledged":false,"payment_started":false,"card_consumed":false,"credit_state":"none"}

保存 id 为 preflight_id。当前有效期从创建开始 120 秒,以 expires_at 为准;ready 不延长期限。未正式提交时 order_id 为空字符串,不是 null。

相同 request_id 和相同内容返回原预检,不创建新的、不刷新期限;同 ID 修改资料返回 409 idempotency_conflict。

GET /v1/preflights/{preflight_id}

字段同创建接口。例如:

{"id":"pfapi_example","created_at":1789700000.0,"expires_at":1789700120.0,"order_id":"","status":"ready","error_code":"","customer_message":"","stage":"preflight","retryable":false,"next_action":"submit_order","cleanup_acknowledged":false,"payment_started":false,"card_consumed":false,"credit_state":"none"}
status 意义 处理
queued 等待准备 可在顾客确认后提交原预检
preparing 准备中 不必重建,可确认原预检
ready 已准备 等待顾客确认,不代表付款
adopted 已接管 有 order_id 时查询原订单
cleanup_pending 安全收尾中 不并行重复提交同账号/卡
closed 已关闭 不可再提交
failed 预检失败 未正式提交则不扣积分,修正后重做
expired 已过期且未接管 新预检需要新的 request_id

预检的公开错误字段、Session 失效和订阅未到期的对应关系,见错误码与处理说明。接口不返回原始异常堆栈、上游正文、Session 或银行卡资料。 若已有 order_id,订单状态优先;不能因为预检时间届满把已提交订单判失败。 card_material_expired 指短期材料无法接管,不是断言银行卡已过期。 资源冲突时先核对原任务;不要用多个 Key 或新 ID 绕过账号/卡互斥。

6. 正式订单

POST /v1/orders

仅在用户明确确认付款后调用:

{"request_id":"order-your-unique-uuid","preflight_id":"pfapi_example","confirmed":true,"allow_fallback":false}
字段 必填 含义
request_id 是 固定的一次逻辑提交标识
preflight_id 是 自己的有效、可接管预检
confirmed 是 必须布尔 true;字符串 "true"、数字 1 都不接受
allow_fallback 否 布尔,默认 false,是否授权可用第三方接手

第三方接手前,需取得客户对必要 Session/卡资料处理的授权。true 不是所有失败都自动重试的承诺,不绕过未知付款结果限制。此接口无需再次传 Session 或卡资料。

{
  "id":"ord_example","request_id":"order-your-unique-uuid","preflight_id":"pfapi_example",
  "status":"queued","credit_state":"frozen","created_at":1789700010.0,
  "started_at":null,"payment_at":null,"success_at":null,"finished_at":null,
  "updated_at":1789700010.0,"result_code":"",
  "error_code":"","customer_message":"","stage":"preflight","retryable":false,
  "next_action":"wait_result","cleanup_acknowledged":null,"payment_started":false,"card_consumed":null
}

立即持久化 request_id → order_id。响应不含 events;200 只代表接单。queued/preparing/ready 的预检可提交,已完成工作会沿用。

GET /v1/orders/{order_id}

{
  "id":"ord_example","request_id":"order-your-unique-uuid","preflight_id":"pfapi_example",
  "status":"succeeded","credit_state":"consumed","created_at":1789700010.0,
  "started_at":1789700011.0,"payment_at":1789700014.0,"success_at":1789700030.5,
  "finished_at":1789700030.5,"updated_at":1789700030.5,"result_code":"",
  "error_code":"","customer_message":"","stage":"payment","retryable":false,
  "next_action":"none","cleanup_acknowledged":true,"payment_started":true,"card_consumed":true,
  "events":[
    {"id":101,"kind":"queued","created_at":1789700010.0},
    {"id":102,"kind":"paying","created_at":1789700014.0},
    {"id":103,"kind":"succeeded","created_at":1789700030.5}
  ]
}
字段 含义
status 业务判断依据,见下表
credit_state frozen / consumed / released
created_at 正式订单接受时间,不含提前预检
started_at 正式执行开始,可空
payment_at 付款阶段记录时间,可空,不单独作为扣款证据
success_at 充值成功确认时间,可空
finished_at 终态记录时间,可空
updated_at 最近状态更新时间
result_code failed 时 PAYMENT_FAILED;review 时 PAYMENT_UNCONFIRMED;其他通常为空
error_code / customer_message 公开细分业务错误码与安全顾客文案;正常和成功时为空
stage / retryable / next_action 公开阶段、当前能否重试及应采取的动作,见错误说明
payment_started / card_consumed 付款是否开始、本次卡付款是否确认成功;允许 null,不可视为 false
cleanup_acknowledged 资源清理是否确认,与付款和积分分别判断
events 详情接口的事件数组,按 ID 顺序,供进度展示

events.kind 可见 queued/preparing/paying/review/succeeded/failed/invoice_ready。中间阶段可能不齐;缺少某步不等于失败。不要把价格字段缺失或事件跳步当失败。

业务状态与积分

status 是否明确终态 积分 建议文案
queued 否 frozen 已提交,等待处理
preparing 否 frozen 正在准备
paying 否 frozen 正在确认支付
review 否,结果不确定 frozen 支付结果待核对,请勿重复提交
succeeded 是 consumed 充值成功
failed 是,确认失败 released 充值失败,积分已释放

充值成功立即显示成功,不等账单/取消续费。晚到的旧 paying 回调不能覆盖成功。 review 不能显示为失败,也不能允许再次付款;继续查询原单或联系管理员。

GET /v1/orders

返回 {"orders":[...订单摘要...]},最近最多 50 条,创建时间倒序。 摘要字段同 POST 返回,没有 events;目前没有日期、邮箱、page 或 limit 参数。 较早订单用已保存的 order_id 查询详情。不同客户的数据不可互查。

耗时口径

7. 超时、幂等与限流

场景 正确处理 禁止做法
创建预检回包丢失 保留原 request_id 与完全相同资料,有限重试 换 ID 无限建预检
提交订单超时/5xx 已有 order_id 则 GET;否则按原 ID/预检/参数恢复同一请求 认定未付款后重新建单
相同 ID 不同内容 修正客户端幂等记录 不断修改参数碰运气
preflight 已绑定订单 查询 preflight.order_id 再次提交另一订单
GET 超时 退避后查原单,保留原状态 将网络失败转换为业务 failed
review 原单查询,必要时联系管理员 再支付、换渠道再次扣款
succeeded、账单未就绪 显示成功,稍后查账单 为获取账单重新充值

重复相同逻辑提交不会授权第二次付款;这不等于允许重发底层付款确认。 建议连接超时5秒、响应超时20秒;是客户端建议,不是服务端保证。 关闭 HTTP 自动重定向,防止 POST 被隐式重放或密钥被转发外域。 不知道 order_id 时可查最近订单匹配 request_id,但列表只有50条,必须自己持久化映射。

访问频率与容量

每客户读请求 120 次/分钟,写请求 30 次/分钟;来源 IP 另有认证入口 240 次/分钟。 多个 Key 共享客户额度,共享出口可能触发 IP 限制。 单单可每2–3秒查一次,长时间未终结应减速;并发多时用回调加少量兜底查询。 四单每2秒分别查会占满120次/分钟,连账户查询也没有余量,不建议长期如此。

429 rate_limited:降低访问频率、指数退避并加随机抖动。 429 customer_capacity_reached:等待在途任务释放名额,不要密集建单。 不能依赖一定返回 Retry-After。

8. 回调 Webhook

配置、读取、关闭

POST /v1/webhook:

{"url":"https://merchant.example.com/hooks/plus"}
{"url":"https://merchant.example.com/hooks/plus","enabled":true,"signing_secret":"whsec_EXAMPLE_ONLY","generation":1}

仅 HTTPS/443、公网地址;不可含账号密码、片段或指向内网。 保存成功只代表配置接受,不代表已经验证投递成功;实际投递时仍会校验 DNS 和目标地址。 签名密钥只在保存响应显示,请立即安全保存。

再次 POST 会轮换密钥,并非幂等读操作。 配置超时先 GET;无法恢复密钥时有意识地再次配置一次,并查询原订单补齐可能遗漏的通知,不要自动无限重试。

GET /v1/webhook 未配置:{"enabled":false};已配置:

{"url":"https://merchant.example.com/hooks/plus","enabled":1,"generation":1}

兼容 enabled 布尔值或0/1;GET 永不返回 signing_secret。 DELETE /v1/webhook 返回 {"ok":true},可重复关闭,不删除订单/积分。

回调格式

POST /hooks/plus HTTP/1.1
Content-Type: application/json
User-Agent: Plus-API-Webhook/1
X-Event-Id: evt_example
X-Webhook-Timestamp: 1789700031
X-Webhook-Signature: sha256=<64位小写十六进制HMAC>
{
  "id":"evt_example","type":"order.updated","created_at":1789700030.5,
  "data":{
    "id":"ord_example","request_id":"order-your-unique-uuid","preflight_id":"pfapi_example",
    "status":"succeeded","credit_state":"consumed","created_at":1789700010.0,
    "started_at":1789700011.0,"payment_at":1789700014.0,"success_at":1789700030.5,
    "finished_at":1789700030.5,"updated_at":1789700030.5,"result_code":"",
    "error_code":"","customer_message":"","stage":"payment","retryable":false,
    "next_action":"none","cleanup_acknowledged":true,"payment_started":true,"card_consumed":true
  }
}

data 是订单摘要,无 events/Session/卡片/账单 URL。账单就绪也使用 order.updated,订单仍 succeeded,不是 invoice.ready 回调类型。

签名校验

message = UTF8(X-Webhook-Timestamp) + "." + 原始HTTP请求体字节
digest = HMAC-SHA256(UTF8(signing_secret), message)
expected = "sha256=" + digest 的小写 hex

使用原始字节,不要先解析 JSON 再序列化。完整 signing_secret(含 whsec_)作为密钥。 常数时间比较签名,校验 body.id 等于 X-Event-Id。建议接收端容忍时间差不超过300秒,并同步系统时间;此为客户端安全策略。

事件 ID 必须数据库持久化去重。同一事件重投 ID/正文不变,时间戳/签名改变。 可能重复、延迟、乱序;不能让旧事件降级 succeeded,不确定时 GET 原订单。 示例仅负责验签,业务去重与数据库事务由接入方实现。

重试与应答

先将验签成功的事件持久化,再快速回任意2xx,无需特定JSON。约5秒投递超时,耗时业务放后台。 非2xx、超时、网络故障重投;3xx不跟随。 最多8次尝试(首次+7次重试),间隔约1、5、30、120、600、1800、3600秒,实际调度可能更晚。 耗尽不再无限重投,必须保留订单查询兜底。 关闭/重新配置使旧配置待投递通知失效;新配置不保证历史补发。没有公开手工重放接口。

9. 账单下载

GET /v1/orders/{order_id}/invoice:

关闭自动跳转,读取 Location 后使用不带本 API Bearer的新请求下载,或给已授权客户打开。账单链接也是敏感资料。账单404绝不能触发新一笔充值。

10. 错误码

HTTP code 处理
400 invalid_json / invalid_fields 修正JSON和允许字段
400 invalid_request_id 使用规定格式的稳定逻辑操作ID
400 invalid_session / session_account_missing 完整授权Session JSON字符串
400 invalid_card_fields 检查卡字符串、年份、月份、CVC格式
400 explicit_confirmation_required 必须用户确认,传布尔true
400 invalid_voucher 检查积分卡密,不是银行卡或充值订单卡密
400 invalid_page / invalid_https_url 修正流水游标/回调URL
401 invalid_api_key 检查Bearer、Key撤销、客户停用
402 insufficient_credits 预检时余额不足,先兑换
403 customer_disabled 联系管理员,不绕过禁用
404 preflight_not_found / order_not_found 检查ID及客户归属
404 invoice_not_ready 只影响账单,稍后重查
409 idempotency_conflict 相同ID内容变化,核对原请求
409 voucher_already_redeemed 已被其他客户使用
409 insufficient_credits 确认订单时余额不足,预检后余额可能变化
409 preflight_already_submitted 查询已绑定的原订单
409 preflight_not_adoptable 预检过期/关闭/失败等,先查预检
409 card_material_expired 短期材料失效,核对原单后重新准备
409 resource_already_in_use / account_already_in_use / card_already_in_use 原任务占用中,等待安全收尾
409account_recently_paid同账号北京时间当日另一笔充值已确认付款。查询原订单,不要换 request_id、卡片或渠道再次充值;本次拒绝不代表原付款失败,也不能把原成功归到新请求。跨天续费仍按原账号资格流程处理;正在付款或付款未知不会因跨天解锁。
413 request_too_large 请求体超过上限
415 json_required / compressed_body_not_supported 使用非压缩application/json
429 rate_limited 降低请求频率并退避
429 customer_capacity_reached 等待在途任务释放容量
503 execution_not_ready / secure_handoff_capacity 暂无可接单资源,已有订单查原单
503 open_api_not_enabled 服务暂未开放,联系管理员

未知code保留脱敏HTTP状态、code、request_id、order_id和时间,请管理员核查。 HTTP/网络错误不是支付证据,不能直接转为业务 failed。

10.1 业务错误码与处理说明

适用:https://api.666666.homes 的 Plus API v1;契约增补日期 2026-09-21。可将本文件直接交给负责对接的开发者。完整端点、鉴权、参数、幂等和回调见接入手册,在线地址为 API 文档。

先分清两种返回

请求错误:HTTP 非 2xx,读取 error.code,例如 invalid_api_key、invalid_session、preflight_not_adoptable。含义是这次接口请求没有按要求完成;不能由此推断某个已有订单未付款。

业务结果:预检或订单查询通常返回 HTTP 200,读取顶层 status、error_code 和 next_action。HTTP 200 下仍可能是预检失败、Session 失效或订单结果待核对。对接端不要只看 HTTP 状态,也不要匹配中文文案或内部异常类名。

同步 invalid_session 表示提交的 Session 结构不合规;异步 SESSION_INVALID 表示执行中已得到登录凭据无效/过期的明确结果。两者都应引导客户提供有效 Session,但不能把一次超时或普通 403 页面自动归为 SESSION_INVALID。

公开业务字段

以下字段添加在预检和订单对象顶层。原字段、原状态和订单 result_code 保留;接入方应兼容以后增加的新字段和新错误码。

字段 类型 准确含义
status string 预检/订单的流程状态,决定是否完成;两类对象状态集合不同
error_code string 可稳定匹配的公开业务错误码;正常处理中、预检 ready、订单成功时为空字符串
customer_message string 可给顾客显示的安全文案;措辞可能改进,不用它做程序判断
stage string 出错或当前所在的公开阶段;不是完整的内部执行轨迹
retryable boolean 当前结果是否允许按 next_action 重新发起相应业务操作;不授权重发付款确认
next_action string 接入端应该采取的动作,见下表
cleanup_acknowledged boolean 或 null true 表示相关资源清理已确认;false 表示未确认;null 表示当前对象没有可靠清理结论
payment_started boolean 或 null 是否进入过付款处理;true 不等于已扣款,null 不可当作 false
card_consumed boolean 或 null 本服务对本单的银行卡成功使用标志;不是积分、余额、剩余次数或发卡方交易查询结果。未知为 null
credit_state string 预检未建订单为 none;关联订单为 frozen、consumed 或 released,指本服务积分。关联订单暂无法读取时为 unknown,不能按未扣积分处理
result_code string 仅订单保留的旧分类:failed 为 PAYMENT_FAILED,review 为 PAYMENT_UNCONFIRMED,其余为空;细分原因读取 error_code

card_consumed=false 不代表卡可立即重新使用;还必须看清理状态和原订单。银行卡实际账务最终以发卡方记录为准。credit_state=released 仅表示服务积分已解冻,不能替代银行卡结果证据。

未正式提交的预检没有付款授权,不扣也不冻结积分;预检 ready 只表示准备完成,绝不是付款成功。预检一旦带非空 order_id,立即改查该原订单;预检过期或关闭不能覆盖订单结果。

业务错误码对应表

error_code 表示什么 顾客/接入端应如何处理
SESSION_INVALID 已明确确认 Session 无效或过期 提示“Session 已失效,请重新登录账号后重试”;换有效 Session,在原流程安全结束后创建新预检
GRACE_SESSION_REFRESH_REQUIRED 已识别宽限期,续费此前已关闭或已尝试发起取消;本次未继续付款,不代表已核实取消成功 展示提示;next_action=renew_session,获取新 Session 后重新预检,不能自动重试旧 Session
GRACE_CANCEL_REQUIRED 已识别宽限期,自动取消未能发出、接口明确拒绝,或当前渠道无法自动处理;本次未付款 展示提示;next_action=cancel_renewal_and_renew_session,顾客自行确认关闭续费并重新登录获取新Session;不得显示“已取消”或自动重试
WEB_RENEWAL_CANCEL_UNVERIFIED 付款前自动取消续费的结果未核实,不能确认宽限期或取消成功 展示提示;next_action=cancel_renewal_and_renew_session,顾客确认并关闭续费,再重新登录获取新Session;retryable=false,不自动重试旧Session
SUBSCRIPTION_NOT_EXPIRED 当前订阅仍有效,暂不符合充值条件 提示会员尚未到期;不重复提交相同资料,不归因于银行卡或网络
UNSUPPORTED_CHANNEL 渠道不支持的预留公开码,仅在服务方有明确渠道结论时返回;当前版本未全面覆盖渠道识别 收到该码才提示不支持该渠道;不能自行把网络、币种或技术错误映射成该码
PREFLIGHT_TECHNICAL_ERROR 支付前准备发生技术失败,已归为可识别技术问题 清理已确认且 retryable=true 时,有限重做预检;不要把账号提示改为 Session 失效
CHECKOUT_CREATE_RESPONSE_LOST 创建支付页面的结果响应未可靠取得 这是准备阶段结果,不是“已经付款待核对”;按照清理和 next_action 处理,不重放底层创建/确认请求
PREFLIGHT_EXPIRED 预检有效期已结束 无关联订单、清理确认且允许重试时,新建预检;新 request_id 不刷新原预检
PREFLIGHT_CLOSED 预检已经关闭 不能再提交此预检;先看 order_id 和 next_action,再决定是否重新准备
PREFLIGHT_CLEANUP_PENDING 正在等待预检安全收尾,暂无更具体公开原因 等待原预检清理确认;不能并行抢占同一账号或卡
PREFLIGHT_FAILED 预检已失败,但旧记录或旧执行端没有保存细分原因 只显示“预检失败”;不可猜成 Session 失效、订阅未到期或付款未知。按 next_action 处理,必要时提供预检 ID 排查
PAYMENT_FAILED 订单已经确认失败;可能是未开始付款的准备失败,也可能是明确未付款的支付失败 展示失败并按积分状态释放本地服务占用;是否重试遵循 next_action,不无条件自动连环提交
PAYMENT_UNCONFIRMED 已有订单付款结果尚未确认 显示待核对,继续查询原单;不可报“未扣款”、不可重新付款或新建平行付款单

错误码和 status 共同解读。例如 status=cleanup_pending,error_code=SESSION_INVALID 表示已识别 Session 失效,但资源尚未安全释放;此时 next_action=wait_cleanup,不能立即重复请求。历史失败没有完整记录时返回兜底码,系统不会补造错误原因。

预检 ID 的实际字段名仍是 id,对接端可以在自己的系统中保存为 preflight_id;响应没有另造一个同义字段。公开 stage 可见 preflight、precheck、session_validating、subscription_check、context_preparing、checkout_creating、stripe_artifacts、reporting_ready、cleanup、payment、response_wait、reconciliation。阶段只作定位,不是必须依次出现的完整步骤表。

宽限期取消续费后需要新 Session

GRACE_SESSION_REFRESH_REQUIRED 表示已识别宽限期,续费此前已关闭,或服务端已经尝试取消。 取消回包丢失不等于已取消,也不再要求旧 Session 查询通过;本次没有继续付款。 展示 customer_message,要求客户重新登录并提供最新 Session,不要自行追加“已取消”的结论。 retryable=false、next_action=renew_session;禁止自动重复提交旧 Session。 取得新 Session 后使用新的请求 ID,重新预检并等待客户确认。清理未完成时先遵循 next_action=wait_cleanup。成功订单和付款结果未知的订单不能据此降级或重新付款。

此代码是可识别的正式处理结果,不意味着只调用预检就会执行取消续费;输入预检不修改订阅。

请求错误 error.code

请求被拒绝仍使用兼容的嵌套格式,不是上述顶层业务错误:

{"error":{"code":"invalid_api_key"}}
HTTP error.code 处理
400 invalid_json / invalid_fields 修正 JSON、类型和允许字段
400 invalid_request_id / invalid_preflight_id 使用符合格式的原逻辑请求 ID/预检 ID
400 invalid_session / session_account_missing 提供原始完整授权 Session JSON 字符串;这是输入格式错误,不是确认登录过期
400 invalid_card_fields / conflicting_card_selection 修正卡字段;card 和 card_id 不能同时传
400 invalid_card_import / invalid_card_line_N 修正导入文本,第 N 行有误;整批不导入
400 invalid_card_use_limit / invalid_card_update 修正次数、布尔操作和版本字段
400 expired_card 卡片有效期已过,换有效卡
400 explicit_confirmation_required 确认客户授权后传 confirmed=true,不接受字符串或数字
400 invalid_voucher 检查积分兑换码
400 invalid_page / invalid_https_url 修正流水游标或回调 URL
401 invalid_api_key 检查 Bearer 密钥、Key 是否撤销及客户状态
402 或 409 insufficient_credits 兑换积分后再操作;正式确认前余额可能变化
403 customer_disabled 联系管理员核实客户状态
404 preflight_not_found / order_not_found / private_card_not_found 核对 ID 及所属客户/Key,不能跨账户查询或用卡
404 invoice_not_ready 账单尚未就绪;不改变充值成功,不重新付款
409 idempotency_conflict 同一 request_id 不能改变内容;核对原请求
409 voucher_already_redeemed 积分卡密已由其他客户兑换
409 preflight_already_submitted 查询预检关联的原订单
409 preflight_not_adoptable 查询原预检的 status、error_code 和 next_action
409 card_material_expired 临时准备资料不可继续使用,不等于银行卡本身过期;先核对原订单
409 resource_already_in_use / account_already_in_use / card_already_in_use 原流程仍占用资源,等待清理;不建平行任务
409account_recently_paid北京时间当日已有另一笔充值确认付款,retryable=false,next_action=query_original_order。请查原订单,不自动重新付款,不计作新单成功或扣除新单积分。
409 no_available_private_card / private_card_capacity_reached 当前 Key 补充可用卡或管理库存;不会借用平台卡
409 private_card_in_use / card_version_conflict 占用结束后再操作,或刷新最新版本后提交
409 card_limit_below_used 次数上限不得小于已成功次数
409 private_card_material_invalid 卡资料不可用,联系管理员核对该 Key 的卡资料
413 request_too_large 减少请求体长度
415 json_required / compressed_body_not_supported 使用非压缩 application/json
429 rate_limited 降频、退避并加随机间隔
429 customer_capacity_reached 等待已有任务释放并发名额
503 execution_not_ready / secure_handoff_capacity 等待服务恢复容量,保留已有任务状态
503 open_api_not_enabled / open_api_storage_configuration_invalid / open_api_secret_not_configured 服务配置暂不可用,联系管理员;不要密集重试

自助网页登录 Cookie 到期还可能返回 401 login_required,与授权 Session 失效是两回事。Bearer 对接应使用 invalid_api_key 判断接口鉴权问题。

next_action 与重试

next_action 接入动作
wait 等待并查询同一预检/订单;没有重新创建的要求
submit_order 预检 ready;只有客户明确确认付款后,才调用 POST /v1/orders
renew_session 请求客户重新登录并提供新 Session;不是原 Session 盲重试
cancel_renewal_and_renew_session 已识别宽限期但未核实取消结果;请客户取消自动续费、重新登录获取新 Session。不得声称已自动取消
wait_subscription_expiry 等待当前会员到期,不自动再次充值
change_channel 当前渠道不支持;修正资料/选择后才能再发起
retry_preflight 原预检已不能复用;满足清理条件且 retryable=true 时创建新的预检
wait_cleanup 继续查询原预检/订单等待清理确认,不并行占用原账号/卡
wait_result 查询原订单或接收回调,不再次支付
contact_support 保存脱敏 ID、状态和时间,交由管理员核查
none 没有后续自动操作;成功显示成功,已结束失败按结果提示

retryable=false 不代表这个账号永远不能充值。Session 失效后换了新 Session、会员到期后条件改变,属于新的输入和新的业务操作。retryable=true 也不表示可以直接自动扣款:新的预检依然不付款,正式提交必须获得客户确认。

网络请求重试与业务重试必须分开:

  1. 创建或提交回包丢失:保留原 request_id 和原参数,查询或恢复同一逻辑请求;不要立即换 ID。
  2. 明确收到失败结果且 next_action=retry_preflight:等待 cleanup_acknowledged=true,核对没有未终结的 order_id,再用新 request_id 创建预检。
  3. 已有关联订单:订单是权威结果;review 或付款状态未知时只查原单。
  4. 达到客户侧设定的有限重试次数后停止;不要无限循环。429/503 先退避并保留已有任务状态。

示例:Session 失效

查询 GET /v1/preflights/pfapi_example_session,HTTP 200:

{
  "id": "pfapi_example_session",
  "created_at": 1789700000.0,
  "expires_at": 1789700120.0,
  "order_id": "",
  "status": "failed",
  "error_code": "SESSION_INVALID",
  "customer_message": "Session 已失效,请重新登录账号后重新提交。",
  "stage": "preflight",
  "retryable": false,
  "next_action": "renew_session",
  "cleanup_acknowledged": true,
  "payment_started": false,
  "card_consumed": false,
  "credit_state": "none"
}

程序匹配 error_code=SESSION_INVALID,给顾客显示 customer_message。这次无订单、无付款、无积分扣除;卡片是否空闲仍以卡池查询和清理确认判断。

示例:订阅未到期

{
  "id": "pfapi_example_subscription",
  "created_at": 1789700000.0,
  "expires_at": 1789700120.0,
  "order_id": "",
  "status": "failed",
  "error_code": "SUBSCRIPTION_NOT_EXPIRED",
  "customer_message": "账号的 Plus 订阅尚未到期,请到期后重试。",
  "stage": "preflight",
  "retryable": false,
  "next_action": "wait_subscription_expiry",
  "cleanup_acknowledged": true,
  "payment_started": false,
  "card_consumed": false,
  "credit_state": "none"
}

不要让客户反复刷新 Session、换卡或不停重试来处理 SUBSCRIPTION_NOT_EXPIRED。

示例:技术失败已经安全结束

{
  "id": "pfapi_example_technical",
  "created_at": 1789700000.0,
  "expires_at": 1789700120.0,
  "order_id": "",
  "status": "failed",
  "error_code": "PREFLIGHT_TECHNICAL_ERROR",
  "customer_message": "预检遇到技术故障,请重新发起预检。",
  "stage": "preflight",
  "retryable": true,
  "next_action": "retry_preflight",
  "cleanup_acknowledged": true,
  "payment_started": false,
  "card_consumed": false,
  "credit_state": "none"
}

这里允许重新预检。若同一原因的返回仍处于 cleanup_pending,则应以当前 retryable=false,next_action=wait_cleanup 为准,等待清理完成。

示例:付款结果待核对

{
  "id": "ord_example_review",
  "request_id": "order-example-review",
  "preflight_id": "pfapi_example_review",
  "status": "review",
  "credit_state": "frozen",
  "created_at": 1789700010.0,
  "started_at": 1789700011.0,
  "payment_at": 1789700014.0,
  "success_at": null,
  "finished_at": null,
  "updated_at": 1789700030.0,
  "result_code": "PAYMENT_UNCONFIRMED",
  "error_code": "PAYMENT_UNCONFIRMED",
  "customer_message": "付款结果仍在核对,请查询原订单,不要重复付款。",
  "stage": "reconciliation",
  "retryable": false,
  "next_action": "wait_result",
  "cleanup_acknowledged": null,
  "payment_started": true,
  "card_consumed": null
}

这类结果不能转换为 failed。积分仍冻结;后续以原订单的明确结果结算,不能通过另建单消除未知状态。仅仅预检异常或准备阶段字段不完整,不应被接入端自行升级成 PAYMENT_UNCONFIRMED。

兼容旧版与排障

本说明不承诺所有历史失败都能追溯具体原因。历史信息缺失会明确使用兜底码,不把推测作为 Session、订阅或付款结论。

11. 常见问题与验收

上线前逐项检查:

  1. 鉴权和租户隔离;前端/日志无Key、Session、PAN、CVC明文。
  2. 重复兑换不重复入账,冻结/扣除/释放能与流水核对。
  3. 预检与确认严格分离,未点击确认不调用订单提交。
  4. request_id、preflight_id、expires_at、order_id持久化,刷新后可查原单。
  5. 超时/503/429/重复点击不产生新的付款逻辑操作。
  6. 根据status显示结果;review不允许再次付款;成功不被旧回调覆盖。
  7. 回调原始字节验签、去重、快速2xx、轮询兜底。
  8. 账单404不改成功;303下载不向外域携带APIKey。
  9. 预检120秒与接单并发限制已正确处理。
  10. 没有用“示例测试”为未经授权的真实付款或生产压测提供入口。

支持时提供客户ID、request_id、preflight_id/order_id、时间和时区、HTTP状态、error.code、是否收到回调即可;不要提供机密正文。