以下情境为教学假设,人物与数字不代表经核实的真实案例。
催收最容易变成情绪劳动:对方不回邮件,就越想发一封更严厉的函;再不行就找律师,仿佛升级本身就是进展。但从财务上看,每一级催收都是一笔投资:投入内部工时、固定费用或第三方佣金,换取一个概率更高的回收。是否升级,取决于期望净回收,而不是取决于有多生气。
催收的每一步都要算期望值
把一级催收看成一个项目,它的期望净回收是:
期望回收 = 应收 × 回收概率
期望净回收 = 期望回收 − 内部工时成本 − 固定费用 − 第三方佣金
内部工时成本按机会成本算:Nord 的咨询费率是 85 美元/小时,所以 2 小时催收的内部成本是 170 美元。这个数字不是工资,而是「同样的时间本可以拿去交付客户」的机会成本。如果创始人的时间没有替代用途,用工资口径也可以,但要在全表里保持一致。
每一级都要单独算,不能把五级叠加。叠加会重复计算同一笔应收:提醒拿回的 30% 和仲裁拿回的 70% 不是两笔钱,而是同一笔钱在不同路径下的概率。表格里的每一行,都是「走到这一级就停下,不再升级」的期望值。
五级阶梯:成本、概率与 Nord 的期望值
成本从哪里来
把催收拆成五级,每级的成本和回收概率如下。所有概率、佣金比例和固定费用都是教学假设,不是统计结果:
| 阶梯 | 内部工时 | 内部成本 @ $85/小时 | 固定费用 | 第三方佣金 | 回收概率(教学假设) |
|---|---|---|---|---|---|
| 提醒 | 2 小时 | $170 | $0 | 0 | 30% |
| 正式函 | 4 小时 | $340 | $30 | 0 | 50% |
| 停止服务 | 8 小时 | $680 | $0 | 0 | 60% |
| 第三方催收 | 2 小时 | $170 | $0 | 回收额的 20% | 70% |
| 仲裁/诉讼 | 12 小时 | $1,020 | $8,500 | 0 | 70% |
固定费用 8,500 = 仲裁费 2,500 + 律师与准备 6,000,是教学假设;真实报价因辖区、金额和争议复杂度差异很大。第三方佣金取回收额的 20%,也是教学假设;市场上常见 25%–35%,但本书不引用真实报价。
回收概率是档位,不是统计结果:30%、50%、60%、70%、70% 只用于比较路径,不能拿去预测某个具体客户。为什么概率递增?因为每一级都在增加压力或增加专业性:提醒利用日常关系,正式函留下书面记录,停止服务影响对方的使用,第三方催收有专门的追讨流程,仲裁或诉讼有强制力。
但递增不是无限的。仲裁的 70% 已经包含「胜诉但执行不到」的情形,不能按 100% 胜诉率算。把胜诉率当成回收概率,是小额官司最常见的算错方式。
把 Nord 的 11,264 代进去
Nord Studio 的应收是 11,264 美元。把每一级代进公式:
| 阶梯 | 期望回收 | 内部成本 | 佣金 | 固定费用 | 期望净回收 |
|---|---|---|---|---|---|
| 提醒 | $3,379 | $170 | $0 | $0 | $3,209 |
| 正式函 | $5,632 | $340 | $0 | $30 | $5,262 |
| 停止服务 | $6,758 | $680 | $0 | $0 | $6,078 |
| 第三方催收 | $7,885 | $170 | $1,577 | $0 | $6,138 |
| 仲裁/诉讼 | $7,885 | $1,020 | $0 | $8,500 | −$1,635 |
这里有三点值得注意。第一,提醒和正式函的成本很低,几乎没有理由不做;它们的问题不是「值不值」,而是「有没有做」。第二,停止服务和第三方催收的期望净回收接近,只差 60 美元;如果停止服务会损失未来收入,第三方催收可能更划算。第三,仲裁在 11,264 的金额上是负的,即使回收概率 70% 也不值得。
第三方催收的 6,138 = 7,885 − 1,577 − 170。佣金是按期望回收算的,不是按本金算的;如果第三方最终只收回一半,佣金也会按实际回收调整,但期望值仍然按概率加权。把 20% 乘在 11,264 上会得到 2,253,那会高估佣金 676 美元。
仲裁的盈亏平衡点是 13,600:
盈亏平衡应收 = (1,020 + 8,500) ÷ 70% = 13,600
11,264 低于 13,600,所以期望净回收为负。这个结论不依赖对方是否「有理」;它只依赖金额、成本和概率。如果应收是 20,000,仲裁的期望净回收就是 20,000 × 70% − 9,520 = 4,480,结论会反过来。
3,000 美元的官司:为什么不该打
假设一笔 3,000 美元的尾款,其他参数不变:
期望回收 = 3,000 × 70% = 2,100
期望净回收 = 2,100 − 1,020 − 8,500 = −7,420
即使胜诉率 100%,期望净回收也是 3,000 − 9,520 = −6,520。也就是说,官司还没开始,成本已经超过本金。这就是「为了 3,000 美元打两年官司」的反例:它把「赢」当成目标,而不是把「净回收」当成目标。
两年是另一个成本。仲裁和诉讼通常不是「立案就回款」,而是排期、开庭、裁决、执行,两年并不夸张。按 10% 折现率,两年后的 3,000 只值:
3,000 ÷ 1.10² = 2,479
而 8,500 的仲裁与律师费现在就要付。时间价值让这笔账更差。
更现实的问题是执行:赢了裁决不等于拿到钱。如果对方没有可执行资产,回收概率不是 70%,而是接近 0。仲裁的 70% 已经是「胜诉且能执行」的混合概率;如果对方已经失联或资不抵债,这个概率还要下调。失联与破产的回收顺序见 4.3 对方破产或失联:能拿回多少 。
更理性的选择是:按 4.1 坏账准备:计提、核销与转回 核销,或者接受一个折扣和解。比如现在拿 1,500 和解,虽然只有本金的 50%,但比期望净回收 −7,420 好得多。
和解的折扣也有下限。如果对方只能给 500,而继续催收的期望净回收是 3,209(提醒档),那就继续提醒;和解不是无条件接受第一个报价。
停止服务、第三方催收与和解的边界
停止服务是五级里最容易被情绪驱动的一级。它的成本不只是 8 小时内部工时,还包括未来收入损失、数据交接和关系修复。如果这个客户本来还会续约,停服可能把 6,078 的期望净回收再打掉一部分。表里的 6,078 没有扣未来收入,是因为案例没有给续约金额;使用时要自己补上。
第三方催收适合「金额中等、自己没有时间、对方还有一定还款能力」的区间。20% 的佣金看起来高,但它把催收工作外包,并且通常只在收回时收费;对于 11,264 这样的金额,6,138 的期望净回收高于自己停服的 6,078。如果金额只有 3,000,第三方可能不愿接,或者佣金比例更高,这时它的优势会消失。
和解适合「对方有支付意愿但暂时没有全额现金」的情形。50% 的折扣是否接受,要和下一级的期望净回收比,而不是和账面全额比。账面全额是 11,264,但如果继续打官司的期望净回收是负的,账面全额只是一个参照点,不是可实现的现金。
这一节不讨论具体的催收话术、函件模板或程序步骤;那些属于操作层。这里只回答一个问题:在这一级停下,期望净回收是多少。停止服务前是否被合同允许、和解协议怎么写,属于合同与争议解决条款,见 5.2 争议解决:管辖、仲裁与调解 ;本节只做财务估算,不构成法律意见。
写成代码:collection.ts
下面的 collection.ts 把每一级催收写成 CollectionStage,用 evaluateStage 算期望回收、佣金、内部成本、固定费用和期望净回收,用 evaluateLadder 一次算完五级,用 bestStage 选出期望净回收最高的一级。breakEvenReceivableCents 直接给出这一级的盈亏平衡应收。
// file: src/contract/collection.ts
// tier: L1
// verify: node --test test/contract/collection.test.ts
import { type Cents, add, sub } from "../core/money.ts";
import { type Bps, applyRate } from "../core/rate.ts";
import { type Result, fail, ok } from "../core/result.ts";
export type CollectionStage = {
name: string;
recoveryBps: Bps;
internalHours: number;
hourlyCostCents: Cents;
fixedCostCents: Cents;
commissionBps: Bps;
};
export type StageResult = {
name: string;
expectedRecoveryCents: Cents;
internalCostCents: Cents;
commissionCents: Cents;
fixedCostCents: Cents;
expectedNetCents: Cents;
breakEvenReceivableCents: Cents;
};
function checkAmount(value: Cents, label: string): Result<true> {
if (!Number.isInteger(value) || value < 0) {
return fail(`${label} must be a non-negative integer amount in cents`);
}
return ok(true);
}
function checkRate(value: Bps, label: string): Result<true> {
if (!Number.isInteger(value) || value < 0 || value > 10000) {
return fail(`${label} must be between 0 and 10000 bps`);
}
return ok(true);
}
/**
* Expected net recovery is probability-weighted cash minus the cost of that
* stage:
* expected recovery = receivable x recovery probability
* commission = expected recovery x third-party rate
* net = expected recovery - internal - fixed - commission
* The break-even receivable is the balance at which net equals zero, so a
* small invoice can be rejected without arguing about the merits.
*/
export function evaluateStage(
receivableCents: Cents,
stage: CollectionStage,
): Result<StageResult> {
const receivable = checkAmount(receivableCents, "receivable");
if (!receivable.ok) {
return receivable;
}
if (stage.name.trim() === "") {
return fail("stage name cannot be empty");
}
const recovery = checkRate(stage.recoveryBps, `${stage.name} recovery rate`);
if (!recovery.ok) {
return recovery;
}
if (stage.recoveryBps === 0) {
return fail("recovery probability must be greater than zero");
}
const commission = checkRate(stage.commissionBps, `${stage.name} commission`);
if (!commission.ok) {
return commission;
}
if (!Number.isInteger(stage.internalHours) || stage.internalHours < 0) {
return fail("internal hours must be a non-negative integer");
}
const hourly = checkAmount(stage.hourlyCostCents, "hourly cost");
if (!hourly.ok) {
return hourly;
}
const fixed = checkAmount(stage.fixedCostCents, "fixed cost");
if (!fixed.ok) {
return fixed;
}
const expectedRecoveryCents = applyRate(receivableCents, stage.recoveryBps);
const internalCostCents = Math.round(stage.internalHours * stage.hourlyCostCents);
const commissionCents = applyRate(expectedRecoveryCents, stage.commissionBps);
const expectedNetCents = sub(
expectedRecoveryCents,
add(add(internalCostCents, stage.fixedCostCents), commissionCents),
);
const netRateBps = Math.round(
(stage.recoveryBps * (10000 - stage.commissionBps)) / 10000,
);
const breakEvenReceivableCents =
netRateBps === 0
? 0
: Math.ceil(
((internalCostCents + stage.fixedCostCents) * 10000) / netRateBps,
);
return ok({
name: stage.name,
expectedRecoveryCents,
internalCostCents,
commissionCents,
fixedCostCents: stage.fixedCostCents,
expectedNetCents,
breakEvenReceivableCents,
});
}
/**
* Evaluate each rung as a standalone path: "stop here, do not escalate".
* Summing the rows would double count the same receivable.
*/
export function evaluateLadder(
receivableCents: Cents,
stages: CollectionStage[],
): Result<StageResult[]> {
if (stages.length === 0) {
return fail("at least one collection stage is required");
}
const results: StageResult[] = [];
for (const stage of stages) {
const evaluated = evaluateStage(receivableCents, stage);
if (!evaluated.ok) {
return evaluated;
}
results.push(evaluated.value);
}
return ok(results);
}
/**
* Pick the highest expected net. A tie keeps the earlier, cheaper rung so the
* ladder never escalates without a positive gain.
*/
export function bestStage(results: StageResult[]): StageResult | null {
if (results.length === 0) {
return null;
}
let best = results[0];
for (const result of results) {
if (result.expectedNetCents > best.expectedNetCents) {
best = result;
}
}
return best;
}
evaluateStage 的输入是应收美分和一级催收的参数。Nord 的 11,264 美元是 1,126,400 美分;提醒档的内部工时 2 小时、小时成本 8,500 美分,期望回收 337,920 美分,净回收 320,920 美分。仲裁档的固定费用 850,000 美分,期望净回收 −163,520 美分,盈亏平衡应收 1,360,000 美分。
evaluateLadder 不会把五级相加,它只是把每一级单独算一遍。bestStage 在五级里选期望净回收最高的;Nord 的结果是第三方催收 613,784 美分。如果某一级的期望净回收为负,它仍然会出现在结果里,方便看到「为什么不该升级」。
代码不处理时间价值、执行风险和未来收入损失;它只覆盖本节表格里的口径。时间价值可以在调用前把应收折现,执行风险可以下调回收概率,未来收入损失可以加进固定费用。三种调整都不改变公式,只改变输入。
小结
- 每一级催收都是一笔投资:期望净回收 = 应收 × 回收概率 − 内部成本 − 固定费用 − 佣金。
- Nord 的 11,264 应收:提醒 3,209、正式函 5,262、停止服务 6,078、第三方催收 6,138、仲裁 −1,635。
- 仲裁的盈亏平衡点是 13,600;11,264 低于它,所以即使 70% 回收概率也不值得。
- 3,000 美元的尾款打官司,期望净回收是 −7,420;即使 100% 胜诉,也是 −6,520。
- 两年后的 3,000 按 10% 折现只值 2,479,而 8,500 的成本现在就要付。
- 和解要和下一级的期望净回收比,不要和账面全额比;50% 的 3,000 是 1,500,好过负的期望值。
阅读导航:上一节:4.1 坏账准备:计提、核销与转回 · 下一节:4.3 对方破产或失联:能拿回多少 。
常见错误与边界
- 把升级当进展。 发更严厉的函、找律师,都是成本;如果期望净回收为负,升级只是把亏损做大。
- 把五级期望值相加。 五级是同一笔应收的不同路径,不是五笔独立收入;相加会重复计算。
- 用工资而不是机会成本。 如果创始人的时间本可以交付客户,内部成本应按 85 美元/小时算;用工资会低估催收成本。
- 忽略未来收入损失。 停止服务的 6,078 没有扣未来续约收入;如果客户本来会续约,这个数字会下降。
- 用胜诉率代替回收概率。 胜诉不等于拿到钱;仲裁的 70% 应理解为「胜诉且可执行」的混合概率。
- 忽略执行风险。 对方没有可执行资产时,裁决只是一张纸;失联或资不抵债要把回收概率下调。
- 把佣金按本金算。 20% 的佣金乘的是期望回收,不是应收全额;乘错会高估成本。
- 用账面全额和和解折扣比。 账面全额不是可实现的现金;应和继续催收的期望净回收比。
- 不做盈亏平衡。 13,600 是这一级的门槛;低于门槛的应收,不应该进入仲裁。
- 把教学概率当统计结果。 30%、50%、60%、70% 是档位,用于比较路径,不是对某个客户的预测。
本节的边界是「催收阶梯的期望值比较」:只覆盖内部工时、固定费用、第三方佣金和回收概率,不展开具体法律程序、证据规则、执行措施和跨境管辖。所有概率与费率均为教学假设;具体法律选择因司法辖区而异,本节不构成法律意见。坏账的账务处理见第 4.1 节,破产与失联的回收顺序见第 4.3 节。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。