




















适用架构:阿里云 RDS + ECS + Nginx;SaaS 平台库 + 每租户独立业务库(可分片)
目标:订阅计费 + 用量计费 + 账单生成 + 支付对账 + 到期冻结 + 授权快照,一套闭环
关键原则:可重算、可审计、幂等、安全、可扩展
Tenant(租户):购买升鲜宝 SaaS 服务的企业实体
Plan(套餐):订阅基础(包含模块、基础价、周期)
Component(计费组件):收费项(订阅/用量/一次性)
Meter(用量指标):用量计费的统计口径(门店数、订单数、API 次数等)
Invoice(账单):一个计费周期内的应收单
Entitlement Snapshot(授权快照):业务系统只读的最终模块+配额+到期信息
Proration(按比例补差):周期中途升级产生的补差价
租户订阅:tenant_subscription
套餐/价格:saas_plan / saas_price_component / saas_price_tier
用量快照:tenant_usage_daily(由采集器写入)
变更记录:subscription_change
支付结果:tenant_payment 回调
账单:tenant_invoice / tenant_invoice_item
审计:invoice_calculation_log
授权快照:tenant_entitlement_snapshot
状态变更:tenant.status、tenant_account_state_log
账单幂等键:invoice_biz_key = tenantId + ':' + periodStart + ':' + periodEnd + ':' + subscriptionId
账单号:INV-YYYYMM-<tenantId>-<seq>
支付单幂等键:pay_biz_key = invoiceId + ':' + channel
插入 invoice 前先查:同 invoice_biz_key 是否存在
存在则:
若状态=已支付 → 返回已生成
若状态=待支付/逾期 → 可“重算并覆盖 item”(需带版本号与审计记录)
每次计算必须写:invoice_calculation_log
输入快照:订阅信息、组件信息、用量数据、折扣、税率、proration
输出快照:明细、汇总金额、四舍五入规则
1 = 订阅(Subscription)
2 = 用量(Usage)
3 = 一次性(One-time)
1 = 固定价(Fixed)
2 = 阶梯价(Tiered)
3 = 按量单价(PerUnit)
业务系统每日写入平台库(或平台拉取):
tenant_usage_daily(tenant_id, meter_code, stat_date, used_value)
计费只读 usage 表,不要扫描业务大表(订单、流水会非常大)。
SHOP_COUNT:门店数量(当天快照)
USER_COUNT:用户数量(当天快照)
ORDER_COUNT:订单量(当日新增量)
API_CALL:网关计数(当日新增量)
STORAGE_GB:存储量(当日快照/最大值)
月付:periodStart ~ periodEnd(自然月或按订阅起始日滚动,推荐自然月)
年付:自然年或按起始日滚动(推荐按起始日滚动,避免跨年复杂)
账单生成时间:到期前 T-7天(可配置)
宽限期:到期后 grace_days(例如 7 天)
累计型(如 ORDER_COUNT):对 stat_date 在周期内求和
快照型(如 SHOP_COUNT):取周期内最大值(或最后一天值),推荐 最大值 防止规避
quantity = 1
unit_price = plan.base_price 或 component 固定价
amount = unit_price
不建议:订阅类保持固定价,阶梯放到用量
meter_policy 建议:
快照型:max(daily.used_value)
累计型:sum(daily.used_value)
amount = fixed_price
不依赖 usageValue(用于“包含多少量的包月”也可以配合阶梯做)
quantity = usageValue
amount = roundMoney(quantity * unit_price)
阶梯表:tier_start, tier_end, unit_price, fixed_price
两种阶梯模式(强烈建议选一种并固化):
分段累进(推荐,类似电费)
每段按照该段单价计算
整段匹配(不推荐)
落在哪一段,全部按该段单价计算(容易引争议)
分段累进伪代码:
订阅周期内发生:
升级(new_plan_price > old_plan_price)
或增购模块(等价升级)
按剩余天数补差(按日计)
生成 tenant_invoice_item:
component_code = PRORATION
amount = proration
meta_json 记录 old/new plan 与天数
降级一般下期生效,不退费(除非你要做更复杂的退款策略)。
折扣来源:
优惠券(一次性抵扣)
年付折扣(比例)
渠道代理折扣(比例/固定)
计算顺序建议:
subtotal(所有 item)
discount(可按 item 分类:订阅折扣、用量折扣)
tax(对折后金额计税)
total
折扣必须写入
invoice_calculation_log,并可复算。
如果你在美国也可能涉及税(州税),建议税费做成策略:
税率来源:tenant.billing_address / tax_region
tax_amount = roundMoney((subtotal - discount) * taxRate)
建议全平台统一:
金额:DECIMAL(18,2),四舍五入
单价/成本/用量单价:DECIMAL(18,6)
运算中间值用 BigDecimal,最后落库时 setScale(2, HALF_UP)
运行频率:每天 02:00(可配置)
筛选条件:
tenant_subscription.status=有效
end_time - now <= invoice_generate_days(例如 7 天)
当前周期账单不存在(幂等键)
输出:
invoice + items
通知(短信/邮件/站内信)
筛选:
auto_renew=1
invoice.status=待支付
到期前 X 天或到期当天
动作:
创建支付单(tenant_payment)
拉起扣款(Stripe/支付宝/微信或线下)
频率:每小时
规则:
now > subscription.end_time
且 invoice 未支付
且 now > grace_end_time
动作:
tenant.status = 欠费停用
tenant_entitlement_snapshot.status=冻结
业务系统 AOP 拦截(返回“租户已过期/欠费”)
频率:每日 01:00
将业务系统上报的用量落 tenant_usage_daily
或平台从业务库采集(不推荐扫大表)
按 pay_no 或第三方交易号做唯一
重复回调只更新一次
如支持对公转账分笔:
invoice.status = 部分支付
paid_amount 记录累计
paid_amount >= total → 已支付
module_json:模块开关
quota_json:配额上限
expire_time:订阅到期时间
status:有效/冻结/过期
当前订阅 plan 的 module 列表
tenant_module_override(运营后台强制开/关)
配额:plan 默认 quota + tenant_quota_override(可选)
到期:取 subscription.end_time(已支付后的)
支付成功
订阅升级/降级生效
运营调整模块或配额
到期冻结/解冻
账单生成失败:DB 临时不可用、锁等待超时、网络抖动
用量汇总失败:上报延迟、聚合任务异常
授权快照刷新失败:平台库写入失败
支付回调处理失败:验签失败/数据库异常/幂等冲突
自动续费扣款失败:支付渠道超时/失败
建议计费引擎所有异步动作统一走 billing_outbox_event(可选新增表):
事件写入平台库(与业务状态同事务)
定时 Job 扫描未成功事件进行重试
指数退避:1m / 5m / 15m / 1h / 6h
最大重试次数达到阈值 → 标记 DEAD 并告警
事件示例
INVOICE_CREATED
PAYMENT_CREATED
ENTITLEMENT_REFRESH
SUBSCRIPTION_EXTEND
TENANT_FREEZE
推荐两种方式:
方式 A:数据库唯一约束(强烈推荐)
tenant_invoice 增加唯一键:uk_invoice_biz_key(invoice_biz_key)
多线程并发插入时,只有一个成功,其它捕获重复键即可返回已存在账单
方式 B:分布式锁(可选)
Redis 锁 key:LOCK:INVOICE:{tenantId}:{periodStart}
超时 30s,防止死锁
仍建议保留数据库唯一约束做最终兜底
tenant_payment.pay_no 唯一键
payment_callback_log.pay_no 唯一键(可选)
回调处理逻辑:先落日志再处理,保证可追溯
用量口径调整
价格策略调整(tier 变更)
税率变更
折扣策略变更
Bug 修复后要能“回放”历史账单计算
策略 1:只对未支付账单重算
invoice.status in (待支付, 逾期) 才允许重算
重算会:删除旧 items → 写新 items → 写 calculation_log 新版本
策略 2:支付后不改账单,只做“差异单”
已支付账单不允许改动
差异通过下一期账单增加 ADJUSTMENT 明细体现(更符合财务审计)
在 tenant_invoice 增加:
invoice_biz_key(幂等键)
calc_version(计算版本号,如 2026.02)
recalc_count(重算次数)
locked(支付后锁定)
建议取 周期最大值:
避免用户在结算前临时删减规避计费
更贴近资源占用事实
建议取 周期内累计和:
如果要统计“独立活跃用户”,不要用简单 sum,应改为:
日去重集合 → 月去重(成本较高)
或用数据仓库/日志系统(后续扩展)
订阅类:BASE_SUBSCRIPTION
用量类:SHOP_COUNT / USER_COUNT / ORDER_COUNT / API_CALL / STORAGE_GB
调整类:PRORATION / ADJUSTMENT
税费类:TAX
折扣类:DISCOUNT
用于可追溯与解释账单:
tenant.status:正常 / 试用 / 冻结 / 欠费停用
subscription.status:有效 / 过期 / 取消 / 冻结
宽限期只读:允许查询、不允许新增单据/审核
宽限期限制写:只允许支付/续费相关操作
无宽限期:到期立刻停用(不推荐)
subscription.auto_renew=1
当前周期账单存在且 status=待支付
当前时间进入扣款窗口(到期前 1 天/到期当天)
第一次失败:5 分钟后重试
连续失败:改为每日一次
达到重试阈值:通知租户管理员 + 运营
月付:
newEnd = addMonths(end_time, 1)(按订阅规则:自然月或滚动月)
年付:
newEnd = addYears(end_time, 1)
注意:一定以“原 end_time”做加法,避免多次回调导致延长过多。
当套餐/组件/阶梯调整时,不要覆盖旧记录,建议:
新增 price_version 或直接用 created_at 做版本
订阅绑定“价格版本”(或在 calculation_log 里冻结一份价格快照)
每次生成账单时,把以下快照写入 invoice_calculation_log.input_snapshot_json:
plan/base_price
component 定义
tiers 列表
税率/折扣策略
这样未来重算时能选择:
用旧快照重算(对账一致)
用新策略重算(纠错)
tenant_subscription(end_time):到期扫描用
tenant_invoice(tenant_id, status, due_time):账单列表/逾期扫描
tenant_usage_daily(tenant_id, meter_code, stat_date):周期汇总
tenant_entitlement_snapshot(tenant_id):业务系统高频读
所有 Job 扫描必须分页(按主键/时间范围)
避免一次扫描全表导致平台库抖动
月付套餐:基础订阅价 199 元
用量:订单数 ORDER_COUNT 阶梯累进
0~1000:0.02 元/单
1001+:0.015 元/单
本周期订单数:5231 单
税率:0(示例)
BASE_SUBSCRIPTION:199
ORDER_COUNT:
1000 * 0.02 = 20
4231 * 0.015 = 63.465 → 四舍五入 63.47
合计 83.47
subtotal = 199 + 83.47 = 282.47
total = 282.47
业务库:无需关心计费表
每次请求:
读取 tenant_entitlement_snapshot(缓存)
做模块授权 AOP + 配额校验(门店上限/用户上限等)
用量采集:
每日上报平台:tenant_usage_daily
套餐/价格配置后台
订阅管理后台(续费、升级、冻结)
账单与支付后台
用量报表与对账
必做:
plan / component / tier
subscription
invoice + items
payment 回调幂等
entitlement snapshot
到期冻结 job
可延期:
用量计费(先只做订阅固定价)
proration(先升级下期生效)
税费/折扣/渠道分账
平台库建表(Tenant/Catalog/Subscription/Billing/Payment/Entitlement)
定义模块枚举 + 配额枚举(升鲜宝标准)
用量采集器(daily)上线
InvoiceGenerateJob(幂等 + 审计)上线
Payment 回调处理(幂等 + 对账)上线
EntitlementSnapshot 刷新机制上线
ExpireEnforcementJob 上线(宽限期策略)
后台页面(运营/租户)逐步补齐
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。