全球收单 › 指南
订单状态、退款,以及一句最容易算错的账:
completed不代表全额成交。
订单与退款
GET /v1/qrpay/orders ← 列表
GET /v1/qrpay/orders/{id} ← 详情(含逐笔退款明细)
状态
| 值 | 含义 | 钱在哪 |
|---|---|---|
pending | 已锁款,还没通知上游(极短暂) | 锁定中 |
processing | 已通知上游,等终态 | 锁定中 |
completed | 上游成功 | 已结算 |
failed | 上游明确失败 | 已解冻 |
expired | 超时没拿到终态 | 已解冻 |
canceled | 会员在通知上游之前放弃 | 已解冻 |
refunded | 已全额退款 | 已退回 |
⚠
completed不是绝对终态。 收款商户随时可能发起退款,一笔
completed的单会变成refunded(或保持completed而refunded_total变大)。别在拿到
completed之后就把这笔单从同步队列里永久摘掉。
⚠ 认不出的状态告警挂起,绝不 default 成「处理中」。
把一笔失败的单显示成还在跑,用户会一直等。
这一页最要紧的一句:status 算不出「收了多少」
部分退款不会改 status。 只有退满(结算额 − 我方收入)才会推到 refunded。
所以:
这一单最终收了多少 = customer_total − refunded_total
⚠ 只看状态会把一笔退了九成的单当成全额成交。 它在报表上的表现是
收入虚高,而每一笔单自己看都是对的 —— 没有任何断言抓得到。
逐笔明细(哪天退了多少、收单侧退了多少)在详情端点。
两个端点的金额格式不一样
| 端点 | refunded_total 的形态 |
|---|---|
GET /v1/qrpay/orders(列表) | 定点十进制串("12.34") |
GET /v1/qrpay/orders/{id}(详情) | 最小单位整数串("12340000") |
⚠ 别复用同一个解析函数。 这是我方的历史包袱,不是设计 ——
但它现在是契约的一部分,改它会打断已经接好的商户。
failure_code 可能是空串
上游给的失败原因是自由文本。映不出对外码时我方给空字符串,不是 api_error。
⚠ 一个恒定的错值比没有这个字段更糟 —— 你会拿它去做分支。
退款:你发不了,但你必须能收
- 没有「发起退款」端点 —— 上游的接口清单里没有。退款只能由收款商户那一侧发起。
- 没有「补一笔款」 —— 钱是会员自己按支付键放的。
所以你要做的是接住退款:
- 订阅扫码付订单状态 webhook
- 收到之后回查订单拿
refunded_total(webhook 只告诉你变了) - 用
status_version向前合并,别让慢响应把新状态打回旧的
码制清单是活的
GET /v1/qrpay/schemes
不是静态常量 —— 上游的码制表会变,我方跟着同步。上游不再返回的码会从清单里消失(但历史订单仍能解释它当时走的什么码)。
⚠ 别把码制表抄进你的代码。抄了之后上游下线一个码,
你的界面还会让用户去扫它。