重写计费引擎:把「实收利息 × 回收进度」翻译成系统语言
系列说明:这是「计费地基换了」线的第二篇。上一篇《早偿、逾期、减免、核销:实收利息从来不是一个静态字段》讲的是哪些场景会把账算错;这一篇讲的是系统要怎么改才不会算错——从模型对照,到四段式重构,到五模块的施工顺序。两篇可各自独立阅读。
摘要
1.结论:金规〔2025〕9 号那句话在架构上真正的动作只有一个——把计费从「一个点」改成「一条线」。点可以靠一个字段承载,线只能靠事件流承载;这不是加个报表能糊过去的。
2.量级:一笔 12 期贷款,旧模型只需 1 个计费事件(放款),新模型至少 13 个(放款 + 12 期回单),把每期计息确认也建成事件就是 25 个,叠加逾期、减免、核销后轻松过 30。计费事件量放大 13 到 30 倍——改造的工作量就藏在这个倍数里,不在页面数上。
3.承诺:一张新旧模型十维对照表、四段式重构(业务事件→计量→对账→分账)、五模块三档施工顺序,以及每个模块的验收判据。
一、名单是结果,地基才是原因
上一篇用了一些数字:某民营银行的合作导流获客机构从 56 家降到 10 家,兰州银行一次性停掉 12 家获客平台和 13 家融资担保公司。
但这些是结果。名单能砍,砍完之后剩下的业务怎么算钱,才是每家机构真正要动手的部分。
而动手的地方,就是计费引擎。
它现在的样子大概是:放款系统推一条放款记录过来,计费模块读三个字段——本金、期次、分润比例——乘一下,落一张确认表,月底汇总出账单。
它将来的样子必须是:它不再「读一条记录」,而是「消费一条流」。
旧引擎的输入是一笔业务,新引擎的输入是这笔业务的一生。
二、模型对照:从点到线,十三个地方要重画
先把两个模型摆在一起看。
旧模型:放款事件计费。一个点触发,放款即全额确认,与资产质量脱钩。退贷是唯一需要处理的异常。
新模型:计息与还款事件流计费。一条线驱动,随每期实收利息结转,逾期即少收,早偿要重算,减免与核销要冲回。
图 1|旧模型一个点、新模型一条线:中间隔着的是整个数据模型的重画
十维对照,可以直接拿去和合同条款逐条核:
维度 | 旧:按放款额计费 | 新:实收利息 × 回收进度 |
计费触发 | 放款事件,一个点 | 计息 + 还款事件流,一条线 |
单笔试算事件量 | 1 个 | 13 至 30+ 个 |
收入确认 | 放款当月全额确认 | 随每期实收利息结转 |
与资产质量耦合 | 无,坏账照拿分成 | 直接耦合,逾期即少收 |
异常处理 | 退贷一种 | 早偿重算、减免冲回、核销不冲、收回重建 |
规则载体 | 合同里一个比例数字 | 带生效区间的版本化决策表 |
支付节奏 | 放款即结或固定分期 | 受本金回收进度封顶,与计费基数分离 |
对账颗粒 | 月度总额对平 | 借据 × 期次 × 事件三级对平 |
举证方式 | 财务说明加手工底稿 | 分成记录自带五个可追溯指针 |
失败模式 | 少算,可以追补 | 多算,要退回去,且集中在清算时爆发 |
最后一行值得单独说一句:旧模型的错是少算,新模型的错是多算。少算可以追补,多算要退钱——而且是在对账、审计、监管检查同时找上门的那天退。这就是为什么这件事不能「先上线再优化」。
三、四段式重构:业务事件 → 计量 → 对账 → 分账
地基怎么打?我的做法是把计费引擎拆成四段,每段职责单一、边界清楚,任何一段都可以被独立重放。
第一段:业务事件层——先把「发生过的」和「算出来的」分开
所有输入一律事件化,事件表只追加、不修改(append-only)。至少要覆盖十二类:放款、计息、正常还款、部分还款、逾期回补、提前结清、利息减免、本金减免、核销、核销后收回、退汇、冲正。
每个事件必须带三样东西:幂等键、生效日、反向指针。
event_id = hash(loan_id + period + source_receipt_no + event_type)
-- 唯一索引;上游重发、跑批重跑、补偿叠加,一律不产生第二次计量
biz_date -- 业务发生日,决定进哪个会计期间
effective_date -- 生效日,决定重放到某一天时它算不算数
reverse_of_id -- 冲正事件指向被冲原单,形成双向可查的冲销链
借据与期次的状态也一并事件驱动,禁止任何 UPDATE status = ... 的直改:
- 借据级:正常 → 逾期 → 重组 → 已核销 → 核销后收回
- 期次级:待计 → 已计提 → 已实收 → 已减免 → 已核销 → 已收回
第二段:计量层——一个纯函数,三样口径
计量层不做任何事,只回答一个问题:给定这批事件和这一天,该确认多少、该冲回多少、该付多少。
accrue(loan_id, as_of_date, rule_version) -> { 应计分成, 应冲回, 支付上限 }
它必须同时吐出三个口径,而不是一个数字:
1. 基数口径:累计实收利息 × 比例,决定「该赚多少」。
2. 封顶口径:预估总费用 × 本金回收进度,决定「现在能付多少」。
3. 时点口径:生效日与记账日的错位处理,决定「算在哪个期间」。
上一篇文章里那道 58.5 元与 99 元的差额,就是基数口径与封顶口径没分开的后果。这两道闸门在监管原文里是同一句话里的两个分句,在代码里必须是两个独立函数。
第三段:对账层——差异必须落到事件,不能只落到金额
三方对账:银行回单 ↔ 计费台账 ↔ 总账应收应付。
差异只有四种,分类要写死在系统里而不是靠人判断:时间性差异(跨期回单)、口径差异(罚息、违约金是否入基数)、缺失事件(上游没推)、重复事件(幂等没兜住)。
关键要求是:差异清单要能定位到 event_id 级。只到金额级的对账,最后一定演化成「财务和科技各拿一张表吵三天」。
第四段:分账层——账单调不可变,冲回走红字
账单一旦开出就是快照,永远不改。冲回不修改原单,只追加反向事件、生成红字账单,并与原单号双向挂链。
月度对账单固定输出三列:当期新增 / 当期冲回 / 累计净额。只给净额的账单,等于把差异藏到下一个季度。
把「可举证」设计进架构,而不是设计进说明
这一段是四段式的目的所在。新规之后,审计和监管问的不再是「你们协议怎么签的」,而是「这一分钱是怎么算出来的」。
所以每一条分成记录都必须自带五个指针:
指针 | 作用 |
借据号 | 定位到具体资产 |
期次 | 定位到具体时间 |
驱动事件 ID | 定位到「哪一笔钱」触发 |
规则版本 ID | 定位到「用哪一版算法」 |
当期本金回收率 | 定位到支付进度封顶线 |
有了这五个指针,「这笔 36 元哪来的」的回答是一行数据,不是一份说明文档。
能重放的才叫账,不能重放的只是一个数。
四、五模块三档:顺序反了必返工
改造范围可以收敛成五个模块,但施工必须分三档,顺序不能换。
图 2|五模块三档施工顺序:地基没打完就上看板,返工是必然事件
第一档·地基(M2 → M1 → M3)
- M2 计息与还款事件流:事件表、幂等键、append-only、状态机。它必须排在规则引擎前面——没有事件,规则引擎的输入就不存在。
- M1 计费规则引擎:决策表 + 版本轴 + 纯函数重放。规则不写在代码里,写在带生效区间的配置里。
- M3 总账应收应付:实收利息入账口径、红字凭证、冲销链挂账。这一步要和财务一起定,不是科技单方面交付。
第二档·监管可视(M4)
- M4 综合成本与对账看板:按金规〔2026〕2 号要求,把综合融资成本逐项列明收取方式、标准与主体,并按《中国人民银行公告》〔2021〕第 3 号的内部收益率法折算年化;同时输出三方对账差异分类。
第三档·合规留痕(M5)
- M5 规则版本化与操作留痕:协议版本、规则变更审批链、人工调整的操作日志。它看起来最轻,实际是审计第一问。
为什么顺序反了必返工——三个真实会发生的场景
- 先做看板,后改地基。看板上的「实收利息」取自报表库的快照字段。地基改完,历史曲线全部重画,而重画出来的数与已经对外报送的数不一致——你要为一张已经发出去的报表再写一份解释。
- 先做版本化,没有事件流。版本只能挂在配置表上,无法与具体金额绑定重放。结果就是「改配置留了个记录」,审计照样无法举证,等于白做。
- 先改总账,不改事件流。红字凭证没有驱动来源,只能人工写摘要。应收应付与计费台账两头对不上,差异越积越多,最后在年度清算集中爆发。
共同点是:越靠后的模块越"看得见",越靠前的模块越"没人愿意先做"。而返工成本恰好与可见度成反比。
五、每个模块的完成判据
改造最怕「做完了」这三个字没有定义。给五条,可以直接抄进项目任务书:
1. M2:任选一天重放全量事件,实收利息合计与总账科目余额差异为 0。
2. M1:同一批事件分别用 v1、v2 规则各跑一遍,差异金额可逐笔归因到具体规则条款。
3. M3:任意一笔红字凭证都能反查到驱动它的 event_id,且原单金额未被修改过。
4. M4:明示表口径的年化综合融资成本与实际计费口径的差异,能出一张可解释清单。
5. M5:任选历史三个月,用当前系统重放,能生成与当时已寄出账单逐分一致的账单。
第五条是整件事的总验收。做不到重放历史,前面四条都只是自证。
六、六个我见过的返工点
- 先做报表后改地基:口径被追问时,报表和台账各讲一个故事。
- 把动态值当静态字段:只在正常还款分支更新,早偿与核销后收回直接漏。
- 规则写死在代码里:每次协议变更都要发版,版本轴永远建不起来。
- 对账只到金额级:差异无法归因,最后靠人肉拉群。
- 砍掉红字账单:理由是「先跑通主流程」,代价是当年利润表上多一笔说不清的递延。
- 把计费基数与支付进度合并成一个公式:监管原文是两个分句,不是一个乘号。
七、三类人各三条
如果你是平台方 / 助贷机构。
1.把改造预算按「事件量 × 异常分支」重估,而不是按页面数估——事件量放大十几倍,工作量不会停留在原来那一档。
2.要求技术给出 M5 那条重放验收,作为上线门槛写进项目章程。
3.主动把重算出来的历史差额报给资金方,别等对账差异变成商务问题。
如果你是资金方 / 银行。
4.在合作协议里把「计费基数」「支付进度」「冲正时效」三条分成独立条款,各自可计算。
5.要求合作方按事件级推送,而不是月度汇总数——汇总数无法支撑你的对账与举证。
6.把「多算要退回」的机制写进结算办法,明确红字账单的确认与开票流程。
如果你是技术负责人 / 架构师。
7.先建事件表,再谈规则引擎;顺序反了,规则引擎会变成一个需要人工喂数的报表程序。
8.把「重跑不重复计费」做成一条自动化回归测试,纳入每次发版。
9.在分成表上强制五个可空率为零的指针字段,缺一个就不允许入账。
收束
计费从点变成线,系统就得从表变成流;确认从一次变成每期,账就得从只有正数变成有正有负;举证从人变成机器,架构就得从能跑变成能重放。
一个在金融科技行业做了十几年架构的人。这里聊三件事:政策怎么读、商业模式怎么拆、系统架构怎么改。
你们现在的计费是几个事件——放款那一下,还是每期回单?评论区说说,重放历史三个月,你能对上几分钱。
口径假设与数据出处
口径假设声明。本文的事件量(13 / 25 / 30+)、模块划分(M1—M5)、状态机命名、验收判据均为作者给出的参考实现假设,不代表任何真实系统的既有设计。其中 13 与 25 是由「12 期、每期一个回单(计息另计一个事件)」直接数出来的算术推演,可逐笔复核;30+ 是叠加逾期、减免、核销、退汇等异常分支后的经验估计,属待验证项,各机构应以自身资产的实际异常率重算。技术细节为架构通用实践整理,落地须结合本机构协议条款、会计政策与监管沟通结论。
政策与制度依据。
1.《国家金融监督管理总局关于加强商业银行互联网助贷业务管理提升金融服务质效的通知》(金规〔2025〕9 号),2025 年 4 月 1 日印发,2025 年 10 月 1 日施行——第五条「商业银行按照贷款实收利息的一定比例支付合作费用的,支付进度应当与贷款本金收回进度相匹配」、第三条指标体系、第六条费用标准或区间。
2.《个人贷款业务明示综合融资成本规定》(金规〔2026〕2 号),国家金融监督管理总局、中国人民银行 2026 年 3 月 3 日发布,2026 年 8 月 1 日施行——第二条综合融资成本明示表逐项列明收取方式、标准与主体,第五条合作协议中明确各方落实责任。
3.《中国人民银行公告》〔2021〕第 3 号——贷款利率以内部收益率法(IRR)折算年化,是综合成本看板年化口径的计算依据(金规〔2026〕2 号答记者问明确参照)。
4.《企业会计准则第 22 号——金融工具确认和计量》(财会〔2017〕7 号),2017 年 3 月 31 日修订发布,2018 年 1 月 1 日起施行——第三十九条利息收入、第四十三条减记与终止确认。
5.《商业银行金融资产风险分类办法》(中国银行保险监督管理委员会、中国人民银行令〔2023〕第 1 号),2023 年 2 月发布,2023 年 7 月 1 日施行——第十一条至第十三条逾期天数与分类、第十七条至第二十一条重组资产。
6.《金融企业呆账核销管理办法(2017 年版)》(财金〔2017〕90 号),2017 年 9 月 7 日印发,2017 年 10 月 1 日施行——第四条「账销案存、权在力催」。
行业数据来源。《助贷新规周年考:从名单「减法」走向能力「加法」》,经济日报(经济新闻网),2026 年 10 月 4 日——吉林亿联银行合作导流获客机构由 2024 年 11 月 56 家降至 2026 年 7 月 10 家;兰州银行 2026 年 7 月将 12 家营销获客平台与 13 家融资担保公司全部变更为停止合作。