# 多应用分享获客与返佣（v2）

管理入口 `/console/referrals`，推广人入口 `/referrals`。新应用默认关闭。此版本提供归因、佣金账本、审核和实际线下付款的登记；不自动打款，不自动把已有 Stripe 订单导入，不追溯过去购买，不按拉人头计佣。多层的启用地区和比例须由运营方确定；系统的技术层数上限并不表示适用于所有市场。

## 接入第三方 App / SaaS

1. 管理员选择应用，启用计划并设置比例。例如 `10,3,2` 对应直接推荐人 10%、上一级 3%、再上一级 2%；默认无比例。最多 5 层、合计不超过 50%。冻结期 1–365 天，归因窗口 1–90 天。
2. 使用 `LaunchBaseClient.referralLink(appScopedAccessToken)` 获取专属链接。`/r/CODE` 校验应用和计划后，仅重定向到该应用登记的 `apps.url`，附带签名 `lb_ref` 凭证。它是可转发的推荐归因凭证，不是登录令牌。点击本身不产生报酬。
3. 应用落地页保存 `lb_ref`（服务端 HttpOnly/SameSite Cookie 优先），移除地址栏参数。用户登录后，后端调用 `claimReferral(accessToken, ticket)`。身份来自该应用 SSO 令牌，不能传任意 userId 冒充。第一次有效归因生效，不覆盖推荐人，首笔已记录订单后不允许补绑。已注册但从未下单的用户也可以被归因；不是仅限新注册获客。
4. 应用后端在校验支付平台签名及实际支付后，调用 `reportReferralOrder({orderId,userId,amount,currency})`。userId 必须来自认证账户，不信任客户端提交的订单金额或付款成功标志。`amount` 是排除税费后的可佣金净额，整数最小货币单位，1–1,000,000,000。当前支持 USD/EUR/GBP/CAD/AUD/SGD/HKD/CNY/AED/SAR/MYR/THB/PHP（均为两位小数），不转换币种。调用需应用 API Key。每次续费使用独立的发票/订单号；重试使用原号。
5. 退款调用 `reportReferralRefund({orderId,refunded})`；refunded 为累计退还的可佣金金额。重复/较旧累计值忽略，超过原价拒绝。退款先于订单到达时返回 409，应用必须先补报订单再重试退款。所有失败事件应进入调用方的持久重试队列，保留审计证据。
6. 管理员核对实际支付和推广关系后审核该订单。审核不自动释放冻结期；到期后可线下付款，填写唯一转账参考号登记结算。登记不会转账，也不会自动从支付平台拉取核对结果。

```ts
const client = new LaunchBaseClient({baseUrl:'https://contextbase.vip',clientId:'your-app',apiKey:process.env.LAUNCHBASE_API_KEY});
const {url} = await client.referralLink(accessToken);
await client.claimReferral(accessToken, signedReferralTicket);
await client.reportReferralOrder({orderId:'invoice_123',userId:authenticatedUser.id,amount:990,currency:'usd'});
await client.reportReferralRefund({orderId:'invoice_123',refunded:300});
```

## 算法与账本规则

- 图：每个应用一张不可改绑的单父推荐图，拒绝自荐、循环；单次遍历硬上限 100。应用级事务锁保护并发归因、配置和入账，避免双重支付/重复入账。此锁保证正确性但限制单个大型应用吞吐；目前未做生产压力测试。
- 计佣：`floor(净额 × 层级基点 / 10000)`，BigInt 中间计算。逐层向下取整，尾差留给商户，不随机分配。无上级则停止、不压缩层级、不把缺失佣金补给其他人。
- 快照：订单创建时固化推荐人、层级、比例、原始佣金和冻结截止时间。改比例只影响将来订单。
- 幂等：应用+外部订单号唯一；相同号但不同用户/金额/币种返回 409。跨应用的订单号互不影响。
- 退款：累计冲回 = 原佣金 − `floor((原净额 − 累计退款) × 原比例 / 10000)`；不按每笔退款单独四舍五入，避免拆分退款套利。已结算后退款保留负余额，下次结算抵扣，不删除历史账目。
- 审核/结算：未审核或未到冻结期金额不能结算；按用户+应用+币种分别核算，转账参考号在应用内唯一。结算与退款串行化。重复参考号不同金额/收款人/币种返回 409。
- 风控范围：已防自荐、循环、重放重复入账、跨应用混账和未成熟提现；尚无设备指纹、多账号团伙识别、人工身份核验、黑名单、自动争议同步或自动打款。不要把这些尚未实现的功能当成“智能反作弊”。
- 普通用户仅查看自己的佣金，不返回购买人的姓名、邮箱、订单号或推荐链。服务端和数据库拒绝未授权写入。新表启用 RLS，无 public API 策略；只能由受控后端连接访问。

## API

- `GET /api/console/referrals?appId=ID`：管理员配置、最近 200 条佣金/结算、完整聚合余额。
- `POST /api/console/referrals`：管理员+同源；`{appId,action:'configure'|'approve'|'settle',data}`。
  - configure: `{enabled,rates:[1000,300,200],holdDays:30,windowDays:30}`（rates 为基点）。
  - approve: `{orderId}`（基座内部订单 ID）。
  - settle: `{userId,currency,amount,reference}`（amount 为最小货币单位）。
- `GET/POST /api/referrals`：中央会话，自己的账本 / 创建链接；POST `{appId}`。
- `GET /api/v1/referrals`：应用用户令牌，自己的账本。
- `POST /api/v1/referrals`：`{action,data}`；link/claim 使用应用用户令牌，order/refund 使用应用 API Key。`claim` data 是 `{ticket}`；其他结构见 SDK。

## 迁移与上线

本地数据库初始化执行增量 v2。Cloudflare 必须先用 `node --import tsx scripts/migrate-referrals.ts` 显式迁移生产数据库（DATABASE_URL、DATABASE_SSL_CA 来自安全环境），随后构建部署。不需要改动现有会员或支付记录。数据库迁移不自动启用任何推广计划。部署后先用独立测试应用完成归因、订单、退款、负余额和结算验收，再为真实应用配置比例。现有业务应用不会因为更新基座就自动拥有分享按钮或支付回调调用；必须按上述协议接入。
