失败扣款不是异常,是每月都会发生的正常路径。
一、第一次失败不是异常
周二凌晨两点,一笔团队档续费扣款失败。他第二天早上看到 invoice.payment_failed 的 webhook,第一反应是把这家客户的账号标成停用——毕竟钱没到账。三天后客户写信来问为什么登不上,账单上还挂着一笔未付。
以下情境为教学假设,人物、引语和数字不代表经核实的真实案例。
这一篇的基线里,月付续费 84 笔,按 6% 的失败率算,每月 5.04 笔、约 5 笔会失败。年付续费只有 3 笔,但金额大,失败一次就是 120 或 300 美元。存量上还有 7 个 past_due 账户,占 120 个付费订阅的 5.8%。也就是说,任何一天打开后台,都有一小撮人处在「钱没扣到、服务还在给」的状态。
6% 是平均数,不是每一期都 6%。月初续费集中,失败也集中;换了支付通道的头两个月,失败率通常还会高一截。按周看失败率比按月看更容易发现异常——一周 20 笔续费里失败 4 笔,就已经值得把拒付码逐条拉出来看一遍。
把这 5 笔当异常处理,动作往往是停号;把这 5 笔当正常路径处理,动作才是一套固定的流程。dunning(催收)在订阅计费里指的就是这套流程:什么时候重试、发几封信、给几天宽限、到哪天停用。它不解决「客户没钱」这件事,只解决「客户只是卡过期了」这件事。
判断标准是失败原因里有多少属于软失败。卡过期、余额不足、发卡行临时风控,客户换个卡或等两天就能过;卡号注销、账户关闭,重试多少次都没用。前者占多数,后者只能靠邮件催他换一种支付方式。
失败扣款之所以容易被写成异常,是因为它在代码里长得像异常:接口返回一个错误码,事务回滚,告警响起。但它在业务里是每月固定发生的 5 笔,是 84 笔续费里必须提前设计好去处的一部分。把它交给 try/catch,等于把 5 个客户的去留交给一次报错。
通道返回的拒付码有几十个,内部只需要三种:可重试(余额不足、临时风控)、需换卡(过期、注销)、需人工(争议、欺诈嫌疑)。映射表写在配置里,不要散落在 if 里;新增一个拒付码时,改配置比改代码安全。
还有一层区别:失败不等于欠费。欠费是「该付而不付」,失败是「想付但没扣成」。这两类客户在数据库里可能都是 past_due,但处理方式相反——前者要催,后者要帮。把两者混在一个状态里,邮件就会发给不该发的人。
一个人维护时,重试最好由事件驱动而不是全靠 cron 扫表。收到失败事件就排下一次尝试,同时每天跑一次兜底扫描,把「事件丢了但状态还停在 past_due」的订阅捞出来。两条路都留着,是因为 webhook 会丢,基线里每月 420 个事件按 1.5% 算要丢 6.3 个。
二、重试日程:第 1/3/5/7 天
重试不能每天做。同一张卡连续失败,发卡行会把这条商户的请求标成高风险,后面正常的扣款也会被拒。行业里常见的节奏是隔天一次,四次覆盖一周。
| 次序 | 时点 | 间隔 | 动作 | 硬失败时 |
|---|---|---|---|---|
| 第 1 次 | 失败次日 | 24 小时 | 原卡重试,同时发第一封邮件 | 立即转人工,不再重试 |
| 第 2 次 | 第 3 天 | 48 小时 | 原卡重试,邮件提示更新卡片 | 停止自动重试 |
| 第 3 次 | 第 5 天 | 48 小时 | 原卡重试,邮件说明停用日期 | 停止自动重试 |
| 第 4 次 | 第 7 天 | 48 小时 | 最后一次重试,宽限到期 | 转 past_due,暂停服务 |
四封邮件安排在第 0、1、4、7 天。第 0 天是失败当天,主题只写「这笔扣款没成功」,不要写「您的账户即将被停用」;第 1 天带上更新卡片的链接;第 4 天说明还有 3 天;第 7 天说明今天之后会暂停。
发送时点按客户本地时间上午 10 点,不要跟着扣款失败的凌晨走。凌晨发出的邮件会埋在通知列表底部,客户第二天醒来时已经滑过去十几条,等于没发。
邮件里最有效的一个元素是一键更新卡片的链接,直接跳到托管页,不要让人先登录再翻三层设置。基线里更新卡片的客户占 40%,这 40% 几乎全部来自第二封和第三封——第一封很多人没看到,第四封已经太晚。
时点要按账单锚点所在时区算,不要统一折成 UTC 零点。客户在当地的 23:50 收到「今天到期」,第二天早上发现已经停了,这种投诉一个月只要有一封,就够让人怀疑整个系统。
第 4 次失败之后不要立刻把状态改成 canceled。停在 past_due、服务降级,再给一个「手动完成付款」的入口;客户三天后想起换卡,还能自己救回来,不必写信给你,也不必等你回信。
日程也不必对所有人一样。年付客户失败一次是 120 或 300 美元,比月付重得多,值得多给两天宽限、多一封邮件;把同一套日程套在所有套餐上,省事,但不是最优。
重试的金额要锁定为失败那一期的金额,不能跟着当前套餐走。客户在第 3 天从团队档降到个人档,第 5 天的重试如果按新价扣,就变成一笔金额对不上的扣款,后面退款和对账都要多一轮。
重试请求必须带幂等键,键里至少包含订阅、账期和尝试次数。网络超时后重发同一次请求,通道返回的是同一笔扣款,而不是第二笔;没有这个键,第 1 天那次「超时但其实成功」的重试会在第 3 天再扣一次,正好复现 01 篇那笔多收 420 美元的事故。
重试次数要有上限,也要有一个「立即重试」按钮。上限防止通道把商户的请求标成滥用;按钮让客户更新卡片后不必等到下一个时点。按钮触发的那次重试同样要带幂等键,否则客户连点三下就是三笔。
三、回收率递减:55% 与 35% 的差
回收率不是线性的。第一次重试拿回的最多,后面每一次都在捡漏。
| 次序 | 累计回收 | 本次增量 | 仍未回收 |
|---|---|---|---|
| 第 1 次 | 35% | +35% | 65% |
| 第 2 次 | 44% | +9% | 56% |
| 第 3 次 | 50% | +6% | 50% |
| 第 4 次 | 55% | +5% | 45% |
把 5.0 笔代进去:55% 回收等于 2.75 笔,流失 2.25 笔。按平均每笔 15 美元算,流失收入 33.75 美元/月,405 美元/年,占年确认收入 20,520 的 1.97%。
找回的 2.75 笔也不是原来的 2.75 笔。经历过一次失败扣款的客户,次月留存通常略低,因为他已经被迫认真看过一次「到底要不要续」。所以 1.97% 是流失代价的下限,不是全部。
如果只重试一次,回收 35% 等于 1.75 笔,流失 3.25 笔,48.75 美元/月。两者差 15 美元/月,一年 180 美元。多出来的三次重试不是免费的:5.0 笔 × 3 次 × 0.30 美元的通道费,等于 4.5 美元/月、54 美元/年。净收益 126 美元/年。
那 0.30 是每次重试的固定费用,属于教学假设,不是任何通道的报价。真实成本按笔计,重试越多越贵;如果通道按月收固定费,这 54 美元就不成立,净收益会回到完整的 180 美元。
126 美元一年,对一个人的产品来说值得单独写一套系统吗?值得,但理由不是这 126 美元。真正的理由是那 2.25 笔流失里的多数人并不是想走:他们只是卡过期了,而第一次失败就停号会让他们连「去更新一张卡」的机会都没有。这笔账算不进收入表,算在退款率和口碑里。
通道提供的智能重试,本质是把「什么时候重试」交给失败原因和历史成功率。自建也能做,但要有足够多的失败样本才训得动;一个月只有 5 笔失败的产品,用固定日程就够了,别为了智能而智能。
这组数字还有一个用法:给自己设一个回收率下限。四次重试累计回收低于 40%,说明多半是硬失败占比太高,或者邮件根本没送到;高于 70%,要么是客户结构特别好,要么是统计把「重试期间客户自己主动付款」也算进了回收。
回收率要按队列算,不能按当月算。第 4 天的重试结果落在下个月,按自然月统计会把这个月的回收率压低、下个月抬高,看起来像系统在抽风。
电话和站内信对一个月付 12 美元的客户基本无效。催收渠道要和客单价匹配:个人档只发邮件,团队档才值得在第四天打一个电话——20 个团队客户,一次电话的成本远低于流失一个 30 美元/月的账户。
每次重试的结果都要落一张表:订阅、时点、拒付码、结果。季度复盘按拒付码分组看回收率,如果「余额不足」这一类在第 2 次之后几乎不再回收,就把日程从四次缩到两次,省下的不只是 54 美元。
四、七天宽限、四封邮件与复算
宽限期里给什么、不给什么,要提前写死。给只读访问、给导出,不给新建;这既让客户知道你还在,也让「不付钱也能一直用」不成立。
宽限期的长度与重试窗口最好是同一个数。基线是 7 天,正好覆盖第 1、3、5、7 天四次尝试。把宽限设成 3 天、重试做到第 7 天,就会出现账号已经停了、重试还在跑的情况,客户第三次收到「请更新卡片」时已经进不去后台。
宽限结束后不要立刻删数据。降到只读、保留 30 天、随时可导出,是成本最低的善后;直接删库看着干脆,实际上把一次「卡过期」升级成了一次「数据丢了」的投诉。
停用与删除是两件事。停用是计费状态,删除是数据生命周期,前者由 dunning 决定,后者由合规要求与用户请求决定。把两者绑在同一个按钮上,迟早会有一次误删。
复算:月付续费 84 笔,失败率 6%,每月 5.04 笔按 5.0 计;四次重试累计回收 55%,回收 2.75 笔,流失 2.25 笔;按每笔 15 美元,流失 33.75 美元/月、405 美元/年,占 20,520 的 1.97%。只重试一次回收 35%,流失 3.25 笔、48.75 美元/月;差额 180 美元/年,三次额外重试成本 54 美元/年,净 126 美元/年。
失败扣款 84 × 6% = 5.04 ≈ 5.0 笔/月
四次重试累计回收 55%:5.0 × 55% = 2.75 挽回;流失 2.25 笔
流失 2.25 × 15 = 33.75/月;33.75 × 12 = 405/年;405 ÷ 20,520 = 1.97%
只重试 1 次:5.0 × 35% = 1.75 挽回;流失 3.25 × 15 = 48.75/月
多 3 次重试多挽回 1.0 笔 × 15 = 15/月 = 180/年
成本 5.0 × 3 × 0.30 = 4.5/月 = 54/年;净 180 − 54 = 126/年
五、行动清单与复算验收
- 把最近三个月的失败扣款按软失败、硬失败分类,算出软失败占比
- 把重试日程写成配置:第 1、3、5、7 天共四次,硬失败立即停止
- 检查宽限期末日与最后一次重试是否同一天,不同就改成同一天
- 写四封邮件模板,分别在第 0、1、4、7 天发出,每封只带一个动作
- 在邮件和管理后台各放一个一键更新卡片链接,直达托管页
- 给 past_due 状态划出明确的只读范围,写清能看什么、不能做什么
- 用 5 笔失败分别乘 55% 与 35%,各算一遍年流失,确认差值
- 把重试金额锁定为失败那一期的金额,并为重试请求补上幂等键
验收标准:能在后台指出本月 5 笔失败各自停在哪一步、重试了几次、宽限还剩几天,并算出四次重试比一次多挽回的 1 笔值 15 美元/月。
使用边界与实操验收
边界一:重试收益不是增长。 126 美元/年是「少流失」而不是「多赚」,不能写进增长目标,也不能拿来证明催收流程值得扩建。
边界二:宽限期不是无限期。 7 天是重试窗口的上限;超过还留着全功能访问,等于给所有失败客户一个免费月,而且他们会把这件事告诉朋友。
边界三:邮件语气比频率重要。 四封里只要有一封像讨债,客户会直接点取消而不是换卡;催收的目标是让他完成一次支付方式更新,不是让他感到羞愧。
边界四:拒付与退款不属于这一篇。 拒付要交证据包、退款要同时平三条账,那是 06 篇的话题;把两者塞进催收流程,会让重试逻辑变成一团分支。
边界五:硬失败不要硬重试。 卡号注销的客户重试十次也不会成功,只会拉高整条通道的失败率;识别硬失败靠的是通道返回的拒付码,不是自己的猜测。
关键参考与核验范围
- Revenue recovery :说明失败扣款后的恢复流程包含重试与客户沟通,是这一篇流程划分的依据。
- Smart retries :说明按失败原因选择重试时点,与「硬失败不重试」的判断对应。
- Webhooks :说明扣款失败事件如何送达,以及验签与重投的基本要求。
- Test clocks :说明如何在不等待真实时间的情况下演练 7 天重试窗口。
- Subscriptions overview :说明订阅与账单对象的关系,用于确认 past_due 在生命周期里的位置。
以上链接于 2026-10-26 实测可公开访问;未核验版本、价格与配额。核验日期:2026-10-26。
系列导航
- 回到本组目录:一个人的计费系统:订阅状态机、按比例计费与失败重试
- 上一篇:按量计费与计量:一百二十九万六千条事件怎么变成一张账单
- 下一篇:退款、撤销与拒付:三张单据与一次少收 270 美元
- 相关:一个人的定价与付费墙
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。