04|支付生命周期和参数:一笔 Web3 支付从创建到结算怎么走完

这一篇要解决的问题

上一轮反馈是:“后面继续讲支付的生命周期和参数,不要讲资产,这些我知道。”这一篇按这个方向推进:不展开资产类型,专门拆一笔 Web3 支付从订单创建、支付请求、钱包签名、链上确认、网关监听、订单状态更新、商户履约到结算对账的完整生命周期。

重点放在系统参数上:订单号、金额、币种、网络、收款地址、过期时间、确认数、交易哈希、状态机、回调、幂等键和异常处理。

正文

1. 一笔 Web3 支付不是一笔交易,而是一段生命周期

链上交易只是生命周期中的一个节点。完整支付要从商户创建订单开始,到商户确认可履约、财务可对账、资金可结算结束。

可以先看总流程:

阶段发生什么核心产物
订单创建商户系统生成业务订单merchant_order_id、金额、商品、用户
支付会话创建支付网关生成支付请求payment_id、币种、网络、地址、过期时间
用户确认支付钱包展示交易并签名signed_transaction 或交易请求
链上广播交易进入网络等待确认tx_hash
交易监听网关从链上发现交易observed_transaction
订单匹配链上交易匹配支付会话matched_payment
状态更新网关更新支付状态并回调商户paid、confirmed、failed 等状态
商户履约商户发货、开通服务或记录收款fulfillment_record
结算对账资金进入商户账户并生成报表settlement_id、对账记录

生命周期的关键点是:每一阶段都需要明确输入、输出和状态变化。否则支付系统会在异常场景里混乱,比如用户付了但订单过期、链上到账但网关没监听到、商户重复发货、回调重试导致重复入账。

2. 第一阶段:商户订单参数

支付系统最开始接到的不是链上交易,而是商户业务订单。

商户订单通常包含:

参数含义设计要点
merchant_order_id商户侧订单号商户系统内唯一,后续对账要能查到
amount订单金额明确小数精度和计价单位
currency计价币种表示订单用什么单位计价,不一定等于链上支付资产
goods / description商品或服务说明给用户确认,也给客服和风控查证
customer_id用户标识用于风控、客服、订单历史,不一定上链
callback_url支付结果通知地址网关向商户系统发送状态更新
return_url用户支付后跳转地址只影响前端体验,不应作为支付成功依据
metadata商户自定义字段用于内部业务关联,注意不要放敏感信息

这里要区分两个 ID:

ID谁生成作用
merchant_order_id商户商户业务系统识别订单
payment_id支付网关支付系统识别一次支付会话

一个订单可能对应多次支付尝试。比如用户第一次支付失败,第二次重新选择网络或钱包;原订单不变,但会生成新的 payment_id。这样可以避免把业务订单和链上支付过程绑死。

3. 第二阶段:支付会话参数

支付网关收到商户订单后,会创建支付会话。支付会话是 Web3 支付系统的核心对象。

一个支付会话通常包含:

参数含义为什么重要
payment_id支付会话 ID后续查询、回调、对账都围绕它展开
merchant_order_id商户订单号把支付会话和业务订单连接起来
pay_amount用户需要支付的链上数量要精确到代币最小单位或链上 decimals
pay_currency支付资产符号前端展示用,不能只依赖符号判断合约
chain_id / network支付网络防止用户在错误链上付款
token_contract代币合约地址原生资产可以为空或用特殊标识
recipient_address收款地址或合约地址钱最终进入哪里
expires_at过期时间控制汇率、库存、重复支付风险
min_confirmations需要确认数控制链上回滚和商户履约风险
status当前支付状态驱动订单状态机

支付会话创建之后,前端展示二维码、支付链接或钱包连接按钮。用户看到的内容必须来自支付会话,而不是前端临时拼出来的数据。

这一步最重要的工程原则是:支付请求要由后端生成并可查询。前端可以展示,但不要让前端决定最终收款地址、金额、网络和过期时间。

4. 第三阶段:支付请求参数

支付请求是用户真正拿去付款的东西。它可能是二维码内容、深链接、WalletConnect 请求、网页按钮,或者钱包 SDK 调用。

支付请求需要让钱包或用户知道:

