# 冲云链 · 对外 API 对接文档

更新日期：2026-10-12。本文对应当前冲云链服务，支持仅提链，以及提链后自动提交冲云扫码或 XXSYUN 扫码。所有示例均为占位数据，不可直接用于真实支付。

接口基础地址：`https://chongyun-link.xiaoliai.shop`

冲云扫码上游已切换为 `https://scanapi.jieyoudian.xyz`，由本服务内部调用；对接提链 API 仍使用上述提链网站域名，无需改为扫码域名。

本文所有相对接口路径均拼接上述地址。请求由对接方服务端通过 HTTPS 发起，JSON 请求使用 `Content-Type: application/json`。

## 1. 接入方式与凭证

| 内容 | 参数或请求头 | 用途 |
| --- | --- | --- |
| 账号 AT 或 Session JSON | 创建任务的 `token`，批量为 `sessions` | 用于该账号提链及后续扫码/到账检测 |
| 提链 CDK | `cdk` | 入队校验、开始执行时预留、成功后扣次；同账号已有成功扣次记录则免扣 |
| 支付扫码 CDK | `chongyun_cdk` | 自动扫码任务计次；与提链 CDK 分开 |
| 冲云自动支付开关 | `chongyun_auto_submit` | 只有 JSON 布尔值 `true` 才开启 |
| 扫码渠道 | `scan_provider` | `chongyun`（默认）或 `xxsyun`；每个任务仅一个渠道 |
| XXSYUN API Key | `xxsyun_api_key` | 对方账号中心生成的 API Key，不是提链 CDK 或扫码 CDK |
| XXSYUN 自动支付 | `xxsyun_auto_submit` | 仅布尔值 `true` 开启；渠道需设置为 `xxsyun` |
| 本地任务查询凭证 | 响应 `job_secret` → 请求头 `X-Pay153-Job-Secret` | 查询或停止对应任务 |

普通对接不需要管理员账号或内部服务密钥。保存“业务订单号 → job_id → job_secret”的对应关系。`job_secret` 由本服务返回，对接方不用生成；不同任务不能混用。不要把 AT、CDK、代理密码或查询凭证放在 URL、公开日志、截图或公开页面中。

扫码平台的 `X-Client-Token` 由本服务内部生成和保存，对接方无需自行提供，也无需再次调用扫码平台创建订单。

API 默认仅提链：两个自动提交开关均省略或传 `false` 时，不提交扫码。冲云渠道使用 `chongyun_auto_submit`，XXSYUN 渠道使用 `xxsyun_auto_submit`，不能同时开启。网页默认勾选自动支付，与 API 默认行为不同。不要传字符串 `"true"`。

当前线上启用提链 CDK。可通过 `GET /api/config` 确认 `cdk_required` 和 `batch_max`；普通调用返回这些公开配置，不返回内部凭证。

## 2. 接口一览

| 方法 | 路径 | 功能 |
| --- | --- | --- |
| GET | `/api/health` | 服务健康状态 |
| GET | `/api/config` | CDK 是否必填、批量上限 |
| POST | `/api/cdk-status` | 查询提链 CDK 额度 |
| POST | `/api/cdk-merge` | 融合多个提链 CDK 的剩余次数 |
| POST | `/api/chongyun-cdk-status` | 查询冲云支付扫码 CDK 次数 |
| POST | `/api/xxsyun-quota` | 用 API Key 查询 XXSYUN 可用与占用次数 |
| POST | `/api/xxsyun-resume` | 服务重启后恢复已知 XXSYUN 订单查询 |
| POST | `/api/checkout` | 创建单个提链/自动扫码任务 |
| POST | `/api/checkout-batch` | 创建多个任务，可部分受理 |
| GET | `/api/checkout-progress` | 查询任务、扫码和到账状态 |
| POST | `/api/checkout-cancel` | 停止本地任务 |
| GET | `/api/chongyun-overview` | 查询平台在线扫码员数量 |
| POST | `/api/capacity` | 查询任务并发及队列概况 |
| GET | `/api/link-success-stats` | 查询本项目全站提链统计 |
| GET | `/static/generated/link-success-totals.json` | 查询已保留历史中的累计成功出链数 |
| GET | `/static/generated/payment-success-12h.json` | 查询过去 12 小时最终显示 Plus 已到账的任务数 |

