---
url: /docs/agentbuff-stack/affiliates.md
---
# 联盟营销：推荐绑定与后台核查

I14 选用 Rewardful，默认关闭。目前已本地验收签名回调、当前佣金与结算读取、Stripe 收款 / 退款核对，以及公开推荐采集与登录后的个人 / 当前团队设置。推荐选择、权限复核、过期、重复请求和写入响应丢失都有明确状态。浏览器与设置界面使用受控 SDK 协议本地验收；新积分包与个人 / 团队订阅的首次结账冻结、共享客户创建和丢响应恢复已受控本地验收，具体订单 / 佣金联查与管理员只读证据列表已受控本地验收；已知资源人工复核与有界漏回调补查已受控本地验收，真实购买归因与完整外部对账仍待完成。

这是一份供应商核查记录，不是银行流水或自行计算佣金的账本。本站保留 Rewardful 返回的佣金金额，核对相关收款、退款与结算成员；佣金比例、返佣周期和真实付款仍需供应商测试环境验收。当前不会根据这些记录发积分、改变订阅权限或转移资金。

## 配置与依赖

在 `packages/core/website.ts` 中保留显式配置：

```ts
affiliates: {
  provider: "rewardful",
  enabled: false,
  campaignId: null,
  stripeAccountId: null,
  tracking: { enabled: false, publicKey: null },
},
```

要启用后台核查，填写自己的 Rewardful Campaign UUID 和商户 Stripe Account ID，再设置 `enabled: true`。这些是配置标识，不是密钥。API 使用 Stripe 私钥读取自身账户，必须与 `stripeAccountId` 一致；佣金也必须属于该推广计划、该商户账户和相关 Stripe 客户。

服务端增加两个环境变量：

| 变量 | 职责 |
| --- | --- |
| `REWARDFUL_API_SECRET` | Rewardful REST API secret，作为 Basic 认证的用户名；密码为空。当前接受 16—256 个可打印 ASCII 字符且不含冒号，不输出实际值 |
| `REWARDFUL_WEBHOOK_SECRET` | 独立回调签名 secret，16—256 字符；不是浏览器站点标识 |

两项一起设置或一起留空。开启时还必须配置 `sk_test_` 或 `sk_live_` 开头的 `STRIPE_SECRET_KEY`。当前 API 沿用既有 Stripe 四项完整凭据组：私钥、订阅回调 secret、Starter 和 Pro Price ID；不能只填写一个 Stripe 私钥。测试和正式账户、推广计划及密钥分别配置，见[环境变量](./env)。

先检查配置、备份目标数据库，再按[迁移教程](../database/migrations)完整升级至 **0033** 并重建 API。0026 新增 `affiliate_resource`、`affiliate_event` 和 `affiliate_payout_commission`；0027 新增 `affiliate_binding`；0028 新增 `affiliate_checkout` 和 `affiliate_customer_request`，不修改已有账号、积分或支付表。0029 新增独立 `affiliate_order` 证据表；0030 保存订阅编号和结账正文摘要，并将缺少新核查依据的旧已关联记录重新排队，保留其历史订单、金额及核对时间。0031 加固部分空值的证据约束，避免 PostgreSQL CHECK 的未知值放过不完整关联。0032 保存人工核查来源与审计关联；0033 新增 `affiliate_scan` 和补查事件来源，保留旧通知、人工任务和历史操作者。已生成的旧站合入代码时需补齐显式 `affiliates` 配置。正常主预览仍停留在已授权的 0012，主预览缺少新表时仍可运行；关闭后不调用 Rewardful。已发出结账的冻结记录仍允许按原正文恢复，不能因关模块而改写已接受请求。

```bash
bun config:check
bun affiliates:validate
bun affiliates:validate-bindings
bun affiliates:validate-ui
```

第一条离线检查不证明账户权限、商品、推广计划或供应商真实可用。后三条分别验收回调核查、推荐绑定和受控浏览器流程，只使用独立本地数据库，不会替正常预览应用迁移或连接真实 Rewardful / Stripe。

