扣款授权、订阅与上限——一条没有直接扣款的链,如何照样按月计费。
区块链没有直接扣款。没有人能从钱包里拉取资金;转账总是由持有私钥的人主动推送。这正是定期扣款在这里必须解决的全部问题,而解决办法是链上的扣款授权。
要查询,不要假设。GET /v1/chains 会公布每条链的 subscriptions_available,GET /v1/merchant/chains 会在你已配置的链上公布同一个标志。在该标志为 false 的链上创建扣款授权或订阅,会返回 400 并说明原因。
它与 escrow_available 是两个独立的答案,而且两者确实会不同:一条链可以不开放定期扣款,却依然能够完美地处理一次性付款。订阅中的每一次扣款都是一笔由本网关支付费用的链上交易,而这笔费用在不同链之间相差几个数量级——所以一个部署完全可以在所有链上处理付款,而只在更少的链上开放定期扣款。
已关闭的链上已有的订阅会继续计费。关闭一条链只会阻止新的订阅;不会取消任何东西。
扣款授权是客户在链上授予你的许可,允许你在上限之内、每个周期收取一次。客户只需在本网关托管的页面上,用钱包签名一次。
curl -X POST $PAYGATE/v1/mandates \
-H "Authorization: Bearer $KEY" -H "Idempotency-Key: $(uuidgen)" \
-H 'Content-Type: application/json' \
-d '{"cap_decimal":"50.00","period":"month","customer":"…","reference":"acct_88"}'
你会得到一个 authorization_url。把客户引导到那里。客户签名后,会触发 mandate.authorized——而这个事件是你得知客户已签名的唯一途径。
客户向一个由他们自己控制的资金库存入资金。托管合约每个周期最多只能向你转出一次约定金额。本 API 上没有任何操作可以清空这个资金库,或改变资金的去向。这是合约本身的属性,而不是网关的承诺。
curl -X POST $PAYGATE/v1/mandates/{id}/charges \
-H "Authorization: Bearer $KEY" -H "Idempotency-Key: $(uuidgen)" \
-d '{"amount_decimal":"29.00"}'
可能出现四种情况:
charge.succeeded——资金已从客户那里转出。mandate.insufficient——客户的余额不足以支付。请他们补充资金;扣款授权仍然有效。mandate.requires_authorization——客户的批准额度已经用完,这与余额不足不是一回事,解决办法也不同。客户批准的是 cap × approval_periods,这么多个周期已经完全按设计扣款完毕,授权也走到了它设计时预定的终点。其背后的钱包可能资金充足。把客户引导回 authorization_url,让他们在同一个扣款授权上重新签名——不需要新的扣款授权,不需要新的付款页面,也不需要重新配置任何东西。要求这位客户补充资金,是一条他们无法照做的建议。mandate_cap_exceeded(400)——你请求的金额超过了客户签名同意的额度。这不是客户的失误,而是你的失误。扣款自身的 failure_code 也以同样的方式区分:insufficient_funds 与 approval_exhausted。如果你在这里只根据一件事做分支处理,那就根据它。
charge.succeeded 表示资金已从客户那里转出,并不表示你已经收到。在托管暂存模式下,charge.released 才表示你已收到,charge.returned 则是另一种结局。如果用错了事件来触发履约,你就会在错误的时刻发货,而且永远查不出原因。
扣款授权是授权本身。订阅则是按计划、基于商品目录来使用这项授权的机制:
curl -X POST $PAYGATE/v1/subscriptions \
-H "Authorization: Bearer $KEY" -H "Idempotency-Key: $(uuidgen)" \
-d '{"customer":"…","price":"…"}'
创建订阅会发起客户的链上授权,并返回一个 authorization_url,供你把客户引导过去。每到周期边界就会计费、触发 invoice.paid,然后进入下一个周期。
这是与银行卡不同的地方,它会改变你的催缴通知需要说明的内容。
银行卡没有上限:折扣到期、方案升级或用量超额,都只是一笔更大的扣款。而在这里,这意味着金额要突破客户几个月前签名同意的额度——而提高这个额度,是一笔必须有人去请求客户发起的钱包交易。
所以网关会提前发出警告:
discount.ending_above_cap——在折扣结束、扣款将超过上限的一周前发出。usage.limit_exceeded——超出授权额度的计量用量会被记录,但不会被扣款。schedule.upcoming_charge_at_risk——下一次扣款将无法控制在上限之内。处理好这三个事件,你的客户就能及时收到请求。忽略它们,客户第一次听说这件事,就是续费失败的时候。
在测试模式下,签名就是一次调用:
curl -X POST $PAYGATE/v1/test/mandates/{id}/fund \
-H "Authorization: Bearer $KEY" -H "Idempotency-Key: $(uuidgen)" \
-d '{"amount_decimal":"500.00"}'
curl -X POST $PAYGATE/v1/test/mandates/{id}/authorize \
-H "Authorization: Bearer $KEY" -H "Idempotency-Key: $(uuidgen)" -d '{}'
然后对它发起扣款,让它耗尽资金,并检查你的催缴流程是否做出了合理的处理。参见测试模式。