# CDK v1.5.1 统一项目接入与验收标准

状态：当前唯一的新项目接入标准
API 版本：`1.5.1`
运行时契约修订：`project-contract-v1.5`
适用对象：CDK 管理库、所有注册机、提炼平台、协议支付台和未来新增项目

> 新项目只使用本文件、`GET /api/v1/openapi.yaml`、`GET /api/v1/projects/{project_key}/integration` 和 `GET /api/v1/projects/{project_key}/capabilities`。历史 v1.1-v1.5.0 文档仅用于旧项目兼容，不得作为新项目施工入口。v1.5.1 是可选能力与文档发布，运行时仍返回 `api_version=1.5.0`、`project-contract-v1.5` 和 `project-capabilities-v1`，避免已接入项目因未知版本被 fail-closed 阻断。

## 1. 总体边界

CDK 管理库是唯一授权账本；项目拥有自己的本地用户、业务配置、任务和账号数据。中央库只保存项目授权身份、CDK、绑定、到期、审计摘要和必要的加密配置版本。

```mermaid
flowchart LR
  A[CDK 管理库] -->|签发项目 CDK / 项目客户端密钥| B[项目后端]
  C[项目用户] -->|项目登录界面输入 CDK| B
  B -->|HMAC + 项目 CDK / 会话| A
  A -->|账号 ID、绑定 ID、有效期、能力| B
  B -->|仅展示脱敏授权状态| C
  A -->|管理员查询| D[中央 CDK 管理台]
  B -->|项目用户、绑定、状态同步| A
```

以下数据永远不能跨项目读取、修改或复用：普通 CDK、项目客户端密钥、注册账号、账号配置、绑定、业务任务、会话和工作区。

注册机账号的中央唯一键为：

```text
project_key + username_normalized + client_user_ref
```

同名用户在不同项目中是两个独立账号；CDK 到期、禁用、撤销或融卡都不删除项目用户配置。

## 2. 项目类型决定开卡和界面

| 项目类型 | `integration_mode` | `entitlement_model` | 开卡字段 | 项目用户界面 |
| --- | --- | --- | --- | --- |
| 注册机 | `registration` | `time_only` | 名称、客户标识、有效时长、注册并发上限、备注 | CDK 登录、授权状态、到期、换卡、项目内融卡；不显示任务额度、任务并发、渠道 |
| 批量接入提炼/协议支付 | `task_api` | `quota_only` | 名称、额度、最大并发、备注 | CDK 状态、动态渠道、预检、任务、阶段、额度与并发；不显示有效期 |
| 历史任务项目 | `task_api` | `quota_and_time` | 按已登记契约 | 仅为旧项目兼容，不能作为新批量接入模板 |
| 历史纯授权 | `cdk_access` | `unlimited` | 按项目契约 | 只显示契约声明的功能 |

项目不得凭前端判断字段。必须先读取：

```text
GET /api/v1/projects/{project_key}/integration
GET /api/v1/projects/{project_key}/capabilities
```

返回的 `integration_mode`、`entitlement.model`、`card_form.fields`、`hidden_fields`、`card_policy`、`required_endpoint_ids`、`feature_flags` 和 `endpoints` 是唯一渲染依据。未知 `contract_revision` 必须停止接入，不得猜路径或回退到本地授权。`registration_project_merge` 是 v1.5.1 的可选能力：只有 feature flag 为 `true` 且端点存在时才显示项目内融卡入口。`task_api` 项目必须发现 `task_preflight`；旧 `preflight` 仅为同一路由的兼容能力 ID，不能作为新接入闸门。

### 2.1 批量接入固定项目闸门

以下键由中央服务种子、项目更新和每次访问三处共同校验。项目端必须精确匹配三元组，不能根据渠道、CDK 前缀或旧项目名推断或改写：

