StableOps
常见问题

收不到 webhook,或验签失败

排查 webhook 投递丢失与签名验证错误的检查清单。

webhook 问题大多落在两类:投递根本没到达你的服务器,或到达了但验签失败。请按下面对应的清单逐条排查。

投递没有到达

  1. 该端点订阅了这个事件所在的分组吗? 在控制台里订阅是按分组而非单个事件选择的,共三类:支付事件payment_order.*payment.*)、商家订阅事件end_user_subscription.*end_user_invoice.*)与运维事件address.pool.low 以及 agent.action.* 审批)。只订阅了支付事件的端点不会收到订阅类或运维/Agent 事件,反之亦然。三类都不勾是不允许的;三类都勾表示“全部事件”,含将来新增的类型。(API 的 enabled_events 字段技术上可携带任意子集,但控制台的分组模型无法表达任意子集。在控制台保存一个自定义子集会把它扩成整组。)

  2. 你的服务器是否在 10 秒内返回了 2xx 任何非 2xx 状态、网络错误,或 10 秒内无响应都算失败。处理函数在响应前就做了大量实际工作是常见原因。先返回 200,再异步处理。

  3. 查看投递日志。 每次尝试都会记录 response_statusresponse_duration_mserror_messageattemptsnext_retry_at 以及 statuspending / succeeded / failed / dead_letter)。这通常能立刻看出问题是否出在你这一侧。

  4. 把重试也算进去。 失败的投递按下面的曲线重试,到第 6 次进入死信队列:

    尝试次数延迟
    130 秒
    260 秒
    35 分钟
    430 分钟
    52 小时
    6+死信队列
  5. 修复后重放。 重放接口(以及面板上的重放按钮)会入队一条全新的投递记录,原始审计日志会被保留。用 replay-dead-letters 排空在你的端点宕机期间落入死信队列的投递。

验签失败

  1. 针对原始 body 验签。 在任何 JSON 解析或重新序列化之前,对你收到的精确字节计算签名。会先解析再 re-stringify body 的框架会改变它从而破坏签名。这是最常见的单一原因。

  2. 从正确的请求头读取签名。 使用 SDK 的 SIGNATURE_HEADER 常量,而不要硬编码字段名。

  3. 使用正确的 secret。 secret 仅在创建和轮换时返回。如果没存下来,轮换一次拿新的。

  4. 轮换期间同时接受两个 secret。 轮换后,旧 secret 会与新 secret 并存有效 24 小时。把两者都传给验签器,让灰度过程中在途的投递不会失败:

    import { SIGNATURE_HEADER, verifySignature } from '@stableops/api-sdk/webhooks'
    
    const result = verifySignature({
      secrets: [currentSecret, previousSecret], // 24 小时重叠期内两者都有效
      header: request.headers.get(SIGNATURE_HEADER) ?? undefined,
      rawBody,
    })
    
    if (!result.ok) {
      console.error('Invalid signature:', result.reason)
      // 拒绝该请求
    }

相关

这篇文档怎么样?

最后更新

本页内容