本服务直接返回 JSON 对象，不使用扫码平台原始接口的 `code/message/result` 通用包装。创建任务成功是 HTTP 202；查询成功通常是 HTTP 200。失败响应通常为 `{"error":"错误说明"}`，须检查 HTTP 状态和接口对应字段。

## 3. 查询两种 CDK

### 3.1 提链 CDK 余额

`POST /api/cdk-status`

```json
{"cdk":"<提链 CDK>"}
```

HTTP 200 示例：

```json
{
  "ok": true,
  "enabled": true,
  "total_uses": 100,
  "remaining_uses": 80,
  "reserved_uses": 5,
  "spent_uses": 15
}
```

`remaining_uses` 是当前可用于新任务的次数，预留中的次数见 `reserved_uses`。查询不会预留或扣次。若实例关闭提链 CDK，返回 `{"ok":true,"required":false}`，不能按缺少余额字段推断余额为零。

当前冲云项目记录已成功出链且确实扣过提链次数的账号；相同账号以后再次成功出链不再扣提链次数。规则跨批次、更换 AT、换一张有效提链 CDK 仍适用，且相同账号并发成功最多扣首次一次。仅失败、取消或计次未确认的记录不会产生免扣资格。

免扣账号使用的提链 CDK 仍须真实有效、未停用且未过期，但可用余额可以是 0；执行仍受单卡及全局并发限制，实时上限以 `/api/capacity` 为准。历史记录仅在账号可识别且能核对实际成功扣次时补入；无法匹配的历史不自动授予免扣。服务端依据 AT 中稳定账号标识判断，不接受对接方传入邮箱、账号 ID 或“免扣”参数指定免扣资格。

只保存账号指纹和首次扣次关联，服务重启或清理普通任务统计不会清除免扣资格。该规则仅针对当前冲云项目的提链计次，不影响支付扫码 CDK 的平台计次，也不改变其他提链项目的计次规则。

### 3.2 支付扫码 CDK 剩余次数

`POST /api/chongyun-cdk-status`

```json
{"cdk":"CDK-XXXX-XXXX-XXXX-XXXX"}
```

注意：在余额查询请求中字段叫 `cdk`；在创建任务请求中支付扫码 CDK 字段叫 `chongyun_cdk`。

HTTP 200 示例：

```json
{
  "totalUses": 50,
  "remainingUses": 36,
  "status": "ACTIVE"
}
```

| 字段 | 说明 |
| --- | --- |
| `totalUses` | 总次数，非负整数 |
| `remainingUses` | 当前剩余次数；真实的 `0` 表示无剩余次数 |
| `status` | `ACTIVE` 可用、`DISABLED` 停用、`UNKNOWN` 状态待确认 |

格式为 `CDK-XXXX-XXXX-XXXX-XXXX`，每组四位字母或数字。查询不扣次数，不需要 AT 或任务凭证。响应禁用缓存。

格式错误返回 HTTP 400；上游查询未成功、网络超时或响应异常返回 HTTP 502。失败响应没有有效余额，不能按 0 次处理。即使查询有余额，实际创建扫码订单时仍以平台当时的额度和状态为准。

## 4. 单笔提链与自动扫码

`POST /api/checkout`

### 4.1 一条龙请求示例

```json
{
  "token": "<原始 AT 或序列化后的 Session JSON 字符串>",
  "cdk": "<提链 CDK>",
  "plan": "plus",
  "link_type": "upi",
  "entry_proxies": ["http://proxy-user:proxy-password@entry.example.com:8080"],
  "exit_proxies": ["http://proxy-user:proxy-password@exit.example.com:8080"],
  "use_promo": true,
  "retry_count": 10,
  "chongyun_auto_submit": true,
  "chongyun_cdk": "CDK-XXXX-XXXX-XXXX-XXXX"
}
```

示例代理和凭证都是占位值。使用已获授权的账号、CDK 和实际可用的 IN 代理。流程是：校验并受理入队（不占次数）→ 轮到执行时预留提链额度 → 生成支付链接 → 确认提链扣次 → 后端提交扫码 → 查询扫码进度，同时执行独立到账检测。

### 4.2 请求参数

