支付流程
理解从订单创建到资金提现的完整支付流程对于构建可靠的集成至关重要。
完整支付生命周期
详细支付流程
逐步序列
订单状态
状态转换
状态描述
| 状态 | 代码 | 描述 | 下一状态 |
|---|---|---|---|
| 未支付 | 0 | 订单已创建,等待支付 | 1, -2, -3 |
| 未完成 | 1 | 支付已发起但未完成 | 2, -1 |
| 等待确认 | 2 | 交易已广播,等待确认 | 3, -1 |
| 确认中 | 3 | 交易已确认,正在验证支付 | 4, -1 |
| 成功 | 4 | 支付完成并已验证 | 最终 |
| 失败 | -1 | 支付交易失败 | 最终 |
| 已过期 | -2 | 订单在未支付的情况下过期 | 最终 |
| 已取消 | -3 | 订单被商户取消 | 最终 |
支付流程阶段
阶段 1: 订单创建
过程:
- 商户发送订单创建请求
- API 验证请求和凭证
- 智能合约从池中分配收款地址
- 使用唯一的
payOrderId创建订单 - 使用访问签名生成支付 URL
- 向商户返回响应
关键数据:
payOrderId: 唯一的平台订单 IDaccessSign: 用于查询订单状态的签名receiptAddress: 客户将支付到的地址paymentUrl: 完整的支付 URL
阶段 2: 客户支付
客户体验:
- 客户点击支付链接
- 支付页面显示订单详情
- 客户连接钱包 (MetaMask、TronLink 等)
- 客户审查支付金额和详情
- 客户在钱包中批准交易
- 交易广播到区块链
阶段 3: 支付确认
确认过程:
- 交易广播 (状态: 1): 交易发送到网络
- 等待确认 (状态: 2): 等待区块包含
- 区块确认 (状态: 3): 交易包含在区块中
- 支付验证:
- 验证金额是否匹配订单
- 验证收款地址是否正确
- 验证代币/货币是否正确
- 验证是否有足够的确认
- 成功 (状态: 4): 支付已验证并完成
确认要求:
- Ethereum: 12 个确认 (可配置)
- Polygon: 3 个确认 (可配置)
- BSC: 3 个确认 (可配置)
- TRON: 19 个确认 (可配置)
阶段 4: 通知
通知类型:
-
Webhook (
callBackUrl): 后端通知- 带有支付状态的 POST 请求
- 包括订单详情和交易哈希
- 需要签名验证
-
前端重定向 (
frontendCallbackUrl): 客户重定向- 支付完成后的重定向
- URL 中包含订单参数
- 客户看到成功页面
阶段 5: 资金管理
资金管理步骤:
- 聚合: 从收款钱包收集资金到主合约
- 提现: 从合约转移资金到商户钱包
- 费用扣除: 透明地计算和扣除平台费用
支付 URL 结构
URL 格式
https://testnet.hashnut.io/pay?accessSign={accessSign}&merchantOrderId={merchantOrderId}&payOrderId={payOrderId}&chainCode={chainCode}
URL 参数
| 参数 | 描述 | 必需 |
|---|---|---|
accessSign | 用于查询订单状态的签名 | ✅ 是 |
merchantOrderId | 商户的订单标识符 | ✅ 是 |
payOrderId | HashNut 平台订单 ID | ✅ 是 |
chainCode | 链代码 (例如 "erc20") | ✅ 是 |
URL 安全
- 访问签名: 防止未经授权的订单访问
- 过期: 支付链接在配置时间后过期
- 一次性使用: 每次访问时验证链接
- 签名验证: 所有参数在服务器端验证
Webhook 流程
Webhook 交付
Webhook 负载
{
"payOrderId": "01KBZ292SK2GKFK97916F5EC3B",
"merchantOrderId": "order-123",
"accessSign": "D3DE7E4002057C0EAED1BE2268DA53CC9058DCFC9DCAF50D999AF270A7B033C5",
"state": 4
}
Webhook 重试逻辑
- 重试次数: 3 次重试,指数退避
- 重试间隔: 1s、5s、30s
- 超时: 每次尝试 30 秒
- 成功响应: 必须返回
200 OK,响应体为"success"
错误处理
支付错误
常见错误:
- 余额不足: 客户没有足够的代币
- 错误网络: 客户连接到错误的区块链
- 金额不匹配: 支付金额与订单不匹配
- 地址不匹配: 支付发送到错误的地址
- 交易被拒绝: 客户在钱包中拒绝了交易