posts

一个人的计费系统:订阅状态机、按比例计费与失败重试(10 篇)

一个人收钱,最容易错的不是定价,而是订阅状态、按比例计费与失败重试。本组从 120 个订阅、1710 美元与 105 笔账单出发:五个状态、九条迁移、五个时点、按比例公式、按量计量、催收、退款拒付、发票税务、对账漏损、自建决策,最后用十二项检查验收。

定价决定收多少,计费决定你到底有没有收到。

这个系列解决什么问题

一个人做订阅产品,收费这件事通常在第 30 天就上线了,然后在第 300 天开始对不上账。问题不在支付通道,而在通道和产品之间那一层:订阅有几个状态、状态之间怎么迁移、升级按什么公式补差、扣款失败重试几次、退款要不要开贷记单、发票号能不能跳号、月底怎么证明账是平的。

这个系列把这一层拆成十篇:先把订阅写成一条状态机,再把试用、续费、暂停、取消的五个时点定死;然后处理三种金额变更(升降级、按量、退款),三种不顺利(失败扣款、拒付、漏单),以及两张必须连续的单据(发票与贷记单);最后回答「这些零件该不该自己造」与「怎么验收」。

它服务的是一个每月只有几个小时做账的人。 没有财务系统、没有对账专员、没有第二位能看懂账本的人;你能投入的是每月四十五分钟对账,和一套能重放的检查。

核心主张:五条口径

10 篇速览

#文章核心内容阶段
01计费是一条状态机:五个状态、九条迁移与一次多收 420 美元五个状态、九条合法迁移、幂等键模型
02订阅生命周期:试用、续费、暂停、取消与五个时点五个时点、试用转化、取消生效时点时点
03升降级与按比例计费:九笔变更、十七天与 4.53 美元一个公式、三处易错、升级与降级的非对称变更
04按量计费与计量:一百二十九万六千条事件怎么变成一张账单四步聚合、阶梯价、去重与迟到事件计量
05失败扣款与催收:五次尝试、五十五个百分点与七天宽限重试日程、回收率、宽限期与邮件催收
06退款、撤销与拒付:三张单据与一次少收 270 美元部分退款公式、贷记单、拒付证据退出
07发票、收据与税务:一百零八张单据与七个必填字段号段连续、七个字段、含税与不含税单据
08对账与收入漏损:一百零五行结算单与 0.4% 的差异三张对账表、补拉兜底、漏损账对账
09买还是自己写:七个判据与 4,200 行计费代码的账七个判据、两条路线成本、可外包比例决策
10计费验收:十二项检查、四维评分与一次季度演练十二项检查、四维加权、3.78 与沙箱验收

三条阅读路径

路径 A:还没开始收费。 先读 01 把状态机画出来,再读 02 把五个时点写死,最后读 09 判断哪些零件该买。这条路径的重点是「第一笔钱进来之前,别把账做死」。

路径 B:有订阅,但每月对不上账。 先读 05 看失败扣款是不是被当成异常漏掉了,再读 06 把退款与贷记单补上,用 08 的三张对账表把差异找出来。这条路径的重点是「先承认有漏损,再定位在哪一段」。

路径 C:要做按量计费或出海收税。 先读 04 把计量到出账的四步定下来,再读 07 把发票字段与含税口径分开,最后用 10 做一次季度演练。这条路径的重点是「计量与单据都不能事后补」。

与其它专题的边界

站内已有定价、财务、合规与多租户内容,但都不实现「价格怎么变成账单」。下表说明本组不重复什么。

