以下情境为教学假设,人物与数字不代表经核实的真实案例。
交割(closing)常被写成“签字当天”。签字只是把合同生效,真正决定卖方什么时候拿到钱、拿到多少的,是交割清单是否闭合、资金按什么顺序走。一笔 300,000 美元的交易,合同上的价格是 300,000,交割日到账可能只有 180,000;剩下的 120,000 要等托管释放和 earn-out 条件满足。把这两个数字混为一谈,是交割阶段最常见的预算错误。
这一节把交割拆成两个可以分别检查的部分。第一部分是清单:公司决议、合同签署、付款凭证、资产交付、第三方同意、托管安排,六类各证明一件事,缺一类就可能阻断交割。第二部分是资金流:先付款还是先交付、托管扣多少、什么时候放款。最后用一笔 300,000 美元的交易画表,并算清“延迟 30 天”的利息与现金占用。所有费率与天数都是教学假设。
六类交割清单
清单不是“把文件收齐”,而是“每一类都有人签字、有证据、有兜底”。六类各回答一个不同的问题,不能用一份合同代替另一份。下表列出每类证明什么、缺失时会发生什么,以及它是否通常阻断交割。
| 类别 | 证明什么 | 缺失时会发生什么 | 通常是否阻断 |
|---|---|---|---|
| 公司决议 | 董事会或股东会批准交易,签字人有授权 | 买方无法确认卖方有权出售,付款可能被银行或托管方拦下 | 是 |
| 合同签署 | definitive agreement、转让书、披露函已生效 | 资产范围、赔偿上限、托管条款没有法律载体 | 是 |
| 付款凭证 | 买方汇款、托管入账、卖方债务清偿已确认 | 卖方以为钱到了,实际卡在合规审查或中间行 | 是 |
| 资产交付 | 代码、域名、数据、账号、合同已实际转移 | 钱付了但资产还在卖方名下,交割后还要追 | 是 |
| 第三方同意 | 银行、平台、房东、关键客户同意变更 | 合同或账号不能转,买方拿到的是空壳 | 视条款 |
| 托管安排 | 托管协议、放款条件、索赔流程已签署 | 有争议时没有扣款机制,买方只能事后追偿 | 视结构 |
公司决议最容易被一人公司忽略。一人公司常常认为“我自己签字就够了”,但买方、银行和托管方要看的是“这个签字人有权代表公司出售这项资产”。决议不需要很长,但必须写清交易、价格、授权签字人和生效条件。缺少决议,交割日可能变成“文件补签日”。
合同签署与资产交付是两件事。合同签了,只说明双方同意交易;资产交付,才说明买方真的拿到了东西。资产交付又分“法律交付”和“实际交付”:法律交付是转让书、域名注册人变更、账号所有权变更;实际交付是代码仓库权限、数据库导出、客户名单、密钥清单。两者都完成,才算交付。
付款凭证不是“买方说已经汇了”。跨境汇款可能经过中间行、合规审查、外汇额度,卖方账户看到“待入账”不等于可用余额。交割日要拿到的是银行回单、托管方入账确认和债务清偿证明,而不是一张截图。付款凭证缺失时,卖方不应交付关键资产,否则钱和资产可能同时不在自己手上。
第三方同意决定交易能不能“原样承继”。平台账号、云合同、支付通道、域名注册商、关键客户合同,很多都写着“未经同意不得转让”。这些条款不一定阻断整个交易,但会改变交割结构:要么在交割前拿到同意,要么把这项资产从交易里剔除,要么设置过渡期由卖方继续持有。清单上要写清每一项的处理方式,而不是笼统写“已处理”。
托管安排是最后一道现金流阀门。它不产生交割,但决定交割后发生争议时钱在哪里。托管比例、期限、放款条件、索赔流程要单独列项,不能只写在主合同的一句话里。下一节会展开托管如何放款。
资金流的顺序:先付款还是先交付
“先付款还是先交付”没有统一答案,只有风险分配。先付款,买方承担卖方收钱不交付的风险;先交付,卖方承担买方收货不付款的风险。交易双方通常用三种结构之一来分配这个风险。
| 结构 | 顺序 | 谁承担主要风险 | 适用场景 | 额外成本 |
|---|---|---|---|---|
| 先款后交 | 买方先汇款,卖方确认到账后交付 | 买方 | 资产可快速验证、卖方信用高 | 买方尽调成本 |
| 先交后款 | 卖方先交付,买方验收后付款 | 卖方 | 买方信用高、资产可回退 | 卖方催收与回退成本 |
| 同步交割 | 双方把资金与资产交给托管方,条件满足后同时释放 | 双方分摊 | 跨境、资产复杂、金额较大 | 托管费与时间 |
同步交割是大多数 100,000 美元以上交易的默认结构,但不是“没有风险”,而是“把风险换成流程”。买方把现金存入托管账户,卖方把转让文件、域名转移授权、代码仓库所有权交给托管方;托管方确认条件满足后,一边放款、一边释放资产。任何一方不配合,另一方可以在托管协议下索赔,而不是直接去法院。
对独立开发者,最重要的不是选哪种结构,而是不要在“钱没到”时交出“拿不回来”的资产。代码可以备份、域名可以转回、客户名单可以复制,但平台账号、支付通道、数据库主密钥一旦交出去,回退成本可能高于交易本身。资金流表要标出每一项资产的“可回退性”,再决定交付顺序。
300,000 美元交易的资金流表
设一笔教学假设的资产收购:总价 300,000 美元,交割日支付 70% 现金,30% 作为 12 个月 earn-out,托管为总价的 10%,从现金部分扣留。现金部分 210,000 美元,托管 30,000 美元,卖方交割日实收 180,000 美元;12 个月后托管释放 30,000 美元,earn-out 在条件满足时支付最多 90,000 美元。
| 时点 | 付款方 | 收款方 | 金额 | 触发条件 |
|---|---|---|---|---|
| 交割日 | 买方 | 卖方 | 180,000 | 清单阻断项清零、资产交付确认 |
| 交割日 | 买方 | 托管账户 | 30,000 | 托管协议生效 |
| 交割日 + 12 个月 | 托管账户 | 卖方 | 30,000 | 无未决赔偿,营运资金调整结清 |
| 交割日 + 12 个月 | 买方 | 卖方 | 0–90,000 | ARR 不低于基准的 95%,按比例支付 |
| 合计 | 300,000 |
这张表说明三件事。第一,交割日现金流不是合同价,而是合同价减去托管和 earn-out;对这笔交易,交割日实收 180,000 美元,占价格的 60%。第二,托管不是“罚款”,而是“先扣后放”:30,000 美元在 12 个月内可能因为赔偿、营运资金调整或未披露负债被部分扣减。第三,earn-out 是或有对价,不是应收款;如果条件不满足,卖方拿不到这 90,000 美元,但交易在交割日已经完成。
LedgerLite 的实际锚是 768,768 美元,结构同样是 70% 现金加 30% earn-out、10% 托管。现金部分 538,138 美元,托管 76,877 美元,交割日实收 461,261 美元,占价格的 60.0%。比例与 300,000 美元的示例相同,金额放大了 2.56 倍。把比例记住,比记住单笔金额更有用。
营运资金调整是资金流表里最容易被漏掉的一行。交易常约定“交割日营运资金达到目标值”,实际值低于目标时从价款里下调。LedgerLite 的目标营运资金 15,000 美元、实际 12,000 美元,下调 3,000 美元;这 3,000 美元不是罚款,而是“买方接手时少拿到的现金”。它通常从交割日付款或托管里扣,不改变 headline price,但改变卖方实际到手。
托管如何放款
托管(escrow)的核心不是“把钱扣住”,而是“把放款条件写成可验证的事件”。常见条件有三类:时间条件、无索赔条件、调整条件。时间条件是“交割后 12 个月释放”;无索赔条件是“期间没有买方提出赔偿请求”;调整条件是“营运资金、应收、债务按约定结算完毕”。
| 放款情形 | 托管余额 | 释放金额 | 扣留金额 | 说明 |
|---|---|---|---|---|
| 无索赔,12 个月到期 | 30,000 | 30,000 | 0 | 标准情形 |
| 买方提出 8,000 美元赔偿 | 30,000 | 22,000 | 8,000 | 索赔在托管内解决 |
| 营运资金下调 3,000 | 30,000 | 27,000 | 3,000 | 调整优先于释放 |
| 索赔 8,000 加调整 3,000 | 30,000 | 19,000 | 11,000 | 两项可叠加,以余额为限 |
托管的关键数字是“比例 × 期限 × 索赔上限”。比例决定扣多少,期限决定扣多久,索赔上限决定最坏情况下卖方还能拿回多少。10% 托管、12 个月、赔偿上限等于托管金额,是常见组合;如果买方要求 20% 托管、24 个月,卖方的交割日现金会更少,资金占用时间会更长。
托管还会产生一笔“看不见的成本”:被扣住的钱不能用于经营,也不能用于再投资。按教学假设 8% 的年资金成本,30,000 美元托管 12 个月的机会成本是 2,400 美元;如果托管比例提高到 20%,60,000 美元的机会成本是 4,800 美元。谈判时不能只看“托管会不会被扣”,还要看“被扣住的这段时间值多少钱”。
交割延迟 30 天的利息与现金占用
交割延迟不是“晚几天签字”,而是“钱晚几天到、经营成本照付”。延迟成本分两块:第一块是延迟到账的资金成本,按交割日实收金额 × 年资金成本 × 延迟天数 ÷ 365;第二块是现金占用,即延迟期间卖方仍要支付的运营成本,按月度运营成本 × 延迟天数 ÷ 30。
| 项目 | 基数 | 费率或天数 | 金额 |
|---|---|---|---|
| 延迟到账的利息 | 180,000 | 8% × 30/365 | 1,183.56 |
| 现金占用 | 12,000/月 | 30/30 | 12,000.00 |
| 合计 | 13,183.56 | ||
| 占交割日实收比例 | 180,000 | 7.3% |
30 天延迟的成本是 13,183.56 美元,相当于交割日实收 180,000 美元的 7.3%。这个数字比很多人预期的高,因为主要成本不是利息 1,183.56 美元,而是现金占用 12,000 美元。对一人公司,运营成本可能更低;对有外包和工资的团队,现金占用会更高。延迟 30 天的真实价格,要用自己的月度运营成本替换 12,000 美元再算。
利息的基数也要选对。如果延迟的是全部 300,000 美元,利息是 1,972.60 美元;如果延迟的是交割日实收 180,000 美元,利息是 1,183.56 美元;如果只是托管 30,000 美元晚释放 30 天,利息是 197.26 美元。三个数字对应三种延迟,谈判时要写清“哪一笔钱延迟了多久”,而不是笼统说“交易晚了”。
延迟还会改变交割清单的状态。原本“已完成”的第三方同意可能过期,原本“已确认”的付款凭证可能失效,原本“已交付”的账号可能在等待期间被修改密码。延迟 30 天不是把同样的清单往后挪 30 天,而是要在新的交割日重新确认阻断项。清单上的日期、签字和凭证都有有效期,这一点常被忽略。
写成代码:closing.ts
下面的 closing.ts 把清单和资金流放在同一个函数里。closingChecklist 统计清单是否覆盖六类、完成率、阻断项和缺失类别;同时按 cashAtClosingBps 与 escrowBps 算出交割日实收和托管金额,earn-out 由总价减现金部分推导。延迟成本拆成 delayInterestCents 与 cashOccupancyCents,避免把利息和运营现金混为一谈。
// file: src/capital/closing.ts
// tier: L1
// verify: node --test test/capital/closing.test.ts
import { type Cents } from "../core/money.ts";
import { type Bps, applyRate, divide } from "../core/rate.ts";
import { type Result, fail, ok } from "../core/result.ts";
export type ChecklistCategory =
| "corporate-resolution"
| "contract-signature"
| "payment-proof"
| "asset-delivery"
| "third-party-consent"
| "escrow-arrangement";
export const CHECKLIST_CATEGORIES: ChecklistCategory[] = [
"corporate-resolution",
"contract-signature",
"payment-proof",
"asset-delivery",
"third-party-consent",
"escrow-arrangement",
];
export type ChecklistItem = {
category: ChecklistCategory;
label: string;
done: boolean;
blocking: boolean;
};
export type ClosingInput = {
priceCents: Cents;
cashAtClosingBps: Bps;
escrowBps: Bps;
escrowMonths: number;
delayDays: number;
annualCapitalCostBps: Bps;
monthlyOperatingCents: Cents;
items: ChecklistItem[];
};
export type CategoryProgress = {
total: number;
done: number;
pending: number;
};
export type ClosingOutcome = {
itemCount: number;
doneCount: number;
pendingCount: number;
completionBps: Bps;
blockingPending: number;
missingCategories: ChecklistCategory[];
categoryProgress: Record<ChecklistCategory, CategoryProgress>;
readyToClose: boolean;
cashPortionCents: Cents;
earnOutCents: Cents;
escrowCents: Cents;
sellerCashAtClosingCents: Cents;
deferredCents: Cents;
closingCashShareBps: Bps;
delayInterestCents: Cents;
cashOccupancyCents: Cents;
delayCostCents: Cents;
escrowReleaseMonth: number;
};
function checkAmount(value: Cents, label: string): string | null {
if (!Number.isInteger(value) || value < 0) {
return `${label} must be a non-negative integer amount in cents`;
}
return null;
}
function checkBps(value: Bps, label: string): string | null {
if (!Number.isInteger(value) || value < 0 || value > 10000) {
return `${label} must be between 0 and 10000 bps`;
}
return null;
}
function emptyProgress(): Record<ChecklistCategory, CategoryProgress> {
return {
"corporate-resolution": { total: 0, done: 0, pending: 0 },
"contract-signature": { total: 0, done: 0, pending: 0 },
"payment-proof": { total: 0, done: 0, pending: 0 },
"asset-delivery": { total: 0, done: 0, pending: 0 },
"third-party-consent": { total: 0, done: 0, pending: 0 },
"escrow-arrangement": { total: 0, done: 0, pending: 0 },
};
}
/**
* Closing has two independent questions. The checklist answers "can the deal
* legally close": all six categories must be represented, and no blocking item
* may remain open. The funds flow answers "how much cash arrives on the day":
* the escrow and the earn-out are withheld from the headline price, so the
* seller's closing cash is smaller than the number in the agreement.
*/
export function closingChecklist(input: ClosingInput): Result<ClosingOutcome> {
const priceError = checkAmount(input.priceCents, "price");
if (priceError) {
return fail(priceError);
}
if (input.priceCents === 0) {
return fail("price must be positive");
}
for (const [value, label] of [
[input.cashAtClosingBps, "cash at closing"],
[input.escrowBps, "escrow"],
[input.annualCapitalCostBps, "annual capital cost"],
] as Array<[Bps, string]>) {
const error = checkBps(value, label);
if (error) {
return fail(error);
}
}
if (input.escrowBps > input.cashAtClosingBps) {
return fail("escrow cannot exceed the cash portion paid at closing");
}
if (!Number.isInteger(input.escrowMonths) || input.escrowMonths < 0) {
return fail("escrow months must be a non-negative integer");
}
if (
!Number.isInteger(input.delayDays) ||
input.delayDays < 0 ||
input.delayDays > 365
) {
return fail("delay days must be an integer between 0 and 365");
}
const operatingError = checkAmount(
input.monthlyOperatingCents,
"monthly operating cost",
);
if (operatingError) {
return fail(operatingError);
}
if (input.items.length === 0) {
return fail("checklist must contain at least one item");
}
const categoryProgress = emptyProgress();
let doneCount = 0;
let blockingPending = 0;
for (const item of input.items) {
if (!CHECKLIST_CATEGORIES.includes(item.category)) {
return fail(`unknown checklist category: ${item.category}`);
}
const progress = categoryProgress[item.category];
progress.total += 1;
if (item.done) {
progress.done += 1;
doneCount += 1;
} else {
progress.pending += 1;
if (item.blocking) {
blockingPending += 1;
}
}
}
const missingCategories = CHECKLIST_CATEGORIES.filter(
(category) => categoryProgress[category].total === 0,
);
const itemCount = input.items.length;
const pendingCount = itemCount - doneCount;
const cashPortionCents = applyRate(input.priceCents, input.cashAtClosingBps);
const earnOutCents = input.priceCents - cashPortionCents;
const escrowCents = applyRate(input.priceCents, input.escrowBps);
const sellerCashAtClosingCents = cashPortionCents - escrowCents;
const deferredCents = input.priceCents - sellerCashAtClosingCents;
const delayInterestCents = Math.round(
(sellerCashAtClosingCents *
input.annualCapitalCostBps *
input.delayDays) /
(10000 * 365),
);
const cashOccupancyCents = Math.round(
(input.monthlyOperatingCents * input.delayDays) / 30,
);
return ok({
itemCount,
doneCount,
pendingCount,
completionBps: divide(doneCount, itemCount),
blockingPending,
missingCategories,
categoryProgress,
readyToClose: blockingPending === 0 && missingCategories.length === 0,
cashPortionCents,
earnOutCents,
escrowCents,
sellerCashAtClosingCents,
deferredCents,
closingCashShareBps: divide(sellerCashAtClosingCents, input.priceCents),
delayInterestCents,
cashOccupancyCents,
delayCostCents: delayInterestCents + cashOccupancyCents,
escrowReleaseMonth: input.escrowMonths,
});
}
代码里有四个刻意的选择。第一,托管从现金部分扣,不从总价另加;escrowBps > cashAtClosingBps 会直接报错,因为那意味着托管超过了交割日现金。第二,earn-out 由“总价减现金部分”推导,保证 70/30、80/20 等结构不会出现第三块未定义的钱。第三,readyToClose 只看阻断项和六类是否齐全,不看非阻断项;非阻断项可以交割后补,阻断项不能。第四,延迟利息按 365 天、现金占用按 30 天,两个分母不同,因为利息是年化资金成本,占用是月度运营节奏。
把 300,000 美元交易代入:价格 30,000,000 美分、现金 7,000 基点、托管 1,000 基点、托管 12 个月、延迟 30 天、年资金成本 800 基点、月运营 1,200,000 美分。返回现金部分 21,000,000 美分、earn-out 9,000,000 美分、托管 3,000,000 美分、交割日实收 18,000,000 美分、递延 12,000,000 美分、交割日实收占比 6,000 基点;延迟利息 118,356 美分、现金占用 1,200,000 美分、延迟成本 1,318,356 美分。
小结
- 交割清单分六类:公司决议、合同签署、付款凭证、资产交付、第三方同意、托管安排;每类证明一件不同的事,不能用一份合同代替。
- 阻断项决定能不能交割,非阻断项可以交割后补;清单要标出
blocking,而不是只标“完成/未完成”。 - 300,000 美元、70% 现金、30% earn-out、10% 托管的交易,交割日实收 180,000 美元,占价格 60%;剩余 120,000 美元分 12 个月释放。
- 托管放款看三个条件:时间、无索赔、调整;30,000 美元托管在 12 个月内可能因 8,000 美元索赔和 3,000 美元调整只释放 19,000 美元。
- 延迟 30 天的利息是 1,183.56 美元,现金占用是 12,000 美元,合计 13,183.56 美元,占交割日实收 7.3%。
- 利息基数要写清:全部 300,000 美元是 1,972.60 美元,交割日实收 180,000 美元是 1,183.56 美元,托管 30,000 美元是 197.26 美元。
- 延迟会重置清单有效期;第三方同意、付款凭证、账号密码都可能在新交割日失效,不能把旧清单直接平移。
阅读导航:上一节:7.3 陈述保证、赔偿与托管 · 下一节:8.2 客户、合同与账号迁移 。
常见错误与边界
- 把合同价当交割日现金。 300,000 美元的交易,交割日实收只有 180,000 美元;托管和 earn-out 合计 120,000 美元要等 12 个月。
- 清单只列文件,不列证据。 “合同已签”不是证据,“签字页、生效条件、授权决议”才是;付款凭证要看银行回单和托管入账,不是截图。
- 把公司决议当形式。 一人公司也需要证明签字人有权出售资产;缺少决议,银行和托管方可能拒绝放款。
- 先交付不可回退的资产。 代码可以备份、域名可以转回,但平台账号、支付通道、主密钥交出去后回退成本很高;资金未到账前不要交。
- 忽略第三方同意。 平台账号、云合同、关键客户合同可能禁止转让;没有同意,买方拿到的是空壳。
- 托管比例只看“会不会被扣”。 10% 托管 12 个月的机会成本是 2,400 美元;20% 托管 24 个月的机会成本是 4,800 美元,谈判时要一起算。
- 延迟成本只算利息。 30 天延迟的主要成本是 12,000 美元现金占用,不是 1,183.56 美元利息;用月度运营成本替换后再算。
- 利息基数含糊。 全部价款、交割日实收、托管余额三个基数对应 1,972.60、1,183.56、197.26 美元,谈判要写清哪一笔延迟。
- 把营运资金调整当罚款。 目标 15,000 美元、实际 12,000 美元,下调 3,000 美元是“少拿到的现金”,不是违约罚金。
- 延迟后直接复用旧清单。 第三方同意会过期、付款凭证会失效、账号密码会被改;新交割日要重新确认阻断项。
本节的边界是“单一买方、固定价格、70/30 现金与 earn-out、10% 托管”的静态模型。它假设托管按总价扣、earn-out 按年支付、延迟只影响现金流,不含跨境预提税、外汇管制、买方融资失败、托管争议诉讼和 earn-out 会计确认。它足够回答“交割日能拿多少、延迟 30 天值多少钱”,但不足以回答“买方违约时能不能强制执行”。后者要把托管协议、赔偿上限和争议解决放在一起看,见 7.3 陈述保证、赔偿与托管 ;交割后的客户迁移见 8.2 客户、合同与账号迁移 。本节不构成法律意见,具体交割文件请由律师审阅。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。