本节不提供任何税务意见。 下面讲的是「平台要你填哪张表、系统里怎么落这条规则」的工程问题;表单怎么填、你的具体税务结论是什么、能不能主张协定优惠,请咨询持牌税务专业人士。这一点先写在最前面。
站内那篇出海合规与税务风险清单 是一份风险清单:哪些地区有哪些坑、要办哪些手续、留哪些凭证。本节不重写那份清单,只把其中最容易被程序化的一段——平台侧的税表选择与预扣规则——做成一个可跑的工程落点。前两节(11.1 / 11.2)把「钱怎么来」算清楚了,这一节处理「钱在合规上要过哪一关」。
为什么会有 W-8BEN 这张表
海外平台(尤其是美国主体或经美国通道结算的平台)在给非美国收款方打款前,通常要求你声明税务身份。这个声明落在几张标准表单上:W-9 给美国纳税人用,W-8BEN 给非美国个人用,W-8BEN-E 给非美国实体用。表单不是走过场——它决定两件事:要不要预扣,以及用哪张信息申报表(给美国人的 1099,还是给非美国人的 1042-S)。填错表单(比如个人错填实体表),轻则被要求重填、打款延迟,重则被按最高默认税率预扣。
三张表单,一个选择逻辑
选择逻辑其实只有两个分叉。第一刀:你是美国人吗?是,走 W-9,通常不预扣,申报用 1099。不是,走第二刀。第二刀:你是个人还是实体?个人填 W-8BEN,实体填 W-8BEN-E。预扣率则取决于有没有主张税收协定优惠:主张了(且成立)用协定率,没主张用默认率。这条逻辑干净得可以直接写成代码——但**「协定优惠是否成立」是税务判定,不是代码能替你做主的**,代码只负责在给定前提下把结果算出来。
D 层:表单选择表
先把这张表写成纯数据。forms[].entity 是每张表单适用的主体类型;withholding 里放默认预扣率、协定预扣率与两张信息申报表的名字:
{
"version": 1,
"game": "merge-island",
"observedAt": "2026-10",
"note": "跨境结算的税务表单选择表。口径是「公开可观察机制」的建模假设(观察时点 2026-10,非实测),仅用于工程落点:forms[].entity=该表单适用的主体类型(individual/entity);withholding.defaultRatePermille=无税收协定时的默认预扣千分比(示例取 300=30%);withholding.treatyRatePermille=主张协定优惠后的示例预扣千分比;withholding.reportingForm=非美国人的信息申报表(1042-S);withholding.usForm=美国人的信息申报表(1099)。表单适用性、预扣率与协定优惠须以真实税务判定为准,命令行不可复现(X)。全整数,无浮点。本表不构成税务意见。",
"forms": [
{ "id": "w9", "name": "W-9", "who": "美国纳税人(个人或实体)", "entity": ["individual", "entity"] },
{ "id": "w8ben", "name": "W-8BEN", "who": "非美国个人的受益所有人", "entity": ["individual"] },
{ "id": "w8ben-e", "name": "W-8BEN-E", "who": "非美国实体的受益所有人", "entity": ["entity"] }
],
"withholding": {
"payerCountry": "US",
"defaultRatePermille": 300,
"treatyRatePermille": 100,
"reportingForm": "1042-S",
"usForm": "1099"
},
"applicants": [
{ "id": "cn-indie", "entityType": "individual", "isUsPerson": false, "taxResidence": "CN", "hasTreaty": true },
{ "id": "de-studio", "entityType": "entity", "isUsPerson": false, "taxResidence": "DE", "hasTreaty": true },
{ "id": "us-llc", "entityType": "entity", "isUsPerson": true, "taxResidence": "US", "hasTreaty": false },
{ "id": "sg-dev", "entityType": "individual", "isUsPerson": false, "taxResidence": "SG", "hasTreaty": false },
{ "id": "fr-entity", "entityType": "entity", "isUsPerson": false, "taxResidence": "FR", "hasTreaty": true }
]
}
hasTreaty 只是建模开关,表示「该收款方是否主张了协定优惠」;它是否成立、能主张到什么税率,属于真实税务判定(X)。这些预扣率是示例值,不是任何真实税率。
P 层:表单选择与预扣内核
下面这段是 P 层 TypeScript 内核 core/src/g11/taxform.ts。它读同一份 config/g11_tax_form.json,把「主体类型 + 是否美国人 + 是否主张协定」映射成应填表单、预扣千分比与信息申报表。它只做规则映射,不构成任何税务意见:
// file: core/src/g11/taxform.ts
// tier: P
// verify: node --test 'core/test/g11_*.test.ts'
//
// 税务表单选择与预扣内核:读 config/g11_tax_form.json,按主体类型与税务居民身份
// 选出应填表单、给出预扣千分比与信息申报表。**本内核只做工程落点的规则映射,
// 不构成任何税务意见;表单填写与税务结论请咨询专业人士。**
// 全整数、地板除、无浮点、无随机、禁 Math.random;只用可擦除语法(禁 enum / namespace)。
export interface TaxForm {
readonly id: string; readonly name: string; readonly who: string;
readonly entity: readonly string[];
}
export interface WithholdingRule {
readonly payerCountry: string; readonly defaultRatePermille: number;
readonly treatyRatePermille: number; readonly reportingForm: string; readonly usForm: string;
}
export interface Applicant {
readonly id: string; readonly entityType: string; readonly isUsPerson: boolean;
readonly taxResidence: string; readonly hasTreaty: boolean;
}
export interface TaxConfig {
readonly version: number; readonly game: string; readonly observedAt: string;
readonly forms: readonly TaxForm[];
readonly withholding: WithholdingRule;
readonly applicants: readonly Applicant[];
}
export interface TaxDecision {
readonly id: string; readonly form: string; readonly formName: string;
readonly withholdingPermille: number; readonly reportingForm: string;
readonly netPermille: number;
}
/** 表单选择:美国人 → W-9;非美国实体 → W-8BEN-E;非美国个人 → W-8BEN。 */
export function selectForm(a: Applicant): string {
if (a.isUsPerson) return "w9";
return a.entityType === "entity" ? "w8ben-e" : "w8ben";
}
/** 预扣千分比:美国人无预扣;非美国人主张协定优惠则用协定率,否则用默认率。 */
export function withholdingPermille(a: Applicant, w: WithholdingRule): number {
if (a.isUsPerson) return 0;
return a.hasTreaty ? w.treatyRatePermille : w.defaultRatePermille;
}
/** 信息申报表:美国人 1099,非美国人 1042-S。 */
export function reportingForm(a: Applicant, w: WithholdingRule): string {
return a.isUsPerson ? w.usForm : w.reportingForm;
}
/** 预扣后净额:金额 − 金额 × 预扣千分比(地板除)。 */
export function netAfterWithholding(amountMinor: number, ratePermille: number): number {
return amountMinor - Math.floor((amountMinor * ratePermille) / 1000);
}
function formName(cfg: TaxConfig, id: string): string {
const f = cfg.forms.find((x) => x.id === id);
return f === undefined ? id : f.name;
}
/** 对所有申请人批量决策。 */
export function decideAll(cfg: TaxConfig): TaxDecision[] {
return cfg.applicants.map((a) => {
const form = selectForm(a);
const rate = withholdingPermille(a, cfg.withholding);
return { id: a.id, form, formName: formName(cfg, form),
withholdingPermille: rate, reportingForm: reportingForm(a, cfg.withholding),
netPermille: 1000 - rate };
});
}
/** 跨语言对拍摘要:主体:表单:预扣千分比:申报表。 */
export function digest(cfg: TaxConfig): string {
return decideAll(cfg)
.map((d) => `${d.id}:${d.form}:${d.withholdingPermille}:${d.reportingForm}`)
.join(",");
}
配套测试 core/test/g11_taxform.test.ts 把一份独立手推的金标准摘要写死,断言内核逐字符相等,并逐项验证三分支表单选择、三档预扣率、两张申报表与整数净额:
// file: core/test/g11_taxform.test.ts
// tier: P
// verify: node --test 'core/test/g11_*.test.ts'
//
// 硬断言:表单选择 / 预扣千分比 / 信息申报表的规则映射与独立金标准摘要逐字符一致。
// 本测试只验工程落点,不构成任何税务意见。
import { test } from "node:test";
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { dirname, join } from "node:path";
import { digest, decideAll, selectForm, withholdingPermille, reportingForm,
netAfterWithholding, type TaxConfig, type Applicant } from "../src/g11/taxform.ts";
const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
const cfg = JSON.parse(readFileSync(join(ROOT, "config", "g11_tax_form.json"), "utf8")) as TaxConfig;
const who = (id: string): Applicant => cfg.applicants.find((a) => a.id === id)!;
// 独立算出的金标准摘要(按规则表逐条手推,与实现无关)
const EXPECTED = "cn-indie:w8ben:100:1042-S,de-studio:w8ben-e:100:1042-S,"
+ "us-llc:w9:0:1099,sg-dev:w8ben:300:1042-S,fr-entity:w8ben-e:100:1042-S";
test("跨语言一致:TS 表单决策摘要 == 独立金标准", () => {
assert.equal(digest(cfg), EXPECTED);
});
test("表单选择:个人/实体、美国/非美国三分支", () => {
assert.equal(selectForm(who("cn-indie")), "w8ben"); // 非美国个人
assert.equal(selectForm(who("de-studio")), "w8ben-e"); // 非美国实体
assert.equal(selectForm(who("us-llc")), "w9"); // 美国实体
});
test("预扣千分比:美国人 0、协定 100、无协定 300", () => {
const w = cfg.withholding;
assert.equal(withholdingPermille(who("us-llc"), w), 0);
assert.equal(withholdingPermille(who("cn-indie"), w), 100);
assert.equal(withholdingPermille(who("sg-dev"), w), 300);
});
test("信息申报表:1099 与 1042-S 分界", () => {
assert.equal(reportingForm(who("us-llc"), cfg.withholding), "1099");
assert.equal(reportingForm(who("cn-indie"), cfg.withholding), "1042-S");
});
test("预扣后净额:整数地板除", () => {
assert.equal(netAfterWithholding(100000, 100), 90000); // 预扣 10%
assert.equal(netAfterWithholding(100000, 300), 70000); // 预扣 30%
assert.equal(netAfterWithholding(100000, 0), 100000); // 无预扣
});
test("批量决策:五个主体的净留存千分比", () => {
const d = Object.fromEntries(decideAll(cfg).map((x) => [x.id, x.netPermille]));
assert.equal(d["us-llc"], 1000);
assert.equal(d["sg-dev"], 700);
assert.equal(d["de-studio"], 900);
});
在本机跑 node --test 'core/test/g11_taxform.test.ts',6 个用例全过:
$ node --test core/test/g11_taxform.test.ts
✔ 跨语言一致:TS 表单决策摘要 == 独立金标准
✔ 表单选择:个人/实体、美国/非美国三分支
✔ 预扣千分比:美国人 0、协定 100、无协定 300
✔ 信息申报表:1099 与 1042-S 分界
✔ 预扣后净额:整数地板除
✔ 批量决策:五个主体的净留存千分比
ℹ tests 6
ℹ pass 6
ℹ fail 0
摘要本身也是税表规则回归测试的基准:
cn-indie:w8ben:100:1042-S,de-studio:w8ben-e:100:1042-S,us-llc:w9:0:1099,sg-dev:w8ben:300:1042-S,fr-entity:w8ben-e:100:1042-S
从摘要读出的三条工程结论
第一,表单选择是纯规则映射:五个主体里,cn-indie 与 sg-dev 两个非美国个人都选 W-8BEN,de-studio 与 fr-entity 两个非美国实体都选 W-8BEN-E,us-llc 选 W-9。这条逻辑可以完全自动化——把主体类型与税务身份作为配置项,表单类型由代码唯一确定。第二,预扣差异是量级差异:同样是中国人主体,主张协定(cn-indie,100‰)与未主张(sg-dev,300‰)在示例里差了 20 个百分点;一笔 100000 分的收入,预扣后分别是 90000 与 70000 分。第三,申报表跟着身份走:美国人拿 1099,非美国人拿 1042-S——收款方不会「选」申报表,它是身份的副产品。这三条合起来说明:税务表单的「选择」在系统里是确定性的,但「结论」不是——协定优惠成立与否、实际预扣率、要不要在居住地补税,全在代码边界之外。
这些属于 X 层:真实税务不可复现
下面这些属于 X 层,需真实业务环境,命令行不可复现:
- 你的真实税务居民身份与受益所有人认定(不是你自己在配置里填个
true就算); - 税收协定优惠是否成立、能主张到什么实际税率(示例里的
100‰只是占位); - 平台真实的预扣与申报行为(何时扣、扣多少、报给哪个税务机关);
- 在居住地是否需要就这笔境外收入补税、如何申报。
工程上正确的做法是:把「规则映射」和「税务判定」分开——代码负责前者(选哪张表、按给定的率算净额、产出可回归的摘要),专业人士负责后者。系统里所有涉及税率的字段都应标注为「可配置的建模参数」,绝不硬编码成「真实税率」。
自检表
| 检查项 | 合格标准 |
|---|---|
| 是否提供税务意见 | 否,正文与内核均显式声明「请咨询专业人士」 |
| 表单选择可复算 | 由主体类型 + 是否美国人唯一确定 |
| 预扣口径 | 整数千分比,净额地板除,无浮点 |
| 申报表跟随身份 | 1099 / 1042-S 由身份决定,非人为选择 |
| 参数可配置 | 预扣率是建模参数,不硬编码为真实税率 |
| 边界清晰 | 真实税务判定 / 协定成立 / 补税标 X |
本节把「填税表」从「一件法律事务」拆出了一个可自动化的工程内核:表单选择、预扣计算、申报表映射,全都由规则唯一确定,且摘要可回归。而真正需要专业人士判断的部分,被显式划在代码边界之外。本章三节到此收尾:门槛(11.1)、汇率与费用(11.2)、税表(11.3)——跨境结算这条链上,能算的都用整数算清楚了,不能算的都标了 X。下一章进入出海风控,先看内容与版权的地区差异。
阅读导航:上一节:11.2 汇率、手续费与到账周期 · 下一节:12.1 内容与版权的地区差异 。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。