既有内容它讲什么本组边界
一个人把价格定对:定价、付费墙与涨价成本下限、模型、套餐、试用形态与涨价它决定收多少;本组把已定的价格变成状态机、账单与单据
一个人把用户留住:激活、留存与复购续费率、升级动机、流失分诊与召回它看行为与留存;本组实现续费日、按比例计费与失败重试
一个人的财务:现金流、runway 与月度结账四张表、收入确认、成本清点与月结它按确认口径记账;本组产生它要对的账单与贷记单
一个人的公司:主体、税务与合规主体、税种、申报、合同与知识产权它判断该缴什么税;本组只把税率与发票字段做成参数
一个人的多租户:组织、角色与数据隔离组织、席位、权限与数据隔离它保证席位数量对;本组保证金额与状态对
一个人把 API 做成产品:对外接口与开发者体验配额、限流、Webhook 与版本弃用它管调用许可;本组把调用量聚合成账单行
一个人的架构:技术选型、模块边界与演进模块边界、幂等与补偿、技术债它给通用幂等模式;本组给计费专属的迁移守卫与重放
一个人的运维:可用性、告警、事故与值班容量水位、告警降噪与事故复盘它管系统活着;本组管每一笔交易的钱对不对
独立开发者的资产韧性收款账号、云账号与恢复演练它保证账号能接管;本组保证账单能重放
一个人的消息系统:事务邮件与送达率事务邮件基线、分层与退订它保证催收邮件送达;本组决定什么时候发第一封
一个人的前 100 天:从想法到第一批付费用户验证、手工交付、上线与首次收费它处理第一笔钱;本组处理第二笔到第一万笔
一个人的隐私工程:数据地图、最小化与删除权(10 篇)数据地图、最小化、合法性依据与同意、政策一致性、数据主体请求、删除、保留期、跨境与供应商、泄露响应、十二项验收那组讲账单里的个人数据与 7 年法定保留;本组讲订阅状态、按比例计费与对账
一个人的数据搬家:导入、去重、迁移与退出(10 篇)迁移漏斗、导入契约、字段映射、去重合并、幂等重跑、人工代导入、窗口与回滚、附件限额、导出退出、十项验收那组讲导入批次与计量口径;本组讲订阅状态、按比例计费与对账
一个人的质量守门:验证预算、回归清单与发布门禁(10 篇)47 缺陷与三条红线、12 条关键路径、三层配比、38→26 回归、六项门禁、四级缺陷、测试数据、不可逆保护、测试债、十项验收那组讲发布门禁与关键路径验证;本组讲订阅状态、按比例计费与对账
一个人的安全基线:十四个入口、十七个密钥与一次 72 小时响应14 个攻击面、认证与枚举、会话撤销、17 个密钥三档、四条边界、41 个依赖与 186 条告警、四类限流、9 类审计日志、72 小时三段响应、20 项验收那组把支付账号列为不可逆入口并给保护;本组讲订阅状态、按比例计费与对账
一个人的性能与账单:4.2 秒的首屏、一千二百次查询与每月 210 美元4 项预算与 20 格、RUM 与慢查询、412→168 KB、1,200→3 次查询、0→86% 缓存、34 秒导出、7→4 个第三方、210→118 美元、3 次回归告警、10 项验收那组讲账单里的基础设施成本;本组讲订阅状态、按比例计费与对账
一个人的企业客户:九家询价、一百四十八道问卷与 134 天的第一单9 家询价与 130 h、96 天采购、148 题问卷、7 个子处理者、SLA 43.2/216 分钟、26 h SSO、4 周试点、38 天到账、8 h 实施、10 项验收那组讲企业 PO、账期与发票流程;本组讲订阅状态、按比例计费与对账
一个人的开源产品:4,200 个 star、一百八十个付费与一年 612 小时的维护4,200 star 与 1,949 美元、612 h 维护、214 依赖、48,600 行 78/22、1,180 个 issue、214 条告警、23 个赞助者、9 家双许可、17 位贡献者、10 项验收那组讲开源项目的收入结构;本组讲订阅状态、按比例计费与对账

这十篇不承诺「按这套做就不会算错」,也不推荐任何支付通道。可复算的部分全部用四则运算给出算式;不可复算的部分(通道费率、税务辖区、供应商能力)明确指向官方文档。所有数字都是教学假设,代入前请换成自己的订阅数与账单笔数。

