Webhook 实战

Webhook 实战 做 SaaS 时你很快会遇到 Webhook。支付成功、订阅取消、邮件退信、文件处理完成、第三方数据同步很多外部系统都会通过 Webhook 通知你。Webhook 看起来只是“对方请求你的一个接口”但真正上线后坑很多重复通知、签名校验、超时、乱序、重试、数据一致性、日志排查。如果你把 Webhook 当普通接口写很容易在支付、订单、权限这类关键流程里出问题。Webhook 本质是外部系统主动通知你普通 API 通常是你主动请求对方比如你调用支付平台创建订单。Webhook 则相反是对方在事件发生后主动请求你的接口。比如用户付款成功后支付平台会请求你的/api/webhooks/payment告诉你这笔订单已经完成。这意味着 Webhook 有几个特点请求来自外部系统发生时间不完全由你控制可能重复发送可能延迟到达也可能因为你的服务异常而重试。所以 Webhook 的设计重点不是“能收到”而是“收到后能安全、稳定、可追踪地处理”。第一步为每个来源设计独立入口不要把所有 Webhook 都塞进一个通用接口。支付平台、邮件服务、文件服务、AI 服务、数据同步服务事件结构、签名方式、重试策略都不一样。最好为不同来源设计独立入口。比如POST /api/webhooks/stripe POST /api/webhooks/paypal POST /api/webhooks/email POST /api/webhooks/storage独立入口让签名校验、日志记录、错误排查和权限隔离都更清楚。第二步先验签再处理Webhook 最大的安全问题是不能相信任何请求。攻击者可能伪造支付成功、伪造订阅状态、伪造文件处理完成。如果你只看请求体里的数据不做签名校验就可能被恶意调用。大多数成熟服务都会提供签名校验机制。你需要用平台提供的 secret对原始请求体和请求头做校验确认请求确实来自对方。注意很多 Webhook 验签要求使用原始请求体。如果你的框架提前解析了 body可能会导致签名校验失败。这个细节要在接入时特别确认。第三步事件要先落库Webhook 到达后不要只在内存里处理也不要处理完就忘。建议先把事件 ID、来源、事件类型、原始 payload、接收时间、处理状态存进数据库。这样后面出问题时你能查到到底收到了什么、什么时候收到、处理结果是什么。一个简单事件表可以包含id source event_id event_type payload status received_at processed_at error_message事件落库是 Webhook 可追踪的基础。第四步必须做幂等Webhook 可能重复发送这是正常行为。比如你的接口超时对方没有收到成功响应就会重试网络波动也可能导致重复有些平台本来就保证“至少送达一次”而不是“只送达一次”。所以处理 Webhook 时一定不能假设同一个事件只会来一次。最常见的做法是用对方提供的event_id做唯一约束。如果同一个事件已经处理过直接返回成功不要重复创建订单、重复发邮件、重复增加额度。支付和订阅类 Webhook 尤其要小心。第五步快速返回成功Webhook 接口不适合做很耗时的业务处理。如果你在接口里同步生成报表、发送多封邮件、调用多个第三方服务很容易超时。一旦超时对方可能会重试造成重复处理压力。更稳妥的方式是验签通过后事件落库放入队列或标记待处理然后尽快返回成功。真正耗时的业务放到后台任务里处理。Webhook 接口负责接收和确认业务处理器负责执行和重试。第六步按事件类型分发处理一个来源可能有很多事件类型。比如支付系统可能有checkout.completed、invoice.paid、subscription.updated、subscription.canceled。邮件系统可能有delivered、bounced、complained。不要在一个巨大的 if-else 里处理所有事件。可以按事件类型拆成独立 handler。checkout.completed - 激活订单 invoice.paid - 续费成功 subscription.canceled - 取消订阅 email.bounced - 标记邮箱异常这样每个事件逻辑更清楚也更容易测试。第七步处理乱序和状态覆盖Webhook 不一定按你期待的顺序到达。比如订阅先收到取消事件后收到更新事件订单状态先收到成功后收到旧状态用户权限已经变更晚到的事件又把它改回去。处理这类问题时不要只根据“最新收到的事件”覆盖状态而要看事件时间、业务状态机和当前状态。对于关键业务可以先查询平台当前真实状态再决定本地如何更新。Webhook 是通知不一定是最终真相。必要时要回查来源系统。第八步日志和告警要做好Webhook 出问题很难靠用户描述排查。用户只会说“我付费了但没有开通”或“邮件显示失败”。你需要能快速查到是否收到事件签名是否通过事件是否落库是否处理成功失败原因是什么是否重试过。至少要记录来源、事件 ID、事件类型、处理耗时、错误信息和关联业务对象。对支付、订阅、关键数据同步这类 Webhook失败时应该有告警。本地调试要准备工具Webhook 来自外部系统本地开发时通常无法直接被访问。你可以使用隧道工具把本地服务暴露给外部或者使用平台提供的 CLI、测试事件、重放功能。调试时要关注三件事请求是否到达本地签名是否通过事件处理结果是否符合预期。不要等上线后才第一次测试 Webhook。Webhook 必须在开发和预发布环境里反复验证。一个 Webhook 处理流程可以按下面流程设计1. 接收请求 2. 读取原始 body 3. 校验签名 4. 解析事件 5. 用 event_id 去重 6. 事件落库 7. 快速返回成功 8. 后台分发处理 9. 更新业务状态 10. 记录日志和错误只要这个流程稳定绝大多数 Webhook 场景都能应对。写在最后Webhook 不是普通接口它是外部系统和你内部业务状态之间的桥。真正可靠的 Webhook 设计一定要包含验签、幂等、落库、快速响应、后台处理、日志和重试。尤其涉及支付和订阅时不要省略这些基础步骤。下一篇我们继续聊登录体系OAuth 登录怎么接。原文链接Webhook 实战 | Harries Blog™