| 参数 | 必填 | 说明 |
| --- | --- | --- |
| `token` | 是 | 原始 AT 字符串，或将完整 Session JSON 序列化为字符串；单笔不要传 JSON 对象 |
| `cdk` | 当前线上是 | 提链 CDK；以 `/api/config` 的 `cdk_required` 为准 |
| `entry_proxies` | 是 | Checkout 入口代理，字符串数组或按行分隔的字符串，每个池最多 500 条 |
| `exit_proxies` | 是 | 支付出口代理，同上；普通对接须提供两个代理池 |
| `plan` | 否 | 默认 `plus`，仅支持 `plus` |
| `link_type` | 否 | 默认 `upi`，仅支持 `upi`；其他通道返回 403 |
| `use_promo` | 否 | JSON 布尔值，默认 `true`，应用现有优惠流程 |
| `promo_campaign` | 否 | 沿用服务的优惠流程时省略；指定时传实际活动标识 |
| `upi_vpa` | 否 | 真实 UPI VPA；留空保持当前提链流程，不自动伪造 |
| `retry_count` | 否 | 建议使用正整数，默认 10；同一个已受理任务内部的提链尝试次数 |
| `chongyun_auto_submit` | 否 | 默认关闭；仅布尔值 `true` 开启自动扫码 |
| `chongyun_cdk` | 开启自动扫码时是 | 支付扫码 CDK；关闭时不使用 |

单条代理也可用 `entry_proxy` 和 `exit_proxy`。国家与币种固定 IN/INR。公开对接不提供内部动态代理权限；只发送文档列出的业务字段。

### 4.3 仅提链

使用同一份提链参数，将 `chongyun_auto_submit` 设为 `false`，并省略 `chongyun_cdk`。不需要另一个提链接口，也不会创建冲云扫码订单。

### 4.4 受理响应

HTTP 202 示例（省略部分字段）：

```json
{
  "ok": true,
  "job_id": "<本地任务 ID>",
  "job_secret": "<本服务返回的任务查询凭证>",
  "queue_position": 0
}
```

收到后立即保存 `job_id`、`job_secret`，开始查询。`ok=true` 和 HTTP 202 仅表示任务被受理，不表示已出链或已支付。`queue_position` 会变化，不是预计等待时间。

## 5. 批量创建

`POST /api/checkout-batch`

参数与单笔相同，但去掉 `token`，改用 `sessions` 数组。每项可为 AT 字符串、Session JSON 字符串或 Session JSON 对象；公共参数适用于本次批次中的每项。

```json
{
  "sessions": ["<账号 A 的 AT>", "<账号 B 的 AT>"],
  "cdk": "<提链 CDK>",
  "entry_proxies": ["http://proxy-user:proxy-password@entry.example.com:8080"],
  "exit_proxies": ["http://proxy-user:proxy-password@exit.example.com:8080"],
  "chongyun_auto_submit": true,
  "chongyun_cdk": "CDK-XXXX-XXXX-XXXX-XXXX"
}
```

HTTP 202 部分受理示例：

```json
{
  "ok": true,
  "jobs": [{"row": 1, "job_id": "<任务 A>", "job_secret": "<任务 A 的查询凭证>", "queue_position": 0}],
  "rejected": [{"row": 2, "error": "<该行未受理的原因>", "status": 402}],
  "accepted_count": 1,
  "rejected_count": 1,
  "max_batch": 5000
}
```

`row` 从 1 开始，按原始输入顺序匹配，分别保存每一项任务凭证。必须同时处理 `jobs` 和 `rejected`，HTTP 202 不保证整批全部受理。某些 `rejected` 项没有 `status` 字段。没有任务可受理时返回 4xx，并可能带 `jobs:[]` 和 `rejected`。

单批上限以 `/api/config` 的 `batch_max` 为准。服务会拒绝同一批中相同序列化输入的重复项，但这不是跨请求幂等；不同格式的相同账号也不保证被识别为重复。对接方须按业务订单在自己服务端加锁。

提链调度的单卡和全局上限以 `/api/capacity` 的 `cdk_max_limit` 与 `global_limit` 为准，配置可能调整。有效 CDK 的批量任务先全部登记入队，不因为输入账号数超过当前可用次数而拒绝后面的行；其他参数校验仍可能造成部分受理。队列中的任务不占额度，轮到实际执行且有并发空位时才原子预留一次，已获得免扣资格的账号不占用余额。

例如剩余 69 次、提交 100 个有效账号：100 个均可入队，按当前可用并发执行，其余等待。失败任务释放次数后，后面的任务自动继续；如果可用次数暂时被其他执行中或待核查任务占用，就等待释放。确认次数已全部用完且无预留可释放时，尚未执行的普通任务结束并提示“提链 CDK 次数已用完，任务未执行”，不计入顶部提链失败统计。CDK 被停用或过期时也不会继续执行。排队不能产生额外额度，不要因等待再次提交。

