《独立开发者的商业指南·合同、应收与风险》11.3 客户审查与安全问卷

安全问卷不是合规装饰,而是一笔可以算清的获客成本。本节用 80 工时 × $150 的首次准备、每次 8 工时的复用,算出单次 $12,000 与年 12 次 $25,200 的成本,再对比多签 2 单的 LTV $8,640,说明小客户不问卷、大客户必问卷的分层门槛。

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

安全问卷是 B2B 销售里最像考试的环节:客户发来一份几十页的表,问你的加密方式、访问控制、备份频率、事件响应、子处理者和数据删除。填完之后,客户可能签约,也可能没有下文。对一人公司,问卷最难受的地方不是不会填,而是不知道填一次要花多少时间、能换来多少收入。

LedgerLite 的企业客户会在签约前发安全问卷。问卷通常有三类证据:SOC 2 式自评、渗透测试报告、加密与访问控制证明。这一节把这三类证据拆成工时,算出首次准备和复用的成本,再对比多签 2 单的净得。它不提供问卷模板,也不判断某份问卷是否合理;工时、费率和成交数都是教学假设。

问卷到底在问什么

一份安全问卷看起来有几十个问题,落到证据上通常只有三类。客户不是想知道你「重视安全」,而是想知道三件事:控制是否存在、系统能不能被攻破、数据在传输和存储时是否加密、谁能访问。

证据客户真正想确认的常见形式首次准备工时
SOC 2 式自评控制是否存在、是否持续运行问卷、政策文档、截图40 小时
渗透测试报告系统能不能被攻破第三方测试报告、整改记录24 小时
加密与访问控制证明数据是否加密、权限是否最小化配置说明、IAM 清单、审计日志16 小时
合计80 小时

SOC 2 式自评不是正式认证,而是一套「把你的控制写成客户能读懂的语言」的工作。它占 40 小时,因为要整理政策、流程、角色和证据,不是填一张表。渗透测试报告占 24 小时,主要是选择测试范围、协调外部测试方、跟进整改和整理报告;外部测试费用另计,不在这 80 小时里。加密与访问控制证明占 16 小时,因为要把配置、权限和日志截成客户能核对的证据。

这三类证据的共同点是:它们不是为某一个客户准备的,而是可以复用的。第一次做的时候,你是在建立一套「安全事实库」;后面每来一个客户,只是从库里取材料、补差异。理解这一点,才能理解后面的成本曲线。

准备一次问卷的成本

首次准备按 80 小时、每小时 $150 计算,成本是 $12,000。这个数字对一人公司不小,但它不是每次都要重付。假设同一套答案复用一次只要 8 小时,那么一年 12 次问卷的总工时是 80 + 11 × 8 = 168 小时,成本 $25,200,平均每次 $2,100。

场景首次工时复用工时年问卷数总工时年成本单次平均
只做一次80—180$12,000$12,000
一年 4 次8084104$15,600$3,900
一年 12 次80812168$25,200$2,100

这张表说明,问卷成本的关键变量不是「一次多少钱」,而是「一年几次」和「复用程度」。只做一次,$12,000 全部压在那一单上;做 12 次,单次平均降到 $2,100,但总成本升到 $25,200。复用不是免费的,它仍然要花 8 小时核对客户差异、更新截图、补充说明。

复用成本还会被三类差异推高。第一,客户所在行业不同,问题和证据要求不同;第二,客户规模不同,大型客户会要求更细的访问控制和事件响应材料;第三,产品变化会让旧答案过期,新增数据字段、换云服务、加子处理者,都要重新准备证据。8 小时是「同一套答案、少量差异」的假设;差异越大,复用成本越接近首次成本。

差异来源具体变化对复用工时的教学假设是否要重做证据
行业金融、医疗客户要求更多日志与保留证明+4 到 8 小时部分重做
客户规模大型客户要求渗透测试复测与整改证据+8 到 16 小时重做
产品变化新增数据字段、换云、加子处理者+4 到 12 小时重做
问卷框架客户使用不同框架,问题映射不同+2 到 6 小时不重做,但要重写答案

