卡被拒了,钱就飞了?vibe 项目的订阅续费失败挽回(Dunning)实战
被动流失——用户没想走、但续费扣款失败——通常占订阅总流失的 20%~40%。本篇是订阅收入的第四格:按 decline_code 分诊失败原因、D+1/D+3/D+7/D+14 重试时间表、4 封挽回邮件话术模板、宽限期与降级策略、自助挽回页 8 要素、每周只看 4 个数的指标看板,以及可直接上线的 invoice.payment_failed webhook 代码骨架。

我的第一个 SaaS 收到第一笔"被动退款"通知时,我以为是用户取消了订阅。打开 Stripe 一看:payment failed,卡被拒了。用户没点取消、没发邮件抱怨、什么都没做——钱就是没收到。而且如果我不做点什么,这个用户下个月、下下个月都不会再付钱了。他甚至不知道自己"流失"了。
这就是被动流失(involuntary churn):用户没想走,是支付链路把他弄丢了。主动流失你要靠产品和运营去挽回,难;被动流失是纯工程问题——重试时间表、提醒邮件序列、自助换卡页面,三件套搭好,钱就自己回来了。这是独立开发者 ROI 最高的一类工作:一次投入,每月自动生效,不用你天天盯着。
先划清这篇在系列里的位置:本站支付主题已经发了三篇——《支付接入实战》教你把 Stripe 接进来收钱,《定价与 paywall》讲价格怎么定,《退款政策与 Chargeback》讲钱退回去之后的事。这篇是缺失的第四格:钱没收上来之后怎么办。四篇合起来,才是订阅收入的完整闭环。实现细节可参考 Stripe 官方的 Revenue Recovery 文档(docs.stripe.com/billing/revenue-recovery),公开概念如 Smart Retries 本篇会直接引用,不重复造轮子。
一、先算一笔账:被动流失是你看不见的漏水
先分清两个概念。主动流失是用户亲手点的取消:觉得贵、不用了、换竞品了。被动流失是用户什么都没做,但续费扣款失败了:卡过期、余额不足、银行拒付、3DS 没验证、被风控拦截。两者的挽回难度天差地别:主动流失你要说服一个人改变主意;被动流失你只需要修好一次支付——用户本来就是想付钱的。
被动流失在总流失里占多少?行业经验值通常是 20%~40%(B2C 小额订阅偏高,企业客户偏低;这是经验口径,不是精确统计,各家差异很大)。但一人团队往往根本看不到这个数——Stripe 后台默认把 payment failed 归进 churn,你不拆出来看,会永远以为流失都是"产品不行",然后去改产品,改错了方向。
先把基线量出来:去 Stripe 后台看过去 90 天 invoice.payment_failed 的发票数,除以同期总流失订阅数,这就是你的被动流失占比。量出来之后,再看下面这张"挽回率提升 1 个点"的测算表(假设月流失率 6%、被动流失占比 30%):
| MRR | 每月被动流失金额 | 挽回率 +1pp,每月多收回 | 挽回率 +1pp,年化 | 挽回率 +10pp,年化 |
|---|---|---|---|---|
| $5,000 | $90 | $0.9 | 约 $11 | 约 $108 |
| $20,000 | $360 | $3.6 | 约 $43 | 约 $432 |
| $100,000 | $1,800 | $18 | 约 $216 | 约 $2,160 |
1 个点看起来很小,但注意三点:第一,这是每月自动发生、零边际成本的收入,搭好之后你不用再碰;第二,真实世界里从"裸奔"(只靠支付商默认重试)到"完整 dunning",挽回率通常能提升 10~20 个点(经验值),不是 1 个点;第三,挽回回来的用户会继续续费,是复利。一个周末搭好的 dunning,一年下来可能是你时薪最高的工程投入——没有之一。
判断句:凡是"用户想付钱但没付成"的场景,都是工程问题,不是产品问题。先修工程,再谈产品。
二、失败原因分类决策表:先分诊,再开药
dunning 最常见的错误,是对所有失败一视同仁:统一重试 3 次、统一发一封邮件。但"卡过期"和"余额不足"是完全不同的病:前者重试 100 次也没用(卡已经死了),后者换个日子重试大概率能成。decline_code 就是 dunning 的分诊台——不读它就重试,等于急诊不分科室直接上手术台。
下面这张表覆盖 5 类最常见的失败原因。decline_code 从 PaymentIntent 的 last_payment_error.code 取(第八节代码里有取法)。可挽回性是我给的经验评级:
| 失败原因 | 典型 decline_code | 可挽回性 | 对应策略 | 值得自动重试吗 |
|---|---|---|---|---|
| 卡过期 | card_expired | ★★★ 高 | 重试无意义,卡已经死了。直接转人工触达:邮件/SMS 引导用户换卡;同时检查 Stripe 的卡号自动更新(发卡行支持的话,新卡号会自动同步过来,不用用户动手) | 否。确认一次即可,别浪费重试次数 |
| 余额不足 | insufficient_funds | ★★☆ 中高 | 核心策略是换时间重试:D+3、D+7 错开发薪周期;配合提醒邮件让用户确认余额 | 是。这是重试时间表主要服务的类别 |
| 银行通用拒付 | generic_decline、do_not_honor | ★★☆ 中 | 引导用户联系银行放行(附上"给银行客服的话术"),或直接换一张卡。邮件里把原因翻译成人话,别甩代码 | 是,但 2~3 次不成就转人工触达,别硬刷 |
| 3DS 验证失败 | authentication_required | ★★ 中 | 必须用户亲手完成验证:发 magic link,让用户点开完成 3DS 挑战。重试本身不解决问题,因为每次都要用户操作 | 否。发验证链接,而不是重试扣款 |
| 欺诈拦截 | fraudulent、风控拒绝 | ★ 低 | 不要自动重试!联系用户确认是否为本人操作,建议换支付方式或走人工处理。短时间内连刷会被风控拉黑,越刷越死 | 否。最多 1 次,且绝不在短时间内连续尝试 |
实操建议:webhook 里先按 decline_code 把失败分成三桶——可重试桶(余额不足、银行拒付)、要人桶(卡过期、3DS)、别碰桶(欺诈)。三桶走三套流程,这是整个 dunning 系统的分诊逻辑,后面所有策略都挂在这上面。
一个容易踩的坑:do_not_honor 看起来吓人,其实通常只是银行的默认拒绝,很多用户打个电话给银行就能解。邮件话术里要给用户这条出路,而不是只说"你的卡被拒了"——前者给用户行动,后者只给用户焦虑。
三、重试策略:时间表、智能重试与自研方案的取舍
重试是 dunning 里最便宜的一环:不需要用户做任何事,系统自己就能把一部分钱捡回来。但"什么时候重试"大有讲究——刚被拒就连刷 5 次,除了骚扰用户和触发风控,没有任何好处。
固定重试时间表:一人团队的默认答案
下面这张时间表是我给一人团队的默认推荐:5 次尝试、跨度 14 天、每次重试都配一个"不打扰或只打扰一次"的原则。
| 第几次 | 时机 | 为什么是这个时间 | 配合动作 |
|---|---|---|---|
| 1 | 失败后立即(webhook 到达后) | 偶发网络超时、银行侧抖动,立刻再试一次经常就过了 | 静默重试,不打扰用户 |
| 2 | D+1 | 余额不足隔天可能缓解(工资到账、转账完成) | 发第 1 封提醒邮件(见第四节) |
| 3 | D+3 | 错开发薪周期;给用户留出"看到邮件→处理"的时间 | 不额外打扰 |
| 4 | D+7 | 完整的周循环,覆盖"周末才看邮箱"的用户 | 发第 3 封邮件(最后通牒) |
| 5 | D+14 | 最后一次机会;再往后拖,欠费账期太长 | 发第 4 封邮件(暂停通知),之后按宽限期策略处理 |
注意三个细节:第一,欺诈类失败不适用这张表,只试 1 次(见第二节);第二,卡过期、3DS 这类"重试无意义"的失败,第 1 次确认后就直接转人工触达,别占重试次数;第三,D+14 之后还没收上来,就按第五节的宽限期策略走,不要无限重试——超过 3 周的欠费,挽回率会断崖式下跌(经验值),不如把精力花在新用户上。
智能重试 vs 固定重试:Stripe Smart Retries 与自研方案对比
Stripe 有个公开功能叫 Smart Retries,属于 Billing 的 Revenue Recovery 功能:按官方文档的说法,它用机器学习选择重试时机,而不是固定时间表。概念可以引用,但它的内部模型、具体时间点是黑盒,官方也没承诺具体数字——引用到此为止,不替它背书效果。
| 维度 | Stripe Smart Retries | 自研固定重试 |
|---|---|---|
| 接入成本 | 开关一开即用(Revenue Recovery 功能,具体是否包含在你的 Billing 套餐里以官方定价页为准) | 一个周末:webhook + 队列/定时任务 + 上面的时间表 |
| 效果 | 官方称优于固定时间表(以官方文档为准) | 可控、可解释,对中小体量完全够用 |
| 控制力 | 低:时机黑盒,不能按 decline_code 微调 | 高:分诊三桶、每一步自己定 |
| 适用阶段 | 已有稳定收入、想省事 | 刚起步、非 Stripe 支付商、想省订阅费 |
| 风险 | 与自研双跑会重复扣款 | 时间表写错会骚扰用户 |
我的判断很直接:一人团队先用固定重试跑起来,代码就一两百行(第八节有骨架),MRR 过一万美元再考虑开 Smart Retries。铁律只有一条:只用一套重试系统,Stripe 的和自研的二选一。双跑=同一张发票被扣两次=退款+Chargeback+用户信任崩盘,这是 dunning 里最贵的错误,没有之一。
四、挽回邮件/SMS 序列:4 封话术模板与发送时机
重试是系统自己努力,邮件序列是请用户搭把手。四封邮件的情绪曲线是精心设计的:提醒 → 紧迫 → 最后通牒 → 暂停通知,语气从"客服帮忙"逐渐收紧,到第 4 封再转回温和——别把回头客骂走。每封只有一个 CTA:更新支付方式。
文案三原则,写之前先背下来:① 每封只有一个行动按钮,别加"顺便看看新功能";② 把失败原因翻译成人话,用户看不懂 decline_code;③ 永远附免登录的 magic link,别让用户先登录再找入口——每多一步,转化率掉一截。
第 1 封:D+1,友好提醒
主题:你的 [产品名] 订阅续费遇到了一点小问题
正文:
Hi [名字],
你的 [产品名] 月度订阅在今天续费时没有扣款成功——通常是银行临时拦截或卡片额度问题,不是你的错。
点这里 30 秒更新支付方式,你的数据和设置都原样保留:[一键更新支付方式]
我们会在 3 天后自动再试一次。如果你已经换了卡,也可以点上面的链接手动立即重试。
有问题直接回复这封邮件,我会亲自处理。—— [你的名字]
要点:把责任推给"银行/系统",不指责用户;明确说"数据都保留",消除用户最大的恐惧;落款用真名,一人团队的真人感是优势。
第 2 封:D+3,紧迫 + 给出具体原因
主题:需要你处理一下:[产品名] 续费失败([原因人话版])
正文:
Hi [名字],
3 天前我们提醒过,续费还是没成功。具体原因是:[你的银行拒绝了这笔交易(do_not_honor)——通常打银行客服电话说明是本人消费即可解除,电话一般 5 分钟搞定]。
自动重试还剩 2 次([日期1]、[日期2])。最稳妥的办法是现在花 1 分钟换一张卡:[一键更新支付方式]
如果这张卡你不想用了,直接换一张就行,订阅会自动延续,不用重新购买。
要点:方括号里的"原因人话版"是关键——把第二节分诊表里的原因翻译成用户能行动的句子。给用户两条路(联系银行 / 换卡),而不是只说"失败了"。
第 3 封:D+7,最后通牒
主题:最后 3 天:你的 [产品名] 订阅将于 [日期] 暂停
正文:
Hi [名字],
你的订阅已经欠费 7 天。如果在 [日期] 之前没有更新支付方式,订阅将自动暂停:[高级功能/用量] 会被限制,但你的所有数据会完整保留 [30] 天。
现在恢复,一切如常:[立即恢复订阅]
如果你是故意想取消,直接回复"取消"就行,我帮你处理好,不会再打扰。—— [你的名字]
要点:明确截止日期、明确暂停后会发生什么(消除未知恐惧)、给"故意取消"一个体面的出口——纠缠想走的用户只会换来差评和 Chargeback。这封可以配一条 SMS(仅限用户明确 opt-in 的情况),160 字符内:[产品名] 提醒:你的订阅因续费失败将于 3 天后暂停,点此 1 分钟恢复:[短链接] 回复 TD 退订。
第 4 封:D+14,暂停通知(语气转回温和)
主题:你的 [产品名] 订阅已暂停,数据为你保留 [30] 天
正文:
Hi [名字],
你的订阅在今天正式暂停了。别担心:你的所有数据完整保留到 [日期],期间随时回来,点这里一键重新激活:[重新激活订阅]
如果使用中遇到任何问题,或者对价格有想法,直接回复这封邮件告诉我。—— [你的名字]
要点:不指责、不卖惨、不"求求你回来"。暂停不是终点——相当一部分用户会在未来某个月自己回来,留一扇干净的门比什么都重要。
最后一句大实话:邮件序列的效果天花板,取决于第二节的分诊。给"卡过期"的用户发 4 封"我们会再试一次"是废话——卡死了,重试多少次都没用。对要人桶的用户,第 1 封就应该主推"换卡",而不是"等我们重试"。
五、宽限期与降级策略:欠费期间用户还能用什么
续费失败后到彻底暂停之间,隔着一个宽限期(grace period)。宽限期多长、期间给用户什么权限,是 dunning 里最需要"拍板"的决策——它直接决定你是"多挽回一点钱"还是"多养几个白嫖怪"。
宽限期天数选择
| 宽限期 | 适用场景 | 代价 |
|---|---|---|
| 0 天(立即停) | 欺诈、恶意欠费嫌疑 | 误伤正常用户,差评预定。只留给别碰桶 |
| 7 天 | 低客单价(<$10/月)工具类 | 挽回窗口短,D+7 的第 4 次重试刚好卡在边界上 |
| 14 天 | 默认推荐:$10~100/月的主流区间 | 和上面的重试时间表完美咬合(D+14 最后一次) |
| 21~30 天 | 高客单价(>$100/月)、企业客户 | 坏账风险上升,客服跟进成本高,适合人工介入 |
为什么默认推荐 14 天?两个原因:第一,它和第三节的重试时间表天然咬合——D+14 最后一次重试失败,宽限期结束,流程干净;第二,14 天覆盖了"用户出差/没看邮箱/工资没到"几乎所有正常延迟场景,再往后拖,挽回率会断崖式下跌(经验值),不如把精力花在新用户上。
欠费期间:功能降级 vs 直接停服,取舍矩阵
| 策略 | 用户体验 | 收入保护 | 技术成本 | 适用场景 |
|---|---|---|---|---|
| 全功能宽限 | 最好,用户无感知 | 弱:白嫖窗口 | 最低:几乎不用改代码 | 高客单价 + 人工跟进,赌的是一单回本 |
| 功能降级(限额度、关高级功能) | 中:能用,但不爽 | 中 | 中:需要一套"欠费态"权限 | 默认推荐:大多数 SaaS |
| 只读模式 | 中差:能看不能改 | 强:核心价值被锁住 | 中:读写开关 | 数据型产品(笔记、项目管理):数据在,你就跑不掉 |
| 直接停服 | 最差 | 最强 | 最低 | 欺诈、恶意欠费。正常用户用这招等于赶客 |
我的默认答案:14 天宽限 + 功能降级(数据完整保留、限制用量或关闭高级功能),到期未付转只读 30 天,之后再谈删除。直接停服只留给欺诈桶。记住一个原则:欠费期间的目标是"让用户不爽到愿意付钱",而不是"让用户绝望到直接走人"——前者是降级,后者是停服,一字之差,挽回率差一倍。
技术实现上,"欠费态"就是订阅状态机里的一个中间状态(past_due),权限中间件里加一条规则即可。别为降级单独建一套权限系统——在现有 plan 检查里加一个 if (subscription.status === 'past_due') return degradedLimits 就够了,一人团队不需要更复杂的。
六、自助挽回页 8 要素:让用户 1 分钟内自己搞定
邮件序列的每一封 CTA,最終都指向同一个页面:自助挽回页。这个页面的转化率直接决定整个 dunning 的天花板——用户点开邮件已经是成功了一半,页面再让他登录、再让他找入口,另一半就没了。8 个要素,缺一不可:
- 免登录 magic link:邮件里的链接带一次性 token,点开即验证身份。要求欠费用户先登录是反人类——他可能忘了密码,而"找回密码"这一步会杀死 30% 以上的挽回(经验值)。
- 失败原因人话展示:页面顶部直接说"你的银行拒绝了这笔交易(do_not_honor),通常打银行客服电话说明是本人消费即可解除"。用户知道"为什么",才知道"怎么办"。
- 显示当前卡尾号与过期日:让用户一眼确认"哦,原来是这张旧卡"——很多卡过期失败,用户自己都没意识到卡换了。
- 一键"立即重试"按钮:换卡之前,先给一次即时重试的机会。余额不足的用户可能钱已经到账了,点一下就好,不用换卡。
- 换卡流程 3 步内完成:用 Stripe 的 Payment Element + SetupIntent,用户输入新卡 → 验证 → 完成。别让用户先删旧卡再加新卡,一步完成"替换"。
- 欠费金额 + 下次自动重试时间:透明展示"欠 $29,下次自动重试在 3 天后"。透明减少客服咨询,也给用户一个"不等了,现在就处理"的理由。
- 客服兜底入口:页面底部放"搞不定?直接回复邮件 / 联系客服"。总有 5% 的用户是页面解决不了的,别让他们卡死。
- 移动端可用:超过一半的用户是在手机上点开挽回邮件的。页面在手机上按钮点不到、输入框错位,等于把一半的挽回率扔进垃圾桶。
一句话检验标准:把链接发给一个不懂技术的朋友,看他能不能在 1 分钟内完成换卡。不能,这个页面就不合格。
七、指标看板:每周只看 4 个数
dunning 上线后,别天天盯着——每周花 10 分钟看 4 个数就够了。看多了是焦虑,看少了是失明。
| # | 指标 | 定义 | 健康基准(经验值) | 异常信号 → 查什么 |
|---|---|---|---|---|
| 1 | Recovery rate(挽回率) | 本期挽回成功的订阅数 ÷ 进入 dunning 的订阅数 | 通常 20%~40% | 连续两周 <20% → 查邮件序列(是不是进垃圾箱了)、查重试时间表 |
| 2 | 重试成功率 | 重试成功次数 ÷ 重试总次数 | 通常 10%~25% | 过低 → 时间表太激进,或欺诈类失败没过滤掉、白白消耗次数 |
| 3 | 邮件点击→换卡转化率 | 点开挽回邮件后 7 天内更新支付方式的用户占比 | 通常 5%~15% | 过低 → 查自助挽回页(第 8 要素缺了哪条)、查 magic link 是否有效 |
| 4 | 进入 dunning 的订阅数(绝对值) | 每周新进入宽限期的订阅数 | 随 MRR 增长缓慢爬升 | 突然翻倍 → 先查 Stripe status 和银行侧异常,而不是先怀疑自己的代码 |
两个刻意的设计:第一,指标 3 看的是点击不是打开——Apple 的邮件隐私保护(MPP)让打开率全面注水,只看点击才是真信号;第二,指标 4 是绝对值不是比率——比率会掩盖"支付商侧出问题"这种系统性异常,绝对值 spikes 一眼就能看出来。
这 4 个数从哪来?Stripe 后台的 Revenue Recovery 报表能直接给出一部分,剩下的从你自己的 dunning_attempts 表和邮件服务商(Resend/Postmark)的事件 webhook 里算。一人团队别搞 BI 看板——每周一早上,一个 SQL 查 4 个数,记在表格里,趋势比绝对值重要。
八、上线检查单:webhook 代码骨架 + 幂等与防重发
前面七节是策略,这一节是落地。整个 dunning 系统的入口只有一个:invoice.payment_failed webhook。下面是 Node + Express + stripe-node 的处理骨架,可直接改着用。
// POST /webhooks/stripe —— 注意:必须用 raw body,否则签名校验失败
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
app.post('/webhooks/stripe',
express.raw({ type: 'application/json' }),
async (req, res) => {
let event;
try {
event = stripe.webhooks.constructEvent(
req.body,
req.headers['stripe-signature'],
process.env.STRIPE_WEBHOOK_SECRET
);
} catch {
return res.status(400).send('bad signature');
}
// 1. 幂等:event.id 见过就直接 200(processed_stripe_events 表,event_id 唯一键)
const seen = await db.processedStripeEvents.findUnique({
where: { eventId: event.id },
});
if (seen) return res.sendStatus(200);
try {
if (event.type === 'invoice.payment_failed') {
await handlePaymentFailed(event.data.object);
} else if (event.type === 'invoice.payment_succeeded') {
await handlePaymentSucceeded(event.data.object);
}
// 2. 只有业务处理成功后才记幂等;失败抛 500,让 Stripe 重发
await db.processedStripeEvents.create({
data: { eventId: event.id, type: event.type },
});
} catch (err) {
console.error('dunning webhook failed', event.id, err);
return res.sendStatus(500);
}
res.sendStatus(200);
}
);
async function handlePaymentFailed(invoice) {
// 3. 只处理订阅续费发票:首单、手动发票走别的流程
if (invoice.billing_reason !== 'subscription_cycle') return;
// 4. 分诊:从 PaymentIntent 取 decline_code,归入三桶
const pi = await stripe.paymentIntents.retrieve(invoice.payment_intent);
const declineCode = pi.last_payment_error?.code ?? 'unknown';
const bucket = triage(declineCode);
// 可重试桶 -> 安排下次重试;要人桶 -> 发换卡/验证邮件;别碰桶 -> 只记录+人工介入
// retryable: ['insufficient_funds','generic_decline','do_not_honor',...]
// need_human: ['card_expired','authentication_required',...]
// do_not_touch: ['fraudulent', ...]
// 5. 写 dunning 记录,算出下次重试时间(第三节时间表)
const attempt = await db.dunningAttempts.create({
data: {
subscriptionId: invoice.subscription,
invoiceId: invoice.id,
declineCode,
bucket,
attemptNumber: /* 该订阅本次 dunning 第几次 */,
nextRetryAt: /* 按时间表计算 */,
},
});
// 6. 邮件任务入队,幂等 key = invoice.id + attemptNumber,防重发
await mailQueue.add('dunning-email', {
invoiceId: invoice.id,
attemptNumber: attempt.attemptNumber,
magicLinkToken: /* 一次性 token */,
}, { jobId: `dunning-${invoice.id}-${attempt.attemptNumber}` });
}
async function handlePaymentSucceeded(invoice) {
// 7. 扣款成功:清掉 dunning 状态,恢复全功能
await db.subscriptions.update({
where: { stripeSubscriptionId: invoice.subscription },
data: { status: 'active', pastDueSince: null },
});
await db.dunningAttempts.updateMany({
where: { subscriptionId: invoice.subscription, recoveredAt: null },
data: { recoveredAt: new Date() },
});
// 8. 发一封"已恢复"确认邮件(同样用幂等 jobId,别重复发)
}
上线前逐项打勾的检查单:
- 签名校验用 raw body:用了
express.json()再验签会永远失败,这是 webhook 第一大坑。 - event.id 唯一键幂等:Stripe 会重发 webhook(超时、5xx 都会触发),没有幂等=同一用户收到 4 封"最后通牒"。
- 成功和失败两个事件都要处理:只处理
payment_failed不处理payment_succeeded,dunning 状态永远清不掉,用户付了钱还被降级。 - staging 不发真邮件:用 Stripe 的 test clock 或
stripe trigger invoice.payment_failed造失败事件,邮件发到自己的测试邮箱。 - 重试走队列异步执行,不在 webhook 里同步调 Stripe API:webhook 必须 200 得快,慢了 Stripe 会判定超时重发,重发又触发重试——循环灾难。
- 换卡成功后立即手动重试一次:别等 D+3,用户刚换完卡正是意愿最强的时候,
stripe.invoices.pay(invoiceId)立刻收。 - Smart Retries 和自研二选一:开了 Stripe 的自动恢复,就别再跑自己的重试队列;反之亦然。
- 监控 webhook 的 5xx 率和处理延迟:dunning 入口挂了,等于整个挽回系统静默下线——给它单独配一个告警。
- 每月看一次 decline_code 分布:如果
fraudulent占比突然上升,可能是被羊毛党盯上了,dunning 救不了,要上风控。
结语:今天就能做的 5 件事
dunning 不需要一次做到完美,它是个可以 incremental 上线的系统。按这个顺序做,每一步都有独立收益:
- 去 Stripe 后台拉过去 90 天的
payment_failed,算出你的被动流失占比和金额——先知道漏水有多大。 - 把
invoice.payment_failedwebhook 接上,只做一件事:按 decline_code 分诊记日志。三桶分清楚,策略才有地基。 - 写死第三节的固定重试时间表(D+1/D+3/D+7/D+14),用队列跑起来——这是投入产出比最高的一步。
- 写 4 封邮件,配好 magic link 的自助挽回页。文案直接用第四节的模板改。
- 定下 14 天宽限 + 功能降级的默认策略,写进代码的订阅状态机。然后每周一看那 4 个数。
最后一句:被动流失的用户,是全世界最容易挽回的用户——他们本来就想付钱。你要做的,只是别让一次扣款失败,变成一次永久流失。
评论 (0)
相关文章