## 6. 查询进度与结果

`GET /api/checkout-progress?job_id=<已保存的任务 ID>`

请求头：`X-Pay153-Job-Secret: <该任务的 job_secret>`。使用请求头传凭证，不要放进查询 URL。建议每 10 秒查询一次；查询失败可按 10、20、30 秒退避。

HTTP 200 示例（字段可能随阶段增减）：

```json
{
  "id": "<本地任务 ID>",
  "status": "running",
  "percent": 98,
  "text": "UPI 二维码已生成，正在检测 mandate 与 Plus 到账",
  "queue_position": 0,
  "cdk_state": "consumed",
  "result": {
    "provider_redirect_url": "https://payment.example.com/example",
    "qr_data": "upi://pay?pa=example%40bank&pn=Example",
    "payment_status": "awaiting_payment",
    "payment_check_status": "running",
    "entitlement_active": false,
    "entitlement_inferred": false,
    "chongyun_status": "active",
    "chongyun_polling": true,
    "chongyun_order_id": "<平台订单 ID>",
    "chongyun_order_no": "<平台订单号>",
    "chongyun_order_status": "WAITING_SCAN"
  }
}
```

### 6.1 本地任务及出链

| 顶层 `status` | 含义 |
| --- | --- |
| `queued` | 已受理，排队中 |
| `running` | 提链执行中或等待扫码/到账检测；可能已经出链 |
| `done` | 本地处理结束，仍须检查具体结果 |
| `error` | 本地任务处理失败或检测异常，检查已有链接和扫码状态 |
| `cancelled` | 本地任务已停止，已提交的扫码订单可能仍在进行 |

初期 `result` 可为 `null`。链接/二维码字段包括 `short_link`、`provider_redirect_url`、`checkout_url`、`qr_data`、`qr_image_png`、`qr_image_svg`；不是所有字段都会同时返回。页面使用的打开链接优先级为 `short_link` → `provider_redirect_url` → `checkout_url` → `qr_data`。二维码图片字段可为 data URL；`qr_data` 是二维码内容。

产出可用支付材料表示已出链；不要只靠 `status=done` 或 `percent=100` 判断。链接有效期、二维码过期及扫码进度另行判断；不要用这些字段宣称支付已完成。

`cdk_state` 是提链计次状态，例如 `queued` 已登记、尚未预留，`reserved` 执行前已预留、`consumed` 首次已核销、`free_reserved` 已识别历史成功扣次、当前免占额度执行、`reused` 本次成功重提且免扣、`refunded` 已释放/结束、`pending` 待核对、`review` 待人工核查；`bypassed` 表示该任务未启用提链计次。`free_reserved` 不表示本次已成功出链。计次异常/待核对时不要重复提交新任务。

排队时可能返回 `cdk_queue_reason`：`ready` 等待执行、`occupied` 等待其他任务释放次数、`service` 正在核对额度。未执行就结束的任务可能为 `exhausted` 次数已用完或 `disabled` 已停用/过期。以任务 `status` 和提示共同判断，不把排队误判为提链失败。

### 6.2 冲云扫码状态

下列字段位于 `result` 内。未启用自动扫码时，扫码字段可能不存在。

| `chongyun_status` | 含义与处理 |
| --- | --- |
| `submitting` | 正在提交，继续查询 |
| `active` | 平台已受理，查看 `chongyun_order_status` |
| `unknown` | 提交结果暂不确定，继续查询原任务，不新建 |
| `completed` | 扫码平台按业务规则标记完成 |
| `refunded` | 扫码任务失败，平台已退扫码次数 |
| `rejected` | 未能成功提交扫码，查看 `chongyun_error`；不会自动重提 |
| `manual_review` | 需要人工核对，不当作完成或退款；本服务每 60 秒继续查询原订单，直到平台完成或退次 |

| `chongyun_order_status` | 平台状态 |
| --- | --- |
| `WAITING_ACCEPTANCE` | 排队或等待扫码员接单 |
| `WAITING_SCAN` | 已接单，等待扫码 |
| `SCANNED_PENDING_CONFIRMATION` | 已扫码，等待确认 |
| `COMPLETED` | 平台标记完成 |
| `FAILED_REFUNDED` | 失败并退回扫码次数 |
| `UNKNOWN` | 未识别的平台状态，转人工核对 |

