概念
Confirmations
detected / confirmed / finalized / reverted 的语义。
链上确认(confirmation)是交易逐步变得不可逆的过程。StableOps 把支付追踪划分成四个 阶段,在速度与安全之间做平衡。
总览
链上发起一笔支付后,它会经过几个阶段才算最终化:
DETECTED → CONFIRMED → FINALIZED
↓ ↓
REVERTED REVERTED每个阶段代表不同的可靠度:
- DETECTED:在链上看到交易(0 确认)
- CONFIRMED:达到该链所需的确认数
- FINALIZED:达到 finality,不可逆
- REVERTED:因重组或失败被回滚
为什么需要确认
区块链是分布式系统,多个节点竞争出块,这导致:
- 链重组(reorg):竞争链超过当前链,最近的区块被替换
- 交易失败:合约执行失败、Gas 不足等
- 双花尝试:恶意方试图回滚交易
「确认」就是把交易埋到足够深,使反转在算力上几乎不可能。
各确认阶段
DETECTED(0 确认)
交易已经广播,要么在 mempool 中,要么进了最新一个区块。
可靠度:低(5–10%)
风险:
- 交易可能被 RBF 替换
- 区块可能在重组中被孤立
- 合约执行可能失败
做什么:
- UI 切到「已收到,确认中…」
- 订单标记为「待确认」
- 此时不要履约
示例:
// Webhook: payment.detected
{
"type": "payment.detected",
"data": {
"payment_order_id": "po_abc123",
"from_status": "created",
"to_status": "detected",
"reason": "deposit_detected",
"normalized_event_id": "evt_abc123",
"amount": "10.00",
"settlement_asset": "USDC",
"metadata": {}
}
}CONFIRMED(达到链特定确认数)
交易已经达到该链所需确认数。
可靠度:高(95–99%)
各链确认数要求:
| 链 | 确认数 | 时间 | 说明 |
|---|---|---|---|
| Base | 2 块 | ~4 秒 | OP rollup,回滚风险低 |
| Ethereum | 6 块 | ~1.2 分钟 | 交易所标准 |
| Arbitrum | 1 块 | ~0.3 秒 | OP rollup |
| Polygon | 20 块 | ~40 秒 | 重组风险较高 |
| TRON | 1 块 | ~3 秒 | DPOS |
风险:
- 理论上仍可能深度重组
- 实际极罕见(< 0.01%)
适用:
- 小额(< $100)
- 数字商品交付
- 账户积分
- 对时延敏感的履约
示例:
// Webhook: payment.confirmed
{
"type": "payment.confirmed",
"data": {
"payment_order_id": "po_abc123",
"from_status": "detected",
"to_status": "confirmed",
"reason": "confirmations_reached",
"normalized_event_id": "evt_abc123",
"amount": "10.00",
"settlement_asset": "USDC",
"metadata": {}
}
}FINALIZED(达到 finality)
达到 finality,任何情况都不可逆。
可靠度:绝对(100%)
各链 finality 要求:
| 链 | finality | 时间 | 机制 |
|---|---|---|---|
| Base | 18 块 | ~36 秒 | L1 finality |
| Ethereum | 64 块 | ~13 分钟 | Casper FFG |
| Arbitrum | 20 块 | ~5 秒 | L1 finality |
| Polygon | 128 块 | ~4.3 分钟 | Checkpoint finality |
| TRON | 19 块 | ~1 分钟 | DPOS finality |
风险:无
适用:
- 大额(> $100)
- 实物发货
- 不可逆操作(账户升级、订阅)
- 生产环境所有履约推荐用 finalized
示例:
// Webhook: payment.finalized
{
"type": "payment.finalized",
"data": {
"payment_order_id": "po_abc123",
"from_status": "confirmed",
"to_status": "finalized",
"reason": "finality_reached",
"normalized_event_id": "evt_abc123",
"amount": "10.00",
"settlement_asset": "USDC",
"metadata": {}
}
}REVERTED(交易回滚)
交易被回滚,可能因为链重组或执行失败。
原因:
- 链重组:竞争链变长
- 执行失败:合约执行失败
- Gas 不足:交易耗尽 Gas
- Receipt 丢失:交易从链上消失
频率:极罕见(< 0.01%)
发生后:
- StableOps 检测到重组或失败
- 订单状态变为
REVERTED - 派发
payment.reverted - 地址释放回地址池(变回
AVAILABLE供复用)
示例:
// Webhook: payment.reverted
{
"type": "payment.reverted",
"data": {
"payment_order_id": "po_abc123",
"from_status": "confirmed",
"to_status": "reverted",
"reason": "blockchain_reorg",
"normalized_event_id": "evt_abc123",
"amount": "10.00",
"settlement_asset": "USDC",
"metadata": {}
}
}处理方式:
app.post('/webhooks/stableops', async (req, res) => {
const event = req.body
if (event.type === 'payment.reverted') {
const orderId = event.data.payment_order_id
// 1. 冲销已经做的履约
await reverseOrderFulfillment(orderId)
// 2. 通知客户
await sendEmail(customer, '支付失败,请重试')
// 3. 更新数据库
await db.orders.update({
id: orderId,
status: 'payment_failed',
reason: event.data.reason
})
// 4. 可选:创建新订单
const newOrder = await stableops.paymentOrders.create({
merchantOrderId: `${orderId}_retry`,
amount: originalAmount,
// ...
})
}
res.sendStatus(200)
})选择合适的确认级别
决策矩阵
| 金额 | 类型 | 推荐阶段 | 原因 |
|---|---|---|---|
| < $10 | 数字商品 | CONFIRMED | 快、风险低 |
| $10 – $100 | 数字商品 | CONFIRMED | 平衡速度与安全 |
| $100 – $1,000 | 数字商品 | FINALIZED | 需要更高安全 |
| > $1,000 | 任意 | FINALIZED | 最高安全 |
| 任意 | 实物 | FINALIZED | 发货不可逆 |
| 任意 | 账户升级 | FINALIZED | 操作不可逆 |
| 任意 | 订阅 | FINALIZED | 周期性扣费 |
风险偏好
低容忍(金融服务、高价值商品):
- 一律等 FINALIZED
- 绝不在 DETECTED 履约
- 额外加风控
中等容忍(电商、SaaS):
-
$100 等 FINALIZED
- < $100 用 CONFIRMED
- DETECTED 只用于刷新 UI
高容忍(游戏、低价值数字商品):
- 大多数交易用 CONFIRMED
- 账户变更用 FINALIZED
- 接受偶发回滚
监控确认数
实时推送
StableOps 持续监听链上,确认数每次跨阈值都会推送 Webhook:
// 一笔支付的事件时间线
12:00:00 - payment.detected
12:00:12 - payment.confirmed
12:13:00 - payment.finalized轮询替代方案
如果你倾向轮询:
const checkPaymentStatus = async (orderId: string) => {
const order = await stableops.paymentOrders.retrieve(orderId)
switch (order.status) {
case 'detected':
console.log('已收到,等待确认...')
break
case 'confirmed':
console.log('已确认,等待 finality...')
break
case 'finalized':
console.log('已 finalize,可安全履约')
await fulfillOrder(order)
break
case 'reverted':
console.log('已回滚,处理失败流程')
await handleRevert(order)
break
}
}
// 每 5 秒轮询
const interval = setInterval(() => checkPaymentStatus(orderId), 5000)链重组
什么是重组
竞争链超过当前链,导致最近的区块被替换:
重组前:
Block 100 → Block 101 → Block 102 (你的交易)
↘ Block 102' (竞争链)
重组后:
Block 100 → Block 101 → Block 102' → Block 103'
✗ Block 102 (被孤立)各链重组频率
| 链 | 重组深度 | 频率 | 备注 |
|---|---|---|---|
| Ethereum | 1–2 块 | 每天 | 通常无害 |
| Ethereum | > 3 块 | 罕见 | 需要调查 |
| Base | 1 块 | 偶发 | L2 重组少 |
| Polygon | 1–5 块 | 较常见 | 重组风险高 |
| TRON | 1 块 | 罕见 | DPOS |
StableOps 如何处理重组
- 持续监听:每次确认都比对
blockHash - 检测重组:
blockHash不一致 → 视为重组 - 状态更新:订单切到
REVERTED - 派发 Webhook:发送
payment.reverted - 释放地址:地址释放回地址池(变回
AVAILABLE)
重组防护
StableOps 多层防护:
- Block hash 校验:比对存储的
blockHash与当前链 - Receipt 校验:确认 receipt 仍然存在
- 确认数计数:只统计 canonical chain
- finality 追踪:等到各链的 finality 保证
最佳实践
1. 关键履约用 FINALIZED
// ✅ 正确 —— 等 finality
app.post('/webhooks/stableops', async (req, res) => {
const event = req.body
if (event.type === 'payment.finalized') {
await fulfillOrder(event.data.payment_order_id)
}
res.sendStatus(200)
})
// ❌ 错误 —— 在 detected 就履约
app.post('/webhooks/stableops', async (req, res) => {
const event = req.body
if (event.type === 'payment.detected') {
await fulfillOrder(event.data.payment_order_id) // 危险!
}
res.sendStatus(200)
})2. 处理所有状态
const handlePaymentWebhook = async (event: WebhookEvent) => {
switch (event.type) {
case 'payment.detected':
await updateUI('已收到,确认中…')
break
case 'payment.confirmed':
await updateUI('已确认,最终化中…')
break
case 'payment.finalized':
await fulfillOrder(event.data.payment_order_id)
break
case 'payment.reverted':
await handleRevert(event.data.payment_order_id)
break
}
}3. 向用户展示进度
const PaymentStatus = ({ order }) => {
switch (order.status) {
case 'detected':
return (
<div>
<Spinner />
<p>已收到,确认中…</p>
</div>
)
case 'confirmed':
return (
<div>
<Spinner />
<p>已确认,最终化中…</p>
</div>
)
case 'finalized':
return (
<div>
<CheckIcon />
<p>支付完成!订单正在处理。</p>
</div>
)
}
}4. 详尽日志
app.post('/webhooks/stableops', async (req, res) => {
const event = req.body
await db.webhookLogs.create({
type: event.type,
orderId: event.data.payment_order_id,
fromStatus: event.data.from_status,
toStatus: event.data.to_status,
timestamp: new Date(),
})
// 业务处理...
res.sendStatus(200)
})5. 测试重组场景
describe('支付重组处理', () => {
it('应在重组时冲销履约', async () => {
// 1. 创建订单
const order = await createOrder()
// 2. 模拟 detected
await handleWebhook({ type: 'payment.detected', data: order })
// 3. 模拟 confirmed
await handleWebhook({ type: 'payment.confirmed', data: order })
// 4. 模拟回滚
await handleWebhook({ type: 'payment.reverted', data: order })
// 5. 验证已冲销
const dbOrder = await db.orders.findOne({ id: order.id })
expect(dbOrder.status).toBe('payment_failed')
})
})常见模式
分级履约
不同确认阶段提供不同权益:
app.post('/webhooks/stableops', async (req, res) => {
const event = req.body
const orderId = event.data.payment_order_id
switch (event.type) {
case 'payment.confirmed':
// 临时权益
await grantTrialAccess(orderId)
break
case 'payment.finalized':
// 完整权益
await grantFullAccess(orderId)
break
case 'payment.reverted':
// 全部撤销
await revokeAccess(orderId)
break
}
res.sendStatus(200)
})按金额条件履约
不同金额取不同确认级别:
const shouldFulfill = (order: PaymentOrder): boolean => {
const amount = parseFloat(order.amount)
if (amount < 100) {
// 小额:confirmed 即可
return order.status === 'confirmed' || order.status === 'finalized'
} else {
// 大额:等 finalized
return order.status === 'finalized'
}
}