每一种状态、状态之间如何流转,以及哪些是终态。
| 状态 | 含义 | 终态 | | --- | --- | --- | | requires_payment | 已创建。尚未收到任何款项。 | 否 | | detected | 有一笔匹配的转账出现在内存池或最新区块中。 | 否 | | confirming | 已被打包进区块,正在累积确认数。 | 否 | | paid | 已确认到配置的深度。 | 否——见下文 | | settled | 已从托管合约释放给商家。 | 是 | | underpaid | 到账金额少于要求的金额。 | 否——补足后仍可完成 | | overpaid | 到账金额多于要求的金额。 | 否 | | expired | 已过 expires_at,且没有任何款项得到确认。 | 是 | | cancelled | 你已取消该付款。 | 是 | | reversed | 链重组撤销了一笔已达到 paid 的付款。 | 是 | | partially_refunded | 部分款项已退回。 | 否 | | refunded | 全部款项已退回。 | 是 |
paid 不是终态这是与银行卡支付网关不同的地方,而且绝不是细枝末节。
区块链可能发生重组。如果链重组的深度超过了判定该付款为 paid 的确认窗口,那么支付这笔款项的转账就不复存在——付款会变为 reversed,并发出 payment.reversed。
三种诚实的处理方式:
paid 就发货。最快。你是在知情的前提下承担被撤销的风险,对于低价值的数字商品,这通常是正确的选择。settled 再发货。资金已离开托管合约;那么深的链重组已不可能发生。较慢。paid 就发货,收到 reversed 再冲正。适用于任何可以撤回的东西——账户余额、订阅周期、授权许可。不诚实的做法,是把 paid 当作最终状态,却完全没有 payment.reversed 的处理程序。做决定之前,先演练一遍:
curl -X POST $PAYGATE/v1/test/payments/{id}/reorg \
-H "Authorization: Bearer $KEY" -H "Idempotency-Key: $(uuidgen)" -d '{}'
客户会付错金额。这并不罕见。
underpaid——付款保持未完成状态。第二笔转账使总额达到要求的金额后,付款即告完成。amount_received 会告诉你目前的进度。overpaid——付款已完成,并且有多余的款项。你可以退还、记为账户余额,或者保留;网关不替你做决定。两者都会触发事件。两者都可以在测试模式中复现。
expires_at 是一个 unix 时间戳。创建付款时用 expires_in(单位:秒)来设置。
如果付款过期时已有款项处于 confirming,这笔付款不会被放弃——迟到的转账会触发 payment.late_payment,付款会被完成,而不是丢失。一个把它丢掉的网关,就是一个扣下客户的钱却不告诉任何人的网关。
不得已时可以轮询 GET /v1/payments/{id};能订阅 Webhook 就订阅 Webhook。一笔付款背后的交易——哈希、确认数、金额——可以通过 GET /v1/payments/{id}/transactions 获取。