Base URL:
http://localhost:3000(以实际部署地址为准)本文档仅包含前台用户使用的接口,不包含管理后台(
/admin/*)相关接口。
POST /api/redeem
使用兑换码发起 PayPal 充值请求。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
code |
string | ✅ | 兑换码 |
amount |
number | ✅ | 充值金额(USD),必须为正数 |
{
"code": "ABC123",
"amount": 50
}{
"success": true,
"data": {
"approvalUrl": "https://www.paypal.com/checkoutnow?token=...",
"orderId": "PAYPAL_ORDER_ID"
}
}| 字段 | 类型 | 说明 |
|---|---|---|
success |
boolean | 是否成功 |
data.approvalUrl |
string | PayPal 支付跳转链接 |
data.orderId |
string | PayPal 订单 ID |
| HTTP 状态码 | 错误信息 | 说明 |
|---|---|---|
| 400 | 请输入兑换码 |
未提供兑换码 |
| 400 | 请输入有效的充值金额 |
金额无效或非正数 |
| 403 | 无效兑换码 |
兑换码不存在 |
| 403 | 该兑换码已使用 |
兑换码已被使用 |
| 500 | PayPal 下单失败: ... |
PayPal API 调用失败 |
GET /api/redeem/status?code=xxx
查询兑换码的当前状态和充值记录。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
code |
string (query) | ✅ | 兑换码 |
{
"success": true,
"data": {
"status": "已使用",
"amount": 50,
"createdAt": "2025-01-15 14:30:00",
"usedAt": "2025-01-15 15:00:00",
"paypalEmail": "user@example.com",
"paypalOrderId": "PAYPAL_ORDER_ID"
}
}| 字段 | 类型 | 说明 |
|---|---|---|
data.status |
string | 状态:未使用 / 已使用 |
data.amount |
number | null | 充值金额(USD) |
data.createdAt |
string | 创建时间 |
data.usedAt |
string | null | 使用时间 |
data.paypalEmail |
string | null | PayPal 收款邮箱 |
data.paypalOrderId |
string | null | PayPal 订单号 |
| HTTP 状态码 | 错误信息 | 说明 |
|---|---|---|
| 400 | 请输入查询兑换码 |
未提供兑换码 |
| 404 | 未找到该兑换码记录 |
兑换码不存在 |
POST /api/verify-cdk
验证 CDK 是否有效且未使用。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
cdk |
string | ✅ | CDK 激活码 |
{
"cdk": "CDK-XXXXX"
}{
"success": true,
"data": {
"type": "自助"
}
}| 字段 | 类型 | 说明 |
|---|---|---|
data.type |
string | CDK 类型:自助 / 成品 |
| HTTP 状态码 | 错误信息 | 说明 |
|---|---|---|
| 400 | 请输入 CDK |
未提供 CDK |
| 403 | 该 CDK 已使用 |
CDK 已被使用 |
| 403 | 无效 CDK |
CDK 不存在 |
GET /api/cdk/query?cdk=xxx
查询 CDK 的使用状态和产品信息。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
cdk |
string (query) | ✅ | CDK 激活码 |
{
"success": true,
"data": {
"status": "已使用",
"type": "成品",
"createdAt": "2025-01-15 10:00:00",
"usedAt": "2025-01-15 12:00:00",
"imapKey": "imap_key_string",
"downloadAvailable": true,
"downloadFileName": "credentials_sub2api.json",
"sub2apiAvailable": true,
"sub2apiFileName": "credentials_sub2api.json",
"cpaAvailable": true,
"cpaFileName": "credentials_cpa.json"
}
}| 字段 | 类型 | 说明 |
|---|---|---|
data.status |
string | 状态:未使用 / 已使用 |
data.type |
string | CDK 类型:自助 / 成品 |
data.createdAt |
string | 创建时间 |
data.usedAt |
string | null | 使用时间 |
data.imapKey |
string | null | IMAP 密钥(成品类型) |
data.downloadAvailable |
boolean | 是否可下载(旧字段,等同 sub2apiAvailable) |
data.downloadFileName |
string | null | 下载文件名(旧字段,等同 sub2apiFileName) |
data.sub2apiAvailable |
boolean | Sub2API 凭证是否可下载 |
data.sub2apiFileName |
string | null | Sub2API 凭证文件名 |
data.cpaAvailable |
boolean | CPA 凭证是否可下载 |
data.cpaFileName |
string | null | CPA 凭证文件名 |
| HTTP 状态码 | 错误信息 | 说明 |
|---|---|---|
| 400 | 请输入查询激活码 |
未提供 CDK |
| 404 | 未找到该激活码记录 |
CDK 不存在 |
GET /api/cdk/download?cdk=xxx&kind=sub2api
下载已使用的「成品」类型 CDK 对应的凭证文件。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
cdk |
string (query) | ✅ | CDK 激活码 |
kind |
string (query) | ❌ | 下载类型:sub2api(默认)/ cpa |
直接返回 JSON 文件下载(Content-Disposition: attachment)。
| HTTP 状态码 | 错误信息 | 说明 |
|---|---|---|
| 400 | Missing cdk |
未提供 CDK |
| 400 | Invalid download kind |
kind 参数无效 |
| 403 | CDK not eligible for download |
CDK 无效/非成品类型/未使用 |
| 404 | Credential file not found |
凭证记录不存在 |
| 404 | Credential file missing |
凭证文件不在磁盘上 |
服务端通过 WebSocket 提供任务状态的实时推送,前端可以在发起兑换/CDK 操作后订阅对应任务,实时获取处理进度。
- URL:
ws://localhost:3000(与 HTTP 服务同端口,自动升级为 WebSocket) - 协议: 原生 WebSocket(
ws库实现) - 无需认证: 直接连接即可
const ws = new WebSocket('ws://localhost:3000');连接成功后,客户端发送 JSON 消息订阅某个任务的状态更新:
{
"type": "subscribe",
"jobKey": "redeem_ABC123"
}| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
type |
string | ✅ | 固定为 "subscribe" |
jobKey |
string | ✅ | 任务标识,格式见下表 |
| 类型 | 格式 | 示例 | 说明 |
|---|---|---|---|
| 兑换码 | redeem_{code} |
redeem_ABC123 |
订阅兑换码充值任务 |
| CDK | cdk_{code} |
cdk_CDK-XXXXX |
订阅 CDK 激活任务 |
说明:
- 订阅后服务端会立即推送一次当前快照(snapshot),包含任务的最新状态。
- 同一连接可切换订阅不同 jobKey,切换时自动取消上一个订阅。
- 连接断开时自动取消订阅。
所有服务端推送消息均遵循统一格式:
{
"type": "task_update",
"jobKey": "redeem_ABC123",
"data": {
"status": "pending",
"code": "ABC123",
"message": "描述信息",
// ... 其他字段
}
}| 字段 | 类型 | 说明 |
|---|---|---|
type |
string | 固定为 "task_update" |
jobKey |
string | 对应的任务标识 |
data |
object | 任务状态数据,字段因任务类型而异 |
data.status |
string | 任务状态(见下方各类型详细说明) |
data.code |
string | 兑换码或 CDK 码 |
data.message |
string | 人类可读的状态描述 |
pending → completed
pending → failed
① pending — PayPal 订单已创建,等待支付
{
"type": "task_update",
"jobKey": "redeem_ABC123",
"data": {
"status": "pending",
"code": "ABC123",
"amount": 50,
"paypal_order_id": "PAYPAL_ORDER_ID",
"approval_url": "https://www.paypal.com/checkoutnow?token=...",
"message": "PayPal 订单已创建,等待支付..."
}
}| 字段 | 类型 | 说明 |
|---|---|---|
data.status |
string | "pending" |
data.code |
string | 兑换码 |
data.amount |
number | 充值金额(USD) |
data.paypal_order_id |
string | PayPal 订单号 |
data.approval_url |
string | PayPal 支付链接 |
data.message |
string | "PayPal 订单已创建,等待支付..." |
② completed — 支付成功,充值完成
{
"type": "task_update",
"jobKey": "redeem_ABC123",
"data": {
"status": "completed",
"code": "ABC123",
"amount": 50,
"paypal_email": "user@example.com",
"paypal_order_id": "PAYPAL_ORDER_ID",
"message": "支付成功,充值已完成!"
}
}| 字段 | 类型 | 说明 |
|---|---|---|
data.status |
string | "completed" |
data.code |
string | 兑换码 |
data.amount |
number | 实际充值金额 |
data.paypal_email |
string | 付款人 PayPal 邮箱 |
data.paypal_order_id |
string | PayPal 订单号 |
data.message |
string | "支付成功,充值已完成!" |
③ failed — 处理失败
{
"type": "task_update",
"jobKey": "redeem_ABC123",
"data": {
"status": "failed",
"code": "ABC123",
"message": "PayPal 下单失败: 具体错误信息"
}
}| 字段 | 类型 | 说明 |
|---|---|---|
data.status |
string | "failed" |
data.code |
string | 兑换码 |
data.message |
string | 错误描述 |
订阅后服务端从数据库查询当前状态,推送完整快照:
{
"type": "task_update",
"jobKey": "redeem_ABC123",
"data": {
"status": "completed",
"code": "ABC123",
"amount": 50,
"paypal_email": "user@example.com",
"paypal_order_id": "PAYPAL_ORDER_ID",
"created_at": "2025-01-15T06:30:00.000Z",
"used_at": "2025-01-15T07:00:00.000Z",
"captured_at": "2025-01-15T07:00:05.000Z",
"message": "充值已完成"
}
}| 额外字段 | 类型 | 说明 |
|---|---|---|
data.created_at |
string | null | 兑换码创建时间 |
data.used_at |
string | null | 使用时间 |
data.captured_at |
string | null | PayPal 扣款确认时间 |
processing → completed
processing → failed
① processing — CDK 正在处理中
{
"type": "task_update",
"jobKey": "cdk_CDK-XXXXX",
"data": {
"status": "processing",
"code": "CDK-XXXXX",
"cdkType": "自助",
"message": "正在处理 CDK..."
}
}| 字段 | 类型 | 说明 |
|---|---|---|
data.status |
string | "processing" |
data.code |
string | CDK 码 |
data.cdkType |
string | CDK 类型:自助 / 成品 |
data.message |
string | "正在处理 CDK..." |
② completed(自助类型)— 产品分配成功
{
"type": "task_update",
"jobKey": "cdk_CDK-XXXXX",
"data": {
"status": "completed",
"code": "CDK-XXXXX",
"cdkType": "自助",
"message": "自助产品分配成功"
}
}③ completed(成品类型)— 成品领取成功
{
"type": "task_update",
"jobKey": "cdk_CDK-XXXXX",
"data": {
"status": "completed",
"code": "CDK-XXXXX",
"cdkType": "成品",
"imapKey": "imap_key_string",
"sub2apiAvailable": true,
"sub2apiFileName": "credentials_sub2api.json",
"cpaAvailable": true,
"cpaFileName": "credentials_cpa.json",
"message": "成品领取成功"
}
}| 字段 | 类型 | 说明 |
|---|---|---|
data.status |
string | "completed" |
data.code |
string | CDK 码 |
data.cdkType |
string | "成品" |
data.imapKey |
string | null | IMAP 密钥 |
data.sub2apiAvailable |
boolean | Sub2API 凭证是否可下载 |
data.sub2apiFileName |
string | null | Sub2API 凭证文件名 |
data.cpaAvailable |
boolean | CPA 凭证是否可下载 |
data.cpaFileName |
string | null | CPA 凭证文件名 |
data.message |
string | "成品领取成功" |
④ failed — 处理失败
{
"type": "task_update",
"jobKey": "cdk_CDK-XXXXX",
"data": {
"status": "failed",
"code": "CDK-XXXXX",
"message": "库存不足,请联系客服"
}
}| 字段 | 类型 | 说明 |
|---|---|---|
data.status |
string | "failed" |
data.code |
string | CDK 码 |
data.message |
string | 错误描述(如 "库存不足,请联系客服" / "CDK 处理失败: ...") |
{
"type": "task_update",
"jobKey": "cdk_CDK-XXXXX",
"data": {
"status": "completed",
"code": "CDK-XXXXX",
"cdkType": "成品",
"created_at": "2025-01-15T02:00:00.000Z",
"used_at": "2025-01-15T04:00:00.000Z",
"imapKey": "imap_key_string",
"sub2apiAvailable": true,
"sub2apiFileName": "credentials_sub2api.json",
"cpaAvailable": true,
"cpaFileName": "credentials_cpa.json",
"message": "CDK 已使用"
}
}| 额外字段 | 类型 | 说明 |
|---|---|---|
data.created_at |
string | null | CDK 创建时间 |
data.used_at |
string | null | CDK 使用时间 |
所有 HTTP 接口的错误响应均遵循统一格式:
{
"success": false,
"message": "错误描述"
}| 字段 | 类型 | 说明 |
|---|---|---|
success |
boolean | 固定为 false |
message |
string | 错误原因描述 |
注意:下载接口 (
/api/cdk/download) 的错误响应为纯文本,非 JSON 格式。
// 1. 使用兑换码
async function redeemCode(code, amount) {
const res = await fetch('/api/redeem', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code, amount })
});
const data = await res.json();
if (data.success) {
window.location.href = data.data.approvalUrl; // 跳转 PayPal 支付
} else {
alert(data.message);
}
}
// 2. 查询兑换码状态
async function checkRedeemStatus(code) {
const res = await fetch(`/api/redeem/status?code=${encodeURIComponent(code)}`);
return await res.json();
}
// 3. 验证 CDK
async function verifyCDK(cdk) {
const res = await fetch('/api/verify-cdk', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ cdk })
});
return await res.json();
}
// 4. 查询 CDK 状态
async function queryCDK(cdk) {
const res = await fetch(`/api/cdk/query?cdk=${encodeURIComponent(cdk)}`);
return await res.json();
}
// 5. 下载产品文件
function downloadProduct(cdk, kind = 'sub2api') {
window.open(`/api/cdk/download?cdk=${encodeURIComponent(cdk)}&kind=${kind}`);
}# 使用兑换码
curl -X POST http://localhost:3000/api/redeem \
-H "Content-Type: application/json" \
-d '{"code":"ABC123","amount":50}'
# 查询兑换码状态
curl "http://localhost:3000/api/redeem/status?code=ABC123"
# 验证 CDK
curl -X POST http://localhost:3000/api/verify-cdk \
-H "Content-Type: application/json" \
-d '{"cdk":"CDK-XXXXX"}'
# 查询 CDK 状态
curl "http://localhost:3000/api/cdk/query?cdk=CDK-XXXXX"
# 下载产品文件(Sub2API)
curl -O "http://localhost:3000/api/cdk/download?cdk=CDK-XXXXX&kind=sub2api"
# 下载产品文件(CPA)
curl -O "http://localhost:3000/api/cdk/download?cdk=CDK-XXXXX&kind=cpa"// 1. 订阅兑换码任务状态
function watchRedeemTask(code, onUpdate) {
const ws = new WebSocket(`ws://${location.host}`);
ws.onopen = () => {
ws.send(JSON.stringify({
type: 'subscribe',
jobKey: `redeem_${code}`
}));
};
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.type === 'task_update') {
onUpdate(msg.data);
// 终态时可关闭连接
if (msg.data.status === 'completed' || msg.data.status === 'failed') {
ws.close();
}
}
};
ws.onerror = (err) => console.error('WebSocket error:', err);
ws.onclose = () => console.log('WebSocket closed');
return ws;
}
// 2. 订阅 CDK 任务状态
function watchCDKTask(cdk, onUpdate) {
const ws = new WebSocket(`ws://${location.host}`);
ws.onopen = () => {
ws.send(JSON.stringify({
type: 'subscribe',
jobKey: `cdk_${cdk}`
}));
};
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.type === 'task_update') {
onUpdate(msg.data);
if (msg.data.status === 'completed' || msg.data.status === 'failed') {
ws.close();
}
}
};
ws.onerror = (err) => console.error('WebSocket error:', err);
ws.onclose = () => console.log('WebSocket closed');
return ws;
}
// 3. 完整示例:兑换并实时监听
async function redeemAndWatch(code, amount) {
// 先发起兑换
const res = await fetch('/api/redeem', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code, amount })
});
const result = await res.json();
if (!result.success) {
console.error('兑换失败:', result.message);
return;
}
// 订阅状态推送
watchRedeemTask(code, (data) => {
console.log(`[${data.status}] ${data.message}`);
if (data.status === 'completed') {
console.log('充值完成!PayPal:', data.paypal_email);
}
});
// 跳转支付
window.open(result.data.approvalUrl);
}# 安装 wscat
npm install -g wscat
# 连接 WebSocket
wscat -c ws://localhost:3000
# 连接后发送订阅消息(手动输入)
{"type":"subscribe","jobKey":"redeem_ABC123"}用户输入兑换码 + 金额
│
▼
POST /api/redeem
│
├── 失败 → 返回错误信息
│ (WebSocket 推送 status: failed)
│
└── 成功 → 返回 PayPal 支付链接
│ (WebSocket 推送 status: pending)
│
▼
用户跳转 PayPal 完成支付
│
▼
PayPal 回调确认
│ (WebSocket 推送 status: completed)
│
▼
GET /api/redeem/status → 查询充值结果
用户输入 CDK
│
▼
POST /api/verify-cdk ← 验证有效性
│
├── 无效/已用 → 提示错误
│
└── 有效 → 发起激活
│ (WebSocket 推送 status: processing)
│
├── 失败 → 返回错误
│ (WebSocket 推送 status: failed)
│
└── 成功(WebSocket 推送 status: completed)
│
▼
GET /api/cdk/query ← 查询详情
│
├── 自助类型 → 显示状态信息
│
└── 成品类型 → 显示下载选项
│
▼
GET /api/cdk/download ← 下载凭证
(kind=sub2api / cpa)