如何在不部署到生产环境的情况下测试 Stripe Webhooks
Source: Dev.to
方法一:Stripe CLI
官方推荐的做法是安装 Stripe CLI,登录后将事件转发到本地服务器:
stripe login
stripe trigger payment_intent.succeeded
优点
- 设置快速。
缺点
- 模拟事件只包含通用数据(占位的客户 ID、金额)。
- 终端关闭后隧道会失效。
- 每次重启都会生成新的签名密钥,导致签名验证失效,除非你更新
.env。 - 如果你的处理函数返回 500,CLI 输出并不会清晰地显示错误。
方法二:ngrok
直接使用 ngrok 暴露本地服务器:
ngrok http 3000
- 将生成的公开 URL 粘贴到 Stripe 的 webhook 设置中。
- 你会收到来自测试模式支付的真实事件,这比模拟数据要好。
不足之处
- 免费 URL 每次会话都会变化,需要你不断在 Stripe 仪表盘里更新。
- 如果处理函数在请求途中崩溃,webhook 会丢失;只能等待 Stripe 的指数退避重试或手动重构负载。
- 关闭 ngrok 会清除请求历史。
方法三:先捕获后处理
不要把 Stripe 指向本地服务器,而是指向一个持久化的端点来捕获并存储每个 webhook。随后在需要时将捕获的负载重新发送到本地主机。
创建端点
curl -X POST https://hooklab-webhook-testing-and-debugging.p.rapidapi.com/api/v1/endpoints
响应中会返回一个公开 URL,例如:
https://hooklab-webhook-testing-and-debugging.p.rapidapi.com/hook/ep_V1StGXR8_Z5j
将此 URL 粘贴到 Stripe 的 webhook 设置中。
发送测试支付
在 Stripe Dashboard 中使用测试卡号 4242 4242 4242 4242。Stripe 会发送 webhook,HookLab 将其捕获。
检查捕获的请求
curl https://hooklab-webhook-testing-and-debugging.p.rapidapi.com/api/v1/endpoints/ep_V1StGXR8_Z5j/requests
响应会显示完整的请求头、正文、Stripe-Signature、时间戳等信息,省去了在控制台打印调试的步骤。
重新发送到本地服务器
curl -X POST https://hooklab-webhook-testing-and-debugging.p.rapidapi.com/api/v1/replay \
-d '{"url":"http://localhost:3000/api/webhooks/stripe"}'
重放时会使用相同的请求头、正文和 HTTP 方法,直接把请求发送到你的处理函数。如果再次崩溃,只需修复后再次重放——无需等待 Stripe 的重试逻辑。
为什么捕获‑重放值得使用
最常见的三类 Stripe webhook bug 在此方法下更容易被发现:
- 事件类型错误 – 你可能在处理
charge.succeeded,而 Stripe 实际发送的是payment_intent.succeeded。捕获完整的 checkout 流程可以看到 Stripe 触发的每个事件及其顺序。 - 数据结构意外 – 访问
event.data.object.customer.email会在customer是字符串 ID 而不是展开对象时失败。真实的捕获负载会在你编写处理函数前展示准确的结构。 - 未知字段 – 有时 Stripe 会新增字段,拥有真实负载可以让你快速适配。
大家常碰到的坑
- 立即返回
200 OK。Stripe 要求在 5–10 秒内收到响应。将耗时的处理放到异步任务中;否则 Stripe 会认为投递失败并重试,导致事件重复。 - 使用真实的测试模式交易进行测试。
stripe trigger命令适合检查端点是否可达,但只有在处理来自真实测试结账的 webhook 时,才能真正验证集成,因为负载在细节上会有所不同。
HookLab 入门指南
HookLab 在 RapidAPI 上提供免费套餐(每日 100 次调用和 3 个端点),足以满足大多数 Stripe 集成测试需求。
你的 webhook 测试方案是什么? 欢迎在评论中分享你的做法。