vibe coding 让做出产品变容易、让人知道更难,社区和开源是少数以时间换流量的公平赛道。这篇获客战术执行手册覆盖:社区为什么是获客杠杆、Discord/微信群/GitHub Discussions 阵地选择、冷启动 0→20 的 5 个种子用户来源、0→100 的每日 15 分钟运营 SOP、低成本参与感设计、README 即落地页的写作公式、GitHub trending 机制与发布时间窗口、issue 驱动营销、build in public 节奏模板、社区危机处理、5 个健康度指标,以及从社区到收入的转化路径设计。

邮件列表是独立开发者唯一真正拥有的流量资产。本指南带你从 0 做到 1000 个订阅者:三处收集入口与 5 种高转化 lead magnet 形态、双重确认的三方权衡、Day0 到 Day14 的 5 封欢迎序列完整剧本(含主题行与骨架)、一人可维持的双周报模型、Resend/Loops/ConvertKit/Buttondown 选型对比、SPF/DKIM/DMARC 三件套与 4 周预热计划、关键指标健康基准与最小 A/B 实验设计、三条必备自动化工作流,以及从 1000 到 10000 的三个杠杆。

敢收费不敢退费的心态是错的:清晰的退款政策是最便宜的信任广告,处理得当的退款用户有 30% 会回来,而一次处理失当的 Chargeback 可能吃掉一整月利润。本指南覆盖三档退款政策设计与话术模板、Stripe 手动与 API 退款实战、charge.refunded 的 webhook 自动化闭环、挽留还是秒退决策树、争议证据包 6 类材料、Stripe Radar 防欺诈规则,以及 Paddle/Lemon Squeezy 代扣代缴的跨境税务细节。