## 公开采集与明确选择

浏览器采集另外配置 `tracking.enabled` 和 Rewardful 的公开浏览器 key。它与服务端 REST / 签名 secret 分开；检查拒绝常见私钥前缀，以及与本机服务 secret 相同的值。开启采集要求联盟后台、计划和商户先配置完整。生成新站时后台和采集均关闭，所有标识归空。

SDK 只在 `/referral` 和已启用西语的 `/es/referral` 页面，经访客点击同意按钮后加载。页面不索引、不进入站点地图，不加载统计；页脚入口只传递有效的 `via` 或 `referral` 参数。加载 SDK 前删除其他查询及 fragment；使用固定官方地址、公开 key 与唯一 `ready` 回调，不调用邮件转换或改写来源命令。实际 SDK 可能设置供应商 Cookie，其真实行为仍需供应商环境验收。

仅在 `ready` 返回有效 UUID 且存储成功时保存候选。候选在当前标签页的 sessionStorage 最多保留 24 小时，按计划 / 商户 / 公开 key 隔离；这个时限不代替后台核对的供应商有效期。未确认、空来源、脚本错误、15 秒超时、存储不可用均不会显示已绑定；迟到回调也不能改变失败状态。失败后可继续使用工具。清除候选会重新加载公开页，但不承诺删除供应商 Cookie 或取消后台已保存的选择。

工作台不加载 SDK，也不将邮箱、账号、团队或工具内容传给浏览器供应商。个人 / 团队首次选择各自有未勾选的确认项；不会自动绑定，也不会把管理员个人选择变成团队选择。切换工作区会重置界面确认，旧请求只能更新原范围。清除本地候选后，`awaiting_customer` / `pending` 仍可明确重试后台已保存的选择；重试接口不需要再次传 UUID，重新检查 Origin、当前会话及数据库权限。附有核对时间的 `attached` 是历史元数据确认，不能显示为佣金到账。

## 登录后的推荐绑定

设置页提供个人和当前团队两个独立区域。受保护的 `affiliates.binding` 查询、`affiliates.attach` 首次选择及 `affiliates.retry` 保存后重试共用后台权限。写入必须来自本站 Origin，带明确的 `accepted: true`、推荐 UUID 和 `scope`。个人范围不能夹带团队 ID；团队范围必须提交与当前会话一致的 `organizationId`，并在新鲜数据库中确认操作者仍为 owner / admin。普通成员、被移除成员和过时工作区会话不能修改团队推荐，团队推荐不会自动归到管理员个人名下。

| 状态 | 含义与下一步 |
| --- | --- |
| `awaiting_customer` | 推荐已由官方列表核实，但本站账单客户还不存在。保留选择，等待既有账单流程创建客户；此操作不会创建客户 |
| `pending` | 已固定客户与推荐，尚未确认元数据结果。服务不可用或响应丢失后，由原账户显式重试同一选择 |
| `attached` | 曾确认固定客户的 `metadata.referral` 与选择一致，附核对时间。不是实时供应商状态、计佣成功或银行到账证明 |
| `review` | 推荐失效、客户被替换、归属矛盾、旧付款 / 未解决结账、尝试达到上限等需要复核。不自行改成已绑定 |

推荐 UUID 格式合法不代表有效。当前使用官方 `GET /v1/referrals`、`limit=100`、`expand=affiliate`，最多检查三页；没有猜测单条推荐接口。检查计划、推荐者、商户、客户、停用与转化状态，以及未绑定访客的明确有效期。列表已结束仍找不到则拒绝；超出三页范围则显示暂时无法核查，不把未知写成不存在。无有效期的未绑定推荐目前也需复核，不推断为永久有效。