| `project_key` | `integration_mode` | `entitlement_model` | 渠道规则 |
| --- | --- | --- | --- |
| `grok_registration` | `registration` | `time_only` | 不使用任务渠道 |
| `free_registration` | `registration` | `time_only` | 不使用任务渠道 |
| `image_generation_registration` | `registration` | `time_only` | 不使用任务渠道 |
| `cloud_sms_receiver` | `registration` | `time_only` | 不使用任务渠道 |
| `platform_extract_43211` | `task_api` | `quota_only` | 已登记动态渠道 |
| `protocol_payment_43212` | `task_api` | `quota_only` | 仅 `protocol.paypal.agreement` |

旧 `registration_core`、`extract_platform` 和 `protocol_console` 保持原项目键、卡和历史数据，仅作兼容；它们不会自动别名到上表的新键。迁移必须由管理员为目标项目单独签发 CDK、项目客户端密钥并完成本地 mock 回归，不能靠请求中省略 `project_key`、复制旧客户端密钥或跨项目换卡来放行。

## 3. 管理员开项目与开卡流程

### 3.1 新项目开通

```mermaid
flowchart TD
  A[管理员创建 project_key] --> B{项目类型}
  B -->|注册机| C[固定 registration + time_only]
  B -->|批量提炼/协议| D[固定 task_api + quota_only]
  C --> E[生成项目客户端密钥，仅显示一次]
  D --> F[配置允许的 channel_id 映射]
  E --> G[签发测试 CDK]
  F --> G
  G --> H[项目执行本地合规测试]
  H --> I[云端 test 验收]
  I --> J[等待正式版授权]
```

管理员必须固定以下资料：

```text
project_key             永不复用的项目标识
name                    显示名称，可改
integration_mode        registration / task_api / cdk_access
entitlement_model       registration 只能是 time_only；固定批量 task_api 项目只能是 quota_only
entry_path              项目入口地址或路径
project client key      一个项目一组独立 HMAC 密钥；可由管理员撤销
channel_ids             仅 task_api 项目允许配置
contract_revision       项目接入时记录
```

### 3.2 开卡

注册机只开时长卡。有效期是**一个字段**：预设按钮或手动输入都只是填入 `validity_seconds`，不能同时存在两个互相叠加的“有效期”和“手动时长”表单。`registration_concurrency_limit` 是独立的卡级活跃注册租约上限，不扣次数或额度，也不复用任务卡的 `max_concurrency`。遗留值缺失或为 0 的时长卡会审计迁移为 1，不删除卡、账号、绑定或历史。

```mermaid
flowchart LR
  A[选择 registration 项目] --> B[填写名称、客户标识、有效时长、备注]
  B --> C[中央创建普通项目 CDK]
  C --> D[仅本次显示完整 CDK]
  D --> E[复制功能只复制 CDK 本身]
  E --> F[未使用：不开始计时]
  F --> G[第一次成功项目登录]
  G --> H[activated_at + expires_at]
```

批量开卡结果必须是一行一张的纯 CDK 文本；不能混入编号、指纹、额度、项目名或说明。后台卡库只显示掩码、指纹、项目、状态和到期时间，完整 CDK 只在创建、轮换或项目内融卡成功时返回一次。

管理员 CDK、平台总管理员业务 CDK 和 `super_admin_api_key` 是三类不同凭证：

- `super_admin_api_key` 仅用于开卡、项目、账本、审计和配置管理，禁止作为项目用户 CDK 登录。
- 普通 CDK 只能进入所属项目。
- `admin_cdk` 只有显式 `registration_scope=global` 才能跨注册机项目做业务登录，仍不能调用平台管理接口。

## 4. 注册机首次登录、到期、续期和换卡

项目必须有登录页面。登录页面最少包含：项目名称、CDK 输入、项目本地用户名、错误提示、当前授权状态；不能把管理员接口密钥当成用户 CDK 输入。

