《独立开发者的商业指南·资本与退出》4.3 交易流程与时间表

交易不是签字那一刻,而是一段 14–25 周、17,000–44,000 美元外部费用的项目。本节把接触、意向书、尽调、签约、交割五个阶段拆开,用 Tidecast、LedgerLite 与 Nord 算交易成本占价格的比例,并把拖 6 个月的机会成本与团队分心成本量化。

以下情境为教学假设,人物与数字不代表经核实的真实案例。

交易不是签字那一刻,而是一段有阶段、有费用、有等待的项目。买方从接触、意向书、尽调、签约到交割,每一步都在消耗卖家的时间;律师、会计师、顾问和托管方则在每一步寄来账单。对一笔 768,768 美元的交易,这些费用可能只占 4%;对一笔 102,960 美元的交易,同样的流程可能吃掉 30% 以上。交易成本不是按价格比例发生的,而是按流程固定发生的,这个差别决定了小生意该不该卖、什么时候卖、卖给谁。

这一节用三个案例做同一张时间表:Tidecast 的锚是 1.8× 收入 = 248,708 美元,LedgerLite 的锚是 3.2× ARR = 768,768 美元,Nord 的锚是 3.0× SDE = 102,960 美元。三个案例走的是同一套五阶段流程,外部费用按同一组教学假设估算。差别只在分母:价格越小,固定成本占比越高。最后把「拖 6 个月」拆成时间价值与团队分心成本,算清楚等待本身值多少钱。

时间表不是承诺。真实交易可能因为买方内部审批、融资安排、关键客户访谈或监管问询而延长;也可能因为买方已经有现成团队而缩短。下面的周数和费用是教学假设,用来建立量级,不是行业标准。卖方能用的是把每个阶段的工作提前准备好,让流程在自己的控制范围内尽量短。

五个阶段:时间与外部费用

阶段主要工作最短–最长周数外部费用(教学假设)
接触与筛选买方名单、NDA、teaser、初次电话2–4 周1,000–3,000 美元
意向书估值、结构、排他期、费用分担2–3 周2,000–5,000 美元
尽职调查财务、客户、技术/IP、法务、数据室6–10 周8,000–20,000 美元
签约definitive agreement、陈述保证、托管、竞业2–4 周4,000–10,000 美元
交割账号、合同、代码、数据迁移2–4 周2,000–6,000 美元
合计14–25 周17,000–44,000 美元

接触与筛选阶段花的是「找对人」的钱。卖方要准备一份不含敏感信息的 teaser、一份买方名单、一份 NDA 模板,然后和潜在买方做初次电话。这个阶段通常 2–4 周,外部费用 1,000–3,000 美元,主要是顾问或平台的服务费、差旅和初步法律咨询。它的目的不是谈价格,而是确认对方有没有钱、有没有战略意图、能不能在合理时间内做决定。找错买方,后面所有阶段都会白做。

意向书阶段花的是「把交易写成条款」的钱。一份 LOI 会写估值区间、现金与 earn-out 比例、托管、排他期、费用分担和大致时间表。它通常 2–3 周,外部费用 2,000–5,000 美元,主要是律师审阅和谈判。LOI 不是合同,但它决定了后面尽调的方向:如果 LOI 写的是资产收购,尽调会围绕资产的可转让性;如果写的是股权收购,尽调会围绕公司的负债和合规。第 7 章会展开资产收购与股权收购的区别。

尽职调查阶段花的是「验证」的钱,也是整个流程最长的部分。买方会查财务、客户、技术、IP、合同、税务和合规;卖方要准备数据室、回答问卷、安排客户访谈、配合代码审计。这个阶段通常 6–10 周,外部费用 8,000–20,000 美元,主要是会计师、律师、技术顾问和数据室服务。尽调不是买方故意拖时间,而是他要在付款前确认 LOI 里的假设;卖方能做的是提前把材料准备好,让问题在一轮内回答完。

签约阶段花的是「把条款写成可执行文件」的钱。definitive agreement 会把陈述保证、赔偿上限、托管、竞业、过渡服务和交割条件写清楚;双方律师会来回修改,卖方还要准备披露函。这个阶段通常 2–4 周,外部费用 4,000–10,000 美元。签约不是终点,它只是把交割条件固定下来;如果签约后发现披露不实,买方仍可以在交割前退出或要求调整价格。