每个账单范围固定一个推荐选择，同一商户 / 计划的推荐和同一商户的客户有数据库唯一约束。重试不能更换推荐、计划、客户或偷偷迁移归属。客户 ID 只读取已授权的用户 / 团队账单记录，不接受浏览器传入；客户同时对应多个本站归属时拒绝。决策操作者 ID 作为历史保留，不用于后续授权；每次操作重新校验权限。查询只返回状态、尝试次数和核对时间，不暴露供应商 UUID、客户 ID 或邮箱。

首次写入前读取自身 Stripe 账户、当前客户及其支付 / 结账历史，核对测试 / 正式模式。已有不同推荐元数据、已付正金额、未解决结账、异步付款处理中、无法确定已付金额、历史超过单次 100 条或数据不完整均拒绝，不补记既有付款的归因。写入只更新固定客户的 `metadata.referral`，保留其他元数据；原 `client_reference_id` 不变。五秒外部写入期间持有本站归属和成员锁，权限或客户修改不能插入该事务；供应商控制台或其他外部系统的并发修改仍需真实集成验收。

每个已固定客户的绑定最多尝试八次，租约 90 秒；每个用户写入最多每分钟五次。幂等键来自已持久化的绑定 ID，重试保持正文。如果外部已接受写入却丢失响应，重新读取该客户的相同元数据即可确认，不重复写入，也不因原访客随后过期而丢弃此前接受的结果。未发生写入的过期推荐仍拒绝。Worker 重启后由原账户显式重试；绑定当前没有后台定时扫描或人工重放按钮。最后一次租约过期后，下一次合法重试转为复核。

## 首次购买与原请求恢复

开启联盟模块后，只为新积分购买和新订阅尝试创建结账快照。快照保存首次个人 / 团队范围、当时推荐选择、推广计划、商户和测试 / 正式模式；已有客户也在此时固定。没有推荐选择同样保存，之后选择推荐不会补写旧尝试。模块启用前已有的尝试不补快照，继续既有幂等键和正文；`client_reference_id` 始终表示本站账单归属。

开启时注册不提前创建 Stripe 客户；首次购买才按账单账户创建。积分和订阅共用一份 `affiliate_customer_request`，团队使用团队名称及归属元数据，不发送管理员邮箱。最少的个人名称 / 邮箱与归属参数在 POST 前持久化；幂等键为 `billing-customer:<请求 ID>`。响应丢失后不因资料变化而重建正文，恢复成功并提交固定客户后清除名称 / 邮箱正文。最多尝试八次、租约 90 秒；未知结果超过创建请求起始的 24 小时后必须复核，不自动换键创建新客户。尚未提供人工恢复入口。

已明确保存的推荐，在新结账发送前完成固定客户的元数据绑定。提交前再次读取 Stripe 商户、客户模式、本站唯一归属和当前 `metadata.referral`；历史 `attached` 不能代替当前核对。未选推荐的快照遇到后来出现的推荐元数据也拒绝旧请求，不悄悄补记归因。本站账单客户被替换、记录损坏、回跳站点改变或成员已降权 / 移除，均暂停结账等待复核。

完整 Checkout 正文及其摘要在 POST 前持久化。外部接受但响应丢失后，原积分价格 / 语言 / 回跳地址及订阅试用期、原操作者元数据继续使用；不根据当前配置重构已发送正文。关闭联盟模块后，已冻结且客户已准备的请求仍可重试，不再写入 Rewardful 推荐；未完成客户创建或推荐绑定的请求需恢复配置后继续。整个流程不自动发放购买积分、不确认佣金，也不打款。