```mermaid
sequenceDiagram
  participant U as 项目用户
  participant P as 项目后端
  participant C as CDK 管理库
  U->>P: 输入项目 CDK + 本地用户
  P->>C: client-preflight (HMAC + Bearer CDK)
  C-->>P: 只读状态，不启用时效
  P->>C: registration/login (HMAC + CDK)
  C->>C: 创建或恢复 account + binding
  C->>C: 首次成功登录才开始计时
  C-->>P: account_id、binding_id、一次性会话、到期
  P-->>U: 已登录、到期时间、剩余时长
  P->>C: session/heartbeat
  C-->>P: 授权有效或过期/撤销错误
```

注册机最小接口集：

```text
GET  /api/v1/cdk/status?project_key={project_key}
GET  /api/v1/projects/{project_key}/integration
GET  /api/v1/projects/{project_key}/capabilities
POST /api/v1/projects/{project_key}/registration/client-preflight
POST /api/v1/projects/{project_key}/registration/login
POST /api/v1/projects/{project_key}/registration/session/status
POST /api/v1/projects/{project_key}/registration/session/heartbeat
POST /api/v1/projects/{project_key}/registration/session/logout
POST /api/v1/projects/{project_key}/registration/leases
POST /api/v1/projects/{project_key}/registration/leases/{lease_id}/heartbeat
POST /api/v1/projects/{project_key}/registration/leases/{lease_id}/commit
POST /api/v1/projects/{project_key}/registration/leases/{lease_id}/release
POST /api/v1/projects/{project_key}/registration/account/status
POST /api/v1/projects/{project_key}/registration/rebind
POST /api/v1/projects/{project_key}/registration/merge
```

项目后端请求使用项目客户端密钥签名，签名原文固定为：

```text
timestamp.METHOD.path.sha256(canonical_json_body)
```

其中 `canonical_json_body` 使用 UTF-8、对象键排序、无多余空格；空 body 按 `{}` 签名；`path` 是实际请求路径且不含 query string。每个注册机项目都必须附带：

```text
X-Registration-Client-Key
X-Registration-Timestamp
X-Registration-Signature
```

`registration/login` 和 `registration/rebind` 中的 CDK 只在请求 body 短暂存在。运行期间所有会话请求使用 `Authorization: Bearer <registration_access_token>`；该令牌由登录或换卡成功时返回一次。

### 4.1 项目安装级技术绑定（registration-installation-v1）

注册机后端若不需要把本地用户资料同步到中央，可直接复用上述 `registration/login` 和 `registration/leases` 路径的技术安装模式。登录 body 只允许 `cdk`、随机 `client_run_id`（可选的 `project_key` 必须与路径完全一致）；不得提交 `username`、密码、`client_user_ref`、业务账号、工作区或任务资料。中央按固定 `username={project_key}` 和服务端生成的不可逆 installation ref 建立一条项目安装级技术绑定，不将本地用户资料写入账号配置。

技术安装会话返回与普通会话相同的一次性不透明 Bearer token，但只显示掩码绑定和 `installation_ref_fingerprint`。租约请求沿用 `/registration/leases`：使用技术会话时从 Bearer 会话推导绑定，不需要也不得提交本地身份字段；`client_run_id`、幂等键和 `registration_concurrency.effective_limit` 仍按既有规则生效。过期或撤销卡拒绝新登录/新租约，旧租约仍可查询和 release。技术安装绑定不等同于本地用户 binding，不能调用账号配置、工作区和用户换卡接口。

开始本地注册工作前，项目以 HMAC 和运行时 Bearer 创建租约，并带上本地目标 `requested_concurrency`。中央响应的 `registration_concurrency` 给出 `card_limit`、`active_count`、`available_slots` 和收敛后的 `effective_limit`；本地 worker 数必须不超过该有效值。达到上限时中央稳定返回 `registration_concurrency_full`，项目显示中文错误并等待释放、心跳超时或重启恢复后再试。`quota_only` 的提炼/协议项目不得发送或渲染该字段。

CDK 到期后的固定行为：

