以下情境为教学假设,人物与数字不代表经核实的真实案例。
同一笔交易,报价可以一样,卖方最终拿到的钱却可能差几万美元。差别不在谈判技巧,而在结构:买方是买走挑选过的资产,还是买走持有资产的公司。资产收购让买方把一个新主体摆到合同前面,股权收购让旧主体继续存在,只是换了股东。前者要重新签署合同、重新取得同意、重新设置账号;后者把这些动作省掉,但把主体的历史和负债一起交给买方。
这一节把两种结构拆成四个可比较的维度:承接什么、留下什么、税基怎么走、第三方同意要多少。然后用 Tidecast 的资产型交易和 LedgerLite 的股权型交易各算一遍迁移成本,最后把成本放回报价里,看卖方净得差多少。所有单位成本、同意费用和托管之外的数字都是教学假设,不构成法律或税务意见。
两种结构买的是不同的东西
| 维度 | 资产收购 | 股权收购 |
|---|---|---|
| 买方买到 | 挑选过的资产与合同 | 持有资产的主体(股份或成员权益) |
| 合同 | 需要重新签署或取得转让同意 | 留在原主体内,通常只需控制权变更同意 |
| 负债 | 卖方保留未明确承接的负债 | 买方承接主体全部历史负债 |
| 税基 | 买方通常可按资产重新计价 | 通常延续原主体的历史税基 |
| 第三方同意 | 反转让条款、平台条款、授权协议 | 控制权变更条款、贷款与房东同意 |
| 客户关系 | 要逐户通知与换约 | 合同主体不变,客户通常无感 |
| 主体与历史 | 新设或另行指定的主体 | 原主体、原账套、原记录 |
| 适合场景 | 资产边界清楚、合同少、负债可切分 | 合同多、客户关系连续、主体干净 |
这张表里最容易被低估的是「合同」和「负债」两行。资产收购看起来干净,因为买方可以只挑自己想要的东西;但合同不会自动跟着资产走,尤其是带反转让条款的订阅协议、平台账号和授权协议。股权收购看起来省事,因为合同和客户都留在原主体里;但买方买到的是一段完整历史,包括没写进报价的应付、递延收入、税务申报和潜在索赔。
税基是第二个容易被忽略的维度。资产收购中,买方通常可以把价格分配到各类资产,并按分配后的价值重新计价;股权收购中,主体没变,历史税基通常延续。对卖方来说,两种结构的税负可能不同;对买方来说,税基影响未来的摊销和折旧。本节只把它当作价格谈判的一个变量,不展开税法条文,也不给税务建议。
第三方同意则是交割条件,不只是成本。资产收购要逐份确认合同能不能转、平台账号能不能换主体、授权方是否同意;股权收购要确认控制权变更是否触发同意权、贷款是否提前到期、房东是否接受新股东。一个关键同意拿不到,交易可能从「贵一点」变成「做不成」。
Tidecast:四份 IP 转让和一份音乐授权
Tidecast 的资产是代码、美术、音乐和商标组成的 IP 组合,加上 Steam 发行账号。它的收入来自买断制销售,没有 260 份客户合同,所以资产收购的主要工作量不在客户换约,而在 IP 转让和平台账号迁移。音乐是买断授权,不得转授权,这意味着音乐授权方必须同意转让;如果对方不同意,音乐要么从交易里剔除,要么重新谈一份授权。
按教学假设,代码、美术、商标三份转让各 1,500 美元,音乐转让 1,500 美元,音乐授权方同意 5,000 美元,Steam 账号迁移 2,500 美元,新主体设立 1,000 美元。资产收购的迁移成本是 14,500 美元,占 248,708 美元报价的 5.8%。
| 迁移项 | 数量 | 单价 | 小计 |
|---|---|---|---|
| 代码、美术、商标转让 | 3 | 1,500 | 4,500 |
| 音乐授权转让 | 1 | 1,500 | 1,500 |
| 音乐授权方同意 | 1 | 5,000 | 5,000 |
| Steam 账号迁移 | 1 | 2,500 | 2,500 |
| 新主体设立 | 1 | 1,000 | 1,000 |
| 合计 | 14,500 |
如果改成股权收购,Tidecast 的 LLC 继续存在,四份 IP 转让和主体设立都不需要,只剩音乐授权方同意 5,000 美元和账号迁移 1,000 美元,合计 6,000 美元,占报价的 2.4%。两种结构相差 8,500 美元,约 3.4 个百分点。
这个差距不大,原因是 Tidecast 没有客户合同,资产边界也清楚。买方选择资产收购,往往不是为了省迁移成本,而是为了不继承 LLC 的历史负债和税务记录;卖方接受资产收购,往往是因为买方只想要游戏资产,不想接手主体。8,500 美元就是「干净壳」的价格。如果音乐授权根本不能转,差距就不再是钱,而是交易能不能成立。
LedgerLite:260 份订阅合同决定结构
LedgerLite 是另一个极端。它有 260 家付费客户、企业订阅协议、数据处理协议和新加坡主体。资产收购意味着买方用新主体重新签下这 260 份合同;其中 40 家是 enterprise 客户,合同里有控制权变更或转让同意条款。按教学假设,每份合同重新签署成本 120 美元,40 家 enterprise 客户每家额外 250 美元同意成本,账号迁移 8,000 美元,新主体设立 3,000 美元。
| 迁移项 | 数量 | 单价 | 小计 |
|---|---|---|---|
| 标准订阅合同重签 | 220 | 120 | 26,400 |
| Enterprise 合同重签 | 40 | 120 | 4,800 |
| Enterprise 转让同意 | 40 | 250 | 10,000 |
| 支付、云、支持账号迁移 | 1 | 8,000 | 8,000 |
| 新主体设立 | 1 | 3,000 | 3,000 |
| 合计 | 52,200 |
52,200 美元占 768,768 美元报价的 6.8%。更重的一项不在表里:如果买方不承接 22,000 美元的递延收入,这笔负债留在卖方主体里,卖方要么继续履约,要么退款。把迁移成本和留存负债一起扣掉,卖方净得是 768,768 − 52,200 − 22,000 = 694,568 美元。
股权收购则简单得多。260 份合同留在原主体里,不需要逐份重签;只有 40 家 enterprise 客户的控制权变更同意需要处理,按每家 250 美元算是 10,000 美元,账号迁移 2,000 美元,合计 12,000 美元,占报价的 1.6%。卖方净得 768,768 − 12,000 = 756,768 美元。两种结构相差 62,200 美元,约 8.1 个百分点。
这就是为什么 SaaS 交易更常见股权结构:合同数量越多,资产收购的迁移成本越像一笔按客户数计价的税。260 份合同乘以 120 美元只是纸面成本,真正的风险是重签过程中的流失、同意延迟和客户重新询价。股权收购把这些动作省掉,但买方会要求主体干净;如果账套、税务或合同有瑕疵,买方会把风险转成赔偿上限、托管或更低的报价。
把迁移成本算成价格
把两种结构放在同一张表里,差别就很清楚。公式只有一行:卖方净得 = 报价 − 迁移成本 − 卖方保留的负债。迁移成本 = 重签成本 + 同意成本 + 账号迁移 + 新主体设立。股权收购没有重签和新主体设立,但可能有控制权变更同意和承接的负债。
| 方案 | 报价 | 迁移成本 | 留下负债 | 卖方净得 | 迁移占报价 |
|---|---|---|---|---|---|
| Tidecast 资产收购 | 248,708 | 14,500 | 0 | 234,208 | 5.8% |
| Tidecast 股权收购 | 248,708 | 6,000 | 0 | 242,708 | 2.4% |
| LedgerLite 资产收购 | 768,768 | 52,200 | 22,000 | 694,568 | 6.8% |
| LedgerLite 股权收购 | 768,768 | 12,000 | 0 | 756,768 | 1.6% |
这张表只算迁移成本和留存负债,没有算税。资产收购中,买方可能获得资产重新计价的好处,卖方可能面对不同的税务处理;股权收购中,历史税基延续,卖方通常按股权转让处理。这些差异会移动价格,但方向取决于主体所在地、资产构成和持有期限,不能用一个固定比例概括。本节把它们留作谈判变量,不给税务结论。
第三方同意也不能只用单价表示。Tidecast 的音乐授权如果不同意转让,交易可能要把音乐剔除;LedgerLite 的 40 家 enterprise 客户如果有 5 家拒绝同意,买方要么接受 35 家的合同基础,要么要求价格下调。同意成本是「每份 250 美元」,同意风险是「一份不批就可能改变交易边界」。前者可以算进表里,后者只能写成交割条件。
下面的 deal.ts 把两种结构写成同一组输入:合同组、账号迁移、主体设立、承接负债和留存负债。资产收购会对每个合同组计算重签成本,股权收购只对需要同意的合同组计算同意成本;股权收购不允许留存负债,也不允许新主体设立成本,因为这两个字段在概念上不属于股权结构。
// file: src/capital/deal.ts
// tier: L1
// verify: node --test test/capital/deal.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 DealStructure = "asset" | "stock";
export const STRUCTURE_ORDER: DealStructure[] = ["asset", "stock"];
export type ContractGroup = {
count: number;
reSignCostCents: Cents;
consentRequired: boolean;
consentCostCents: Cents;
};
export type StructureInput = {
structure: DealStructure;
priceCents: Cents;
contractGroups: ContractGroup[];
accountTransferCostCents: Cents;
entitySetupCostCents: Cents;
assumedLiabilityCents: Cents;
retainedLiabilityCents: Cents;
};
export type StructureCost = {
structure: DealStructure;
priceCents: Cents;
contractCount: number;
contractsToRepaper: number;
reSignCents: Cents;
consentCents: Cents;
accountTransferCents: Cents;
entitySetupCents: Cents;
migrationCents: Cents;
migrationBpsOfPrice: Bps;
assumedLiabilityCents: Cents;
retainedLiabilityCents: Cents;
sellerNetCents: Cents;
};
export type StructureComparison = {
cheaper: DealStructure | "equal";
deltaMigrationCents: Cents;
deltaSellerNetCents: Cents;
deltaMigrationBpsOfPrice: Bps;
};
function isStructure(value: string): value is DealStructure {
return (STRUCTURE_ORDER as string[]).includes(value);
}
/**
* An asset deal re-papers every contract because the contracting party is a
* new entity. A stock deal keeps the contracts inside the old entity and only
* pays for change-of-control consents. Retained liabilities stay with the
* seller in an asset deal; a stock deal has no retained liabilities because the
* entity itself changes hands. This is a teaching model, not legal or tax
* advice.
*/
export function assessStructure(
input: StructureInput,
): Result<StructureCost> {
if (!isStructure(input.structure)) {
return fail("structure must be asset or stock");
}
if (!Number.isInteger(input.priceCents) || input.priceCents <= 0) {
return fail("price must be a positive integer amount in cents");
}
if (
!Number.isInteger(input.accountTransferCostCents) ||
input.accountTransferCostCents < 0
) {
return fail("account transfer cost must be a non-negative integer amount");
}
if (
!Number.isInteger(input.entitySetupCostCents) ||
input.entitySetupCostCents < 0
) {
return fail("entity setup cost must be a non-negative integer amount");
}
if (
!Number.isInteger(input.assumedLiabilityCents) ||
input.assumedLiabilityCents < 0
) {
return fail("assumed liability must be a non-negative integer amount");
}
if (
!Number.isInteger(input.retainedLiabilityCents) ||
input.retainedLiabilityCents < 0
) {
return fail("retained liability must be a non-negative integer amount");
}
if (input.structure === "stock" && input.retainedLiabilityCents > 0) {
return fail("a stock deal does not leave liabilities with the seller");
}
if (input.structure === "stock" && input.entitySetupCostCents > 0) {
return fail("a stock deal reuses the target entity; no setup cost");
}
let contractCount = 0;
let reSignCents = 0;
let consentCents = 0;
for (const group of input.contractGroups) {
if (!Number.isInteger(group.count) || group.count < 0) {
return fail("contract count must be a non-negative integer");
}
if (
!Number.isInteger(group.reSignCostCents) ||
group.reSignCostCents < 0
) {
return fail("re-sign cost must be a non-negative integer amount");
}
if (
!Number.isInteger(group.consentCostCents) ||
group.consentCostCents < 0
) {
return fail("consent cost must be a non-negative integer amount");
}
contractCount += group.count;
if (input.structure === "asset") {
reSignCents += group.count * group.reSignCostCents;
}
if (group.consentRequired) {
consentCents += group.count * group.consentCostCents;
}
}
const contractsToRepaper =
input.structure === "asset" ? contractCount : 0;
const entitySetupCents =
input.structure === "asset" ? input.entitySetupCostCents : 0;
const migrationCents =
reSignCents +
consentCents +
input.accountTransferCostCents +
entitySetupCents;
const retainedLiabilityCents =
input.structure === "asset" ? input.retainedLiabilityCents : 0;
const sellerNetCents =
input.priceCents - migrationCents - retainedLiabilityCents;
return ok({
structure: input.structure,
priceCents: input.priceCents,
contractCount,
contractsToRepaper,
reSignCents,
consentCents,
accountTransferCents: input.accountTransferCostCents,
entitySetupCents,
migrationCents,
migrationBpsOfPrice: divide(migrationCents, input.priceCents),
assumedLiabilityCents: input.assumedLiabilityCents,
retainedLiabilityCents,
sellerNetCents,
});
}
/**
* Compares the seller's net proceeds under both structures at the same price.
* The comparison is deliberately narrow: it counts migration cost and retained
* liabilities, not the buyer's post-closing risk, tax basis or financing.
*/
export function compareStructures(
asset: StructureCost,
stock: StructureCost,
): Result<StructureComparison> {
if (asset.structure !== "asset" || stock.structure !== "stock") {
return fail("expected one asset deal and one stock deal");
}
if (asset.contractCount !== stock.contractCount) {
return fail("both structures must describe the same contract base");
}
if (asset.priceCents !== stock.priceCents) {
return fail("both structures must use the same headline price");
}
const deltaMigrationCents = asset.migrationCents - stock.migrationCents;
const deltaSellerNetCents = asset.sellerNetCents - stock.sellerNetCents;
const cheaper =
deltaSellerNetCents < 0
? "stock"
: deltaSellerNetCents > 0
? "asset"
: "equal";
return ok({
cheaper,
deltaMigrationCents,
deltaSellerNetCents,
deltaMigrationBpsOfPrice: divide(
Math.abs(deltaMigrationCents),
asset.priceCents,
),
});
}
把 Tidecast 和 LedgerLite 代进去,assessStructure 会返回各自的迁移成本和卖方净得,compareStructures 会告诉你哪种结构更便宜。Tidecast 的资产收购迁移成本 14,500 美元、净得 234,208 美元;股权收购 6,000 美元、净得 242,708 美元,股权结构便宜 8,500 美元。LedgerLite 的资产收购迁移成本 52,200 美元、留存负债 22,000 美元、净得 694,568 美元;股权收购 12,000 美元、净得 756,768 美元,股权结构便宜 62,200 美元。
代码里的 cheaper 只回答「在这组假设下哪种结构让卖方净得更高」,不回答「哪种结构更好」。股权收购可能让卖方多拿 62,200 美元,但买方会要求主体干净、历史负债可控、控制权变更同意可拿到;如果这些条件不成立,买方会把价格压低,或者坚持资产收购。结构选择不是挑一个数字最大的方案,而是挑一个双方都能完成交割的方案。
小结
- 资产收购买的是挑选过的资产,股权收购买的是持有资产的主体;前者要重签合同,后者承接历史。
- Tidecast 的资产收购要处理四份 IP 转让、一份音乐授权同意和 Steam 账号迁移,迁移成本 14,500 美元,占 248,708 美元的 5.8%。
- Tidecast 的股权收购只剩音乐同意和账号迁移,成本 6,000 美元,占 2.4%;两种结构相差 8,500 美元。
- LedgerLite 的资产收购要重签 260 份订阅合同,迁移成本 52,200 美元,加上 22,000 美元留存递延收入,卖方净得 694,568 美元。
- LedgerLite 的股权收购只处理 40 份控制权变更同意,成本 12,000 美元,卖方净得 756,768 美元;两种结构相差 62,200 美元。
- 合同数量越多,资产收购越像按客户数计价的税;SaaS 交易更常见股权结构,但前提是主体干净。
- 税基和第三方同意会移动价格,也会改变交易能否完成;它们不是表格里的单价,而是谈判变量和交割条件。
- 单位成本、同意费用和迁移比例都是教学假设;结构选择要看双方能否完成交割,不是看哪个净得数字最大。
阅读导航:上一节:6.3 技术与知识产权尽调 · 下一节:7.2 现金、票据与 earn-out 。
常见错误与边界
- 把资产收购当成「只买好东西」。 资产收购可以选择资产,但不能自动带走合同;反转让条款、平台条款和授权协议都要单独处理。
- 把股权收购当成「什么都省了」。 股权收购省掉重签,但买方承接主体全部历史负债;账套、税务、合同瑕疵都会回到价格里。
- 只算单价,不算同意风险。 每份合同 120 美元是纸面成本;一份关键同意拿不到,可能让交易边界改变。
- 忽略递延收入。 资产收购中买方不承接的递延收入会留在卖方主体里,卖方要么履约,要么退款,不能当成零。
- 把税基差异说成固定折扣。 资产重新计价和历史税基延续的方向取决于主体、资产和持有期限,不能用统一比例概括。
- 用报价比较结构。 报价相同不代表净得相同;迁移成本、留存负债和同意成本都要从报价里扣掉。
- 忽略股权结构的前置条件。 股权收购更便宜,但前提是主体干净、控制权变更同意可拿到、历史负债可控。
- 把迁移成本当成交割后的事。 迁移成本影响卖方净得,也影响买方报价;谈判时就要放进价格,而不是交割后再追加。
- 把「合同多」当成资产收购的优点。 合同越多,资产收购的重签成本越高;260 份合同乘以 120 美元只是起点,流失和延迟才是大头。
- 把模型当法律或税务意见。 模型只算迁移成本和留存负债;真实交易需要合同、税务和主体层面的专业核查。
本节的边界是「资产收购与股权收购的结构差异」:只讨论承接什么、留下什么、税基方向、第三方同意和迁移成本,不展开合同条款、税务申报或交割清单。所有单位成本、同意费用、迁移比例和留存负债都是教学假设,不构成法律或税务意见。交割清单见 8.1 交割清单与资金流 ,合同承继见 9.1 雇佣与外包合同的承继 。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。