交割阶段花的是「把资产真的交出去」的钱。账号、域名、合同、代码、数据、客户通知和员工安排都要在这一阶段完成;托管方要放款,银行要处理跨境付款,平台要审核账号转让。这个阶段通常 2–4 周,外部费用 2,000–6,000 美元,主要是托管费、迁移工具和最后的法律文件。交割完成后还有过渡期,通常是 1–6 个月,卖方要回答买方的问题、协助客户迁移、处理遗留工单。过渡期的成本通常不计入上表,但它会占用卖方的时间,第 8 章会把迁移清单展开。

五个阶段合计 14–25 周,中位 19.5 周,约 4.5 个月;外部费用 17,000–44,000 美元,中位 30,500 美元。这个中位数是后面所有比例的分母。它不是「一定会花这么多」,而是「如果每个阶段都落在区间中间,交易的外部 friction 是多少」。卖方可以用自己的报价替换区间,但阶段数量和顺序不变。

交易成本占价格的比例

案例价格锚阶段中位成本占价格读法
Tidecast248,70830,50012.3%小交易,固定成本占比高
LedgerLite768,76830,5004.0%大交易,固定成本被摊薄
Nord102,96030,50029.6%更小的交易,流程成本接近三分之一

同样的 30,500 美元,在 LedgerLite 身上是 4.0%,在 Tidecast 身上是 12.3%,在 Nord 身上是 29.6%。这不是因为 Nord 的律师更贵,而是因为分母更小。交易成本的大部分是固定的:一份 definitive agreement 不会因为交易金额小就少写几页,一次尽调不会因为收入低就少查几项,托管和跨境付款也不会按比例打折。小生意的卖方常常只算「卖多少钱」,不算「卖完还剩多少」,结果发现流程费用和创始人时间把收益吃掉了一大块。

29.6% 还只是阶段成本,不含延迟、不含税、不含卖方自己在交易期间少做的业务。如果把「拖 6 个月」的机会成本加进去,Nord 的摩擦会超过 40%。这也是为什么很多小服务工作室最后没有卖:不是没人买,而是交易成本相对于价格太高,卖方宁愿继续经营或关停。第 11 章会把关停成本拆开,和出售的净收益做对照。

对 LedgerLite,4.0% 的阶段成本是可以接受的:一笔 768,768 美元的交易,花 30,500 美元把结构、尽调和交割做干净,换来的是更高的成交确定性和更少的交割后争议。对 Tidecast,12.3% 已经需要认真考虑:如果买方要求 6 个月排他期,卖方在这 6 个月里不能找其他买方,机会成本会进一步上升。对 Nord,29.6% 意味着卖方要先问「这笔交易的非现金收益是什么」:是合伙人退出、是客户延续、还是品牌保留?如果答案只是「拿一笔钱」,交易成本可能高于它带来的自由。

拖 6 个月的机会成本

交易延迟的成本有两块。第一块是时间价值:钱晚到 6 个月,按教学假设 12% 的年折现率,等于价格少了 6%。第二块是团队分心:创始人和团队在交易期间要回答尽调、准备材料、开会、改合同,能用在产品、销售和支持上的时间变少。分心成本按「少掉的 SDE」和「继续支付的工资」两者取大:一人公司没有工资,分心损失的是利润;两人公司继续发工资,分心损失至少是工资。

案例价格6 个月时间价值分心成本合计占价格
Tidecast248,70814,92212,11727,04010.9%
LedgerLite768,76846,12613,02459,1507.7%
Nord102,9606,1789,60015,77815.3%

时间价值的算法是价格 × 12% × 6/12。Tidecast 的 248,708 × 6% = 14,922 美元;LedgerLite 的 768,768 × 6% = 46,126 美元;Nord 的 102,960 × 6% = 6,178 美元。12% 是教学假设,不是无风险利率,也不是任何买方的资金成本;它只是把「晚拿到钱」换算成一个可比较的数字。如果卖方自己的再投资回报更高,时间价值应该更高;如果钱到账后只是放在账上,时间价值可以更低。