`chongyun_polling=true` 表示本服务仍在查询扫码状态。即使本地 `status` 已是 `done`、`error` 或 `cancelled`，也应继续读取扫码进度。

确认阶段字段为 `chongyun_verification_code`、`chongyun_verification_message`、`chongyun_verification_in_progress`、`chongyun_verification_remaining_seconds`。平台自动复查约每 10 秒一次，扫码确认阶段最多约 2 分钟，不含排队及等待扫码时间。`verification_unconfirmed` 且不再复查时转人工；`at_valid_timeout` 需以平台最终失败退次状态为准。

平台 `COMPLETED` 是平台业务结果，不是支付机构付款凭证，也不是独立的 Plus 升级证明。

### 6.3 Plus 到账与付款结果

| `payment_status` | 含义 |
| --- | --- |
| `awaiting_payment` | 等待扫码/授权 |
| `activation_pending` | 已观察到授权完成，但 Plus 到账尚未确认；检测可能已超时 |
| `completed` | 当前系统按规则标记完成，必须进一步查看是否为推断 |
| `failed` / `expired` | 授权失败或二维码过期 |
| `payment_timeout` | 本地等待/检测超时，不代表扫码平台已失败退款 |
| `payment_check_error` | 到账检测异常，不等同于已付款或已退款 |
| `verification_unavailable` | 无法验证到账，例如服务恢复后仅能恢复扫码订单 |

直接验证到账应同时满足：`payment_status="completed"`、`entitlement_active=true`、`plan_type="plus"`、`has_active_subscription=true`，并且 `entitlement_inferred` 不为 `true`。

当前实现还有一个推断分支：先观察到 UPI mandate 成功，随后账号 Token 过期时，系统会标记 `completed` 和 `entitlement_active=true`，同时返回 `entitlement_inferred=true`、`entitlement_inference_reason="mandate_succeeded_then_token_expired"`、`completion_basis="mandate_observed_token_expired"`。此类结果应展示“按规则推断到账，需核实”，不能展示为已直接验证到账。

`payment_check_status="running"` 表示独立检测仍在进行；`completed`、`failed`、`expired`、`timeout`、`error` 等表示其对应检测结果。不能仅凭扫码 `COMPLETED`、mandate 成功或 AT 失效认定有实际扣款。当前零元授权流程中的 `payment_succeeded`、`payment_was_charged` 也不应被当成通用的扫码完成开关。

### 6.4 何时停止查询

只要 `chongyun_polling=true` 或 `xxsyun_polling=true`，或本地任务仍在 `queued/running`，通常继续查询。对于未取消的任务，`payment_check_status=running` 时也继续查询。本地已终止且没有后续扫码/到账检测时可停止；人工核对或无法确认的结果应单独记录，不能统一记为支付失败。

提链任务主要保存在运行内存中，终态记录会按保留策略清理；服务重启或清理后，原提链详情/链接可能无法再次取回。已落库的扫码记录可凭原 `job_id` 和 `job_secret` 读取；XXSYUN 还需重新提供 API Key 恢复实时查询（第 11 节）。不会恢复完整提链结果。对接方须及时保存业务所需结果，404 不自动意味着订单未创建。

## 7. 停止任务

`POST /api/checkout-cancel`

请求头：`X-Pay153-Job-Secret: <该任务的 job_secret>`。

```json
{"job_id":"<本地任务 ID>"}
```

本地停止成功返回 HTTP 200 和 `{"ok":true}`。停止后查询原任务确认状态。

此操作停止本地任务，不取消已提交到扫码平台的订单。扫码平台目前没有取消接口，已提交的扫码任务仍可能继续完成；查询其 `chongyun_polling` 及平台状态。不要将本地停止当成扫码退款承诺。

提链已出链并核销的次数，不因后续停止或扫码失败自动退回；提链尚未产出支付材料时按预留释放规则处理。支付扫码次数只有平台确认失败退次才退回。

## 8. 在线扫码员、容量与统计

### 8.1 在线扫码员

`GET /api/chongyun-overview`，无需 CDK 或任务凭证。

```json
{"available":true,"onlineWorkers":3}
```

成功 HTTP 200；暂不可用时 HTTP 503，返回 `{"available":false,"onlineWorkers":null}`。在线人数包含忙碌或不参与派单的人员，不等于可立即接单人数。后端缓存约 30 秒，建议最多每 30 秒刷新一次；不可用不能显示成 0 人。