这张表说明,8 小时复用是一个下限,不是平均值。同一套答案只对「同一行业、同一规模、产品没变、问卷框架接近」的客户成立;四个条件里有一个变化,复用工时就会上升。做年度预算时,用「首次 + 复用 × 次数」算出下限,再按差异表加上浮动,才接近真实投入。

因此,问卷成本应该按「首次 + 复用 × 次数」来预算,而不是按「一次 $12,000」来预算。前者能告诉你一年要投入多少,后者只能告诉你第一单有多贵。

多签 2 单的净得

问卷的价值要用「因为它多签了几单」来衡量。LedgerLite 的客户 LTV 是 $4,320(毛利口径,来自卷一),年付是 $924。假设问卷带来 2 单新增,按 LTV 算是 $8,640,按年付算是 $1,848。

口径新增价值问卷成本净得盈亏平衡单数
首轮,2 单 × 年付$1,848$12,000−$10,15213
首轮,2 单 × LTV$8,640$12,000−$3,3603
复用,2 单 × 年付$1,848$1,200+$6482
复用,2 单 × LTV$8,640$1,200+$7,4401

这张表最诚实的结论是:首轮问卷靠 2 个平均客户收不回来。按年付算,2 单只有 $1,848,连 $12,000 的零头都不够;按 LTV 算,2 单 $8,640,仍然净亏 $3,360,需要 3 单才回本。只有把问卷复用到第二次以后,单次成本降到 $1,200,2 单才开始净得 $648(年付口径)或 $7,440(LTV 口径)。

单客价值变化会直接改变结论。下面四档 LTV 是教学情景,不是客户分层数据:

单客 LTV2 单价值首轮成本首轮净得盈亏平衡单数
$2,000$4,000$12,000−$8,0006
$4,320$8,640$12,000−$3,3603
$8,000$16,000$12,000+$4,0002
$12,000$24,000$12,000+$12,0001

这张表说明,同样 2 单,单客 LTV 从 $4,320 升到 $8,000,首轮净得就从 −$3,360 变成 +$4,000;升到 $12,000,一单就覆盖成本。问卷是否值得做,不取决于问卷本身,而取决于它服务的客户价值。

所以「多签 2 单」本身不是一个足够的理由。首轮问卷要成立,需要满足至少一个条件:新增单数超过 3 单;单客 LTV 明显高于平均;或者这套答案能复用到后续多个投标里。三个条件里,第三个最容易由一人公司控制,也最容易被忽略。

这也解释了为什么大客户和小客户不能用同一套问卷策略。对小客户,$12,000 的首次成本要摊到很低的 LTV 上,怎么算都不划算;对大客户,一次问卷可能打开多轮采购、多个部门、多年续约,复用次数和单客价值都更高。

分层策略:小客户不问卷,大客户必问卷

把客户按「年付」和「LTV」分层,问卷策略就不是「做或不做」,而是「做到什么程度」。

客户层判断依据建议理由
小客户年付 ≤ $924,LTV ≈ $4,320不单独问卷首轮 $12,000 需要 3 个 LTV 才回本
中型客户年付 $924 到 $4,320用标准自评 + 复用答案单次边际成本 $1,200,1 单 LTV 可覆盖
大客户LTV > $12,000 或合同明确要求必做,可接受定制问卷1 单即可覆盖首轮成本
平台型客户数据由平台掌握只提供平台合规材料责任边界在平台,不在开发者

这张表的门槛都来自前面的计算:$12,000 是首次成本,$4,320 是平均 LTV,$1,200 是复用成本。小客户不问卷,不是不重视安全,而是单位经济不成立;大客户必问卷,也不是因为大客户更挑剔,而是单客价值足以覆盖固定成本。

平台型客户是另一种情况。如果用户账号、支付和主要数据都在平台上,开发者能提供的证据有限,问卷的重点会从「你的安全体系」变成「你的构建和更新流程」。这时候不需要重建一套企业安全体系,只需要把平台已经提供的合规材料和自己的发布流程准备好。