分心成本的算法是「年 SDE 或年工资 × 20% × 6/12」。Tidecast 是一人公司,没有单独工资,按 SDE 121,171 × 20% × 0.5 = 12,117 美元;LedgerLite 的 SDE 是 130,240,按 20% 算是 13,024 美元,比创始人工资 60,000 的 20% 算出的 6,000 美元大,所以取 13,024 美元;Nord 有两位合伙人,年工资合计 96,000 美元,按 20% 算是 9,600 美元,比 SDE 34,320 的 20% 算出的 3,432 美元大,所以取 9,600 美元。取大的原因是:分心既让利润变少,也让工资继续烧;对现金流紧的团队,工资口径更接近真实压力。

Nord 的数字值得单独看。它的 SDE 只有 34,320 美元,6 个月分心按 SDE 算只有 3,432 美元,看起来不痛;但两位合伙人继续领工资,96,000 美元的年工资在 6 个月里是 48,000 美元,20% 的分心就是 9,600 美元。如果交易拖到 9 个月,分心成本按工资口径是 14,400 美元,加上时间价值 9,266 美元,合计 23,666 美元,已经接近价格的 23%。对小团队,交易拖延不是「晚一点拿钱」,而是「一边烧工资一边等钱」。

Tidecast 的时间价值占比高,是因为价格相对 SDE 不算低,6% 的折现就是 14,922 美元;它的分心成本 12,117 美元约等于 1.2 个月 SDE。两者相加 27,040 美元,相当于价格的 10.9%。如果交易拖到 9 个月,时间价值 22,383 美元、分心 18,176 美元,合计 40,559 美元,占价格 16.3%。对一笔 248,708 美元的交易,这是卖方在谈判桌上应该要求的补偿,或者至少应该写进排他期的例外条款。

阶段成本加延迟成本:总摩擦

案例阶段成本6 个月延迟成本总摩擦占价格税前净得
Tidecast30,50027,04057,54023.1%191,168
LedgerLite30,50059,15089,65011.7%679,118
Nord30,50015,77846,27844.9%56,682

总摩擦是阶段成本加 6 个月延迟成本。Tidecast 的 57,540 美元占价格 23.1%,税前净得 191,168 美元;LedgerLite 的 89,650 美元占 11.7%,税前净得 679,118 美元;Nord 的 46,278 美元占 44.9%,税前净得 56,682 美元。这里的「税前净得」只扣了交易摩擦,没有扣税、没有扣债务、没有扣卖方在交易期间少做的业务;第 10 章会把税后所得算完整。

这张表最重要的不是具体数字,而是排序:LedgerLite 的总摩擦金额最大,但占比最低;Nord 的总摩擦金额最小,但占比最高;Tidecast 在中间。卖方在决定「卖不卖」时,应该看占比,而不是只看金额。一笔 102,960 美元的交易,摩擦 46,278 美元,税前只剩 56,682 美元;如果卖方还要承担交割后的过渡支持、竞业限制和客户流失风险,这笔交易的吸引力会进一步下降。反过来,一笔 768,768 美元的交易,摩擦 89,650 美元,税前仍有 679,118 美元,流程的绝对成本高,但相对收益仍然成立。

这也是为什么「交易时间表」不是行政细节,而是估值的一部分。买方在报价时会考虑交割确定性,卖方在还价时也应该把摩擦算进去。如果买方要求 6 个月排他期,卖方可以把这段时间的机会成本折成价格让步,或者要求买方承担部分尽调费用、缩短排他期、设置分手费。交易条款里的排他期、费用分担和分手费,本质上都是在分配这 57,540、89,650 或 46,278 美元的摩擦。

写成代码:deal-timeline.ts

下面的 deal-timeline.ts 把五个阶段和延迟成本写成两个函数。summarizeTimeline 要求恰好五个阶段,每个阶段有最短/最长周数和最低/最高外部费用;它把区间加总,取中位,再算中位成本占价格的比例。estimateDelayCost 把延迟拆成时间价值、SDE 分心、工资分心和总成本;分心成本取 SDE 与工资两者中的较大值,避免把同一段时间重复计算两次。

