Skip to content

结账页面前端开发规范

本文档说明支付 checkout 页面(Payment Intent / Invoice)的前端状态机设计原则,供维护和二次开发参考。

支付成功的判断依据

前端只负责判断"交易是否已上链",区块确认由后端独立完成。

  • 只要后端通过 Alchemy webhook 捕获到 PaymentReceived 事件,即视为支付成功。
  • 6 次区块确认是后端 listener-service 的内部逻辑,前端无需等待、展示进度,也无需轮询。
  • 前端收到 listener_pendingonchain_confirmed 阶段时,应直接显示"支付成功",不应向用户传达"等待中"或"确认中"的不确定感。

CheckoutPhase 状态机

Phase触发时机前端展示
loading初始加载 intent加载中
readyintent 加载完成,status = requires_payment等待用户操作
already_paidintent 加载时 status = confirmed | processing订单已支付,禁止操作
unavailableintent 加载失败或不可用错误提示,禁止操作
switching_network切换链网络中准备中
checking_allowance查询 ERC20 授权额度准备中
approving等待用户在钱包确认授权等待用户操作
submitting_payment等待用户在钱包确认支付等待用户操作
signing_gasless等待用户签名(免 Gas 路径)等待用户操作
submitting_relay中继服务提交交易中进行中
onchain_confirmedwagmi 本地确认交易落块支付成功
listener_pending后端收到链上事件,区块确认进行中支付成功
listener_confirmed后端完成区块确认支付成功
redirecting支付成功,正在倒计时跳转商家页支付成功
failed任意环节出错或用户拒绝失败,可重试

前端"支付成功"的触发点

onchain_confirmed 起即为支付成功,对应逻辑链路:

用户钱包确认 → wagmi 等到 1 次本地确认 → phase = onchain_confirmed
  → 后端 Alchemy webhook 收到事件 → phase = listener_pending
  → 后端完成 6 次区块确认 → phase = listener_confirmed

三个 phase 在前端的表现完全一致:绿色按钮、"支付成功"文案。

已支付订单的防护

加载 intent 时,若后端返回 status = "confirmed""processing",前端直接设为 already_paid

  • 按钮禁用,显示绿色"订单已支付"
  • 不展示连接钱包 / 更换钱包等操作
  • 禁止任何支付流程入口

这是前端的幂等保护。链上层面的防重放由合约 processedOrder mapping 保证(同一 (merchant, orderId) 只能支付一次,重复调用会 revert)。

Gasless 支付路径

当商家启用 Gas 赞助时,支付流程改为签名路径:

用户签名 EIP-712 → 前端发送签名到后端 relay 接口
  → 后端 RelayService 调用 payWithSig → 交易上链

前端判断成功的逻辑与普通路径相同,onchain_confirmed 起即为支付成功。

不要在前端做的事

  • 不要轮询区块确认次数:6 次确认是后端内部逻辑,不要在前端显示"3/6 确认"之类进度。
  • 不要在前端等待 listener_confirmed 才显示成功onchain_confirmed 已经足够。
  • 不要在前端重新实现已支付判断:直接信任后端 intent.status 字段。