### 8.2 提链容量

`POST /api/capacity`，JSON `{"cdk":"<提链 CDK>"}`；不传 CDK 时不能据返回值推断某张卡的可用并发。

主要字段：`global_active` / `global_limit`（当前全局执行数/上限）、`queued_jobs`（队列任务）、`cdk_active` / `cdk_limit`（该卡执行数/当前可用并发上限）、`cdk_max_limit`（单卡配置上限）、`cdk_available`（可用次数）、`cdk_quota_checked`（本次是否成功读取额度）。容量是快照，真正受理以创建接口为准。

`quickjs_available` / `quickjs_size` 是 JS 运算进程池空闲数/配置数，与扫码人员数量不同，不要用来显示扫码员人数。

### 8.3 本项目全站统计

`GET /api/link-success-stats?hours=12&bucket_minutes=5`

返回 `success`、`failed`、`total`、`success_rate` 和时间段 `buckets`；`success_rate` 为百分数，无记录时为 `null`。该统计是当前项目全站的出链统计，不是当前 CDK、当前批次或扫码付款成功统计，也不合并其他项目的数据。

### 8.4 历史累计成功与过去 12 小时成功支付

`GET /static/generated/link-success-totals.json`：读取 `providers.upi.success`，为已保留历史中按任务去重的累计成功出链数，不受 12 小时窗口影响。页面在工作台标题区域单独显示。

`GET /static/generated/payment-success-12h.json`：读取 `success`，为过去 12 小时内首次最终显示“Plus 已到账”的任务数量；`window_hours=12`、`basis="displayed_plus_arrived"`。不区分冲云、XXSYUN 或其他扫码方式，同一任务只记一次，按到账时刻进入窗口，到期移出。平台接单、扫码完成、仅出链成功均不计入。

成功支付的显示口径包含第 6.3 节的推断到账分支，不表示发生真实现金扣款，也不能替代直接验证的到账证明。成功出链与成功支付的时间不同，不能用两个滚动窗口数值直接判断某批任务的付款转化率。

两个静态统计均约每 30 秒更新，`generated_at` 为 Unix 秒。建议以 `cache: no-store` 获取，每 30 秒最多查询一次；超过 180 秒未更新或请求失败时标记统计暂不可用，不按 0 处理。该统计是全站聚合，不返回账号或凭据。`/api/link-success-stats` 返回原始出链统计，页面曾进行的历史失败展示调整不改变该接口数据。

## 9. 幂等、错误与重试

创建任务的 `/api/checkout` 和 `/api/checkout-batch` 当前没有对外业务幂等键。重复 POST 可能创建另一项任务；首次成功扣次尚未确认时也可能再次预留额度，最终相同账号成功按去重规则只扣一次。同账号提链免扣不等于请求幂等，重复任务仍可能生成新的支付链接并再次提交扫码、消耗扫码次数；不要添加自定义订单号后就假定已幂等。

| 情况 | 建议处理 |
| --- | --- |
| 创建返回 202 | 保存全部已受理任务 ID/凭证并查询；批量同时处理拒绝项 |
| 400 参数错误 | 修正参数；批量先检查是否存在已受理项 |
| 402 提链 CDK 校验/登记失败 | 检查提链 CDK；服务异常时先核对原请求，不盲目重复提交 |
| 403 通道不支持 | 使用 `upi` |
| 404 查询/停止失败 | 核对任务 ID 与对应凭证；也可能是记录已清理，不要直接新建 |
| 429 限流 | 遵守 `Retry-After`；仅在明确未受理时重试创建 |
| 503 明确维护/暂停 | 按错误说明等待恢复；传输层网关错误仍按结果未知处理 |
| 创建超时、断连、502/504 或非 JSON | 可能已创建；有任务凭证时先查询，无凭证时人工核对，不自动重发整批 |
| 查询暂时失败 | 保留原任务与最后有效状态，退避后重查，不映射为业务失败 |

`retry_count` 只控制已受理任务内部提链重试，不赋予外部 POST 幂等能力，也不是扫码订单重提次数。平台扫码提交结果未知时，本服务查询原订单，不盲目重复创建；对接方也不能另开任务规避等待。

当前没有对外完成回调/webhook、按对接方业务订单号找回任务的接口或专用合作方 API Key。业务关联、并发锁、结果保存由对接方自行维护。

## 10. 联调建议