```mermaid
flowchart TD
  A[到达 expires_at 或后台禁用/撤销] --> B[中央关闭项目会话]
  B --> C[项目停止创建新任务或注册]
  C --> D[保留本地用户、中央 account、项目配置和历史]
  D --> E{处理方式}
  E -->|管理员给原卡充值| F[状态恢复，原绑定保留]
  E -->|用户输入新卡| G[registration/rebind]
  G --> H[旧 binding = replaced]
  H --> I[account_id 与配置版本不变]
  F --> J[重新登录或恢复未完成工作]
  I --> J
```

项目不得出现 `if not enabled: return ok` 这类本地放行。CDK 未配置、过期、禁用、撤销、项目不匹配、会话无效或中央网络不可用时，必须停止新业务并显示对应稳定错误码。

中央同步检查由管理员使用以下接口完成，项目端不得自己写入中央账本：

```text
GET /api/v1/admin/projects/{project_key}/registration/accounts
GET /api/v1/admin/projects/{project_key}/registration/accounts/{client_user_ref}/history
GET /api/v1/admin/projects/{project_key}/registration/overview
POST /api/v1/admin/projects/{project_key}/registration/client-keys/{key_id}/revoke
```

管理员能查看项目用户、当前绑定、掩码 CDK、到期、最近活动和 `replaced` 历史；不会返回完整 CDK、客户端密钥、密码、Cookie、AT 或项目工作区内容。项目客户端密钥泄露、测试结束或项目停用时，管理员必须调用 `revoke` 使其立即失效；接口只返回掩码记录、`status=revoked` 和撤销时间，重复撤销保持幂等且不签发替代密钥。

## 5. 项目内批量融卡

项目内融卡是注册机用户页面的功能，中央管理台只保留管理员查询和异常处理入口。项目页面至少提供多行 CDK 输入、融卡确认、仅 CDK 的一次性复制结果、当前账号剩余时长和失败原因。

```mermaid
flowchart TD
  A[当前已登录的注册机用户] --> B[粘贴多行 CDK]
  B --> C[项目后端 HMAC + 当前 registration_access_token]
  C --> D[POST registration/merge]
  D --> E{中央原子校验}
  E -->|失败| F[返回稳定错误码，不改变任何源卡]
  E -->|成功| G[合计所有源卡剩余秒数]
  G --> H[创建一张新项目时长卡]
  H --> I[源卡状态 merged，项目用户侧隐藏]
  I --> J[原账号保留，旧绑定 replaced]
  J --> K[返回新 CDK 明文一次 + 新会话]
  K --> L[项目只显示并复制新 CDK]
```

项目级融卡接口：

```text
POST /api/v1/projects/{project_key}/registration/merge
Authorization: Bearer <registration_access_token>
X-Registration-Client-Key: <project client key>
X-Registration-Timestamp: <unix timestamp>
X-Registration-Signature: <HMAC>

{
  "source_cdks": ["第一张 CDK", "第二张 CDK"],
  "device_summary": "optional-safe-label"
}
```

固定规则：

1. 至少两张、最多一百张，必须属于同一 `project_key`。
2. 必须包含当前登录账户正在使用的 CDK。
3. 仅允许 `customer_cdk + time_only + active` 源卡；管理员卡、额度卡、已过期、已禁用、已撤销、已融卡或存在活动注册租约的源卡全部拒绝。
4. 每张已绑定源卡只能属于当前账户；其他账户的卡不能被合并。
5. 新卡的 `validity_seconds` 等于所有已启用卡的剩余秒数，加上未启用卡的完整有效秒数；任一永久时长源卡会产生永久新卡。
6. 融合后源卡对项目用户等同删除，永久不可再用；中央库保留 `merged` 记录、账本和审计用于追溯，不向项目页面展示为可用卡。
7. 新卡、账号和绑定均为同一项目；账号 `account_id`、项目配置与本地用户数据不变。
8. 响应中新卡明文和新会话都只返回一次，项目只能在内存或安全会话中使用，不能写入日志、普通设置或前端持久化存储。

