# 应用接入验收

> 账号模式探针现在取得短期身份断言。负向 Launch 购买状态只是购买证据，不证明 Licentivo 运行时拒绝。运行时证明要求官方 SDK 探针；不表示已部署生产环境或发布 SDK。参见[账号职责边界](/zh/launch-documents/account-purchase-boundary/)。

证据分为两级：受控的本地自动检查与外部商户 sandbox 探针。测试替身、成功读取目录、生成配置或健康检查通过，都绝不证明真实付款、许可证签发或激活。本开发迭代已明确延期配置外部服务。

## 本地自动检查

从仓库根目录运行：

```powershell
go test ./...
go vet ./...
node --check internal/httpapi/web/app.js
node --check internal/httpapi/web/integration.js
node --check internal/httpapi/web/integration-prompts.js
node --test scripts/integration-prompts.test.mjs scripts/integration-state.test.mjs
python scripts/test_acceptance.py
./scripts/test-mysql.ps1
```

MySQL 辅助工具在 `.cache` 下创建独立服务器和数据库，绝不能指向客户数据库。未提供独立 DSN 时，普通 `go test ./...` 会跳过数据库场景。SDK 和分发包测试及构建参见 [integration-packages.md](/zh/launch-documents/integration-packages/)。应随发布记录保留测试输出和工具版本。

本地矩阵涵盖认证、条件邮箱验证、产品与环境所有权、pending/expired/suspended 状态、支付事件重放、授权对账、续费退款、账号切换、受保护的会话持久化和离线回退决定。生命周期测试使用受控运行时；实际加密运行验证仍由官方 SDK 负责，并在发布前单独测试。

For **运营工具 → AI 快速接入**, check all nine language choices, platform/project/feature selection, an unconfigured draft, and a prompt generated from validated public configuration. Check copy and download against the visible prompt, and ensure changing the product or environment cannot retain a previous validated configuration. Prompts must exclude sessions, passwords, runtime license/device credentials and management keys; generating or copying a prompt must not call an AI service. The source-bundle Node test command above exercises the pure generator. A generated prompt, or code subsequently produced by an AI tool, still needs the language tests and the external acceptance steps below.

## 准备外部 sandbox 验收

1. 部署专用 Launch 测试实例，提供可访问的 HTTPS 来源并配置支付提供方 Webhook。保持正式销售关闭。
2. 配置**一个**支付提供方的 sandbox 凭据，以及具有读写权限的 Licentivo 连接。支付 sandbox 记录相互隔离，但仍使用已配置的 Licentivo 服务，可能创建真实的测试许可证和设备记录。必须使用专用测试产品、策略和客户。
3. Publish a sandbox plan and use **程序接入** to export `launch-config.json` and validated `product.json` into `.local/acceptance/`.
4. 本地只安装经审核的 Launch Python 包：`python -m pip install ./sdk/python`，或安装其 wheel。包中包含私有 SDK 实现。
5. 使用不计次的测试功能，如 `export`。`--activate` 启动官方 SDK，同步调用 `refresh_online()`，要求在线有效状态，并调用受保护操作 API。只有缓存离线授权无法通过此探针；该探针不测试计次。

凭据通过不回显的提示输入。自动化可在私有进程环境中提供 `LAUNCH_ACCEPTANCE_EMAIL` 和 `LAUNCH_ACCEPTANCE_PASSWORD`。不得将密码放入命令参数、报告、截图或已提交文件。工具在每次认证探针后撤销自己的临时桌面会话，但普通退出不会停用设备。

```powershell
python scripts/acceptance.py preflight --config .local/acceptance/launch-config.json --report .local/acceptance/01-preflight.json
python scripts/acceptance.py verify --config .local/acceptance/launch-config.json --expect not_purchased --report .local/acceptance/02-no-purchase.json
```

工具拒绝正式环境配置、非回环 HTTP、携带凭据的重定向、不匹配产品，以及包含运行时秘密的公开配置。发请求前会拒绝 `verify` 之外的激活或订单断言，以及 `checkout` 之外的结账创建选项。已有报告绝不覆盖。报告只包含白名单证据，不包含原始响应、访问令牌或许可证密钥。缺少服务端或 SDK 会使探针失败，不会生成虚假成功。

本地商户探针使用 `scope: local_sandbox_probe` 和 `licensing_scope: not_called`。即使商户运行在 localhost，`verify --activate` 仍会联系官方外部授权服务；此组合记录为 `local_merchant_external_licensing_probe`，并使用 `licensing_scope: official_external_service`。

