StableOps
概念

Webhooks

签名、重试、死信与重放机制。

Webhook 让你的应用在 StableOps 中发生事件(例如支付被检测、确认、最终化)时实时收到通知。

总览

不必轮询 API 查支付状态,事件发生时 StableOps 会直接 HTTP POST 到你的服务。好处:

  • 实时:到账立刻知道。
  • 节省调用:不必持续轮询。
  • 可靠投递:失败自动指数退避重试。
  • 可回放:所有投递都有日志,可在 dashboard 重放。

工作流

┌─────────────┐         ┌─────────────┐         ┌─────────────┐
│   区块链     │────────▶│  StableOps  │────────▶│   你的应用   │
└─────────────┘         └─────────────┘         └─────────────┘
    支付发出                 检测到事件              派发 Webhook
  1. 某个事件发生(例如链上检测到支付)
  2. StableOps 创建一条 Webhook 投递
  3. 向你的端点发 HTTP POST
  4. 你的服务处理后返回 200 OK
  5. 投递失败则指数退避重试

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.requestedAgent 发起一个需要审批的写操作
agent.action.approved待审批的 Agent 操作被批准
agent.action.executed已批准的 Agent 操作完成执行

各事件完整 payload 结构请参见 Operational Events API 参考

接入 Webhook

1. 创建端点

在 StableOps dashboard:

  1. 进入 Webhooks(左侧导航栏)
  2. Add Endpoint
  3. 填入你的 Webhook URL(生产环境建议使用 HTTPS)
  4. 勾选订阅的事件类型
  5. 保存 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):

第几次间隔
130 秒
260 秒
35 分钟
430 分钟
52 小时
6+DLQ

投递记录字段

webhook-deliveries 上你能查到的关键字段:

字段说明
attemptsHTTP 已尝试的次数
response_status最近一次下游返回的状态码
response_duration_ms最近一次耗时
error_message最近一次失败的简短原因
next_retry_at下次重试时间;进入终态时为 null
statuspending / 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.revertedpayment.expired,以及 end_user_invoice.paid / end_user_subscription.renewed 等订阅事件。未处理的事件虽然也算投递成功,但你的订单和订阅状态会逐渐失准。

6. 详尽记录日志

记录每次投递的 X-Event-IdX-Delivery-Id、事件类型以及你的处理结果。完善的日志是排查漏单或重复履约最快的手段。

7. 监控投递健康度

定期检查投递日志,留意 faileddead_letter 数量上升的端点。对过去一小时未返回 2xx 的端点设置告警,问题修复后用 replay 接口清空死信队列。

下一步

这篇文档怎么样?

最后更新

本页内容