Stripe 官方[幂等请求说明](https://docs.stripe.com/api/idempotent_requests)允许相同键重试同一参数，并说明键可能在至少 24 小时后清理；因此未知客户创建不会越过本地保守时限自动重发。归因依据 Rewardful 官方[自定义 Stripe 集成方法](https://help.rewardful.com/en/articles/14859642-custom-stripe-integration-method)，使用 Customer `metadata.referral`；该协议说明不能代替真实付款和计佣验收。

## 回调与当前资源

回调入口是 `POST /api/affiliates/webhook`。关闭模块时返回 404；开启后按原始请求字节计算 HMAC-SHA256，核对 `X-Rewardful-Signature` 的 64 位十六进制值，再解析官方 `object` / `event` 信封。载荷最多 64 KiB，超限返回 413；签名失败 401、信封错误 400、同事件身份冲突 409、无法持久化 503。

回调中的金额与状态只用于唤醒核查，不作为入账证据。当前支持 `commission.created / updated / paid / voided / deleted` 以及 `payout.created / updated / due / paid / failed / deleted`；其他合法事件返回已忽略，不写财务记录。未来时间超过五分钟的事件拒绝。供应商重投可能更换 `request.id`，因此使用 `event.id` 去重，只比较事件类型、创建时间和资源 ID，不把整个载荷摘要当作身份。

先保存事件，再读取当前 Rewardful 资源和商户 Stripe Charge。八个并发重复回调只产生一份事件；已核查事件的重复通知不再调用供应商。较晚到达的旧回调仍读取当前资源，旧版本响应不能覆盖较新记录，同版本出现矛盾也转为待复核。回调核查只使用固定供应商地址的 GET，不跟随重定向，每次请求有五秒期限，Rewardful 响应最多 512 KiB；不保存原始载荷、推荐者邮箱或客户邮箱。登录后的绑定后端另有上述固定客户元数据 POST，不能把整个模块称为只读。

佣金核对包括推广计划、商户账户、客户、币种、测试 / 正式模式、已付款且已捕获的 Charge、争议状态、捕获金额及退款金额。回调不会把外部客户自动归为某个本站账号或订单；本站推荐选择由上述登录后绑定操作建立。当前通过独立证据表关联具体订单，不自行计算佣金比例，也不将供应商状态解释为银行到账。

部分退款可能先出现在 Stripe，稍后 Rewardful 才重算；两方未一致时保留旧记录并等待重试。全额退款后，供应商佣金必须为 `voided` 才可核查成功。作废记录可能仍保留原佣金金额，不强行改成零。原金额及状态只表达当时读取的供应商记录，不代表仍可支付。

## 结算与历史

每份结算最多核对 20 个不重复佣金成员；逐个重读佣金和 Charge，要求推荐者、币种、成员金额一致、没有作废佣金，成员合计等于结算金额。供应商标为 `paid` 的结算还要求成员佣金均为 `paid`。超过限制、重复成员、归属不符、版本矛盾或资源删除转为待复核，不删除此前已核查历史。

相关佣金进入新核查时，原佣金和相关结算设置 `reviewRequired: true`；供应商失败、删除或退款同步滞后不会让旧结算继续被视为刚刚确认。自己的资源完成当前核查后清除此标记；结算需要再次完整核查才能清除。佣金金额、状态或退款发生变化也会重新标记相关结算。退款发生在已标记付款之后，仍保留原 `paid` 历史和金额，不能假装已经追回资金。

Rewardful 的 `paid` 可能来自人工标记，也可能涉及其 Managed Payouts 服务。本站当前只读取状态，不能仅凭该字段宣称银行到账；没有调用供应商付款接口或实现追款。真实打款、费用、币种、退款后扣回和对账仍是外部验收项目。

## 恢复与人工复核

事件状态为 `pending`、`checked`、`review`。回调返回 200 只说明通知已持久接收；响应中的状态才说明核查进度，不代表真实打款成功。`checked` 保存当次核查的金额、币种、状态、退款和供应商更新时间；资源表保存最新已核查快照，关联表保存最新完整核查的结算成员。

每个事件最多尝试八次，包含第一次即时核查。每次有 90 秒租约防止重复执行；到期可由已有维护入口恢复。失败按 30 秒到一小时退避，但实际执行受既有定时入口频率影响，当前为每 15 分钟一次，不保证 30 秒后立即重试。每批最多三个事件；最后一次租约过期会转为待复核，不永远停留在等待。

不可恢复的身份 / 数据矛盾直接转为 `review`；供应商超时、限流、退款同步滞后等继续有限重试。推广计划或商户配置改变后，旧事件不能被当作新计划重新接受。维护故障单独记录错误代码或错误名称，文件与任务维护不因此撤回业务结果；不记录密钥或完整响应。

已知佣金和结算可由站点管理员在 `/admin` 请求重新核查。每次先填写 5—500 字原因，再检查确认内容；提交只排队，不把等待当作核实成功。原始供应商通知保留 `source=rewardful`，人工任务单独记录为 `source=administrator`，不能伪造签名回调。每个资源只能有一项未完成的人工请求；进行中的订单租约不能被重置。

请求固定资源版本、操作者、实际登录会话、原因与独立请求编号。相同编号和内容重试返回原受理结果，不重新排队、不清空已完成结果；编号复用不同内容、资源版本已变化、推广计划 / 商户不符均拒绝。客户端不自动重试写操作；响应丢失时保留已确认内容，再次提交使用同一编号。取消确认不提交写入。每位管理员每分钟最多五次请求，包含重试。

人工流程沿用已有财务核查与订单证据队列，不新增一套后台执行器。先核查当前供应商资源，再核查具体订单；财务尚在等待时不消耗订单尝试次数。每批三个、90 秒租约、每轮八次和既有退避继续适用。佣金进入人工核查会同时标记相关结算待复核，结算要独立重新完整核查才能清除。漏回调可通过下述有界补查发现；不会定期无界扫描全部供应商历史。

排队和外部读取后的提交都重新检查原操作者的数据库角色、封禁、邮箱验证、非匿名身份，以及原会话仍存在、未过期、创建距今不超过 24 小时。会话撤销、降权或过旧会停止确认，结果进入复核；新管理员或新登录不能替旧请求继续确认，需检查记录后重新发起。已经完成的人工请求保留历史来源，后续正常维护不再依赖旧管理员登录。

操作审计分别保存“请求已排队”和“最终结果”，包括原因、操作者、资源、任务及前后状态。佣金最终结果为已关联、无匹配或待复核；结算最终结果为财务已核查或待复核。连续失败或最后一次租约过期也留下终态审计，维护会补齐中断后的完成记录。历史订单归属仍不可改给其他订单，缺少原始推荐或固定结账仍保持无匹配。没有强制确认、补计佣、付款或追款按钮。关闭模块暂停核查并保留数据，不注销远程推广计划。

## 漏回调补查：范围、进度与边界

站点管理员在 `/admin` 的补查面板选择佣金或结算，填写起始页（1—10000）、最多页数（1—3）与原因（5—500 字），检查确认内容后排队。每页最多 20 条，每次维护只读取一个列表页；最多发现 60 条候选，同一推广计划 / 商户 / 类型只能有一个进行中的范围。请求与人工复核共用每位管理员每分钟五次的限额。取消确认不写入；响应未确认时保留原页码、原因和请求编号，再次提交返回原任务，不启动第二次扫描。

官方[佣金列表](https://developers.rewardful.com/rest-api/commissions/list)及[结算列表](https://developers.rewardful.com/rest-api/payouts/list-payouts)按创建时间倒序，[通用分页说明](https://developers.rewardful.com/rest-api/overview)使用页码和下一页。核对日期为 2026-10-11；未核实到更新时间筛选或稳定快照游标。新记录可能使页码偏移，历史旧记录的后续变化也不会自动出现在第一页。因此界面记录所选范围、已读页数、下一页和未覆盖情况，不把翻完几页称为全量对账。需要补后续范围时另行检查并提交；同一范围内的重复记录按类型 / 供应商编号去重。

列表仅用于发现编号。佣金要求展开推广计划并跳过其他计划；结算列表没有已核实的计划筛选，发现后仍逐个核对全部佣金成员及商户。列表金额、邮件或其他正文不作为财务证据，不保存完整列表响应。新事件单独记录为 `source=reconciliation` 并关联原补查请求，不伪装为签名通知或人工资源复核。发现记录进入原财务队列，重新读取当前 Rewardful 对象和 Stripe 收款；具体本站订单关联由原独立订单队列完成。找不到原选择或冻结结账时仍显示无匹配，不补绑历史订单。

进度分为发现记录中、等待财务核查、所选范围处理结束、补查待复核。分别展示发现数、财务核查数、等待数、复核数和跳过其他计划的条数。范围处理结束只表示该范围的列表与财务任务进入终态，不表示全部候选已关联本站订单，也不证明银行到账。财务复核结果可能仍需人工处理；列表停止时已发现但尚未核查的数量会保留，停止审计不写成成功。

列表进度持久保存；服务重启后从原下一页恢复。每页有 90 秒租约，失效或替换的租约不能提交旧进度；连续失败最多八次、30 秒至一小时退避，实际运行仍受每 15 分钟维护入口影响。成功读取一页后重置连续失败次数；最后租约过期可由维护转为待复核。列表领取、列表提交以及发现记录的财务领取 / 提交都重新核对原管理员和近期会话，并核对原推广计划、商户与测试 / 正式模式。权限撤销或配置改变后停止确认，不沿用过期权限。请求和终态分别留下范围、原因、操作者及当时计数的审计。

## 具体订单与管理证据

财务回调完成核查后只排队订单关联，不在回调中追加整条订单查询链路。已有每 15 分钟维护入口最多处理三个到期记录；首批若多于三个，余下记录等后续批次。丢失排队写入时，维护会从已核查佣金补建记录，不能因此撤销商品付款或任务交付。

积分包沿 `Charge → PaymentIntent → purchaseId → 本地购买 → 固定 Checkout` 核对。付款、已捕获金额、币种、测试 / 正式模式、客户、原用户与 SKU 均须一致；本地已发放并完成支付核查才能确认。付款若同时被分配到发票，拒绝猜测用途。

订阅沿 `Charge → PaymentIntent → InvoicePayment → Invoice → Stripe Subscription → 本地订阅 → 首次固定 Checkout` 核对。付款分配使用当前 SDK 的实际 PaymentIntent 过滤参数，不假设 Charge 有 `invoice` 字段。最多读取 100 条付款分配及 100 条结账记录，不能存在下一页；要求全笔已捕获金额只分配到一张已付发票，并找到唯一已完成的原订阅结账。多发票、部分分配、重复成员、歧义结账或远程元数据不符均待复核，不从客户编号单独推算账号。

原推荐必须先于结账保存，原结账须冻结该推荐、客户、个人 / 团队归属及正文摘要。提交关联前再次锁定佣金版本、自己的操作租约、绑定、结账和本地订单，核对实际订阅编号及正文摘要；外部查询期间本地记录变化不能让旧读取结果提交。已保存的订单、付款、发票、范围和结账依据不可被后来的回调重新归给其他订单。

| 关联状态 | 含义与后续 |
| --- | --- |
| `pending` | 等待核查、付款 / 本地退款状态同步或可重试供应商响应；有限重试 |
| `linked` | 在显示的核对时间，具体订单及财务证据一致；后续每 15 分钟到期复核，受每批三个限制 |
| `unmatched` | 缺少原本站推荐、原结账或本地订单证据；保留未归属，不补绑历史购买 |
| `review` | 数据矛盾、读取范围超限、关联发生变化或连续八次未知结果；不能手改为已关联 |

每轮最多八次连续失败、90 秒租约，成功核查后重置失败次数；长期正常复核不会耗尽终身尝试额度。源佣金版本变化会开启新一轮，但历史关联仍保持。旧已关联记录进入等待 / 复核时保留历史编号与金额，界面明确标为“此前关联订单”。积分订单的退款须与本地支付复核一致；存在本地争议或待复核则不能确认。

启用模块后的 `/admin` 提供站点管理员证据列表：佣金 / 结算分类、人工复核进度、具体订单、个人 / 团队范围、积分包 / 订阅类型、发票、收款 / 退款、供应商佣金及核对时间。每页 20 条，刷新只重新读取本站记录，不触发供应商查询或付款。普通用户和团队所有者无站点管理权限；当前数据库角色、封禁与验证状态每次重新确认。关闭模块时隐藏面板并停止可选表查询，旧 0012 主预览仍可运行。

页面不返回客户编号、PaymentIntent、绑定标识、账号邮箱、冻结正文或私钥。英文与西班牙语共用业务结构；美元、英镑、加元、澳元和欧元按百分之一单位展示，其他币种明确标出原始最小货币单位，避免假设所有货币都有两位小数。供应商 `paid` 只表示其标记已付，不能作为银行转账或实际佣金到账证明。缺少真实供应商验收时保持模块关闭。

## 本地验收与新站

`bun affiliates:validate` 使用一次性 loopback PostgreSQL、实际生产 workerd 和受控的官方 HTTP 形状。它验证签名 / 超限、八个并发重复回调、当前资源金额、退款滞后、实际 Worker 销毁重启后的原事件恢复、已付结算成员、全退作废、旧通知、供应商删除、重定向拒绝与八次上限。所有供应商请求均被受控接口截获且只能 GET，没有真实账号、积分发放或资金转移；结束后清理自己的数据库、Worker 和文件，核对正常配置字节未改变。

测试另外执行实际 0026 SQL，确认已有普通账号和积分余额保持；连续两次生成站验证推广配置不继承。`site:create` 将模块关闭、计划与商户 ID 重置为 `null`，新站不会复制服务密钥。

`bun affiliates:validate-bindings` 在已有普通账号、余额和联盟事件的实际 0026 数据库上升级至 0027，确认原数据保持，再使用真实 workerd 验证 Origin / 明确选择、个人 / 团队权限、八个并发请求、等待账单客户、旧付款 / 结账拒绝及 Worker 销毁重启后的丢响应恢复。它还观察 PostgreSQL 的真实 Lock 等待：元数据 POST 期间，组织成员修改必须等绑定事务结束；写入前已降权则拒绝。仅受控的固定客户元数据 POST 被允许，没有访问真实供应商、创建客户 / 结账 / 佣金或转账，验收库和文件随后清理。

当前后端采用官方 Stripe **Customer metadata 的 `referral`** 方案，保留现有 `client_reference_id` 的用户 / 团队及支付归属。公开采集与设置已受控本地验收，原结账幂等与绑定已接入受控本地购买路径，仍需真实供应商付款验收，不能据此宣称已交付完整计佣。

兼容边界仍保留：启用前已经准备的积分尝试没有归因快照，继续原先不含 `customer` 的正文；新尝试才会固定客户及推荐选择。已经尝试创建的积分包 / 订阅可能丢失响应，重试不能增加客户或推荐参数而改变原幂等请求。个人和团队客户按既有权限分别绑定，不从当前页面或工作区切换推算旧购买归属。

官方公开 REST 文档当前提供推荐列表和客户过滤，没有列出按推荐 UUID 获取单条对象的接口。本阶段按上述三页范围实现；大型推广计划超出范围时需要补充官方可核实的定位方案，不无界遍历全部访客或擅自调用猜测地址。

`bun affiliates:validate-checkouts` 在独立 PostgreSQL 中实际从 0027 升级至 0028，核对普通账号、初始积分和旧购买逐字段保持，再运行生产 workerd。它验证注册延迟创建客户、首次积分归因、客户及结账响应丢失后的实际 Worker 重启恢复、关闭模块后仍沿用旧正文、旧积分请求不补客户、个人订阅共用客户、真实 Better Auth 结账恢复、八个并发请求只创建一个客户，以及团队结账 POST 期间真实成员锁与之后降权拒绝。所有供应商请求截获，新增 POST 只允许客户创建、明确元数据绑定和受控 Checkout；没有真实支付或购买积分发放。主库仍为 0012，验收资源会清理。

`bun affiliates:validate-orders` 在独立 PostgreSQL 实际应用 0028→0029→0030→0031→0032→0033，保存普通账号、初始积分、已付购买、推荐和结账；0029 历史已关联记录在 0030 重新排队，事实保持。生产 workerd 验证八次并发通知只排队一次、具体积分 / 团队订阅订单、退款等待本地结算及实际服务重启恢复、管理员降权 / 封禁即时拒绝和连续八次失败转复核。0031→0032 保持已有事件来源为供应商通知，原账号和历史事实保持。实际管理员接口验证当前 Origin、普通账号拒绝、强制状态拒绝、并发重复意图只排队一次、人工任务重启后完成原团队订单与独立结算，并分别保存请求 / 完成审计。此验证使用预先保存的历史付款事实，只读受控供应商，不代表真实付款交付。

加 `--ui` 会构建真实管理界面并开启本地浏览器验收：实际 API 读取、等待 / 已关联 / 无匹配 / 待复核、英 / 西文案、哑光黑白主题及刷新；真实表单提交后的响应主动丢失，再次提交保持同一请求编号，随后通过原维护入口完成并核对终态审计。全部验收资源属于一次性环境，结束后清理。正常预览仍为 0012，没有自动应用 0029—0033 或提升主账号权限。

加 `--scan-ui` 验证实际补查表单。原生验收先保存 0032 的人工任务，升级 0033 后逐字段核对旧来源 / 操作者；在没有本地回调或财务记录时发起有界扫描，重复意图只受理一次。实际销毁并重启 workerd，继续第二页并找回原团队订单。浏览器首次成功提交后主动丢响应，重试同一正文，执行原维护入口后核对范围、财务计数和两阶段审计；英 / 西文案、哑光黑白及 320 / 390 像素布局已检查。所有供应商请求截获，只读 GET，历史付款事实预先保存；没有真实购买积分发放、计佣或转账。

## 官方依据与外部待验

核对日期：2026-10-11。签名与事件依据 [Signed webhooks](https://developers.rewardful.com/webhooks/signed-webhooks)、[Requests](https://developers.rewardful.com/webhooks/requests)、[Event types](https://developers.rewardful.com/webhooks/event-types)；当前对象依据 [Commission](https://developers.rewardful.com/rest-api/commissions/object)、[Payout retrieval](https://developers.rewardful.com/rest-api/payouts/retrieve-a-payout)。客户元数据方案依据 [Custom Stripe Integration Method](https://help.rewardful.com/en/articles/14859642-custom-stripe-integration-method)，浏览器 API 依据 [JavaScript overview](https://developers.rewardful.com/javascript-api/overview)。推荐核查依据 [Referral list](https://developers.rewardful.com/rest-api/referrals/list)、[Referral object](https://developers.rewardful.com/rest-api/referrals/object)与 [REST API overview](https://developers.rewardful.com/rest-api/overview)。订阅分配参考 Stripe [Invoice Payment](https://docs.stripe.com/api/invoice-payment)及 [Charge 对象](https://docs.stripe.com/api/charges/object)，具体过滤字段另以已安装 Stripe 22.6 的类型定义核对。付款边界参考 [人工佣金付款](https://help.rewardful.com/en/articles/2773351-how-do-i-pay-commissions)与 [Managed Payouts](https://help.rewardful.com/en/articles/11930744-merchants-faq-managed-payouts)。

尚未使用真实 Rewardful 账户、浏览器 SDK、推荐链接、Stripe 沙箱付款 / 退款、真实佣金结算或 Cloudflare 定时运行。本地返回值不能证明供应商规则、延迟、实际 Cookie、真实归因或银行到账。完整 I14 与 I0—I16 目标仍进行中，进度见[迭代记录](./iteration-progress)。