本组统一口径

全组共用一组教学假设,各篇引用时不换分母:

产品:一人 SaaS,与既有组同一产品;账号 344,付费订阅 120
套餐:个人档 12 美元/月 × 100;团队档 30 美元/月 × 20 → 标价 MRR 1,800
计费周期:月付 84(个人 70 + 团队 14);年付 36(个人 30 + 团队 6)
年付价 = 10 个月价:个人 120/年、团队 300/年 → 年付现金 5,400/年
年付按月直线确认 450/月;月付确认 1,260/月 → 确认收入 1,710/月
标价差 1,800 − 1,710 = 90/月(年付两个月免费);年确认收入 20,520
订阅状态:active 113 + past_due 7 = 120 付费;trialing 18;paused 2
本月取消 4 → 3.33%
账单:月付续费 84 + 年付续费 3 + 新购 6 + 升降级 9 + 退款 3 = 105 笔/月
webhook:420 事件/月;丢失/重投 1.5% = 6.3;疑似重复 0.9% = 3.8
按比例计费:升级 6、降级 3;价差 8、剩余 17 天 / 30 天 = 4.53/笔
失败扣款:84 × 6% = 5.0 笔/月;重试 4 次回收 55%
退款 36/月 = 432/年;拒付 3.8 笔/年;单据 108 张/月 = 1,296/年
钱相关表 7 张 / 全库 34 张;billing 模块 4,200 行 / 全库 62,400 行 = 6.7%
计费事故(14 个月 3 次):多收 420 + 少收 270 + 重复退款 120 = 810
按量计费:12 客户 × 108,000 = 1,296,000 条/月;5 分钟桶 8,640 个
机会成本 40 美元/小时;供应商抽成 0.5% = 102.6/年
对账:结算单 105 行/月;差异 0.4% = 0.42 行;45 分钟/月

1,710 是不含税确认收入,1,800 是标价 MRR。 凡写「收入」的复算一律用 1,710;年付两个月免费造成的 90/月差额不是折扣费用,而是收入口径差。

金额只在最小单位里算。 数据库存分、公式用整数、展示时除 100;这条口径决定了 4.53 这类数字在系统里其实是 453。

105 笔是「账单对象」的数量,不是支付次数。 一次重试会产生新的支付尝试,但不产生新账单;两者混算会让失败率虚高。

所有成本都是教学假设。 40 美元/小时、6% 失败率、55% 回收率只用来展示数量级;真实估算请用自己最近三个月的结算单。

使用说明

先画状态机,再写任何计费代码。 五个状态与九条迁移画不出来,后面的试用、暂停与退款都会变成 if-else。

把「失败」写进主流程。 重试日程、宽限期与降级策略要在第一版就有,不能等第一次扣款失败再补。

每月留四十五分钟对账。 不是月底补账,而是固定一天把结算单与内部表逐行比;差异超过 1 行就开始查。

发票字段先于发票模板。 七个字段定不下来,模板换十次也不合规。

本组的证据与使用方法

证据类型。 本组用的是「订阅台账 + 账单流水 + 结算单 + 贷记单 + 事故记录」:五个状态的实际分布、每月 105 笔账单、420 个 webhook、三次计费事故的金额与根因、14 个月的差错率。没有「最佳实践」式的断言。

如何使用。 先用 01 的九条迁移对照自己的状态字段,再用 03 的公式复算最近三笔升降级,用 05 的重试日程检查失败扣款是否被当成异常,最后用 10 的十二项检查做一次季度体检。

如何复核。 取最近一个月,把结算单的每一行与内部 payments 表对上;再取最近十笔退款,看是否都开了贷记单。两项都能对上,这套基线才适用。

系列导航

全部文章 game Indie golang 客户端开发 Minigame Saas rust Python Java cg