Skip to content

想象一个场景:月底对账,财务拿着报表来问——这个客户 8 月账单怎么算出来的?他 8 月 10 号从基础版升到专业版,补了差价;15 号又加了 5 个席位;隔壁那个按用量计费的客户调了 120 万次 API……三笔钱三套算法,对不上账。

这就是 SaaS 计费的全部难度所在:计费不难,“变化中的计费”才难。客户随时升级、降级、加减席位、退订,每一笔变更都要折算成公平的金额。这篇把套餐、订阅、计费模式与升降级结算一次讲清。

一、三种计费模式

SaaS 计费:三种模式与升降级结算

模式公式收入可预测性适合
包周期固定价 × 月/年最高(订阅制核心)功能型 SaaS 基础定价
按席位单价 × 席位数高(随客户扩张增长)协作类产品
按用量单价 × 消耗量低(客户不可控)API / 存储 / 消息类

实际产品里常见混合:基础月费 + 超出配额按量——“每月 1 万次 API 调用内免费,超出每次 0.01 元”。混合模式兼顾了可预测性(保底月费)和公平性(用得多付得多),但也把两种模式的复杂度都集齐了。

二、订阅模型:四个实体

计费系统的骨架是四个实体,先把关系立住:

Plan(套餐)        基础版 300/月、专业版 900/月……定义"卖什么"

Subscription(订阅)某租户订了专业版,2026-08-01 ~ 2026-08-31……定义"谁买了、买到什么时候"

UsageRecord(用量) 该租户本月 API 调用 120 万次……按用量模式才有

Order / Invoice(订单/账单)本期应收 900 元……定义"收多少钱"

要点三条:

  1. 套餐定义与订阅记录分离。套餐价格调整时,已生效的订阅按签约时的价格走,新订阅按新价——所以订阅上要快照下单时的价格,不能只存 plan_id 去实时查价。
  2. 账期对齐方式一开始就定死:自然月(1 号出账,统一好对账)还是开通日对齐(客户记忆友好)。两种都行,混着用必乱。
  3. 状态机:订阅自身也有状态(生效 / 到期 / 续费宽限 / 取消),和《租户生命周期管理》的租户状态联动但不是一回事——订阅到期未续费,触发的是租户冻结,这是两个系统的一次握手。

三、升降级怎么算钱

核心工具是按比例折算(proration):一个周期内的价值按天线性分配。

升级:立即生效 + 补差价。 客户 8 月 1 日订了 300 元月费的基础版,8 月 10 日要升级 900 元的专业版:

旧套餐剩余价值 = 300 × (30 − 10) / 30 = 200 元
新套餐剩余价值 = 900 × (30 − 10) / 30 = 600 元
补差价         = 600 − 200            = 400 元

客户当场补 400,立刻用上专业版——这是客户预期里的“公平”。

月中加席位同理。 每席每月 50 元,月初 10 席,用了 15 天后加到 15 席(新增部分用 15 天):

原有 10 席整月  = 10 × 50            = 500 元
新增 5 席 15 天 = 5 × 50 × 15/30     = 125 元
本月账单        = 500 + 125          = 625 元

降级:周期生效,当期不退钱。 8 月 20 日从专业版降回基础版:本月继续享受专业版到月底,9 月 1 日起按 300 出账。

为什么升级立即、降级下期?用户预期不对称:升级的人想马上用新功能,降级的人不介意月底再省;而且当期退款要走财务流程,成本高、周期长。Stripe 等成熟计费系统的默认行为也是如此。

这些数字不是拍脑袋,验证程序实跑结果(JDK 21):补差价 400.00、席位账单 625.00、降级下期生效,10 项断言全 PASS——proration 公式看着简单,实现时一个舍入错误就会在几万账单上放大成对账灾难。

四、按用量计费:先记账后出账

按用量模式的链路是“计量 → 聚合 → 出账”三段:

  1. 计量:业务链路上发计量事件(tenant_id, feature, quantity, timestamp),异步写入。量大的话走消息队列削峰,绝不能让计量阻塞业务请求。
  2. 聚合:按账期聚合每个租户的用量,快照落库——出账依据必须可追溯,不能出账时再去原始明细里现算。
  3. 出账:账期结束后按聚合结果生成账单。

两个坑提前知道:封顶与配额——用量上不封顶会引来“恶意刷量”投诉,配额用超要不要硬停(影响生产客户)是产品决策,工程上要支持“软配额告警 + 硬配额拦截”两档;对账——计量事件会丢会重,月度账单要和业务侧日志抽样核对,重不丢靠幂等。

五、常见误区

① 金额用 double。 300 × (30-10) / 30 这种浮点运算积累误差,账单金额一律 BigDecimal(更稳的是以“分”为单位的整数),舍入规则全局统一(如四舍五入到分)。

② 订阅不存价格快照。 套餐涨价后,老客户的账单跟着涨——这是违约级事故。价格跟着订阅走,套餐表只影响新订阅。

③ 升降级边界日期含糊。 “第 10 天升级”是第 10 天结束算还是当天就算?剩余 20 天还是 19 天?规则必须写死在代码里并全公司统一,两个算都能接受,漂着不行。

④ 退款和降级混为一谈。 降级是“下期少收”(周期生效),退款是“把已收的钱退回去”(财务流程 + 功能回收)。产品上把它们设计成两个动作,别共用一个接口。

系列导航