Webhooks
签名、重试、死信与重放机制。
Webhook 让你的应用在 StableOps 中发生事件(例如支付被检测、确认、最终化)时实时收到通知。
总览
不必轮询 API 查支付状态,事件发生时 StableOps 会直接 HTTP POST 到你的服务。好处:
- 实时:到账立刻知道。
- 节省调用:不必持续轮询。
- 可靠投递:失败自动指数退避重试。
- 可回放:所有投递都有日志,可在 dashboard 重放。
工作流
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 区块链 │────────▶│ StableOps │────────▶│ 你的应用 │
└─────────────┘ └─────────────┘ └─────────────┘
支付发出 检测到事件 派发 Webhook- 某个事件发生(例如链上检测到支付)
- StableOps 创建一条 Webhook 投递
- 向你的端点发 HTTP POST
- 你的服务处理后返回 200 OK
- 投递失败则指数退避重试
Webhook 事件
支付相关事件
payment_order.created
新订单创建时发送。
payment.detected
扫描器首次看到对应转账时发送。扫描可能落后于链头,此时不一定恰好是 0 确认。
动作:把 UI 切换到「支付已收到,确认中…」。
注意:此时不要履约,交易仍有可能被回滚。
payment.confirmed
支付获得足够链上确认时发送。交易基本不可能回滚,但尚未达到链的 finality 保证。
动作:可以更新内部系统并通知用户,但建议等待 payment.finalized 后再发货。
payment.finalized
支付达到 finality、不再可回滚时发送。
动作:可以安全履约。资金已经稳定。
这是推荐用来触发履约的事件。
payment.expired
订单超过过期时间仍未匹配到任何转账时发送。
动作:把订单标记为过期,引导用户重新下单。
payment.reverted
已检测到的支付被回滚时发送。receipt 执行失败,或区块在重组后 block hash 不再与存储的事件匹配。
动作:撤销任何基于 payment.detected / payment.confirmed 做的乐观处理。这也是
建议等 payment.finalized 再履约的原因。
payment_order.canceled
仍处于 created(尚未检测到任何入账)的订单被调用 cancel 取消时发送。
动作:把订单标记为已取消,释放任何为它预留的资源。
注意:虽然取消由用户主动触发、HTTP 响应已经返回结果,仍会回推此事件。这样以 webhook 流为唯一事实源(event sourcing)的接入方不会漏掉这次终态流转。不关心的接入方 可以直接忽略它。
完整 payload 结构请参见 Payment Events API 参考。
商家订阅事件
StableOps 也会为商户管理的终端用户订阅和订阅账单推送事件。你可以用这些事件开通账号、跟踪续费、暂停逾期用户,并对账账单支付结果。
| 事件 | 触发时机 |
|---|---|
end_user_subscription.created | 商家订阅创建 |
end_user_subscription.activated | 首期账单已支付,订阅变为 active |
end_user_subscription.renewed | 续费账单已支付,账期向前推进 |
end_user_subscription.canceled | 订阅被立即取消 |
end_user_subscription.expired | 订阅因期末取消或未付款而过期 |
end_user_subscription.past_due | 订阅进入 past_due 状态 |
end_user_invoice.open | 新的订阅账单已开出 |
end_user_invoice.paid | 订阅账单已支付 |
end_user_invoice.payment_failed | 订阅账单支付失败 |
end_user_invoice.payment_late | 账单已判为不可收回后又收到付款 |
订阅账单 Checkout 底层仍会创建支付单,因此你也可能收到该底层支付单的普通支付事件。订阅状态请以
end_user_invoice.* 和 end_user_subscription.* 事件为准。
各事件完整 payload 结构请参见 Subscription Events API 参考。
运维与 Agent 事件
除支付事件外,StableOps 还会在以下场景推送事件。在控制台里它们构成运维事件订阅分组。一个开关覆盖全部,与支付事件分组相互独立:
| 事件 | 触发时机 |
|---|---|
address.pool.low | 某条链的可用收款地址池低于水位线 |
agent.action.requested | Agent 发起一个需要审批的写操作 |
agent.action.approved | 待审批的 Agent 操作被批准 |
agent.action.executed | 已批准的 Agent 操作完成执行 |
各事件完整 payload 结构请参见 Operational Events API 参考。
接入 Webhook
1. 创建端点
在 StableOps dashboard:
- 进入 Webhooks(左侧导航栏)
- 点 Add Endpoint
- 填入你的 Webhook URL(生产环境建议使用 HTTPS)
- 勾选订阅的事件类型
- 保存 Webhook secret
secret 字段只在创建与 rotate 时返回一次,Dashboard 不会再展示,请安全保存。
调用 rotate-secret 后,旧 secret 仍保留 24 小时与新 secret 同时有效,便于
你平滑地把验签 key 切换到新值,不会丢投递。
2. 验证签名
始终验证签名,确保请求来自 StableOps。
import { SIGNATURE_HEADER, verifySignature } from '@stableops/api-sdk/webhooks'
const result = verifySignature({
secrets: [webhookSecret],
header: request.headers.get(SIGNATURE_HEADER) ?? undefined,
rawBody,
})
if (!result.ok) {
console.error('Invalid signature:', result.reason)
// 拒绝请求
}投递、重试与死信
成功判定
任何 2xx 响应都算成功。其它状态码、网络错误、或者超过 10 秒 没返回,都按
失败处理。
重试退避
失败后按下表延迟重排,第 6 次仍失败转入 DLQ(dead letter queue):
| 第几次 | 间隔 |
|---|---|
| 1 | 30 秒 |
| 2 | 60 秒 |
| 3 | 5 分钟 |
| 4 | 30 分钟 |
| 5 | 2 小时 |
| 6+ | DLQ |
投递记录字段
webhook-deliveries 上你能查到的关键字段:
| 字段 | 说明 |
|---|---|
attempts | HTTP 已尝试的次数 |
response_status | 最近一次下游返回的状态码 |
response_duration_ms | 最近一次耗时 |
error_message | 最近一次失败的简短原因 |
next_retry_at | 下次重试时间;进入终态时为 null |
status | pending / succeeded / failed / dead_letter |
Replay
三个重放接口(endpoint replay、单条 delivery replay、批量 replay dead letters) 都是新建一条 delivery 记录进行投递,原审计日志保持不变。Dashboard 的 "replay" 按钮就是调用这些接口。
最佳实践
1. 始终验证签名
拒绝任何不带有效 X-Product-Signature 的请求。不验签的话,只要别人知道你的端点 URL 就能伪造事件。
2. 尽快返回 200
验签并记录 X-Event-Id 后立即返回 200。履约、通知、账本更新等耗时操作应异步处理,否则投递超时会触发无谓的重试。
3. 按 X-Event-Id 幂等处理
在变更自身状态前先记录 X-Event-Id。网络重试和平台重放可能多次投递同一事件,重复执行必须安全。
4. 验签必须用 raw body
签名计算的是收到的原始字节,任何 JSON 解析或重新序列化都会改变 body 内容、破坏签名。务必在验签完成后才解析 JSON。
5. 处理全部相关事件类型
订阅并妥善处理你可能收到的每种事件,包括 payment.reverted、payment.expired,以及
end_user_invoice.paid / end_user_subscription.renewed 等订阅事件。未处理的事件虽然也算投递成功,但你的订单和订阅状态会逐渐失准。
6. 详尽记录日志
记录每次投递的 X-Event-Id、X-Delivery-Id、事件类型以及你的处理结果。完善的日志是排查漏单或重复履约最快的手段。
7. 监控投递健康度
定期检查投递日志,留意 failed 或 dead_letter 数量上升的端点。对过去一小时未返回 2xx 的端点设置告警,问题修复后用 replay 接口清空死信队列。
下一步
这篇文档怎么样?
最后更新