目录
Stripe 什么都发邮件,也就等于什么都没通知你。一笔成功扣款、一次订阅续费失败、一个拒付争议,最后都变成同一个收件箱里长得一样的消息;等你翻到的时候,它们之间的区别已经没意义了。
解决办法不是发更多通知,而是让每类 Stripe 事件带上它真正该有的紧急程度:有人付款时安静推一条,续费失败时用时效性通知,而争议交易——那个自带回应期限的——直接打电话给你。
哪些 Stripe 事件真的需要你
不是每个事件都值得通知,值得通知的那些也不一样急。
| Stripe 事件 | 为什么重要 | 紧急级别 |
|---|---|---|
payment_intent.succeeded | 有人付款了。知道就好,从来不急。 | 普通 |
invoice.payment_failed | 续费失败。在订阅真正失效前还有一个重试窗口。 | 时效性 |
charge.dispute.created | 拒付争议。Stripe 会给出一个由卡组织设定的回应期限,错过就等于默认败诉。 | 来电 |
payment_intent.payment_failed | 一次性支付失败。量大时有价值,单看一条很吵。 | 普通,或者干脆不看 |
值得一通电话的只有争议交易。其余的都可以等你自己拿起手机再说;而一个拖了一周才发现的拒付,那笔钱本来就留不住了。
第一步 —— 按紧急级别各建一个频道
在 Echobell 里建三个频道,因为紧急级别是按频道设置的:
- Stripe · 收入 —— 普通通知,用于收款成功
- Stripe · 扣款失败 —— 时效性通知
- Stripe · 争议交易 —— 来电
分别从频道设置里复制 webhook URL,等下 Stripe 三个都要用。
顺手把每个频道的 仅 POST 打开。Stripe 一定是用 POST 发送的,开启后能避免链接预览和地址栏自动补全把频道误触发。
第二步 —— 在 Stripe 里添加端点
进入 Stripe 后台,Developers → Webhooks → Add endpoint,粘贴频道 URL,然后只勾选这个频道该处理的事件。
建议一个频道对应一个端点,而不是所有事件塞进一个端点。Stripe 允许按端点选择事件,等于把路由放在了 Stripe 那边,你的频道可以保持简单。
如果你更想只用一个端点,那就指向一个频道,然后用条件过滤:
type == "charge.dispute.created"
条件不需要写模板里那对 {{ }} 包裹。
第三步 —— 用模板把 payload 变成人话
Stripe 发过来的 JSON 大致长这样:
{
"type": "charge.dispute.created",
"data": {
"object": {
"amount": 4900,
"currency": "usd",
"reason": "fraudulent",
"status": "warning_needs_response"
}
}
}
所以频道模板要伸进 data.object 里取值:
标题
争议交易:{{data.object.reason}}
正文
{{data.object.amount}} {{data.object.currency}} 被发起争议
状态:{{data.object.status}}
有个坑几乎人人都踩: Stripe 的金额单位是该货币的最小单位。4900 是 49.00 美元,不是 4900 美元。Echobell 模板会原样代入这个值,所以要么在发送前先做除法,要么把货币写在后面、心里明白它是「分」。如果这个数字必须一眼就对,就把除法放在 Stripe 和 Echobell 之间的那一层里——一个 Worker、一个自动化流程,或者你自己的接口。
收入频道用同样的思路,字更少:
标题
+{{data.object.amount}} {{data.object.currency}}
正文
{{data.object.description}}
第四步 —— 不用等真实付款就能测
Stripe CLI 可以把真实结构的事件打到你的端点上:
stripe trigger payment_intent.succeeded
stripe trigger charge.dispute.created
上线依赖之前先跑一遍。这也是最快能看清「某个你以为一定存在的字段是 null 时模板会渲染成什么样」的方式。
这套方案没解决的问题
在把它放到收入链路上之前,有两个限制需要知道。
Echobell 不校验 Stripe 的签名。 Stripe 会给每个 webhook 带上 Stripe-Signature 头,校验它需要端点密钥,而 Echobell 的 webhook 接口并不做这件事——也就是说,保护这个频道的只有 URL 本身。请把它当作密钥对待:保持仅 POST 开启,不要粘进公开仓库或者截图里。
你可以用条件加一层很弱的检查:
header["stripe-signature"] != ""
它能确认「有个东西发来了长得像 Stripe 的请求」,但确认不了「这就是 Stripe」。如果一条伪造的通知会造成真实损失(而不只是让你困惑一下),那就在前面放一个你自己的接口,在那里校验签名,再由它去调 Echobell。
这是告警链路,不是账本。 Stripe 会重试失败的 webhook 投递,但通知本身不构成发生过什么的记录。对账请以 Stripe 为准,不要拿通知历史当依据。
常见问题
Stripe 能直接打电话给我吗?
不能。Stripe 只发邮件和 webhook。要变成一通电话,得把 webhook 转给一个能打电话的服务。
争议交易的告警真能把我叫醒吗?
如果频道设成来电模式,能。它会以来电形式抵达,穿透专注模式和勿扰。详见让关键告警穿透 iOS 专注模式。
合伙人能收到同样的告警吗?
可以。把频道分享出去,每个订阅者自己选通知类型。你可以负责接电话,同一个事件他那边只是一条安静的推送。
测试模式怎么办?
Stripe 测试模式的事件只会发给在测试模式下注册的端点。想看到测试流量就在两种模式下都注册频道 URL;也可以先只在测试模式下注册,等你信任这套设置了再切。
小结
三个频道、三个端点、每个一份模板。重点不是「你能收到 Stripe 的通知」——你本来就能。重点是一笔拒付不再和一张收据长得一模一样。
下载 iPhone 版 Echobell 或在 Google Play 获取,然后执行一次 stripe trigger charge.dispute.created,故意让手机响一回。