1. 先请求 `/api/health`、`/api/config`，再检查两种 CDK 余额及扫码员人数；这些查询不创建支付任务。
2. 用获授权账号、实际可用代理及两种 CDK 提交一笔任务，保存返回的 `job_id` 和 `job_secret`。真实提链/扫码按各自规则计次。
3. 查询原任务，分别记录“已出链”“扫码平台状态”“直接验证或推断的 Plus 到账”，验证后再增加批量规模。
4. 发生问题只提供任务号、平台订单号、请求时间、HTTP 状态及脱敏错误；不要发送完整请求体、请求头、AT、CDK、代理密码或任务查询凭证。

所有余额和状态示例均为说明用途，不代表真实平台当前余额、扫码员数量或任何支付结果。


## 11. XXSYUN 扫码支付

平台接口文档： https://upi.xxsyun.xyz/employer/api/ 。本服务调用固定 HTTPS 地址 `https://upi.xxsyun.xyz/api/v1`，不接受自定义平台域名。

先在对方平台注册并激活雇主账号、兑换扫码额度，再从账号中心生成 API Key。无需把登录密码提交给本服务。启用后，本服务将该任务的原始 AT 和 Stripe UPI 支付说明页链接发送给 XXSYUN，并使用 API Key 查询进度；请仅提交已获授权的账号。

### 11.1 创建单个或批量任务

单个任务沿用 `POST /api/checkout`，批量沿用 `POST /api/checkout-batch`。提链参数及任务查询凭证保持原规则，添加以下字段：

```json
{
  "scan_provider": "xxsyun",
  "xxsyun_auto_submit": true,
  "xxsyun_api_key": "<平台账号中心生成的 API Key>",
  "chongyun_auto_submit": false
}
```

这只是需要附加到完整提链请求的支付字段，单独提交会因缺少 AT、代理和提链 CDK 而被拒绝。不要同时开启两个渠道，也不要把 Key 填入 `chongyun_cdk`。每个任务只向所选平台提交一次业务订单，不会失败后自动切换到另一个平台。派单采用对方 `dispatch_mode=auto`。

API Key、AT 不进入本地扫码订单数据库、请求抓取日志或公开任务结果。网页的 API Key 仅保留在当前页面内存，刷新需重新填写；后端在当前进程内保留 Key 至该订单查询结束。数据库只保存本地任务号、幂等标识、任务查询凭证的哈希及经过白名单过滤的订单状态；第三方平台的数据保存规则由该平台决定。

XXSYUN 接收 `https://payments.stripe.com/upi/instructions/...` 说明页链接。若该任务未产出此链接，仅显示扫码提交未成功，保留已有提链结果，不会用原始 `upi://` 字符串或其他地址替代。

### 11.2 查询 XXSYUN 余额

`POST /api/xxsyun-quota`，请求体：

```json
{"api_key":"<平台账号中心生成的 API Key>"}
```

成功示例：

```json
{"remainingUses":69,"totalUses":100,"reservedUses":8}
```

对应对方 `summary.available_uses`、`summary.total_uses`、`summary.pending_uses`。可用次数已扣除占用，不能再次相减。查询不扣次数。无法读取可用次数时返回错误，不会伪装成余额 0；总数或占用字段不可识别时为 `null`。响应设置 `Cache-Control: no-store`，不会返回额度来源、完整 CDK、账号资料或原始上游错误。

### 11.3 任务结果与状态

继续使用 `GET /api/checkout-progress` 和原 `X-Pay153-Job-Secret` 查询。`result` 增加：

| 字段 | 说明 |
| --- | --- |
| `xxsyun_status` | 本地扫码流程状态，见下表 |
| `xxsyun_order_id` | 平台订单 UUID，得到成功或重复订单响应后保存 |
| `xxsyun_order_status` | 平台最新状态；本地输入错误或提交结果未知时可能没有 |
| `xxsyun_polling` | 是否仍在后台查询 |
| `xxsyun_failure_code` | 经过白名单过滤的失败码 |
| `xxsyun_error` | 本地固定脱敏提示，不转发原始平台报错 |
| `xxsyun_submitted_at`、`xxsyun_updated_at` | Unix 秒 |