代码不判断交易该不该做,也不预测真实买方的时间表。它只保证三件事:阶段必须齐全、区间必须有效、延迟成本必须能拆开。阶段齐全意味着卖方不会漏掉交割后的迁移;区间有效意味着最低费用不能高于最高费用;延迟成本能拆开意味着卖方可以和买方讨论「这段时间谁承担」。这三件事都能在谈判前准备好,不需要等到律师发来第一版合同。

// file: src/capital/deal-timeline.ts
// tier: L1
// verify: node --test test/capital/deal-timeline.test.ts

import { type Cents } from "../core/money.ts";
import { type Bps, divide } from "../core/rate.ts";
import { type Result, fail, ok } from "../core/result.ts";

export type DealStageKey =
  | "outreach"
  | "loi"
  | "diligence"
  | "signing"
  | "closing";

export const STAGE_ORDER: DealStageKey[] = [
  "outreach",
  "loi",
  "diligence",
  "signing",
  "closing",
];

export const STAGE_LABELS: Record<DealStageKey, string> = {
  outreach: "接触与筛选",
  loi: "意向书",
  diligence: "尽职调查",
  signing: "签约",
  closing: "交割",
};

export type DealStage = {
  key: DealStageKey;
  minWeeks: number;
  maxWeeks: number;
  minCostCents: Cents;
  maxCostCents: Cents;
};

export type TimelineInput = {
  priceCents: Cents;
  stages: DealStage[];
};

export type Timeline = {
  minWeeks: number;
  maxWeeks: number;
  midWeeks: number;
  minCostCents: Cents;
  maxCostCents: Cents;
  midCostCents: Cents;
  costBpsOfPrice: Bps;
};

export type DelayInput = {
  priceCents: Cents;
  annualSdeCents: Cents;
  annualPayrollCents: Cents;
  delayMonths: number;
  discountRateBps: Bps;
  distractionBps: Bps;
};

export type DelayCost = {
  timeValueCents: Cents;
  sdeDistractionCents: Cents;
  payrollDistractionCents: Cents;
  distractionCents: Cents;
  totalCents: Cents;
  totalBpsOfPrice: Bps;
};

function isStageKey(value: string): value is DealStageKey {
  return (STAGE_ORDER as string[]).includes(value);
}

/**
 * External costs only: legal, accounting, advisory, data room and escrow
 * fees. Founder time is not included here because it is priced separately in
 * estimateDelayCost, where a month of delay has both a time-value and a
 * distraction component.
 */
export function summarizeTimeline(input: TimelineInput): Result<Timeline> {
  if (!Number.isInteger(input.priceCents) || input.priceCents <= 0) {
    return fail("price must be a positive integer amount in cents");
  }
  if (input.stages.length !== STAGE_ORDER.length) {
    return fail(`expected ${STAGE_ORDER.length} stages, got ${input.stages.length}`);
  }

  const seen = new Set<DealStageKey>();
  let minWeeks = 0;
  let maxWeeks = 0;
  let minCostCents = 0;
  let maxCostCents = 0;

  for (const stage of input.stages) {
    if (!isStageKey(stage.key)) {
      return fail(`unknown stage ${stage.key}`);
    }
    if (seen.has(stage.key)) {
      return fail(`duplicate stage ${stage.key}`);
    }
    seen.add(stage.key);
    if (
      !Number.isFinite(stage.minWeeks) ||
      !Number.isFinite(stage.maxWeeks) ||
      stage.minWeeks < 0 ||
      stage.maxWeeks < stage.minWeeks
    ) {
      return fail(`stage ${stage.key} weeks must be a valid range`);
    }
    if (
      !Number.isInteger(stage.minCostCents) ||
      !Number.isInteger(stage.maxCostCents) ||
      stage.minCostCents < 0 ||
      stage.maxCostCents < stage.minCostCents
    ) {
      return fail(`stage ${stage.key} costs must be a valid integer range`);
    }
    minWeeks += stage.minWeeks;
    maxWeeks += stage.maxWeeks;
    minCostCents += stage.minCostCents;
    maxCostCents += stage.maxCostCents;
  }

  for (const key of STAGE_ORDER) {
    if (!seen.has(key)) {
      return fail(`missing stage ${key}`);
    }
  }

  const midCostCents = Math.round((minCostCents + maxCostCents) / 2);

  return ok({
    minWeeks,
    maxWeeks,
    midWeeks: (minWeeks + maxWeeks) / 2,
    minCostCents,
    maxCostCents,
    midCostCents,
    costBpsOfPrice: divide(midCostCents, input.priceCents),
  });
}