参数说明
chain_id要在哪条链上支付
recipient收款地址或支付合约
amount支付数量,通常要转换成最小单位
token支付资产,包含合约地址和 decimals
calldata如果调用合约,具体函数和参数
memo / reference如果链支持备注,用于订单匹配
gas_policyGas 由用户付、商户代付,还是账户抽象代付
expires_at支付请求有效期

这里有一个常见坑:用户界面显示的是“10 USDC”,链上交易里实际传的是整数最小单位,比如 10000000。系统必须统一处理 decimals,否则会出现金额差错。

另一个坑是币种符号。多个链上都可能有 USDT 或 USDC,合约地址不同。支付系统不能只看 symbol=USDT,必须同时校验 chain_id 和 token_contract。

5. 第四阶段:交易广播和链上确认

用户签名后,交易会被广播到链上。此时支付会话通常进入 pendingsubmitted 状态。

需要记录这些参数:

参数含义用途
tx_hash交易哈希查询链上状态、给用户和客服定位
from_address付款地址风控、对账、用户识别
to_address收款地址或合约校验是否付给正确对象
block_number交易所在区块计算确认数
block_time区块时间记录支付发生时间
gas_used实际消耗 Gas成本分析和异常排查
tx_status交易成功或失败区分广播成功和执行成功

广播成功不等于支付成功。交易可能长时间 pending,可能失败,可能被替换,可能因为 Gas 不足卡住,可能在合约执行时 revert。

支付网关要区分几类状态:

状态含义
created支付会话已创建,用户还没提交链上交易
submitted已拿到交易哈希,但链上还没确认
detected网关已在链上发现交易
confirmed达到确认数或最终性条件
failed交易失败、金额错误、链错误或超时
expired支付会话过期,后续到账要走异常处理

状态名可以不同,但含义要稳定。商户系统依赖这些状态决定是否发货。

6. 第五阶段:交易监听和订单匹配

网关监听链上交易时,不能只查一个 tx_hash。很多用户会直接转账,不一定通过前端流程提交交易哈希。支付系统需要从链上事件里反向发现付款。

常见监听方式:

方式适用场景注意点
监听收款地址入账一次性地址或固定收款地址要处理多链、多代币、重组和重复扫描
监听代币 Transfer 事件ERC-20、TRC-20 等代币支付要校验 token_contract、from、to、amount
监听支付合约事件合约收款事件里应包含 payment_id 或 order_id
查询用户提交的 tx_hash前端已拿到交易哈希不能只信用户提交,仍要链上校验

订单匹配需要校验这些条件:

条件校验目的
网络一致防止用户在错误链付款
代币合约一致防止同名假币或错误资产
收款地址一致防止地址替换或误转
金额足够防止少付
支付会话未过期防止旧价格、旧库存或重复支付
交易成功执行防止 revert 或失败交易被当成到账
确认数足够防止重组或未最终确认

匹配成功后,支付会话才能进入 confirmed 或 paid 状态。

7. 第六阶段:状态机和幂等

支付系统一定要有状态机。状态机的作用,是限制状态只能按允许路径变化。

一个简化状态机可以这样设计:

created
  → submitted
  → detected
  → confirmed
  → settled

created
  → expired

submitted / detected
  → failed

confirmed
  → refunded

实际系统里会更复杂,但原则相同:每个状态都要定义进入条件、退出条件和是否可逆。

幂等也很重要。链上监听、回调、用户刷新页面、商户查询接口都可能重复触发同一动作。系统必须保证重复请求不会导致重复发货、重复入账或重复结算。

常见幂等键包括:

幂等键用途
payment_id保证同一个支付会话只确认一次
tx_hash + log_index保证同一个链上事件只处理一次
merchant_order_id + status保证同一个订单状态更新不重复应用
callback_event_id保证商户回调可以安全重试

Web3 支付里,链上事件天然可能被重复扫描。没有幂等设计,系统迟早会出重复处理问题。

8. 第七阶段:回调和商户确认

支付网关确认支付后,会通知商户系统。这个通知通常叫 webhook 或 callback。

回调内容通常包含:

字段含义
event_id本次事件 ID,用于幂等
event_typepayment.confirmed、payment.failed、payment.expired 等
payment_id支付会话 ID
merchant_order_id商户订单号
status当前支付状态
tx_hash链上交易哈希
amount实际支付金额
chain_id支付网络
token_contract代币合约地址
confirmed_at确认时间
signature网关对回调内容的签名