| 状态 | 处理 |
| --- | --- |
| `submitting` | 正在提交，或使用原幂等标识核实请求 |
| `awaiting_worker` | 等待接单，继续查询 |
| `claimed` | 已接单，继续查询 |
| `verifying` | 已扫码，平台核验中，继续查询 |
| `completed` | 平台标记完成，停止平台轮询；Plus 到账仍以本服务独立检测为准 |
| `failed`、`expired`、`cancelled` | 平台终态，停止平台轮询；额度以余额查询为准 |
| `rejected` | 未受理，包括 HTTP 200 响应中的失败行；检查错误后处理 |
| `manual_review` | 提交结果未知或状态无法识别，停止自动重试，到平台核对，不能当成失败重新创建 |
| `credentials_required` | 服务重启后缺少内存中的 Key，需恢复原订单查询 |

平台订单 `completed` 不等于独立的 Plus 到账证明。没有平台订单 ID 的未知提交不可通过邮箱猜测匹配其他订单。`/api/checkout-cancel` 只停止本地任务，不取消平台已经创建的扫码订单；如需取消待接单订单，请在该平台处理。

### 11.4 超时、去重与恢复

每个本地任务生成独立 `Idempotency-Key`，发送给平台 `/orders/batch`。连接超时、5xx 或非 JSON 等未知结果最多重试 2 次，全部使用原 Key 和完全相同的请求体；收到 `duplicated` 时跟踪返回的原订单。HTTP 200 仍检查 `created`、`duplicated`、`failed`，不把失败行当成功。

已知订单每 5 秒查询一次；网络错误按 5、10、15 秒逐步退避至 30 秒，保留原状态。超时重试耗尽、进程中断且未拿到订单 ID 时转人工核查，不会生成新 Key 再提交。

本服务重启后不会从磁盘加载 API Key。已知订单转为 `credentials_required` 时，用原任务查询凭证调用 `POST /api/xxsyun-resume`：

```http
Content-Type: application/json
X-Pay153-Job-Secret: <原任务查询凭证>
```

```json
{"job_id":"<原本地任务号>","api_key":"<同一平台账号的 API Key>"}
```

该接口只查询原订单，不创建新订单。平台允许该 Key 查询并返回匹配的订单后恢复后台轮询；无原任务凭证返回 404。网页任务卡片会出现“恢复查询”按钮，填写 API Key 后点击即可。

不要因本地连接失败、页面刷新或等待超时重新调用 `/api/checkout` 或 `/api/checkout-batch`；创建新的本地任务仍可能生成新扫码订单。对外创建任务本身没有业务幂等键，平台提交去重只作用于同一个本地任务。

平台若按服务器请求特征拒绝 API 访问，本服务返回固定提示；余额接口返回 HTTP 502 和 `error_code="upstream_access_blocked"`。需联系平台管理员开放 API 访问，不会伪装浏览器或绕过平台拦截。


## 12. 融合提链 CDK

`POST /api/cdk-merge`，使用 `Content-Type: application/json`。仅支持本平台提链 CDK，不支持扫码平台 CDK 或 API Key。

```json
{"codes":["<第一个提链 CDK>","<第二个提链 CDK>","<第三个提链 CDK>"]}
```

一次提交 2–20 个不重复的有效 CDK，每个必须有剩余次数。旧码存在排队、执行或待核查占用时拒绝融合；请先结束相关任务。全部校验通过后，在同一事务内生成新码、转移全部剩余次数并永久停用旧码。任一校验失败不会转移额度。

例如剩余 1、2、3 次，融合后新码为 6 次，旧码全部不可再用；已消费历史保留。新码沿用来源中最早的到期时间；全部不限期时新码也不限期。

```json
{
  "ok":true,
  "code":"<新提链 CDK，请妥善保管>",
  "source_count":3,
  "transferred_uses":6,
  "total_uses":6,
  "remaining_uses":6,
  "enabled":true,
  "expires":null,
  "reused":false
}
```

`expires` 为 Unix 秒时间戳或 `null`。请求超时或响应丢失时，必须用完整的同一组旧码重试（顺序可变），可取回同一个新码，`reused` 为 `true`，不会重复生成或转入次数。返回的 `remaining_uses` 是新码当前可用次数，后续消费可能使其小于最初转入次数。若新码之后已停用、过期或再次融合，返回 `enabled: false`。

旧码不得重新启用或追加次数。完整原始码组可用于恢复新码，应与新码一样保密。不要把请求或响应内容写入日志、URL 或公开截图。接口返回 `Cache-Control: no-store`；每个 IP 每分钟最多 8 次请求，超限返回 429 和 `Retry-After`。跨站浏览器请求被拒绝，服务端对接无需 `Origin`。