## 6. 提炼与协议支付项目流程

`task_api` 项目和注册机不同：使用额度、最大并发、动态渠道、预检和云端任务阶段。批量接入项目使用 `quota_only`，因此不启用或展示有效期。项目必须从 API 获取目录，不能抓网页或复制云端脚本。

```mermaid
flowchart LR
  A[GET channel-catalog] --> B[选择 channel_id + revision]
  B --> C[POST task_preflight]
  C -->|不占额度| D{校验通过}
  D -->|否| E[显示稳定错误]
  D -->|是| F[POST tasks + Idempotency-Key]
  F --> G[queued/running/retry_scheduled/waiting_sms]
  G --> H[extract]
  H --> I[payment]
  I --> J[sms]
  J --> K[plus]
  K -->|succeeded + plus_enabled=true| L[最终成功并扣费]
  K -->|failed/cancelled/timeout| M[释放预留，不伪造成功]
```

每个 `task_preflight`、`POST /api/v1/tasks` 和兼容的 `POST /api/v1/link-tasks` 请求都必须显式携带 `project_key`。`task_preflight` 的实际路由仍是 `POST /api/v1/preflight`，旧能力 ID `preflight` 与它指向同一兼容路由；新项目必须以 `task_preflight` 作为发现闸门。服务端先验证该项目为已启用 `task_api`、再验证其渠道映射和 CDK 项目边界；缺失、未知或模型不匹配不得从渠道历史归属或默认项目回退。

`task_preflight` 永远不创建任务、不占并发、不预留额度、不开始时效；只有成功标准明确达成时才扣费。相同 CDK、项目和 `Idempotency-Key` 只有在规范化脱敏请求体哈希一致时才返回原任务；哈希不同固定返回 `idempotency_conflict`，不创建第二个任务或预留。

## 7. 项目端实现结构

每个接入方必须有独立适配器，不能把 CDK 判断散落在业务代码：

```text
CdkRemoteClient          请求、HMAC、响应脱敏、超时重试
ProjectContractLoader    读取 capabilities 与 contract_revision
RegistrationBindingStore 保存 account_id、binding_id、到期和掩码信息
ProjectAccountStore      保存项目本地用户和项目配置，不保存 CDK 明文
CdkGuard                 在登录、任务创建、worker 执行、恢复前强制校验
ProjectUiState           登录、到期、换卡、融卡和错误状态展示
```

项目客户端密钥和会话令牌只能加密存储在项目后端。普通 CDK 只可以在登录、预检、换卡或融卡请求中短暂处理；不得写 Git、日志、页面历史、导出文件或数据库明文。

## 8. 新项目施工与验收门禁

```mermaid
flowchart TD
  A[本地创建项目配置] --> B[本地接入适配器与登录界面]
  B --> C[本地模拟 API 合规测试]
  C -->|失败| B
  C -->|通过| D[本地完整项目测试]
  D -->|失败| B
  D -->|通过| E[本地 Git 独立提交]
  E --> F[云端 test 部署同一提交]
  F --> G[云端 test API + WebUI 深度验收]
  G -->|失败| B
  G -->|通过| H[向用户交付验收结果]
  H --> I[仅用户明确授权后覆盖正式版]
```

所有项目都必须满足：

- 本地是唯一 Git、备份与回退源；test 不作为备份，不允许在 test 热改代码。
- 先本地测试，再云端 test；正式版只在明确授权后覆盖。
- 云端部署仅覆盖代码和必要迁移，不能覆盖数据、账号池、CDK、日志、配置、密钥或运行目录。
- 每一个逻辑改动独立 Git commit；每次真实测试说明“本地测试”或“云端 test 测试”。

### 8.1 注册机验收清单