商户收到回调后,不应该只因为 HTTP 请求到了就发货。还要验证回调签名、检查 event_id 是否处理过、确认订单金额和状态,然后再更新业务订单。

回调必须支持重试。商户服务器可能短暂不可用,网关应该按退避策略重发。商户接口也要支持幂等,重复收到同一个 confirmed 事件时,只返回成功,不重复发货。

9. 第八阶段:异常处理

Web3 支付的大量复杂度来自异常场景。

异常表现处理方式
少付用户支付金额小于要求标记 underpaid,提示补差或人工处理
多付用户支付金额大于要求标记 overpaid,按规则退款或记入余额
过期后到账订单过期后用户才付款标记 late_payment,按当前价格或人工处理
转错链用户在不支持网络付款如果商户控制地址,人工找回;否则可能无法处理
转错币用户转了错误代币看是否可识别和可退回
重复支付同一订单收到多笔付款第一笔确认订单,后续进入退款或余额处理
交易失败tx_hash 存在但执行失败不确认订单,提示用户重新支付
链拥堵长时间 pending保持等待、提示加 Gas 或重新发起
回调失败商户没收到通知网关重试,商户可主动查询状态

异常处理要提前写进产品规则。用户支付后才临时决定怎么处理,会让客服、财务和风控都很难办。

10. 第九阶段:结算和对账

支付确认后,商户还需要对账。对账不是确认用户是否付款,而是把业务订单、链上交易、手续费、汇率、结算记录放到同一张表里。

对账记录至少要包含:

字段用途
merchant_order_id对应业务订单
payment_id对应支付会话
tx_hash对应链上交易
chain_id对应网络
token_contract对应支付资产
paid_amount用户实际支付数量
expected_amount应支付数量
fee网关费、链上费或换汇费
settlement_amount商户最终结算金额
settlement_time结算时间
settlement_status待结算、已结算、失败、冻结

对账系统要支持从两边查:财务拿订单号能查到链上交易;客服拿 tx_hash 能查到订单;风控拿地址能查到相关订单和处理记录。

Web3 支付系统成熟与否,很大程度体现在对账能力上。只让用户完成链上转账,还不能算完整商户支付系统。

11. 一张生命周期检查表

看一个 Web3 支付产品时,可以按这张表检查:

检查项要问的问题
订单创建merchant_order_id 是否唯一?金额和计价单位是否清楚?
支付会话payment_id、chain_id、token_contract、recipient、expires_at 是否明确?
支付请求用户看到的金额、网络、收款方是否来自后端可信数据?
钱包签名钱包展示的内容是否可理解?授权范围是否过大?
交易监听系统如何发现交易?监听地址、Transfer 事件还是合约事件?
订单匹配网络、代币、地址、金额、确认数、过期时间如何校验?
状态机created、submitted、detected、confirmed、failed、expired 等状态是否定义清楚?
幂等重复扫描、重复回调、重复刷新是否会造成重复发货或入账?
回调webhook 是否签名?是否支持重试?商户是否验证幂等?
异常处理少付、多付、过期到账、转错链、重复支付如何处理?
对账订单、payment_id、tx_hash、费用、结算记录能否互相追溯?

这张表可以直接用来读一个支付网关的 API 文档,也可以用来设计自己的支付系统。

小结

  1. Web3 支付是一段生命周期,不是一笔孤立链上交易。
  2. 关键对象包括 merchant_order_id、payment_id、支付请求、tx_hash、状态机、回调事件和结算记录。
  3. 支付会话要明确 chain_id、token_contract、recipient_address、pay_amount、expires_at 和 min_confirmations。
  4. 订单匹配必须校验网络、代币、收款地址、金额、过期时间、交易状态和确认数。
  5. 状态机、幂等、回调签名、异常处理和对账,是商户支付系统稳定运行的核心。

下一篇预告

下一篇继续沿着支付生命周期展开,进入“支付网关 API 设计”。我们会拆创建支付、查询状态、接收回调、退款、对账这几类接口应该怎么设计,重点看字段、状态码、幂等键和错误码。

← 上一篇