/**
 * Six months of delay costs money twice. The buyer's price sits in the future,
 * so every month loses time value; and the founder spends part of the month on
 * diligence instead of on the business. Distraction is the larger of the SDE
 * forgone and the payroll still being paid, because a salaried team can burn
 * cash even when profit is thin.
 */
export function estimateDelayCost(input: DelayInput): Result<DelayCost> {
  if (!Number.isInteger(input.priceCents) || input.priceCents <= 0) {
    return fail("price must be a positive integer amount in cents");
  }
  if (!Number.isInteger(input.annualSdeCents) || input.annualSdeCents < 0) {
    return fail("annual SDE must be a non-negative integer amount in cents");
  }
  if (
    !Number.isInteger(input.annualPayrollCents) ||
    input.annualPayrollCents < 0
  ) {
    return fail("annual payroll must be a non-negative integer amount in cents");
  }
  if (!Number.isFinite(input.delayMonths) || input.delayMonths < 0) {
    return fail("delay months must be a non-negative number");
  }
  if (
    !Number.isInteger(input.discountRateBps) ||
    input.discountRateBps < 0 ||
    input.discountRateBps > 10000
  ) {
    return fail("discount rate must be between 0 and 100%");
  }
  if (
    !Number.isInteger(input.distractionBps) ||
    input.distractionBps < 0 ||
    input.distractionBps > 10000
  ) {
    return fail("distraction share must be between 0 and 100%");
  }

  // Basis points and twelve months share the same denominator: 10000 × 12.
  const timeValueCents = Math.round(
    (input.priceCents * input.discountRateBps * input.delayMonths) / 120000,
  );
  const sdeDistractionCents = Math.round(
    (input.annualSdeCents * input.distractionBps * input.delayMonths) / 120000,
  );
  const payrollDistractionCents = Math.round(
    (input.annualPayrollCents * input.distractionBps * input.delayMonths) /
      120000,
  );
  const distractionCents = Math.max(
    sdeDistractionCents,
    payrollDistractionCents,
  );
  const totalCents = timeValueCents + distractionCents;

  return ok({
    timeValueCents,
    sdeDistractionCents,
    payrollDistractionCents,
    distractionCents,
    totalCents,
    totalBpsOfPrice: divide(totalCents, input.priceCents),
  });
}

把 Tidecast 代进去:五个阶段合计 14–25 周,中位 19.5 周,外部费用 17,000–44,000 美元,中位 30,500 美元,占 248,708 美元的 12.3%;拖 6 个月的时间价值 14,922 美元,分心成本 12,117 美元,合计 27,040 美元。LedgerLite 的阶段成本占比 4.0%,6 个月延迟成本 59,150 美元,其中时间价值 46,126 美元、分心 13,024 美元。Nord 的阶段成本占比 29.6%,6 个月延迟成本 15,778 美元,其中时间价值 6,178 美元、工资口径分心 9,600 美元。代码返回的阶段成本 costBpsOfPrice 分别是 1226、397、2962,和正文的 12.3%、4.0%、29.6% 一致;延迟成本 totalBpsOfPrice 分别是 1087、769、1532,对应 10.9%、7.7%、15.3%。

代码的边界是「外部费用与创始人时间」。它不包含税、债务、过渡期工资、客户流失和交割后诉讼;这些要分别放进第 10 章、第 8 章和第 7 章。它也不包含买方的时间成本,因为那部分通常已经体现在报价里。卖方要算的是自己的净得,不是双方的共同成本;把买方的尽调成本也算进来,只会让自己的模型变复杂,不会让谈判更有力。

