# ChatGPT 订阅激活 API

面向已签约客户的服务端接口。用一把 API Key 提交卡密与 ChatGPT Session，为指定账号开通 ChatGPT 订阅，并轮询激活结果。

| | |
|---|---|
| 接口地址 | `https://www.academicgate.org` |
| 版本 | `v1`（路径内定版，见 [13. 版本策略](#13-版本策略)） |
| 认证 | `X-API-Key` 请求头 |
| 传输 | HTTPS only，TLS 1.2+ |
| 编码 | 请求与响应均为 `application/json; charset=utf-8` |
| 自助激活页 | `https://www.academicgate.org/store`（可直接转给终端用户，用户无需 API Key；支持 Plus / Pro 5x / Pro 20x / Pro 500，见 8.3） |

> **一句话上手**：`POST /api/v1/gptplus/redeem` 提交 `card_code` + `session_data` 拿到 `task_id`，再每 3 秒 `GET /api/v1/gptplus/task/{task_id}` 直到 `finished: true`。轮询中若出现 `action_required` 字段，按 [5.6 安全验证](#5-6-查询任务结果) 让用户完成一次验证。

---

## 1. 快速开始

```bash
export API_KEY="aisub_你的Key"
export BASE="https://www.academicgate.org"

# 1) 校验 Key 是否可用
curl -s "$BASE/api/v1/ping" -H "X-API-Key: $API_KEY"

# 2) 看支持哪些套餐（别把卡密前缀硬编码在你那边）
curl -s "$BASE/api/v1/products" -H "X-API-Key: $API_KEY"

# 3) 提交激活（session_data 为 https://chatgpt.com/api/auth/session 的完整 JSON）
curl -s -X POST "$BASE/api/v1/gptplus/redeem" \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json; charset=utf-8" \
  -d '{"card_code":"PLS-XXXXXXXXXXXXXXXX","session_data":{"accessToken":"eyJ...","user":{"email":"user@example.com"}}}'

# 4) 轮询结果（每 3 秒一次）
curl -s "$BASE/api/v1/gptplus/task/替换为task_id" -H "X-API-Key: $API_KEY"
```

---

## 2. 套餐与卡密

**档位由卡密前缀自动判定，请求中无需也不应包含任何档位参数。** 把卡密原样传入即可，系统会识别其所属套餐并按相应方式处理。

这样设计是为了杜绝一类事故：档位一旦由调用方指定且指定错误，会被计为一次有效提交，直接消耗卡密次数且不可退。交由前缀判定则从根本上不存在这种可能。

| 套餐 | `product` | 卡密前缀 | 示例 | 凭据 |
|---|---|---|---|---|
| ChatGPT Plus | `gpt_plus_self` | `PLS-` | `PLS-XXXXXXXXXXXXXXXX` | Session JSON |
| ChatGPT Pro 5x | `gpt_pro_5x_self` | `P5X-` | `P5X-XXXXXXXXXXXXXXXX` | Session JSON |
| ChatGPT Pro 20x | `gpt_pro_20x_self` | `P20X-` | `P20X-XXXXXXXXXXXXXXXX` | Session JSON |
| ChatGPT Pro 20x 续费 | `gpt_pro_20x_renew` | `PH20-` | `PH20-XXXX-XXXX-XXXX` | Session JSON |
| ChatGPT Pro 500 | `gpt_pro_500_self` | `P500-` | `P500-XXXXXXXXXXXXXXXX` | Session JSON |

这张表以 `GET /api/v1/products` 的实时返回为准。套餐与卡密前缀可能调整，接口会先于文档更新，**请按接口返回做提示与分流，不要在你的代码里写死前缀列表**。此前发放的其他前缀卡密仍可正常兑换，但不会出现在目录里——**本地校验只做提示、不要据此拒收**，以服务端返回为准（格式不对返回 `invalid_card_format`，不消耗卡密）。

### 2.1 套餐选用说明

- **Plus / Pro 5x / Pro 20x**：卡密前缀 `PLS-` / `P5X-` / `P20X-`。**Plus 仅支持当前无生效订阅的账号**；**Pro 20x（2026-09-30 起支持新开）：无订阅账号可直接开通，也支持已有生效菲区(PHP) Plus / Pro 5x 的账号升级（无需先取消原订阅；非菲区计费返回 `plus_not_ph_region`、卡密不消耗）**；**Pro 5x 额外支持已有生效 ChatGPT Plus 的账号（自动升级为 Pro 5x，无需先取消 Plus；仅限菲律宾区 PHP 的 Plus，非菲区 Plus 返回 `plus_not_ph_region`、卡密不消耗）**。其余不符合的账号（如已是 Pro 5x/更高档、或非 Plus 的付费订阅）会被拒绝（`account_has_subscription`），卡密不消耗。
- **Pro 20x 开通条件**：支持三类账号——① 无订阅的账号（免费版）；② 已有生效的菲区(PHP) Plus；③ 已有生效的菲区(PHP) Pro 5x（②③ 自动升级，无需先取消）。已是生效 Pro 20x 的账号请到期后再续费；官方暂未对其开放 Pro 20x 的账号返回 `account_not_eligible_pro20`（较少出现），**卡密不消耗**。
- **Pro 500 开通条件**：卡密前缀 `P500-`。**仅支持无任何订阅的免费账号**，且**不是升级产品**——不支持从 Plus / Pro 5x / Pro 20x / Go / Team 升级，也没有补差价；已取消续订但未到期的账号同样视为有订阅，会被拒绝（`account_has_subscription`，卡密不消耗）。账号若已有计费币种且不是菲律宾(PHP)，返回 `promax_billing_not_php`。`P500-` 卡密为人工发货，不提供在线购买，详见 [2.4 Pro 500 开通说明](#2-4-pro-500-开通说明)。

### 2.2 所有套餐的共同前提

- **目标账号当前不能有生效中的付费订阅**（例外：**Pro 5x 也接受已有生效 Plus 的菲区账号，Pro 20x 也接受已有生效 Plus / Pro 5x 的菲区账号，自动升级**）。提交时会先校验账号当前的订阅状态，其它有生效订阅的账号立即返回 `account_has_subscription`（卡密不消耗）。**已取消续订、但订阅尚未到期的账号同样视为有生效订阅**（Plus 卡密需等订阅到期后再提交）；订阅已过期的账号视为无订阅，可以正常开通。**Pro 500 是方向相反的例外、条件更严：任何订阅都不接受，也没有升级路径（见 2.4）。**
- `session_data` 必须是目标 ChatGPT 账号的**完整** Session JSON，且含 `accessToken`。
- 提交后到出结果前，该账号需保持在浏览器中的登录态，否则 Session 会失效导致任务失败。

### 2.3 重复充值保护

对同一个 ChatGPT 账号反复充值**不会叠加时长**，只会覆盖当前订阅，而卡密照常消耗且不退。为此我们在提交环节做了拦截，按距上次成功充值的时间分两档：

| 距上次成功充值 | 行为 | `error` |
|---|---|---|
| 24 小时以内 | **直接拒绝**，`confirm_duplicate` 也不放行 | `duplicate_email_blocked` |
| 24 小时 ～ 30 天 | 拒绝并要求确认，带 `confirm_duplicate: true` 重发即可放行 | `duplicate_email_confirm` |
| 超过 30 天 | 正常放行 | — |

两者都返回 HTTP `409`，响应体带上次记录（24 小时内的拒绝另带 `blocked: true`，需确认的另带 `need_confirm: true`；下例为需确认的情形）：

```json
{
  "success": false,
  "error": "duplicate_email_confirm",
  "detail": "This account was already topped up on 2026-09-14 08:51:21 …",
  "detail_zh": "该账号最近已充值过…",
  "duplicate": true,
  "last_product": "ChatGPT Plus",
  "last_product_zh": "ChatGPT Plus",
  "last_at": "2026-09-14 08:51:21",
  "hours_ago": 77.83,
  "need_confirm": true
}
```

判定按**邮箱 + 任意 GPT 套餐**：该账号充过 Plus 之后再充 Plus 同样会命中，因为只要账号还有生效中的订阅，充任何档位都会失败或覆盖。升级（Plus→Pro 5x、Plus / Pro 5x→Pro 20x）不触发判重。Pro 500 不属于升级，同样适用判重。

> **对接建议**：拿到 `need_confirm` 时，把 `last_product` 与 `last_at` 展示给你的终端用户，由他确认后再带 `confirm_duplicate: true` 重发；**不要在代码里无条件自动重发**，那等于把这道保护关掉了。`blocked` 则应直接告知用户并终止，不要重试。

判重覆盖我们的全部入口（本 API、自助激活页、我们自己的激活页），所以同一个账号在别处充过也会被识别到。

### 2.4 Pro 500 开通说明

`P500-`（Pro 500）的规则与其它套餐**不同**，对接前请单独确认：

- **账号条件**：仅限**无任何订阅的免费账号**。Plus / Pro 5x / Pro 20x / Go / Team、已取消续订但未到期、欠费的账号一律拒绝（`account_has_subscription`），不提供升级与补差价；账号计费地区非菲律宾(PHP)返回 `promax_billing_not_php`。提交时如果暂时读不清账号的订阅状态，同样拒绝（`precheck_failed`），**不会放行**。以上拒绝都发生在受理之前，卡密不消耗。
- **开通方式**：先为账号开通一个基础档（Plus），再升级到 Pro 500，全程由我们自动完成；成功后我们会自动关闭自动续费。正因为分两步，升级失败时账号可能已停留在基础档（见下方「失败处理」）。
- **获取方式**：`P500-` 卡密为人工发货，**不提供在线下单**，目录与接口也不返回价格。请联系我们按代理价获取；拿到卡密后，本 API 与自助激活页都可以兑换。
- **排队**：Pro 500 在整个系统内**同一时间只处理一单**，我们自己的激活页、自助激活页和所有经销商共用这一条队列；处理中的 Pro 500 在调用引擎的那一小段，也可能让 Pro 5x / Pro 20x 任务短暂排队。任务受理后可能长时间停在 `queued`（数分钟甚至更久），这是正常排队而不是故障，请继续轮询、不要重提；单笔耗时也比其它套餐长，且不固定。建议轮询间隔 3–5 秒，等待上限至少 20 分钟，超时后转低频复查或联系我们，不要判失败。
- **同账号并发**：提交 Pro 500 时，如果该账号已有 `PLS-` / `P5X-` / `P20X-` / `P500-` 卡密的任务在受理 / 排队 / 处理中（不分入口与经销商），新提交返回 `409 account_busy`，卡密不消耗；请等原任务结束后再提交。
- **失败处理（重要）**：Pro 500 失败后**不要自动重提**。`promax_upgrade_incomplete`（基础档已开通、升级未成功）与 `payment_unverified`（付款结果待核实）都表示**请勿重复提交、请联系我们人工处理**——此时账号已是基础档，重提会因账号已有订阅被拒绝。这与 Pro 20x 的 `upgrade_incomplete`（可用同一卡密重提）**含义相反**，请分开处理，见 7.3。其余失败码以 7.3 的「处置」列为准。
- **失败冻结**：同一账号计费类失败连续 2 次、任何原因失败连续 3 次将暂停提交，规则与其它套餐相同，见 7.3。

---

## 3. 认证与安全

每个请求都要带 Key。两种写法等价：

```http
X-API-Key: aisub_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

```http
Authorization: Bearer aisub_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

Key 由我们签发，形如 `aisub_` + 32 位随机串。一个 Key 对应一个客户，所有流水按 Key 归属隔离——你只能查到自己提交的任务。

### 3.1 对接方安全要求

以下是对接的最低要求，不是建议：

1. **Key 只存在于你的服务端。** 放进浏览器、App、小程序或任何客户端等同于公开泄露。
2. **不要把 Key、完整卡密、Session JSON、accessToken 写进日志。** 需要排查时记录 `task_id`，它不含敏感信息。
3. 出口 IP 固定的话请告知我们，可以为你的 Key 开启 IP 白名单。
4. 怀疑泄露请立刻联系我们停用并换发。旧 Key 停用后所有请求立即返回 `401`。
5. Key 不设有效期，但建议按贵方密钥轮换周期定期换发。

---

## 4. 请求与响应约定

### 4.1 成功响应

顶层恒有 `success: true` 与 `client_name`，业务字段平铺在同一层：

```json
{
  "success": true,
  "client_name": "示例客户",
  "task_id": "a1b2c3d4e5f64789abcd1234",
  "status": "queued"
}
```

### 4.2 错误响应

**同时判断 HTTP 状态码和响应体的 `success` 字段**，不要只看其中一个。

```json
{
  "success": false,
  "error": "invalid_card_format",
  "detail": "Activation key format is invalid. Please check that the key is complete (including dashes).",
  "detail_zh": "卡密格式不正确，请核对卡密是否完整（含横线）"
}
```

| 字段 | 说明 |
|---|---|
| `error` | 机器可读错误码，**程序分支只认这个字段**，不要匹配文案 |
| `detail` | 英文说明，可直接展示给终端用户 |
| `detail_zh` | 中文说明，可直接展示给终端用户 |

### 4.3 时间与字符

- 所有时间为 **UTC**，格式 `YYYY-MM-DD HH:MM:SS`。
- 卡密大小写不敏感，服务端统一转大写后处理。
- 请求体必须是 JSON 对象；非法 JSON 返回 `400 invalid_json_body`。

### 4.4 频率限制

按 Key 计算，滑动窗口：

| 类别 | 接口 | 限额 |
|---|---|---|
| 提交类 | `POST /api/v1/gptplus/redeem` | 60 次 / 分钟 |
| 查询类 | 其余全部 `GET` | 600 次 / 分钟 |

超限返回 `429 rate_limited`。请做指数退避（1s → 2s → 4s，上限 30s）后重试，**但提交类接口例外，见 4.5。**

### 4.5 重试语义（必读）

> **`redeem` 不是幂等接口。提交成功即消耗一次卡密次数，重复提交等于再消耗一次，不可退。**

本接口没有 idempotency key。因此：

| 情况 | 正确做法 |
|---|---|
| 返回 `2xx` 并拿到 `task_id` | 存下 `task_id`，只轮询，不重发 |
| 返回 `4xx`（参数 / 卡密 / 格式类错误） | 卡密未被消耗，修正后可以重发 |
| 返回 `429` | 未被消耗，退避后重发 |
| 返回 `5xx` / `502` / 网络超时 / 无响应 | **不要直接重发。** 先用 `GET /api/v1/gptplus/cdk-info?code=<卡密>` 或 `GET /api/v1/gptplus/jobs?card_code=<卡密>` 确认任务是否已经建立；建立了就去轮询，确实没建立才重发 |

查询类接口都是只读的，可以放心重试。

**提交的耗时与超时**：`redeem` 在受理前要核验账号订阅状态，通常数秒，最坏约 **80 秒**才返回；请把提交请求的客户端超时设为 **≥ 90 秒**（设 60 秒会比服务端先超时）。超时或收到 `502 upstream_error` 时，我们这边**可能已经建单**——先按上表查证（`jobs` / `cdk-info`）。若任务确已建立，用**同一张卡密**重提会得到 `409 card_in_progress`（带 `job_id`，即原 `task_id`），不会产生第二单；但**不要换另一张卡密去提交同一个账号**。

---

## 5. 接口参考

### 5.1 校验 API Key

```http
GET /api/v1/ping
```

无参数。用于对接自检与健康探测。

```json
{ "success": true, "message": "pong", "client_name": "示例客户" }
```

---

### 5.2 套餐目录

```http
GET /api/v1/products
```

返回当前在售的全部套餐与卡密前缀。建议启动时拉一次并缓存，用它做界面分流与输入提示；目录之外的前缀交给服务端判定，不要在本地拒收。目录含 Pro 500（`P500-`），其 `note` / `note_zh` 写明了账号条件与排队说明；目录不返回价格。

```json
{
  "success": true,
  "client_name": "示例客户",
  "total": 5,
  "items": [
    {
      "product": "gpt_plus_self",
      "name": "ChatGPT Plus",
      "name_zh": "ChatGPT Plus",
      "key_prefix": "PLS-",
      "key_sample": "PLS-XXXXXXXXXXXXXXXX",
      "credential": "session_json",
      "note": "Only accounts without an active subscription can be activated.",
      "note_zh": "仅支持当前无订阅的账号开通。"
    }
  ]
}
```

---

### 5.3 服务状态

```http
GET /api/v1/gptplus/status
```

充值服务的实时可用性。**建议在展示下单入口前调用**，`available` 为 `false` 时先拦下用户，别让卡密提交进维护窗口。

```json
{
  "success": true,
  "client_name": "示例客户",
  "available": true,
  "tier": "ready",
  "label": "Operational",
  "label_zh": "服务正常",
  "tone": "ok",
  "pending": 0,
  "wait_seconds": 0,
  "checked_at": "2026-09-17 10:10:50"
}
```

| 字段 | 取值 | 含义 |
|---|---|---|
| `available` | `true` / `false` | `false` 表示维护中，此刻不要提交 |
| `tier` | `ready` / `busy` / `down` | `busy` 表示排队较多，仍可提交但耗时更长 |
| `pending` | 整数 | 兼容保留字段，目前恒为 0，不反映排队情况 |
| `wait_seconds` | 整数 | 兼容保留字段，目前恒为 0 |

结果有 20 秒服务端缓存，无需高频调用。该接口只反映充值服务整体是否可用，**不反映单个任务的排队位置**（尤其是 Pro 500 的单车道排队），请以任务自身的 `status` 为准。

---

### 5.4 查询卡密使用情况

```http
GET /api/v1/gptplus/cdk-info?code=PLS-XXXXXXXXXXXXXXXX
```

| 参数 | 必填 | 说明 |
|---|---|---|
| `code` | 是 | 卡密。亦接受 `card_code` 或 `cdk` |

**提交前先调这个**，能挡掉绝大多数"卡密已用过"的客诉。

```json
{
  "success": true,
  "client_name": "示例客户",
  "product": "gpt_plus_self",
  "product_name": "ChatGPT Plus",
  "product_name_zh": "ChatGPT Plus",
  "card_key": "PLS-XXXXXXXXXXXXXXXX",
  "status": "unused",
  "redeemable": true,
  "total": 0,
  "success_count": 0,
  "running_count": 0,
  "records": []
}
```

| 字段 | 说明 |
|---|---|
| `status` | `unused` 未使用 / `running` 充值中 / `used` 已成功用掉 |
| `redeemable` | 当前是否可提交。**为 `false` 时不要提交** |
| `records` | 该卡密的历史记录，最多 20 条；其中账号邮箱按隐私要求打码 |

> 该卡密在任何入口用过都会体现在这里，不只限于你的 Key。要查你自己提交的任务详情（含完整邮箱），用 5.6 与 5.7。

---

### 5.5 提交激活任务

```http
POST /api/v1/gptplus/redeem
```

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `card_code` | string | 是 | 卡密。亦接受 `cdk` / `cdk_code` |
| `session_data` | object \| string | 是 | 目标账号完整 Session JSON，须含 `accessToken`。亦接受 `session` / `session_json`；传字符串时服务端会解析 |
| `confirm_duplicate` | bool | 否 | 仅在收到 `duplicate_email_confirm` 后使用，表示用户已确认覆盖。默认 `false`，详见 [2.3 重复充值保护](#2-3-重复充值保护) |

**请求中不要包含任何档位参数**，传了也会被忽略——档位一律按卡密前缀判定。

**提交耗时**：受理前会先核验账号状态，通常数秒，最坏约 80 秒才返回，客户端超时请设为 **≥ 90 秒**（详见 4.5）。成功返回只代表**已受理**（`queued`），不代表已开通，结果以轮询为准；Pro 500 受理后可能排队较久，见 2.4。

```json
{
  "card_code": "P20X-XXXXXXXXXXXXXXXX",
  "session_data": {
    "accessToken": "eyJhbGciOi...",
    "user": { "email": "user@example.com" },
    "expires": "2026-12-16T00:00:00.000Z"
  }
}
```

成功返回 `200`：

```json
{
  "success": true,
  "client_name": "示例客户",
  "product": "gpt_pro_20x_self",
  "product_name": "ChatGPT Pro 20x",
  "product_name_zh": "ChatGPT Pro 20x",
  "task_id": "a1b2c3d4e5f64789abcd1234",
  "status": "queued",
  "message": "Accepted",
  "message_zh": "已受理",
  "card_key": "P20X-XXXXXXXXXXXXXXXX",
  "email": "user@example.com",
  "submitted_at": "2026-09-17 10:12:00"
}
```

成功响应不含 `duplicate` 字段；重复提交在提交阶段拦截（返回 `duplicate_email_blocked` / `duplicate_email_confirm`，见 2.3）。

---

### 5.6 查询任务结果

```http
GET /api/v1/gptplus/task/{task_id}
```

只能查到本 Key 提交的任务，查别人的返回 `404 task_not_found`。

```json
{
  "success": true,
  "client_name": "示例客户",
  "product": "gpt_plus_self",
  "product_name": "ChatGPT Plus",
  "task_id": "a1b2c3d4e5f64789abcd1234",
  "status": "success",
  "finished": true,
  "message": "Activation completed",
  "message_zh": "充值成功",
  "error_code": "",
  "duration_seconds": 142.6,
  "card_key": "PLS-XXXXXXXXXXXXXXXX",
  "email": "user@example.com",
  "submitted_at": "2026-09-17 10:12:00"
}
```

| 字段 | 说明 |
|---|---|
| `status` | `queued` / `running` / `success` / `failed` |
| `finished` | `status` 为 `success` 或 `failed` 时为 `true`，**轮询以此为终止条件** |
| `message` / `message_zh` | 状态说明；失败时为失败原因，可直接展示给终端用户 |
| `error_code` | 仅失败时非空，取值见 [7. 错误码](#7-错误码)；卡密是否消耗、能否重提以 7.3 为准 |
| `duration_seconds` | 任务结束后才出现：本次处理耗时（秒），不含排队等待 |
| `activation_email` | 保留字段，恒为空串；账号邮箱请看 `email` |
| `action_required` | 仅在需要用户本人完成一次安全验证时出现，取值 `manual_captcha`（见下方「安全验证」） |
| `verification_url` | 与 `action_required` 同时出现，我方托管的验证页地址 |
| `verification_expires_at` | 验证有效截止时间（Unix 秒），过期后该字段消失、任务转失败 |

**轮询建议**：每 3 秒一次；单笔通常 1–3 分钟完成。上限建议 15 分钟（约 300 次）；**Pro 500 单车道排队、耗时更长，上限请至少 20 分钟**（见 2.4）。超时后不要判失败，转为低频复查或人工跟进。等待用户完成验证的时间不要计入超时。

**安全验证**：少数订单在处理中需要用户本人完成一次安全验证，此时响应形如：

```json
{
  "success": true,
  "status": "running",
  "finished": false,
  "action_required": "manual_captcha",
  "verification_url": "https://www.academicgate.org/verify/a1b2c3d4e5f64789abcd1234",
  "verification_expires_at": 1790000000
}
```

- `status` 仍是 `running`（**不是成功**）。把 `verification_url` 交给这笔订单的用户：在你的 HTTPS 页面用 iframe 嵌入（建议 `referrerpolicy="no-referrer"`）或新窗口打开。
- 用户完成后**继续轮询原 `task_id`**，以 `success` / `failed` 为准；关闭窗口不代表成功。链接过期后如出现新链接再展示。
- **不要因此重新提交**；验证链接只给这笔订单的用户，不要写进日志或转给无关第三方。

---

### 5.7 任务记录查询

```http
GET /api/v1/gptplus/jobs?limit=50&offset=0&card_code=&status=&product=
```

你名下的任务流水，用于对账与超时找回。

| 参数 | 默认 | 说明 |
|---|---|---|
| `limit` | 50 | 1–200 |
| `offset` | 0 | 分页偏移 |
| `card_code` | — | 按卡密精确过滤 |
| `status` | — | 按状态精确过滤，取值见 [6. 任务状态机](#6-任务状态机) |
| `product` | — | 按套餐过滤，取值见 `GET /api/v1/products` 的 `product` |

```json
{
  "success": true,
  "client_name": "示例客户",
  "total": 128,
  "limit": 50,
  "offset": 0,
  "items": [
    {
      "task_id": "a1b2c3d4e5f64789abcd1234",
      "card_key": "PLS-XXXXXXXXXXXXXXXX",
      "product": "gpt_plus_self",
      "product_name": "ChatGPT Plus",
      "product_name_zh": "ChatGPT Plus",
      "status": "success",
      "message": "Activation completed",
      "email": "user@example.com",
      "created_at": "2026-09-17 10:12:00",
      "updated_at": "2026-09-17 10:14:31",
      "finished_at": "2026-09-17 10:14:31"
    }
  ]
}
```

---

## 6. 任务状态机

```
                 ┌──────────┐
   redeem  ─────▶│  queued  │  已受理，等待处理
                 └────┬─────┘
                      ▼
                 ┌──────────┐
                 │ running  │  充值处理中
                 └────┬─────┘
            ┌─────────┴─────────┐
            ▼                   ▼
      ┌──────────┐        ┌──────────┐
      │ success  │        │  failed  │   ← 均为终态，finished: true
      └──────────┘        └──────────┘
```

- 状态**只会向前推进**，不会从终态回退。
- `rejected` 只出现在流水接口（5.7）里，表示请求在进入处理队列前就被拒绝、**未消耗卡密次数**；它不会作为 `task` 接口的 `status` 返回。
- 流水接口（5.7）里还可能见到另外两种仅属于流水的状态：`submitting`（提交已收到、任务尚在建立的短暂占位，最长约 3 分钟，随后转入其它状态）与 `reset`（我们人工重置过的记录，卡密是否可再用请以 `cdk-info` 为准，有疑问请联系我们）。`task` 接口的 `status` 只会是 `queued` / `running` / `success` / `failed`。
- Pro 500 在 `queued` 阶段可能停留较久（全系统单车道排队），这不是故障，见 2.4。
- 终态之后再轮询会持续返回同一结果，不产生副作用。
- `running` 期间可能要求用户做一次安全验证（响应带 `action_required`）。这不是新状态，完成后照常走到终态，见 5.6。

## 7. 错误码

程序分支只认 `error` 字段。

### 7.1 认证与配额

| HTTP | `error` | 含义 | 处置 |
|---|---|---|---|
| 401 | `missing_api_key` | 未带 Key | 检查请求头拼写 |
| 401 | `invalid_api_key` | Key 无效或已停用 | 联系我们 |
| 403 | `key_not_allowed` | 该 Key 无权调用此接口 | 联系我们换发正式 Key |
| 429 | `rate_limited` | 超出频率限制 | 指数退避重试 |

### 7.2 参数与卡密

| HTTP | `error` | 含义 | 处置 |
|---|---|---|---|
| 400 | `invalid_json_body` | 请求体不是 JSON 对象 | 检查 `Content-Type` 与序列化 |
| 400 | `missing_card_code` | 未传卡密 | — |
| 400 | `invalid_card_format` | 卡密前缀不在支持列表内 | 比对 `GET /api/v1/products` |
| 400 | `missing_session` | 未传 `session_data` | — |
| 400 | `invalid_session_json` | Session 不是合法 JSON 对象 | 让用户重新完整复制 Session 页面 |
| 400 | `session_missing_access_token` | Session 缺 `accessToken` | 用户未登录 ChatGPT 或只复制了片段 |
| 400 | `missing_task_id` | 未传 `task_id` | — |
| 404 | `task_not_found` | 任务不存在或不属于本 Key | 核对 `task_id` |
| 409 | `duplicate_email_blocked` | 该账号 24 小时内已充值过 | 终止并告知用户，不要重试 |
| 409 | `duplicate_email_confirm` | 该账号 30 天内已充值过，需确认 | 让用户确认后带 `confirm_duplicate: true` 重发 |
| 400 | `product_not_supported_here` | 该卡密类型暂不支持在此处激活（例如对应套餐的入口临时关闭） | **卡密未消耗**；联系我们 |

### 7.3 充值业务错误

以下出现在 `redeem` 的错误响应（带 HTTP 状态码），或 `task` 的 `error_code`（任务失败：HTTP 为 `200`、`status: failed`）中；同一个码两处都可能出现。**是否消耗卡密、能否用原卡密重提，以「处置」列为准：标注「卡密未消耗」的修正后可重提；标注「请勿重复提交」的不要自动重试。**

| HTTP | `error` / `error_code` | 含义 | 处置 |
|---|---|---|---|
| 400 | `invalid_card_key` | 卡密无效、不存在、未售出或剩余次数不足 | 用 `cdk-info` 核实后给终端用户明确答复 |
| 409 | `card_already_used` | 该卡密已成功使用过 | 卡密一次性，换一张 |
| 409 | `card_in_progress` | 该卡密已有任务在处理中，或刚刚（约 3 小时内）已用同一账号充值成功；响应体带 `job_id`（等于原 `task_id`，用同一 Key 提交的可直接轮询；刚受理的几秒内可能暂时没有该字段） | 别重复提交，改用 `task` 轮询原任务（已成功的会直接返回成功结果） |
| 409 | `account_has_subscription` | 目标账号当前有生效中的订阅（**例外：Pro 5x 单已有生效 Plus 的菲区账号、Pro 20x 单已有生效 Plus / Pro 5x 的菲区账号可升级，不会返回此错误**；已是生效 Pro 20x 的账号请到期后再续费；**Pro 500 不接受任何订阅，也不支持升级**） | **卡密未消耗**；换一个无订阅的账号（或按上面的例外用可升级的账号），或等订阅到期 |
| 409 | `account_busy` | （Pro 500）该账号已有 `PLS-` / `P5X-` / `P20X-` / `P500-` 卡密的任务在受理 / 排队 / 处理中，不分入口与经销商 | **卡密未消耗**；等原任务结束后再提交 |
| 409 | `email_frozen` | 该账号（邮箱）计费类失败（在付款环节被拒的失败）连续 2 次，已暂停提交；同一账号成功一次或我们解冻后重新计数 | **卡密未消耗**；请勿对该账号继续重试，联系我们解冻 |
| 409 | `session_failed_too_many` | 该账号（session）无论什么原因连续失败 3 次，已暂停提交（`detail_zh`：当前session失败次数过多，请联系苏哲）；同一账号成功一次或我们解冻后重新计数 | **卡密未消耗**；请勿对该账号继续重试，联系我们解冻 |
| 409 | `plus_not_ph_region` | 该账号的 Plus 是非菲区（非 PHP）订阅，Pro 5x / Pro 20x 升级仅支持菲区(PHP) 订阅或免费账号 | **卡密未消耗**；改用免费账号或菲区(PHP) Plus 账号 |
| 409 | `promax_billing_not_php` | （Pro 500）账号计费地区不是菲律宾（PHP） | **卡密未消耗**；更换账号或联系我们 |
| 409 | `account_not_eligible_pro20` | 官方明确暂未对该账号开放 Pro 20x（较少出现） | **卡密未消耗**；换一个账号（免费账号，或菲区 Plus / Pro 5x）重试 |
| 409 | `account_not_eligible_promax` | （Pro 500）官方暂未对该账号开放 Pro 500（较少出现） | **卡密未消耗**；联系我们 |
| 409 | `pro20_already_active` | 账号已是生效中的 Pro 20x，尚未到期 | **卡密未消耗**；到期后再续费 |
| 400 | `session_expired` | 登录凭证（Session/accessToken）已过期或失效 | **卡密未消耗**；让用户重新登录 ChatGPT 复制最新完整 Session 后重试 |
| 502 | `precheck_failed` | （Pro 20x / Pro 500）暂时无法核验账号的订阅状态或开通资格（预检服务临时不可用）；无法核验就**不会受理** | **卡密未消耗**；稍后重试 |
| — | `session_missing_access_token` | Session 缺 `accessToken` | 让用户重新完整复制 Session 页面 |
| — | `invalid_session_data` | Session 被判定为无效 | 让用户重新导出 Session，用新 Session 重试 |
| — | `invalid_product_line` | 卡密类型暂不受支持 | 联系我们登记，通常是新卡种 |
| — | `invalid_recharge_method` | 该卡密暂不可用 | 联系我们 |
| — | `card_key_busy_other_product` | 卡密正被其他任务占用 | 稍后用 `cdk-info` 复查 |
| 200 | `payment_declined` | 开通 / 升级阶段付款未成功 | **卡密未消耗**；稍后重试或联系我们。Pro 500 若在基础档开通之后失败，重提时会提示账号已有订阅（`account_has_subscription`），说明账号已是基础档，请联系我们人工处理 |
| 200 | `upgrade_incomplete` | 开通未完成：账号已开通基础档，尚需完成升级（Pro 20x 免费号新开可能出现；Pro 500 无此码，见下一行） | **卡密未消耗**；用同一张卡密重新提交即可，仍失败请联系客服 |
| 200 | `promax_upgrade_incomplete` | （Pro 500）基础档已开通，但升级到 Pro 500 未成功，账号现在停在基础档 | **请勿重复提交**（重提会因账号已有订阅被拒）；联系我们人工处理。**与 `upgrade_incomplete` 相反** |
| 200 | `payment_unverified` | 付款请求已发出，但无法核实结果（任何套餐都可能出现） | **请勿重复提交**，也不要换卡密重试；联系我们核对 |
| 200 | `verification_timeout` / `verification_failed` | 安全验证超时未完成 / 未通过 | **卡密未消耗**；可重新提交 |
| 200 | `account_verification_required` | 该 ChatGPT 账号需要额外验证，暂时无法自动开通 | **卡密未消耗**；联系我们 |
| 200 | `invoice_renew_required` | 账号存在未结清的欠费账单，无法自动开通 | **卡密未消耗**；处理欠费或更换账号后重试 |
| 200 | `promax_trial_unsupported` | （Pro 500）账号当前触发官方试用优惠，无法自动开通；未产生扣款 | **卡密未消耗**；联系我们 |
| 200 | `activation_failed` | 兜底：开通未成功（具体原因不对外细分） | **卡密未消耗**；Plus / Pro 5x / Pro 20x 可稍后用同一张卡密重试，多次失败联系我们；**Pro 500 请勿自动重试**，联系我们 |

HTTP 为「—」的码出现位置不固定（可能是同步错误，也可能是任务失败码），状态码以实际响应为准；程序分支始终只认 `error` / `error_code`。

> 对当前在售卡密（`PLS-` / `P5X-` / `P20X-` / `P500-`），`invalid_card_key` / `account_has_subscription` / `session_missing_access_token` 都在受理前返回，**卡密未消耗**，修正后可直接重发；`card_already_used` / `card_in_progress` 表示这张卡已在用，不要另开新单。

### 7.4 服务端异常

| HTTP | `error` | 含义 | 处置 |
|---|---|---|---|
| 500 | `server_error` | 服务端异常 | 带 `task_id` 联系我们 |
| 502 | `upstream_error` | 充值服务暂时无响应 | **按 4.5 先查证再决定是否重发** |
| 503 | `unavailable` | 服务维护中 | 用 `status` 接口确认恢复后再提交 |
| 503 | `tier_maintenance` | Pro 5x / Pro 20x 自助开通临时维护中，或 Pro 500 暂未开放（Pro 500 有独立开关，不受 5x / 20x 维护影响），卡密未消耗；Pro 500 任务排队期间恰遇关闭时，也会以失败码形式出现（HTTP 200） | 稍后重试；Plus 不受影响 |
| 200 | `system_maintenance` | **不是 HTTP 错误**：只会作为失败任务的 `error_code`（`status: failed`）出现，表示充值系统正在加急更新中，卡密未消耗 | 稍等片刻后用同一张卡密重试 |

## 8. 推荐对接流程

```
① 用户在你站下单
        │
        ▼
② GET /api/v1/gptplus/status      ← available=false 就拦下，别收单
        │
        ▼
③ GET /api/v1/gptplus/cdk-info    ← redeemable=false 就换一张卡
        │
        ▼
④ 引导用户提供 Session JSON
        │
        ▼
⑤ POST /api/v1/gptplus/redeem     ← 拿到 task_id 立刻落库
        │
        ▼
⑥ 每 3 秒 GET /task/{id} 直到 finished=true
        │
        ├─ success → 通知用户完成
        └─ failed  → 按 error_code 给出处置文案
```

### 8.1 必须落库的字段

`task_id`、`card_code`、`product`、提交时间、最后一次状态。出问题时这五个字段足以定位，缺一不可。

### 8.2 常见踩坑

| 现象 | 原因 | 解法 |
|---|---|---|
| 提交后立即失败，提示 Session 无效 | 用户复制 Session 后退出了 ChatGPT 登录 | 要求提交期间保持登录 |
| 明明是新卡却提示已使用 | 同一张卡在其他入口也提交过 | 提交前用 `cdk-info` 核实 |
| 充值成功但用户看不到订阅 | 客户端缓存 | 让用户刷新 ChatGPT 网页或重新登录 |
| 超时后重发，卡密被扣两次 | 违反 4.5 的重试语义 | 5xx / 超时先查证再重发 |
| Pro 20x 提示已有订阅 | 账号已是生效中的 Pro 20x，或是 Plus / Pro 5x 以外的付费订阅 | 请在订阅到期后再续费；菲区的 Plus / Pro 5x 账号可直接升级，非菲区计费会返回 `plus_not_ph_region` |
| 提交超时后担心重复消耗卡密 | 提交最坏约 80 秒，客户端超时设得太短；服务端其实已建单 | 超时设 ≥ 90 秒；超时后先查 `jobs`，或用同一张卡密重提（已建单或刚成功会得到 `409 card_in_progress` + `job_id`，照常轮询即可） |
| Pro 500 任务长时间停在 `queued` | Pro 500 全系统单车道，需要排队 | 继续轮询，等待上限设 ≥ 20 分钟，不要重提（见 2.4） |
| Pro 500 失败后重提被 `account_has_subscription` 拒绝 | 失败发生在基础档开通之后，账号已是基础档 | 不要重提，联系我们人工处理（见 2.4、7.3） |

### 8.3 自助激活页（面向终端用户）

如果你不想自己开发提交页面，可以直接把 `https://www.academicgate.org/store` 发给终端用户：

- **用户无需 API Key**：粘贴你发给他的卡密和自己的 Session JSON 即可。页面按卡密前缀自动识别套餐，支持 Plus / Pro 5x / Pro 20x / Pro 500（Pro 500 有单独页签，粘贴 `P500-` 卡密会自动切换），中英文界面。
- 账号规则、判重、失败冻结与失败文案与本 API 一致；页面提交的任务在 `cdk-info` 的卡密历史里可见，但**不会**出现在你的 `jobs` 流水中（`jobs` 只含用你的 Key 提交的任务）。
- 页面对每个终端用户 IP 的限额比本 API 更紧（提交 8 次 / 分钟、读取 60 次 / 分钟）；大量用户共用同一出口 IP 时可能触发限频。
- 页面等待结果最长约 6.5 分钟（Pro 500 约 15 分钟），之后停止自动刷新并提示稍后用卡密重新查询，任务本身不受影响。页面没有账号资格预检，资格在提交时判定，被拒绝的卡密不消耗。
- 页面只供终端用户在浏览器里直接使用：它调用的是面向网页的匿名接口，不是 Key 接口，**不能**被第三方站点跨域调用。要在自己的站点内嵌入激活流程，请由你方服务端调用本 API（Key 只放服务端，见 3.1）；如需在你方域名下提供同类激活页，请联系我们沟通部署方式。

## 9. 代码示例

### 9.1 Python

```python
import time
import requests

BASE = "https://www.academicgate.org"
API_KEY = "aisub_你的Key"
HEADERS = {"X-API-Key": API_KEY, "Content-Type": "application/json; charset=utf-8"}

class ActivationError(Exception):
    def __init__(self, code, detail):
        super().__init__(f"{code}: {detail}")
        self.code = code

def _check(resp):
    data = resp.json()
    if resp.status_code >= 400 or not data.get("success"):
        raise ActivationError(data.get("error", "http_%d" % resp.status_code),
                              data.get("detail_zh") or data.get("detail") or "")
    return data

def activate(card_code, session_data, timeout_seconds=900):  # Pro 500 排队较久，建议 ≥ 1200
    # 1) 服务可用性
    if not _check(requests.get(f"{BASE}/api/v1/gptplus/status", headers=HEADERS, timeout=30))["available"]:
        raise ActivationError("unavailable", "充值服务维护中")

    # 2) 卡密可用性
    if not _check(requests.get(f"{BASE}/api/v1/gptplus/cdk-info",
                               params={"code": card_code}, headers=HEADERS, timeout=30))["redeemable"]:
        raise ActivationError("invalid_card_key", "卡密已使用或正在处理中")

    # 3) 提交。成功即消耗次数，此处绝不重试；提交最坏约 80 秒才返回，timeout 须 ≥ 90；超时 / 502 先查 jobs 再决定
    job = _check(requests.post(f"{BASE}/api/v1/gptplus/redeem", headers=HEADERS, timeout=90,
                               json={"card_code": card_code, "session_data": session_data}))
    task_id = job["task_id"]
    print("submitted", task_id, job["product_name_zh"])

    # 4) 轮询
    deadline = time.time() + timeout_seconds
    while time.time() < deadline:
        time.sleep(3)
        try:
            st = _check(requests.get(f"{BASE}/api/v1/gptplus/task/{task_id}", headers=HEADERS, timeout=30))
        except (requests.RequestException, ActivationError):
            continue  # 查询是只读的，网络抖动继续轮询即可
        if st["finished"]:
            return st
    raise ActivationError("poll_timeout", f"超过 {timeout_seconds} 秒仍未出结果，task_id={task_id}")
```

### 9.2 Node.js

```js
const BASE = 'https://www.academicgate.org';
const API_KEY = process.env.AISUB_API_KEY;
const HEADERS = { 'X-API-Key': API_KEY, 'Content-Type': 'application/json; charset=utf-8' };

class ActivationError extends Error {
  constructor(code, detail) { super(`${code}: ${detail}`); this.code = code; }
}

async function call(path, options = {}) {
  const res = await fetch(BASE + path, { headers: HEADERS, ...options });
  const data = await res.json();
  if (!res.ok || !data.success) {
    throw new ActivationError(data.error || `http_${res.status}`, data.detail_zh || data.detail || '');
  }
  return data;
}

const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

async function activate(cardCode, sessionData, timeoutMs = 900_000) { // Pro 500 排队较久，建议 ≥ 1_200_000
  const status = await call('/api/v1/gptplus/status');
  if (!status.available) throw new ActivationError('unavailable', '充值服务维护中');

  const info = await call(`/api/v1/gptplus/cdk-info?code=${encodeURIComponent(cardCode)}`);
  if (!info.redeemable) throw new ActivationError('invalid_card_key', '卡密已使用或正在处理中');

  // 成功即消耗次数，此处绝不重试；提交最坏约 80 秒才返回，如设置请求超时须 ≥ 90 秒
  const job = await call('/api/v1/gptplus/redeem', {
    method: 'POST',
    body: JSON.stringify({ card_code: cardCode, session_data: sessionData }),
  });

  const deadline = Date.now() + timeoutMs;
  while (Date.now() < deadline) {
    await sleep(3000);
    try {
      const st = await call(`/api/v1/gptplus/task/${job.task_id}`);
      if (st.finished) return st;
    } catch (_) { /* 查询只读，抖动继续 */ }
  }
  throw new ActivationError('poll_timeout', `超时，task_id=${job.task_id}`);
}
```

---

## 10. 数据处理与隐私

### 10.1 Session 凭据

`session_data` **仅用于完成本次充值，不作任何形式的存储**。它不写入数据库、不落盘、不进日志，处理结束即从内存释放。

### 10.2 留存的数据

为对账、售后与风控，我们留存以下字段：

| 数据 | 用途 | 留存 |
|---|---|---|
| 卡密、任务号、套餐、状态与结果说明 | 对账与售后举证 | 长期 |
| 从 Session 中提取的账号邮箱 | 定位是哪个账号被充值 | 长期 |
| 调用方 Key 标识与请求 IP | 归属判定与异常排查 | 长期 |

邮箱在 `cdk-info` 的历史记录中对外一律打码；你自己提交的任务在 `task` 与 `jobs` 接口中返回完整值。

### 10.3 你的义务

终端用户的 ChatGPT Session 属于其个人凭据。请在你的产品中明确告知用户该凭据的用途，并遵守你所在司法辖区的个人信息保护要求。我们仅按本节所述处理你传入的数据。

---

## 11. 服务等级与支持

| 项目 | 说明 |
|---|---|
| 单笔处理时长 | 通常 1–3 分钟，高峰期更长；建议轮询上限 15 分钟。Pro 500 全系统单车道排队，耗时更长且不固定，建议上限至少 20 分钟 |
| 计划内维护 | 期间 `status` 接口 `available` 返回 `false`，请据此拦截下单 |
| 故障处置 | 通过 `status` 与错误码实时反映，恢复后自动可用，无需你方操作 |
| 技术支持 | 工作时间响应；紧急问题请标注"紧急" |

提交支持请求时请提供 **`task_id` + 卡密前 6 位 + UTC 时间**。

> 出于安全考虑，**请勿在任何渠道向我们发送完整卡密、Session JSON 或 accessToken**。我们不会主动索取这些信息。

联系方式见 `https://suzhe.ai` 页面底部。

---

## 12. 上线自检清单

对接完成、放量之前逐条确认：

- [ ] API Key 只存在于服务端密钥管理中，代码仓库与前端产物里搜不到
- [ ] 日志中不含 Key、完整卡密、Session JSON、accessToken
- [ ] 同时校验了 HTTP 状态码与响应体 `success`
- [ ] 业务分支基于 `error` 字段，没有匹配 `detail` 文案
- [ ] `redeem` 的 5xx / 超时路径走的是"先查证再决定"，不是无脑重试
- [ ] `task_id` / `card_code` / `product` / 提交时间 / 最后状态均已落库
- [ ] 轮询有上限，超时后转人工而不是判定失败
- [ ] 提交请求的客户端超时 ≥ 90 秒，超时后先查证（`jobs` / `cdk-info`）再决定是否重发
- [ ] Pro 500：只提交给免费账号；`promax_upgrade_incomplete` / `payment_unverified` 不会被自动重提；轮询上限 ≥ 20 分钟
- [ ] 卡密前缀取自 `GET /api/v1/products`，没有硬编码；目录外的前缀交给服务端判定，本地不拒收
- [ ] 下单入口前查过 `status`，维护期不收单
- [ ] `429` 有指数退避
- [ ] `duplicate_email_confirm` 走的是「展示给用户 → 用户确认 → 重发」，不是代码里无条件自动确认
- [ ] 已跑通一次完整成功与一次失败路径

---

## 13. 版本策略

- 版本定在路径上（`/api/v1/...`）。
- **向后兼容的变更不升版本**，随时可能发生，你的解析必须容忍：新增响应字段、新增错误码、新增套餐与卡密前缀、新增可选请求参数。
- **破坏性变更会启用新路径前缀**（如 `/api/v2/`），老版本至少保留 90 天并提前通知。
- 请按"忽略未知字段"的方式解析响应，不要用严格模式反序列化。

---

## 14. 变更记录

| 日期 | 变更 |
|---|---|
| 2026-10-10 | **文档补充（无接口变更）**：`409 card_in_progress` 的含义补充——除「该卡密已有任务在处理中」外，同一账号用同一张卡密在约 3 小时内重提且上次已成功时也返回它（带原 `job_id`，轮询即得成功结果），用于网络中断/响应丢失后的安全重提；超过窗口仍返回 `card_already_used`。 |
| 2026-10-06 | **新增 ChatGPT Pro 500**：卡密前缀 `P500-`，`product` 为 `gpt_pro_500_self`，`GET /api/v1/products` 目录由 4 项变为 5 项；沿用同一组接口，自助激活页同步开放。仅支持无任何订阅的免费账号（无升级、无补差价），人工发货、不提供在线购买；全系统单车道，任务可能排队数分钟；**失败码 `promax_upgrade_incomplete` / `payment_unverified` 表示请勿重复提交**（与 `upgrade_incomplete` 含义相反）。新增错误码 `account_busy`、`promax_billing_not_php`、`account_not_eligible_promax`、`promax_trial_unsupported`、`promax_upgrade_incomplete`，详见 2.4、7.3。**接口路径与既有字段不变，存量对接无需改动。** |
| 2026-10-06 | **文档与线上行为对齐（无接口变更）**：7.3 补全 HTTP 状态码，以及此前已上线但未入文档的错误码（`pro20_already_active`、`payment_unverified`、`verification_timeout`、`verification_failed`、`account_verification_required`、`invoice_renew_required`、`activation_failed`），并写明 `email_frozen`（计费类失败连续 2 次）与 `session_failed_too_many`（任何原因连续 3 次）的阈值；`card_in_progress` 响应带 `job_id`；订正 `system_maintenance` 为失败任务的 `error_code`（不是 HTTP 503）；说明提交最坏约 80 秒、客户端超时请设 ≥ 90 秒；`task` 响应补充 `duration_seconds`，`jobs` 流水补充 `submitting` / `reset` 状态；`status` 的 `pending` / `wait_seconds` 标明为兼容保留字段；2.3 判重示例补充 `last_product_zh` / `blocked`；`account_not_eligible_pro20` 改为较少出现；新增 8.3 自助激活页说明。 |
| 2026-09-30 | **Pro 20x 开放新开通与升级**：无订阅账号可直接开通；已有生效菲区(PHP) Plus / Pro 5x 的账号自动升级为 Pro 20x（无需先取消原订阅）。已是生效 20x 的账号仍需到期后续费。新增错误码 `upgrade_incomplete`（账号已开通基础档、尚需完成升级，卡密未消耗，用同一卡密重提即可）；`account_not_eligible_pro20` 含义更新为「官方暂未对该账号开放」。**接口路径与字段不变，存量对接无需改动。** |
| 2026-09-25 | **Pro 5x 现额外支持已有生效 ChatGPT Plus 的账号**：自动升级为 Pro 5x（无需先取消 Plus），此类账号不再返回 `account_has_subscription`；Plus 仍仅支持当前无订阅的账号。（Pro 20x 的开通口径已于 2026-09-30 更新，见上一条。）**接口路径与字段不变，存量对接无需改动。** |
| 2026-09-24 | 卡密前缀更新为 `PLS-`（Plus）/ `P5X-`（Pro 5x）/ `P20X-`（Pro 20x），`product` 为 `gpt_plus_self` / `gpt_pro_5x_self` / `gpt_pro_20x_self`（见 2、5.2）。仅支持当前无订阅的账号，有订阅返回新错误码 `account_has_subscription`；另新增 `card_already_used` / `card_in_progress`（见 7.3）。此前发放的卡密仍可兑换，只是不再出现在 `/products` 目录里。**请以 `/products` 实时返回为准，不要硬编码前缀，也不要在本地拒收目录外的前缀。** |
| 2026-09-24 | 安全验证统一为 `action_required: "manual_captcha"` + `verification_url`（我方地址），iframe 或新窗口打开；`status` 仍为 `running`，完成后继续轮询原 `task_id`（见 5.6）。不再下发其他验证类型。 |
| 2026-09-23 | 接口地址改为 `https://www.academicgate.org`（原 `aisubscription.vip` 停用）；接口路径、参数与 API Key 均不变，只需替换域名。 |
| 2026-09-17 | 新增重复充值保护：同一账号 24 小时内再次提交直接拒绝，30 天内需带 `confirm_duplicate: true` 确认，详见 2.3。新增错误码 `duplicate_email_blocked` / `duplicate_email_confirm`。 |
| 2026-09-17 | 覆盖全部 GPT 套餐（Plus / Pro 5x / Pro 20x 新开与续费）。新增 `GET /api/v1/products`；`cdk-info` / `redeem` / `task` / `jobs` 响应新增 `product` 等字段；`jobs` 新增 `product` 过滤参数。同步收敛对外字段：移除内部标识，`cdk-info` 历史记录中的账号邮箱改为打码（你自己提交的任务在 `task` / `jobs` 中仍返回完整值）。**接口路径与既有业务字段未变，存量对接无需改动。** |
| 2026-09-07 | 文档迁至 `suzhe.ai/gptapi`；接口地址固定为 `aisubscription.vip`。 |
| 2026-08 | ChatGPT Plus 卡密格式更新。 |