1. 读取契约并确认 `integration_mode=registration`、`entitlement_model=time_only`。
2. 登录页只显示 CDK 登录相关字段，不显示任务额度、任务并发或渠道；本地 worker 必须按 `registration_concurrency.effective_limit` 收敛。
3. `client-preflight` 不创建账号、绑定、会话或租约，也不开始计时。
4. 首次 `registration/login` 创建一个中央账户和绑定，并开始计时。
5. 中央管理台按 `project_key` 能查询到该项目用户、当前绑定、掩码 CDK、到期和状态。
6. 同一用户重复登录不创建第二个账户或第二个绑定。
7. 过期、禁用、撤销后，心跳和新业务被拒绝；账号配置仍存在。
8. 充值原卡或 `rebind` 新卡后，账号 ID 和配置版本保持不变，旧绑定为 `replaced`。
9. 批量融卡后，新卡时长正确，源卡不可用、旧会话关闭、账号和配置保留。
10. 普通 CDK 跨项目失败；平台业务管理员不能调用开卡、账本、审计等管理接口。

### 8.2 task_api 验收清单

1. 动态目录、渠道版本和代理档案均从中央 API 获取；能力发现必须含 `task_preflight`，并调用其声明的 `POST /api/v1/preflight` 路由。
2. 预检不占位、不扣费；版本不匹配、渠道下线、代理缺失有稳定错误码。
3. 同一个 `Idempotency-Key` 不重复创建任务或占位。
4. 失败、取消、超时和重启恢复释放预留；仅真实最终成功扣费。
5. `extract -> payment -> sms -> plus` 阶段可查询；只有 `plus_enabled=true` 才能作为开通成功。

## 9. 公开入口和版本规则

```text
GET /api/v1/openapi.yaml
GET /api/v1/integration-guide/latest
GET /api/v1/integration-guide/v1.5.1
GET /api/v1/projects/{project_key}/integration
GET /api/v1/projects/{project_key}/capabilities
GET /api/v1/errors
```

`latest` 和 `v1.5.1` 指向本标准。兼容路径仍保留，但新项目不得只读取旧版本文档。项目验收报告必须记录文档版本、运行时 API 版本、`contract_revision`、`capabilities.revision`、项目 `project_key` 和所用测试提交。

## 10. 稳定错误处理

项目必须展示稳定 `error.code`，不要根据中文错误文本判断。注册机融卡重点错误：

| 错误码 | 项目动作 |
| --- | --- |
| `registration_merge_needs_multiple` | 要求至少两张不同 CDK |
| `registration_merge_current_required` | 要求把当前登录卡加入列表 |
| `registration_merge_card_bound_other_account` | 拒绝并提示该卡属于其他用户 |
| `registration_merge_model_mismatch` | 拒绝额度卡或非注册机卡 |
| `registration_merge_status_forbidden` | 拒绝过期、禁用、撤销或已融卡 |
| `concurrency_full` | 仅 task_api 的任务额度/并发已满 |
| `registration_concurrency_full` | 注册机卡级活跃租约已满；显示中文提示并按 `retry_after_seconds` 重试 |
| `registration_cdk_expired` | 展示续卡/换卡入口，保留本地配置 |
| `registration_session_closed` | 清除本地会话，返回登录页，不删除本地账号 |
| `project_channel_not_allowed` | 刷新该项目 capabilities；不得用其他项目渠道改写 project_key |
| `project_access_denied` | 使用该项目独立签发的 CDK；普通卡不能跨项目调用 |
| `project_key_required` | 补充明确的 task 项目键；不得从渠道或默认项目推断 |
| `project_contract_mismatch` | 停止调用并由项目管理员修复固定三元组 |
| `idempotency_conflict` | 使用新的 Idempotency-Key；不要重试或复用原键发送不同请求体 |

网络错误、5xx、`upstream_timeout` 和 `upstream_unavailable` 可以按有限重试策略处理；权限、项目隔离、参数、过期、撤销和状态错误不可伪造成功。