Nord Studio 的客户以品牌官网为主,客户数据少,问卷通常停留在标准自评;它不需要为每个项目重做 80 小时。Tidecast 通过 Steam 发行,用户账号与支付数据由平台掌握,开发者能提供的证据更多是构建与更新流程,而不是一套完整的企业安全体系。

对 LedgerLite,分层还意味着不要把 260 家客户都当成企业客户。要求 DPA 和安全问卷的只是一部分;问卷成本要摊到那部分客户上,而不是摊到全部收入上。摊错对象,会得出「问卷很便宜」或「问卷贵得离谱」两个相反的错误结论。

代码:问卷成本与成交净得

下面的 security-review.ts 把这一节的口径固化成两个函数。reviewCost 用「首次工时 + 复用工时 ×(次数 − 1)」算年成本和单次平均;reviewNetGain 用「新增单数 × 单客价值 − 年成本」算净得,并给出盈亏平衡单数。金额用整数美分,工时可以是小数。

// file: src/contract/security-review.ts
// tier: L1
// verify: node --test test/contract/security-review.test.ts

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

export type ReviewCostInput = {
  firstReviewHours: number;
  reuseHoursPerReview: number;
  reviewsPerYear: number;
  hourlyRateCents: Cents;
};

export type ReviewCost = {
  totalHours: number;
  firstReviewCostCents: Cents;
  reuseCostCents: Cents;
  annualCostCents: Cents;
  averageCostCents: Cents;
};

export type ReviewDealInput = {
  annualCostCents: Cents;
  extraDeals: number;
  valuePerDealCents: Cents;
};

export type ReviewNetGain = {
  addedValueCents: Cents;
  netGainCents: Cents;
  breakEvenDeals: number;
};

function validHours(value: number): boolean {
  return Number.isFinite(value) && value >= 0;
}

function validAmount(value: Cents): boolean {
  return Number.isInteger(value) && value >= 0;
}

/**
 * The first questionnaire is mostly a one-time documentation job; later ones
 * reuse the same answers. Total hours therefore grow with the number of
 * reviews, but not proportionally: firstReviewHours + reuse * (n - 1).
 */
export function reviewCost(input: ReviewCostInput): Result<ReviewCost> {
  if (!validHours(input.firstReviewHours)) {
    return fail("first review hours must be non-negative");
  }
  if (!validHours(input.reuseHoursPerReview)) {
    return fail("reuse hours must be non-negative");
  }
  if (!Number.isInteger(input.reviewsPerYear) || input.reviewsPerYear < 1) {
    return fail("reviews per year must be a positive integer");
  }
  if (!validAmount(input.hourlyRateCents)) {
    return fail("hourly rate must be a non-negative integer amount");
  }

  const reuseCount = input.reviewsPerYear - 1;
  const totalHours = input.firstReviewHours + input.reuseHoursPerReview * reuseCount;
  const firstReviewCostCents = Math.round(
    input.firstReviewHours * input.hourlyRateCents,
  );
  const reuseCostCents = Math.round(
    input.reuseHoursPerReview * reuseCount * input.hourlyRateCents,
  );
  const annualCostCents = firstReviewCostCents + reuseCostCents;

  return ok({
    totalHours,
    firstReviewCostCents,
    reuseCostCents,
    annualCostCents,
    averageCostCents: Math.round(annualCostCents / input.reviewsPerYear),
  });
}

/**
 * A questionnaire is worth preparing only when the value of the extra deals
 * it unlocks exceeds the cost. Break-even deals are rounded up: a fractional
 * deal still costs one full questionnaire cycle.
 */
export function reviewNetGain(input: ReviewDealInput): Result<ReviewNetGain> {
  if (!validAmount(input.annualCostCents)) {
    return fail("annual cost must be a non-negative integer amount");
  }
  if (!Number.isInteger(input.extraDeals) || input.extraDeals < 0) {
    return fail("extra deals must be a non-negative integer");
  }
  if (!validAmount(input.valuePerDealCents) || input.valuePerDealCents === 0) {
    return fail("value per deal must be a positive integer amount");
  }

  const addedValueCents = input.extraDeals * input.valuePerDealCents;

  return ok({
    addedValueCents,
    netGainCents: addedValueCents - input.annualCostCents,
    breakEvenDeals: Math.ceil(input.annualCostCents / input.valuePerDealCents),
  });
}