## 首次付款与激活

在商户网站创建测试结账，或使用稳定的操作 UUID 创建：

```powershell
$operation = [guid]::NewGuid().ToString()
python scripts/acceptance.py checkout --config .local/acceptance/launch-config.json --provider stripe --plan-version-id PLAN_VERSION_UUID --operation-id $operation --report .local/acceptance/03-checkout.json
```

将提供方和套餐版本替换为你已发布的 sandbox 套餐。结账创建结果不确定时，重试必须保留并复用同一操作 UUID。工具会输出订单 ID。通过商户账号中心，使用提供方官方 sandbox 结账完成此所属订单。探针明确不模拟提供方界面交互。

提供方确认付款且 Webhook 已收到后：

```powershell
python scripts/acceptance.py verify --config .local/acceptance/launch-config.json --order-id ORDER_UUID --expect-order paid --expect active --activate --product-config .local/acceptance/product.json --feature export --report .local/acceptance/04-activation.json
```

此检查验证已认证账号、所属 sandbox 订单、权益、凭据与产品对应关系，以及一次真实的官方 SDK 受保护操作。待处理授权在有界窗口内最多尝试五次，默认 30 秒、最长 120 秒，并遵循 `Retry-After`。意外授权失败会记录，但不暴露 SDK 原始错误文本。

## 生命周期验收矩阵

每个已发布的语言与操作系统组合，都应保存带日期的记录，包含源码修订、Launch/客户端/SDK 版本、提供方环境、匿名客户标签、订单 ID、预期结果、实际结果和相关报告文件名。未执行行标为**未运行**。不能仅凭首次付款探针就将整个矩阵标为通过。

| 场景 | 步骤 | 必需证据 |
| --- | --- | --- |
| 注册 / 验证 | 不购买直接注册，分别在启用和禁用验证时重复 | 禁用验证时不阻止；启用验证时，在完成前返回 `EMAIL_VERIFICATION_REQUIRED` |
| 未购买 | 运行 `verify --expect not_purchased` | 拒绝凭据，不执行受保护操作 |
| 首次购买 | 完成 sandbox 付款，再运行激活探针 | 提供方确认付款；一份已付费权益、一份同步许可证，SDK 操作成功 |
| 重启 | 关闭已记住登录的所选语言示例，启动新进程并恢复 | 不要求输入密码；使用同一账号与产品缓存及有效签名授权 |
| 离线启动 | 成功激活并记住登录后，禁用测试进程或机器的网络，再重启 | 签名缓存授权只允许策略开放的操作；禁止离线或浮动策略拒绝启动 |
| 离线到期 / 时钟回退 | 使用短期测试策略和可丢弃的测试机器 | SDK 拒绝已到期或时钟回退状态；功能回调不执行 |
| 续费 / 升级 | 通过 sandbox 商店购买续费或已报价升级 | 订单和覆盖期正确；同步及 SDK 刷新后可见新的有效期和功能 |
| 退款 | 申请并批准全额 sandbox 退款，等待提供方确认 | 提供方退款与本地覆盖一致；按情况运行 `verify --expect suspended` 或 `--expect expired`；其他付费覆盖仍可使用 |
| 退出 / 切换账号 | 退出 A，使用未购买的 B 登录；重启并观察行为 | 清除 A 已保存的会话和缓存；B 不能使用 A 的许可证、设备或功能 |
| 产品 / 环境隔离 | 使用另一产品，另行切换 sandbox/live 配置 | 凭据和缓存不能跨身份范围复用；外部探针自身拒绝 live |
| 设备已满 / 释放 | 在策略允许的最多不同测试设备上激活，再尝试增加一个，随后明确释放一个 | SDK 拒绝超额设备，确认释放后允许替换；普通关闭不消耗释放额度 |
| 处理方式 | 在待签发期间重启 Launch，重放已签名 sandbox Webhook | 只有一份订单覆盖与许可证，最终完成核对；无重复结账或扣款 |

退款或暂停对已签发离线授权的影响，仍受官方 SDK 签名离线策略约束。不保证断网时即时撤销。合约到期时，长时间 Launch 故障可能延迟上游暂停；参见[合约限制](/zh/launch-documents/licentivo-contract/)。

## 完成与保留证据

每份探针报告明确设置 `acceptance_complete: false`，只记录该次调用实际执行的检查。接受发行版前，应审核矩阵和提供方记录。之后配置外部服务，再继续上述命令；不得用通过标志替代缺失结果，也不能仅因本地测试通过就启用正式销售。