小结

  • 交易流程分五个阶段:接触与筛选、意向书、尽职调查、签约、交割;教学假设下合计 14–25 周,外部费用 17,000–44,000 美元。
  • 阶段中位成本约 30,500 美元;占价格的比例:Tidecast 12.3%、LedgerLite 4.0%、Nord 29.6%。
  • 交易成本大部分是固定的,小生意的占比更高;Nord 的流程成本接近价格的三分之一,还没算延迟和税。
  • 拖 6 个月的机会成本 = 时间价值 + 分心成本;时间价值按价格 × 12% × 0.5 计,分心成本取 SDE 与工资两者中的较大值。
  • 6 个月延迟成本:Tidecast 27,040 美元(10.9%)、LedgerLite 59,150 美元(7.7%)、Nord 15,778 美元(15.3%)。
  • 阶段成本加延迟成本:Tidecast 57,540 美元(23.1%)、LedgerLite 89,650 美元(11.7%)、Nord 46,278 美元(44.9%)。
  • 税前净得:Tidecast 191,168 美元、LedgerLite 679,118 美元、Nord 56,682 美元;税、债务和过渡期成本尚未扣除。
  • 排他期、费用分担和分手费不是行政条款,它们是在分配交易摩擦;卖方应该把摩擦算进还价。

阅读导航:上一节:4.2 买方如何看独立开发者资产 · 下一节:5.1 ARR/SDE/EBITDA 倍数 。

常见错误与边界

  1. 只算律师费,不算创始人时间。 外部费用只是交易摩擦的一半;拖 6 个月的分心成本可能和律师费一样大,Nord 的工资口径分心就是 9,600 美元。
  2. 用大交易的费率套小交易。 30,500 美元对 768,768 美元是 4.0%,对 102,960 美元是 29.6%;小生意要按固定成本算,不能按比例算。
  3. 忽略排他期的机会成本。 排他期内不能找其他买方,等于放弃了竞争;6 个月排他期的成本至少是时间价值加分心,谈判时要换条件。
  4. 把 LOI 当合同。 LOI 不约束交割,但排他期和费用分担可能约束;签字前要看清哪些条款有约束力。
  5. 把尽调当成买方单方面的事。 卖方要准备数据室、安排访谈、回答问卷;准备不足会让尽调延长,延长就是成本。
  6. 把交割当成终点。 交割后还有 1–6 个月过渡期,账号迁移、客户通知、遗留工单都要人;过渡期成本通常不在阶段费用表里。
  7. 只算税前价格。 交易摩擦、税、债务和过渡成本都要从价格里扣;第 10 章会算税后所得,本节只算到税前净得。
  8. 把 12% 折现率当成唯一答案。 12% 是教学假设;如果卖方能把钱再投资到更高回报的地方,时间价值更高,延迟更贵。
  9. 用 SDE 口径算两人团队的分心。 两位合伙人继续领工资时,工资口径可能比 SDE 口径大;Nord 的 9,600 美元对 3,432 美元就是例子。
  10. 忽略交易失败的概率。 拖 6 个月不只是晚拿钱,还可能最后不成交;时间价值加 distraction 是「成交的延迟成本」,失败时还要加上全部沉没成本。

本节的边界是「交易流程、时间与摩擦成本」:只拆接触、意向书、尽调、签约、交割五个阶段,以及 6 个月延迟的时间价值与分心成本,不展开尽调清单、交易结构、税务和交割后的客户迁移。所有周数、费用、折现率、分心比例和案例数字都是教学假设,不构成投资、税务或法律建议;真实交易的时间表与费用应由专业人员评估。资产收购与股权收购的选择见 7.1 资产收购与股权收购 ,交割清单见 8.1 交割清单与资金流 ,知识转移见 9.3 交接期与知识转移 。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「books」更多文章

  1. 《移动游戏产品的架构·商业化与发行工程》附录 C 商业化与发行评审清单
  2. 《移动游戏产品的架构·商业化与发行工程》附录 B 双案例商业化事实表
  3. 《移动游戏产品的架构·商业化与发行工程》附录 A 商业化与发行术语速查