应用内收银台

在你自己的应用里绘制收银台——二维码、付款的实时推送流,以及你的界面必须遵守的规则。

把客户引导到 checkout_url 是收款的一种方式。另一种方式是自己绘制收银台,放在你自己的应用里,用你自己的设计。你拿到的数据与托管页面所用的完全相同,而界面最容易出错的那几处,由网关替你判定。

基本流程

  1. 你的服务器用你的密钥创建付款,与托管页面的做法完全相同。
  2. 它把响应中的 checkout_stream_url、checkout_data_url 以及每条路由的 qr_url 交给你的应用。
  3. 你的应用打开推送流。第一帧就包含绘制所需的一切:地址、精确金额、二维码、剩余时间。
  4. 其中任何内容发生变化——转账到账、新增确认、窗口关闭——都会再推送一帧。你的应用按最新的一帧重新绘制。
  5. 你的服务器依据 payment.paid Webhook 履约。绝不要依据应用端。

这三个 URL 放在客户的设备上是安全的。每个 URL 都带有这笔付款的收银令牌,它只能打开这一笔付款的付款人视图——也就是 checkout_url 本来就向客户展示的内容——不含你账户的任何信息。你的密钥始终留在你的服务器上。

读取推送流

const stream = new EventSource(payment.checkout_stream_url);

stream.onmessage = (event) => {
  const checkout = JSON.parse(event.data); // the whole object, every time
  render(checkout);
};

每一帧都是完整的收银台对象——也就是 checkout_data_url 返回的对象——所以请按最新的一帧绘制,不要自己保存状态。EventSource 重连后,它收到的第一帧就会把你带到最新状态。

状态变化会在提交时立即到达。确认数每 25 秒重新读取一次,因此可能比链上落后这么久。30 分钟后服务器会发送 event: reconnect 并关闭连接,EventSource 会自行重连。

有些网络会切断长连接。推送流中断期间,请每隔几秒轮询一次 checkout_data_url——它是同一个对象。托管页面正是这样做的。

在浏览器之外,任何 Server-Sent Events 客户端都可以使用。这三个 URL 都无需 API 密钥,并可从任何来源访问。

只绘制可以付款的内容

这一部分一旦出错,付款人就会损失资金,所以由网关来判定。

一旦有任何资金到账,payable 就会变为 false——包括少付。不要索要差额:在托管路由上,网关对一笔付款的资金只归集一次,此后到账的补款永远不会被归集——它会留在充值地址上。请展示已到账的金额,并请付款人联系你。

在 expires_at 时它也会按时钟变为 false,即使其他任何内容都还没有变化。此时推送流会发送一帧。

二维码

qr_url 是一张图片:PNG,或者使用 ?format=svg 获取 SVG。它是白底黑码,并带有完整的静区,因此放在深色页面上也能扫描。

<img src="https://pay.paygatehq.com/c/cs_…/qr/bsc" width="240" height="240" alt="Scan to pay">

它编码的是该路由的 uri——包含精确金额的钱包链接。如果想自己绘制二维码,用任何二维码库编码 uri 即可;两者是同一个字符串。路由关闭后,该图片会返回 409 payment_not_payable,因此停留在旧帧上的界面无法再把它重新加载成一个可被扫描的二维码。

当某条路由没有 uri 时,说明网关无法确定其网络。请提供地址供复制,不要显示二维码,也不要提供钱包按钮。

界面还应该说明什么

测试

在测试模式下,付款会带有一条 sim 路由。它的 qr_url 编码的是裸地址——模拟链没有钱包——因此你可以在正式上线之前完成界面布局。从你的服务器支付这笔付款,然后观察各帧陆续到达:

curl -X POST $PAYGATE/v1/test/payments/{id}/pay \
  -H "Authorization: Bearer $KEY" \
  -H "Idempotency-Key: $(uuidgen)" -d '{}'

改为传入 {"amount":10000000}(单位为微单位,即 10 USDT),即可看到少付对 payable 的影响。其余情形见测试模式。