代码里有四个口径值得注意。第一,totalHours 不是「单次成本 × 次数」,而是首次加复用,所以次数越多,单次平均越低,但总成本仍然上升。第二,averageCostCents 用总成本除以次数并四舍五入,适合做报价参考,不适合做现金流预测。第三,breakEvenDeals 用 Math.ceil 向上取整,因为半个客户也要走一整轮问卷。第四,reviewNetGain 要求单客价值大于零;价值为零时无法算盈亏平衡,直接报错比返回无穷大更诚实。

代码不判断问卷是否真的能带来新增单,也不判断客户 LTV 是否稳定。它只把「准备成本」和「成交净得」变成一个可复算的数;新增单数和单客价值仍然是输入假设,换一组假设,结论就会变。

小结

  • 安全问卷通常要三类证据:SOC 2 式自评 40 小时、渗透测试报告 24 小时、加密与访问控制证明 16 小时,合计 80 小时。
  • 首次准备 80 小时 × $150 = $12,000;复用一次 8 小时 × $150 = $1,200。
  • 一年 12 次问卷的总工时是 80 + 11 × 8 = 168 小时,成本 $25,200,单次平均 $2,100。
  • 多签 2 单按 LTV 算是 $8,640,首轮净亏 $3,360,需要 3 单才回本;复用后净得 $7,440。
  • 按年付算,2 单只有 $1,848,首轮净亏 $10,152,需要 13 单才回本;口径不同,结论差很远。
  • 小客户不问卷,因为首轮 $12,000 摊到 $4,320 的 LTV 上不成立;大客户必问卷,因为 1 单就能覆盖首轮成本。
  • 平台型客户的数据在平台,问卷重点应放在构建和更新流程,而不是重建企业安全体系。

阅读导航:上一节:11.2 数据泄露的责任与通知 · 下一节:12.1 风险登记册:概率、影响、责任人 。

常见错误与边界

  1. 把问卷当成一次性合规成本。 首次 80 小时之后,每次复用还要 8 小时;一年 12 次的总成本是 $25,200,不是 $12,000。
  2. 用单次成本乘次数。 正确算法是「首次 + 复用 ×(次数 − 1)」;直接乘会高估总成本,也会高估单次平均。
  3. 只用年付算成交价值。 年付 $924 和 LTV $4,320 差 4.7 倍;用错口径,盈亏平衡单数会从 3 单变成 13 单。
  4. 把「多签 2 单」当成充分理由。 首轮 2 单按 LTV 仍净亏 $3,360;要 3 单才回本,或者靠复用降低成本。
  5. 对所有客户用同一套问卷深度。 小客户不问卷、大客户必问卷;分层依据是 LTV 和复用次数,不是客户态度。
  6. 忽略外部渗透测试费用。 80 小时只是内部工时;第三方测试、复测和整改可能另有报价,要单独计入。
  7. 把 SOC 2 式自评当成正式认证。 自评是把自己的控制写成客户能读懂的语言,不等同于第三方审计报告;两者不能互相替代。
  8. 假设复用成本固定为 8 小时。 行业、客户规模、产品变化都会推高差异;差异越大,复用成本越接近首次成本。
  9. 把平台合规材料当成自己的安全体系。 平台型客户的数据在平台,开发者能证明的是构建和更新流程;不要承诺平台层面的控制。
  10. 把本节数字当成报价。 80 小时、8 小时、$150/小时和 2 单都是教学假设;真实成本要按自己的证据库、客户要求和外部报价重算。

问卷的成本在签约前发生,收益在签约后出现,所以它天然容易被低估:签单时没人会记得那 80 小时,续约时也没人会把它算进获客成本。把它当成一笔可以复用的固定投资,按客户层级分配,才能既不被问卷拖垮,也不因为答不上来丢掉本可以拿下的单。

继续阅读

探索更多技术文章

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

全部文章 返回首页

「books」更多文章

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