所有文章
StripeStripe Connectmarketplacewebhook debugging

Stripe Connect Webhook:本地测试指南

Stripe Connect 是与普通 Stripe 不同的 Webhook 生态。签名数学相同。其余一切都不同。如果你一直在追查为什么你的 Connect 处理器悄悄忽略 account.application.deauthorized,这篇文章是为你写的。

Connect 改变你接收哪些事件

普通 Stripe Webhook 来自你的平台账户:charges、customers、subscriptions。Connect Webhook 来自关联账户以及平台上 Connect 特有的生命周期事件。列表大量重叠,但包含只有启用 Connect 才能看到的事件:

  • account.updated — 验证状态、capabilities、要求
  • account.application.deauthorized — 关联账户撤销了你的访问
  • capability.updated — payouts/transfers 激活状态
  • person.createdperson.updated — 用于 Custom 和 Express 账户
  • payout.failedpayout.paid — 在关联账户上,而非你的平台

大多数团队在部署后才发现这点。本地测试反而在几分钟内就暴露问题。

Stripe-Account 头改变一切

当事件发生在关联账户上时,Stripe 附上带关联账户 ID(acct_xxx)的 Stripe-Account 头。你的处理器需要按那个头路由,而不是按 payload 里的内容。

const connectedAccountId = req.headers['stripe-account'];
const event = stripe.webhooks.constructEvent(
  rawBody,
  req.headers['stripe-signature'],
  endpointSecret,
);

// Now process the event in the context of connectedAccountId
await handleConnectEvent(event, connectedAccountId);

如果你忘了读这个头,处理器就把每个 Connect 事件当作发生在你平台上的来处理。这种模式的 bug 通常表现为「payout 显示给了错误的商户」。

用 PortPreview 在本地测试 Connect

设置与普通 Stripe 本地测试相同,加一个额外步骤:

  1. 在本地运行你的平台应用。
  2. 启动隧道:npx portpreview 3000
  3. 在 Stripe 仪表盘里把隧道 URL 加为 Webhook 端点,勾选 Events on Connected accounts 选项。这就是改变整个事件流的开关。
  4. 使用测试 Express 或 Custom 关联账户。Stripe 测试模式包含一个会触发真实事件的假 "Jenny Rosen" 账户创建器。
  5. 从 Connect 测试界面触发事件:完成入驻、请求 payout、解除授权一个账户。

解除授权流程是难点

当关联账户撤销你应用的访问时,Stripe 恰好触发一次 account.application.deauthorized。如果你的处理器崩溃、返回 5xx 或没及时确认事件,Stripe 会重试——但关联账户已经没了。你随后对该账户的 API 调用返回 401。

仔细测试 deauth 流程。用 Connect 测试模式解除测试账户的授权,捕获 webhook,并对捕获的 payload 运行你的清理逻辑。重放直到这条路径坚不可摧。

Express vs Standard vs Custom

账户类型改变你接收哪些 person/capability 事件。Express 和 Custom 账户发出 person.* 事件,因为你的平台帮助完成入驻。Standard 账户通过 Stripe 托管的界面处理自己的入驻,所以你看到的事件更少。如果你在项目中途切换账户类型——人们确实会——你的 webhook 处理器需要调整。

我们实际会怎么做

对于一个有平台级分析和按商户报表的真实市场,从第一天起就构建两条不同的处理器路径:一条用于平台事件(没有 Stripe-Account 头),一条用于关联账户事件。在函数顶部路由。这避免了后面 90% 的「这是给我们还是给商户的」bug。

Connect Webhook 共享 Stripe 的签名方案,所以验证机制与普通 Stripe Webhook 测试完全相同。关于跨提供商的签名数学背景,见签名验证指南加入 PortPreview 等候名单,以内置捕获和重放测试 Connect。

常见问题

Stripe Connect Webhook 与普通 Stripe Webhook 有什么不同?
Connect 事件来自关联账户,并携带一个 Stripe-Account 头标识哪个账户触发了事件。签名机制与普通 Stripe 相同,但你必须在仪表盘中启用 Events on Connected accounts,并在处理器中按头路由。
Stripe-Account 头用来做什么?
它标识触发事件的关联账户。处理器应在处理前读取它,因为同一事件类型可能在你的平台或关联账户上触发,而业务逻辑不同。
我如何在本地测试解除授权 webhook?
用隧道设置一个 Connect 端点,连接一个测试 Express 或 Custom 账户,然后从 Connect 测试界面解除其授权。account.application.deauthorized 事件会到达你的本地处理器——在你调优清理逻辑时按需重放它。