LinkQueue
返回操作页

LinkQueue 用户 API v1

本文档描述 LinkQueue 的公开用户 API。API 与 /kakaoscan 和 /kakaopay 操作页面并存,使用同一 CDK USDT 钱包、任务队列、商家在线状态、退款规则和 30 分钟有效期。

接入地址

生产基础地址(部署包含 API v1 的 0.14.0 或更高版本后生效):

https://kakao.whitexfox.cn/api/v1

本地开发地址:

http://127.0.0.1:3000/api/v1

/api/v1 下的所有响应均为 JSON,并包含以下响应头:

X-LinkQueue-API-Version: 1
Cache-Control: private, no-store, max-age=0

API 面向服务端调用,不开放跨域浏览器凭证共享。不要把 CDK 放进公开前端代码、URL、日志或分析平台。

浏览器扩展验收接口不属于 /api/v1,位于 /api/user/tasks/{taskId}/verification/*。这些端点为不携带 Cookie 的扩展请求开放 CORS,但每次操作仍必须提交完整 CDK,并由服务端校验该 CDK 对任务的所有权。

浏览器扩展与手动验收

方法路径用途
POST/api/user/tasks/{taskId}/verification/challenge用 CDK 申请五分钟有效的一次性 challenge
POST/api/user/tasks/{taskId}/verification/evidencemultipart 提交截图或插件状态证据
POST/api/user/tasks/{taskId}/verification/status用 CDK 读取当前验收状态
POST/api/v1/tasks/{taskId}/appeal商家确认扫码满 60 秒后,提交图片任务未到账申诉
POST/api/merchant/tasks/{taskId}/verification/scan商家启动验收窗(链接 60 秒,插件图片 120 秒)
GET/api/merchant/tasks/{taskId}/verification/status任务所属商家读取验收与证据摘要
GET/api/merchant/tasks/{taskId}/verification/evidence/{evidenceId}任务所属商家读取二维码截图

challenge 请求体为 {"cdk":"LQ-..."}。challenge 绑定任务与 CDK、使用后立即失效,每个任务最多签发 10 次;它不扣减钱包或扫码次数。证据接口接收 cdk、challenge、source、kind、JSON 字符串 metadata,二维码证据还需一个 image 文件。source=manual 只允许 kind=qr_capture,支持真实 JPEG/PNG/WebP、最大 5 MiB、宽高 120-8192 像素;同任务相同 SHA-256 图片拒绝重复提交。

插件证据类型为 qr_capture、checkout_callback 和 account_subscription。回调证据只要求 provider=adyen、returnObserved=true、回跳 URL 哈希和观测时间;扫码端被视为可信,不强制校验回调域名、路径来源或网络请求完成状态,同一验收会话仍只能提交一次。新建图片任务的插件会在强制刷新二维码页后读取 ChatGPT 会话 AT,并使用 /availability 返回的 P-256 公钥封装为 ECDH/AES-GCM 密文,服务器只保存密文。商家声明扫描后,服务端 worker 每 15 秒解密一次并请求 ChatGPT /backend-api/me 与 /backend-api/subscriptions;只有服务端确认 AT 有效、账号指纹一致且订阅已激活时,才会设置账号已验证。只要 AT 有效、账号指纹一致且订阅仍未激活,服务端就记录 valid_no_subscription。扫码记录满 60 秒后,CDK 持有者可以调用 POST /api/v1/tasks/{taskId}/appeal,申诉会立即触发一次轮询;满足上述三项条件时自动写入唯一 complaint_refund 流水并退回实际扫码扣费。Plus 已激活、AT 失效、网络异常或账号不一致的申诉保留为待复核。没有服务端 AT 的旧链接任务不会仅凭客户端 account_subscription JSON 完成自动验收;AT 密文不会返回到客户端。

浏览器扩展运行在客户设备上,无法提供硬件级远程证明。以上机制可阻止 CDK 越权、challenge 重放、跨任务误传和常见重复数据,但不能证明被完全控制的客户端没有修改扩展或模拟请求。争议订单应保留原始证据,并以支付处理方或平台服务端可验证的交易结果为最高等级依据。

CDK 鉴权

除商家在线状态接口外,每个请求都必须携带 CDK Bearer 凭证:

Authorization: Bearer LQ-ABCD-EFGH-JKLM-NPQR

一个 CDK 只能查看和操作自己的任务。CDK 等同于密码,泄露后持有者可以查看对应任务记录并消耗 USDT 余额。

用户自主合并 CDK

/kakaoscan 与 /kakaopay 都提供用户自主合并入口。当前页面中已验证的 CDK 作为保留目标,用户必须同时证明持有每一个完整来源 CDK:

POST /api/user/cdks/merge
Content-Type: application/json
{
  "targetCdk": "LQ-ABCD-EFGH-JKLM-NPQR",
  "sourceCdks": [
    "LQ-1111-2222-3333-4444",
    "LQ-5555-6666-7777-8888"
  ]
}

一次可合并 1 至 99 个来源 CDK。目标和来源必须全部有效、未过期、属于同一发卡分组,并且不能存在等待、派发、处理中的提链任务或已经提链但尚未进入扫码队列的任务。合并在一个数据库事务中完成:来源剩余 USDT、扫码任务、提链记录、投诉归属和兼容计数转入目标;来源 CDK 永久变为 revoked,但数据库记录、原发卡分组和原创建管理员归属都不会删除或改写。管理页面会将目标和来源标记为“用户自主合并”。

成功响应包含 mergeId、targetPrefix、sourcePrefixes、sourceCount、transferredBalanceUsdt、targetBalanceUsdt、movedTaskCount、movedExtractionCount、movedComplaintCount 和 submissionsUsed。接口响应为 private, no-store;每个客户端 IP 最多 10 次/5 分钟,同一目标 CDK + IP 最多 5 次/5 分钟。合并不可撤销,客户端不得自动重试状态不明的合并请求,应先重新查询目标和来源状态。

接口概览

方法路径鉴权用途
GET/availability无查询在线商家和可用扫码栏目
GET/accountBearer CDK查询 USDT 余额和任务历史
POST/tasksBearer CDK一次提交 1 至 50 条链接(链接栏目)
POST/image-tasksBearer CDKmultipart 上传 1 至 50 张图片(图片栏目)
POST/tasks/{taskId}/cancelBearer CDK取消仍在排队的任务并退回该单扫码费用
POST/tasks/{taskId}/complaintsBearer CDK对已扫描订单提交投诉和图片证据

查询商家在线状态

GET /api/v1/availability

请求示例:

curl -sS https://kakao.whitexfox.cn/api/v1/availability

成功响应,HTTP 200:

{
  "online": true,
  "onlineCount": 2,
  "capabilities": {
    "imageDeliveryVerification": {
      "enabled": true,
      "windowSeconds": 120
    }
  },
  "defaultScanService": "kakao-scan",
  "scanServices": [
    {
      "slug": "kakao-scan",
      "nameZh": "Kakao扫码",
      "nameEn": "Kakao Scan",
      "isDefault": true,
      "scanPriceUsdt": "0.800000",
      "submissionType": "link",
      "noticeZh": "",
      "noticeEn": "",
      "complaintsEnabled": true,
      "placeholderUrl": "https://pay.nicepay.co.kr/v1/checkout/pay/merchant-token/payment-token",
      "onlineMerchantCount": 2,
      "available": true
    }
  ]
}

capabilities.imageDeliveryVerification 明确表示服务器支持插件图片任务的双信号到账核验及等待窗口;capabilities.accessTokenDeliveryVerification 返回 AT 申诉等待时间、轮询间隔和一次性传输公钥。插件必须在截图和提交前检查两个能力,并要求创建响应包含任务验收会话。scanServices 只包含后台启用的栏目。用户页面左侧栏目名称来自 nameZh / nameEn,每个栏目有独立 scanPriceUsdt;available 表示该栏目至少有一名获授权且在线的商家。submissionType 为 link 或 image,noticeZh / noticeEn 是用户端显眼提醒,complaintsEnabled 控制该栏目是否允许新投诉。服务端仍会在提交事务中再次校验,避免商家在状态查询后离线或权限变化造成竞态。后台在 /admin/scan-pages 管理栏目和商家权限,初始默认栏目为 Kakao扫码。管理员可以逐栏目启用路径规则匹配;开启时执行路径模板与查询参数策略,关闭时只接受配置的精确 HTTPS 域名并允许该域名下任意路径和查询参数。现有栏目升级后默认保持开启、使用链接、允许投诉且不显示提醒。ideal 栏目匹配 https://pm-redirects.stripe.com/authorize/acct_.../(结尾 / 可省略),不接受其他域名、非 acct_ 路径或查询参数;栏目名称、价格、提交类型、提醒、投诉开关和商家权限继续由管理员配置。

查询钱包与任务

GET /api/v1/account?offset=0&limit=100
Authorization: Bearer <CDK>

查询参数:

参数类型默认值范围说明
offsetinteger00 至 100000从第几条任务开始
limitinteger1001 至 100本页最多返回多少条任务

任务按创建时间倒序返回。继续分页时使用响应中的 page.nextOffset,直到 page.hasMore 为 false。

请求示例:

curl -sS \
  -H "Authorization: Bearer LQ-ABCD-EFGH-JKLM-NPQR" \
  "https://kakao.whitexfox.cn/api/v1/account?offset=0&limit=100"

成功响应,HTTP 200:

{
  "cdk": {
    "prefix": "LQ-ABCD-EFGH-JKLM",
    "initial": 3,
    "capacity": 3,
    "remaining": 2,
    "consumed": 1,
    "status": "active",
    "expiresAt": null,
    "submissionLimit": 6,
    "submissionsUsed": 1,
    "submissionRemaining": 5,
    "balanceUsdt": "1.600000",
    "scanPriceUsdt": "0.800000"
  },
  "tasks": [
    {
      "id": "a7642aae-8e6a-4d88-a06d-366a4d98a07f",
      "url": "https://pay.nicepay.co.kr/v1/checkout/pay/.../...",
      "status": "queued",
      "cdkPrefix": "LQ-ABCD-EFGH-JKLM",
      "createdAt": "2026-08-01T03:00:00.000Z",
      "claimedAt": null,
      "resolvedAt": null,
      "refunded": false,
      "queuePosition": 4,
      "merchantName": null,
      "complaint": null
    }
  ],
  "page": {
    "total": 1,
    "nextOffset": 1,
    "hasMore": false
  }
}

balanceUsdt 是权威可用余额,scanPriceUsdt 是当前单条扫码价格。提交时按当前价格扣款并把金额快照写入任务;取消、失效、超时或投诉成立时只返还该任务记录的扫码费用。initial、capacity、remaining、submissionLimit 和 submissionRemaining 是旧版客户端兼容字段,不再限制已充值钱包的提交能力;新客户端不应使用它们判断余额。

CDK 的发卡分组(卡网售卖或管理员手动发卡)和创建管理员归属只在认证后的 /admin/cdks 管理页面中展示。后台会分别统计两个分组内有效且未过期 CDK 的 USDT 余额。公开用户 API 不返回发卡分组、管理员名称、登录账号或内部用户 ID。

商家领取后的支付二维码、二维码检查状态和批量结单属于登录后的内部商家工作台,不属于公开 /api/v1 合约,也不会出现在 CDK 用户任务 JSON 中。商家工作台会保存领取时提取的二维码,并在页面可见且仍有待处理任务时每 1 分钟检查一次;检测为失效只改变商家端提示,不会自动选择 scanned 或 invalid,也不会自动退款或计入结算。公开 API 调用方仍应只依据任务的正式 status 和 refunded 字段处理业务结果。

每个商家最多同时持有 15 条未处理订单。只要当前持有量低于 15,商家就可以继续补领空余数量,不需要先处理完全部订单。自动打开支付页面是浏览器端的尽力操作;弹窗被拦截时订单仍会成功认领并保留在商家工作台中。

批量提交任务

POST /api/v1/tasks
Authorization: Bearer <CDK>
Content-Type: application/json

请求体:

{
  "scanService": "kakao-scan",
  "urls": [
    "https://pay.nicepay.co.kr/v1/checkout/pay/VVQwMDA4OTg2Zw==/fpmandate_example1",
    "https://pay.nicepay.co.kr/v1/checkout/pay/VVQwMDA4OTg2Zw==/fpmandate_example2"
  ]
}

要求:

  • 每次提交 1 至 50 条链接。
  • scanService 可选;省略时使用当前默认栏目。指定后,所有链接必须符合该栏目的精确 HTTPS 域名。栏目启用路径规则匹配时,模板中的 * 匹配一个路径段,** 匹配多个路径段,并按配置决定是否允许查询参数;关闭路径规则匹配时,该精确域名下的任意路径和查询参数均可提交。
  • 如果栏目返回 submissionType=image,请改用下方的 /image-tasks;链接接口会返回 IMAGE_SUBMISSION_REQUIRED。图片接口字段为 scanService 和重复的 images 文件字段,每张必须是真实 JPEG、PNG 或 WebP,单张不超过 5 MiB,每批最多 50 张。图片私有保存,只有领取任务的商家可以读取,任务终态后一小时由清理任务删除原图。
  • iDEAL 重定向链接应指定 "scanService": "ideal",并使用 https://pm-redirects.stripe.com/authorize/acct_.../ 格式。
  • 同一批次不能包含重复链接。
  • 必须至少有一名商家在线。
  • CDK 当前 USDT 余额必须覆盖 当前扫码单价 × 本批链接数。
  • 整个批次位于一个数据库事务中:全部成功或全部失败,失败时不会扣除部分额度。

批量提交图片任务

POST /api/v1/image-tasks
Authorization: Bearer <CDK>
Content-Type: multipart/form-data

表单字段:scanService(必须指定为 submissionType=image 的栏目)和一个或多个 images 文件字段。服务端会验证 MIME 与文件头,拒绝伪造扩展名;单批最多 50 张、单张最多 5 MiB、请求体最多 16 MiB。扩展可额外提交 verification JSON 字符串,字段为 64 位十六进制 accountFingerprint、受支持的 pageOrigin 域名、ISO capturedAt 和 accessTokenEnvelope(ECDH-P256-AES-256-GCM 的临时公钥、IV、密文及 keyId)。此时必须且只能上传一张图片,服务端会在同一事务创建图片任务、二维码证据与验收会话。成功响应与 /tasks 相同,任务的 inputType 为 image,url 是内部占位符。

请求示例:

curl -sS \
  -X POST \
  -H "Authorization: Bearer LQ-ABCD-EFGH-JKLM-NPQR" \
  -F "scanService=gcashscan" \
  -F "images=@gcash-qr.png;type=image/png" \
  -F 'verification={"accountFingerprint":"<64-hex-sha256>","pageOrigin":"m.gcash.com","capturedAt":"2026-08-08T12:00:00.000Z","accessTokenEnvelope":{"version":1,"algorithm":"ECDH-P256-AES-256-GCM","keyId":"<16-hex>","ephemeralPublicKey":"<base64url>","iv":"<base64url>","ciphertext":"<base64url>"}}' \
  https://kakao.whitexfox.cn/api/v1/image-tasks

成功响应,HTTP 201:

{
  "tasks": [
    {
      "id": "a7642aae-8e6a-4d88-a06d-366a4d98a07f",
      "url": "image://6f35d1ab-1f8e-4e51-b077-98d91d0f89f8",
      "status": "queued",
      "inputType": "image",
      "cdkPrefix": "LQ-ABCD-EFGH-JKLM",
      "createdAt": "2026-08-01T03:00:00.000Z",
      "claimedAt": null,
      "resolvedAt": null,
      "refunded": false,
      "queuePosition": 4,
      "merchantName": null,
      "verification": {
        "state": "waiting_scan",
        "attemptCount": 1,
        "verificationDeadline": null,
        "callbackSeen": false,
        "accountVerified": false,
        "failureReason": null
      },
      "complaint": null
    }
  ],
  "remaining": 2,
  "balanceUsdt": "1.600000",
  "prefix": "LQ-ABCD-EFGH-JKLM",
  "submissionsUsed": 1,
  "submissionRemaining": 5
}

响应任务会包含 scanServiceSlug、scanServiceNameZh 和 scanServiceNameEn 快照字段;scanService 价格会在 tasks 的扫码扣费快照中固定,之后后台改价不会影响已提交任务。用户页面和 API 共用同一套栏目规则。

接口当前不接受 Idempotency-Key。如果客户端在提交后超时,不能直接重复上传;应先调用 /account 检查最近返回的完整图片任务 ID,避免重复创建和扣费。

取消排队任务

POST /api/v1/tasks/{taskId}/cancel
Authorization: Bearer <CDK>

请求不需要 JSON 请求体。

curl -sS \
  -X POST \
  -H "Authorization: Bearer LQ-ABCD-EFGH-JKLM-NPQR" \
  https://kakao.whitexfox.cn/api/v1/tasks/a7642aae-8e6a-4d88-a06d-366a4d98a07f/cancel

成功响应,HTTP 200:

{
  "task": {
    "id": "a7642aae-8e6a-4d88-a06d-366a4d98a07f",
    "url": "https://pay.nicepay.co.kr/v1/checkout/pay/VVQwMDA4OTg2Zw==/fpmandate_example1",
    "status": "cancelled",
    "cdkPrefix": "LQ-ABCD-EFGH-JKLM",
    "createdAt": "2026-08-01T03:00:00.000Z",
    "claimedAt": null,
    "resolvedAt": "2026-08-01T03:02:00.000Z",
    "refunded": true,
    "queuePosition": null,
    "merchantName": null,
    "complaint": null
  },
  "remaining": 3,
  "balanceUsdt": "2.400000"
}

只有 queued 状态可以取消。任务被商家领取、处理或超过 30 分钟后不能取消。重复取消已经取消的同一任务是安全的,不会重复退款。

投诉已扫描订单

POST /api/v1/tasks/{taskId}/complaints
Authorization: Bearer <CDK>
Content-Type: multipart/form-data

只允许投诉属于该 CDK、状态为 scanned 且尚未退款的订单。每个任务只能提交一次投诉。表单字段如下:

字段类型要求
reasontext投诉说明,去除首尾空格后 10 至 1000 个字符
imagesfile,可重复1 至 3 张真实 JPEG、PNG 或 WebP 图片,每张最多 5 MiB

服务端会同时检查声明 MIME 类型和文件魔数,修改扩展名或伪造 Content-Type 的文件不会被接受。

curl -sS \
  -X POST \
  -H "Authorization: Bearer LQ-ABCD-EFGH-JKLM-NPQR" \
  -F "reason=商家标记已扫描,但实际页面未完成处理" \
  -F "images=@evidence-1.png;type=image/png" \
  -F "images=@evidence-2.jpg;type=image/jpeg" \
  https://kakao.whitexfox.cn/api/v1/tasks/a7642aae-8e6a-4d88-a06d-366a4d98a07f/complaints

成功响应,HTTP 201:

{
  "complaint": {
    "id": "28f7a3dc-d5df-4694-a547-a57fbb13a239",
    "status": "pending",
    "reason": "商家标记已扫描,但实际页面未完成处理",
    "adminNote": "",
    "createdAt": "2026-08-01T06:00:00.000Z",
    "reviewedAt": null
  },
  "attachments": [
    {
      "id": "f42ce667-b6cf-4ad4-b1fa-9df88420dde3",
      "originalName": "evidence-1.png",
      "mimeType": "image/png",
      "byteSize": 48213
    }
  ]
}

投诉提交成功后,任务状态立即变为 complained,但在投诉仍为 pending 时继续计入商家的已扫描与未结算数量。之后调用 /account 可在任务的 complaint 字段看到 pending、approved 或 rejected。approved 表示管理员或该订单所属商家确认投诉:返还该任务记录的扫码费用,同时从商家已扫描及可结算数量中扣除 1 条;rejected 表示驳回且不返还,任务恢复为 scanned。已经产生的历史结算记录不会被回写。

任务状态

状态含义钱包处理
queued等待商家领取提交时已扣除,可由用户取消
claimed商家已领取不可取消
scanned商家标记已扫描不退款
complained用户已投诉,等待处理或投诉成立暂不计入商家结算;投诉成立时退款一次
invalid商家标记已失效返还该单记录的扫码费用
unclear商家标记图片不清晰返还该单记录的扫码费用,不计入商家扫描
cancelled用户在排队时取消退款一次
expired提交 30 分钟后仍未完成返还该单记录的扫码费用

以 refunded 字段判断任务是否已经退回扫码费用,不要只根据状态推断。

错误响应

所有错误使用同一结构:

{
  "error": "当前没有商家在线,暂时无法提交任务",
  "code": "NO_MERCHANT_ONLINE"
}

程序逻辑应判断稳定的 code,不要解析可能调整的中文 error。

HTTPcode说明
400INVALID_INPUTJSON 结构、参数类型/范围、UUID 或用户合并来源数量不符合要求
400INVALID_URL不是允许的 Nicepay HTTPS 链接
400SCAN_URL_RULE_MISMATCH链接不符合所选扫码栏目的 HTTPS 域名、路径或查询参数规则
400DUPLICATE_URL同一批次包含重复链接
400INVALID_JSONJSON 语法无效
401CDK_REQUIRED缺少 Authorization 头
401INVALID_AUTHORIZATIONAuthorization 不是 Bearer 格式
401INVALID_CDKCDK 格式不正确
404CDK_NOT_FOUNDCDK 不存在
400DUPLICATE_SOURCE_CDK合并来源 CDK 存在重复项
400TARGET_IN_SOURCE_CDKS目标 CDK 同时出现在来源列表中
409INVALID_MERGE_TARGET合并目标不是有效、未合并的 CDK
409SOURCE_CDK_REVOKED合并来源已经删除或被合并
409SOURCE_CDK_INACTIVE用户合并来源不是有效状态
409SOURCE_CDK_EXPIRED用户合并来源已经过期
409CDK_MERGE_CHANNEL_MISMATCH目标与来源不属于同一发卡分组
409CDK_HAS_ACTIVE_EXTRACTIONS参与合并的 CDK 仍有活动提链任务
409CDK_MERGE_LIMIT_EXCEEDED合并后的累计金额或任务计数超出系统上限
404TASK_NOT_FOUND任务不存在或不属于该 CDK
404EXTRACT_JOB_NOT_FOUND提链任务不存在或不属于该 CDK
409CDK_DISABLEDCDK 已停用或撤销
409CDK_EXPIREDCDK 已过期
409INSUFFICIENT_USDT当前 USDT 余额不足
409NO_MERCHANT_ONLINE当前没有在线商家
404SCAN_SERVICE_NOT_FOUND指定扫码栏目不存在
409SCAN_SERVICE_DISABLED指定扫码栏目已停用
409IMAGE_SUBMISSION_REQUIRED当前栏目要求图片上传
409LINK_SUBMISSION_REQUIRED当前栏目要求链接提交
400INVALID_SCAN_IMAGE图片格式或文件魔数不合法
413SCAN_IMAGE_TOO_LARGE单张图片超过 5 MiB
409COMPLAINTS_DISABLED当前扫码栏目已关闭投诉
409URL_UNAVAILABLE至少一条 URL 已在有效任务中
409TASK_NOT_CANCELLABLE任务已被领取或处理
409EXTRACT_JOB_NOT_CANCELLABLE提链任务或关联扫码任务已进入不可取消状态
409TASK_EXPIRED任务超过 30 分钟
409TASK_NOT_COMPLAINABLE任务不是未退款的已扫描订单
409COMPLAINT_EXISTS该任务已经提交过投诉
400INVALID_COMPLAINT_REASON投诉说明长度不符合要求
400INVALID_COMPLAINT_IMAGES未上传 1 至 3 张图片
400INVALID_COMPLAINT_IMAGE图片格式或文件魔数不合法
413COMPLAINT_IMAGE_TOO_LARGE单张图片超过 5 MiB
413BODY_TOO_LARGEJSON 请求体超过限制
415MULTIPART_REQUIRED投诉接口未使用 multipart/form-data
415JSON_REQUIRED提交接口未使用 application/json
415CONTENT_ENCODING_UNSUPPORTED请求体使用了不支持的压缩编码
429RATE_LIMITED触发限流,按 Retry-After 等待
500INTERNAL_ERROR服务端暂时无法处理请求

限流

限流窗口为 5 分钟,同时按客户端 IP 和 CDK 维度保护接口:

接口每客户端 IP每 CDK + 客户端 IP
GET /availability600-
GET /account1500120
POST /tasks600600
POST /tasks/{id}/cancel1000120
POST /tasks/{id}/complaints30020

超过限制时返回 HTTP 429 和 Retry-After 秒数。反向代理可能设置更严格的瞬时流量限制,因此批量客户端应平滑发送请求,不要在同一毫秒同时启动全部任务。

KakaoPay 提链 + 扫码页面

网页入口为 /kakaopay,与 /kakaoscan 共用匿名 CDK 认证,但钱包按 USDT 定点金额扣费。数据库初始价格为提链成功 0.100000 USDT、提链失败 0.050000 USDT、扫码 0.800000 USDT,管理员可以在后台分别修改;提交提链任务时会锁定当时的三项价格,处理中再次改价不会影响该任务。实际响应中的 prices 字段仍表示当前配置:

{
  "extractionPrice": "0.100000",
  "extractionFailurePrice": "0.050000",
  "scanPrice": "0.800000"
}

提链所需的上游 CDK 由管理员在 /admin/extraction-pool 的“提链池管理”页面维护,调用方不需要也不能在用户 API 中传入上游 CDK。渠道 1 对接现有 masi.cc.cd 异步任务接口,使用 KSCAN-* CDK;渠道 2 对接 tilian.kuyaoapi.com,使用 KAKAO-* Bearer Key、Idempotency-Key 和 NDJSON 流式终态。管理员分别保存和同步两个渠道的凭据,并在两者中激活一个供新任务使用。任务入队时会固化当时的激活渠道;后续切换只影响新任务,已经排队或处理中的任务继续使用原渠道及原凭据。完整上游 CDK 仅在服务端加密保存,管理页和 API 都只显示掩码。

这组接口供页面或服务端集成使用,CDK 放在 X-CDK 请求头中,不要放入 URL:

方法路径作用
POST/api/kakaopay/extract提交单条 accessToken,或提交 1 至 300 条 accessTokens;AT 加密后先进入持久队列
GET/api/kakaopay/extractX-CDK 查询提链历史和汇总,支持 offset 与 limit(最大 300)
GET/api/kakaopay/extract/{job_id}X-CDK 轮询提链任务;成功后有在线商家会自动进入扫码队列
DELETE/api/kakaopay/extract/{job_id}X-CDK 取消等待、派发、处理中的提链任务,或取消已提链但扫码仍在排队的任务

提链任务先以 waiting 加密落库,再依次变为 dispatching、processing 和最终状态。调度器只会为任务选择与其渠道快照一致的凭据。每条已配置且可用的上游 CDK 都有独立的 30 条本地并发上限:1 条 CDK 最多同时处理 30 条,5 条 CDK 最多同时处理 150 条;上游实时额度仍是硬边界。渠道 1 创建任务后持续轮询,渠道 2 的每个本地任务使用稳定幂等键并从 NDJSON 流读取对应 Token 的终态。完成或失败后 worker 会按全局先进先出顺序自动补入后续任务,因此 300 条突发请求会按两个渠道各自已接受任务及其凭据容量运行,其余任务保持可见排队。processing 默认超过 15 分钟仍没有终态时会变为 failed,错误码为 EXTRACT_TIMEOUT,并按任务快照扣除一次提链失败费。

worker 默认每个 tick 最多认领或启动 300 条任务(KAKAO_EXTRACT_DISPATCH_BATCH_MAX=300),同时最多发出 50 个创建请求(KAKAO_EXTRACT_DISPATCH_CONCURRENCY=50);每轮最多选取 300 条处理中任务进行轮询(KAKAO_EXTRACT_POLL_BATCH_MAX=300),同时轮询请求限制为 50(KAKAO_EXTRACT_POLL_CONCURRENCY=50)。这些参数限制单次调度和网络负载,不是提链任务的总并发上限;总在途容量由“可用上游 CDK 数量 x 每条 30”及各 CDK 的实时 available_uses 共同决定。

等待和处理中任务不会提前扣费。每条任务按“提链成功价 + 扫码价”和“提链失败价”中的较大值计入 reservedUsdt,并从 availableBalanceUsdt 扣除,防止直接扫码或管理员扣减挪用已承诺资金。上游成功返回有效 Nicepay 链接时只扣提链成功费,成功进入扫码队列时再扣扫码费;上游明确失败、派发异常、派发中断或处理超时进入 failed 时只扣一次任务快照中的失败费。用户主动取消进入 cancelled,不扣失败费并释放未使用承诺。取消已经提链但仍在扫码队列中的任务会取消对应扫码单、释放 URL 并只退回该单记录的扫码费用;已经交付的提链费用不退。扫码任务被商家领取后不能再取消。上游目前没有取消接口,因此取消 dispatching 或 processing 任务会立即停止本站跟踪并释放本地承诺,但远端操作可能继续自行结束;上游 CDK 的实时 pending_uses 仍会限制新任务,防止本地超发。接口返回 balanceUsdt、reservedUsdt、availableBalanceUsdt、extractionCharged、extractionFailureCharged、scanCharged、queuePosition 和掩码后的 atMask,不会返回完整 AT。

批量请求示例:

{
  "cdk": "LQ-ABCD-EFGH-JKLM-NPQR",
  "accessTokens": ["AT_VALUE_1", "AT_VALUE_2"]
}

接口按一个批次返回 batchId、accepted 与 jobs。同一批次包含重复 AT、超过 300 条、钱包可用余额不足或没有在线商家时,整个批次不会部分入队。POST 仍按同一 IP 每 5 分钟 60 次、同一 CDK + IP 每 5 分钟 30 次限制,但一次批量请求只占用一次请求计数。收到应用限流 429 时响应包含 Retry-After;上游暂时无槽位不会丢弃已经入库的任务,任务保持 waiting 并由 worker 自动重试。

批量客户端建议

  • 每个最终用户分配独立 CDK,不要让多个用户共享一个 CDK。
  • 客户端总并发建议控制在 50 个连接以内;通常使用 10 至 20 个工作线程即可。
  • 同一个 CDK 同时只发送一个提交请求;不同 CDK 可以并行。服务端会串行锁定同一 CDK,提高同一 CDK 的线程数只会增加等待。
  • 查询任务状态时每个 CDK 间隔 15 至 30 秒,并加入随机抖动。
  • 先查询一次 /availability,没有在线商家时暂停提交。
  • 收到 429 时严格遵守 Retry-After,不要立即重试。
  • 网络超时后的提交必须先查历史去重;取消接口可以对同一任务安全重试。
  • 记录任务 ID、URL、状态和时间,但不要记录完整 CDK 或 Authorization 头。

Python 批量示例

下面示例按 CDK 并行提交,每个 CDK 的 urls 最多 50 条,并处理 HTTP 429。requests 可安装在调用方自己的 Conda 环境中。

from concurrent.futures import ThreadPoolExecutor, as_completed
import random
import time

import requests

BASE_URL = "https://kakao.whitexfox.cn/api/v1"


def api_request(method, path, cdk=None, json=None, attempts=4):
    headers = {"Authorization": f"Bearer {cdk}"} if cdk else {}
    for attempt in range(attempts):
        response = requests.request(
            method,
            f"{BASE_URL}{path}",
            headers=headers,
            json=json,
            timeout=(5, 30),
        )
        if response.status_code != 429:
            response.raise_for_status()
            return response.json()

        retry_after = int(response.headers.get("Retry-After", "5"))
        time.sleep(retry_after + random.random())

    raise RuntimeError("API rate limit retry exhausted")


def submit_for_user(item):
    cdk = item["cdk"]
    result = api_request("POST", "/tasks", cdk, {"urls": item["urls"]})
    return cdk[-4:], [task["id"] for task in result["tasks"]]


jobs = [
    {"cdk": "LQ-ABCD-EFGH-JKLM-NPQR", "urls": ["https://pay.nicepay.co.kr/v1/checkout/pay/.../..."]},
    {"cdk": "LQ-2345-6789-ABCD-EFGH", "urls": ["https://pay.nicepay.co.kr/v1/checkout/pay/.../..."]},
]

availability = api_request("GET", "/availability")
if not availability["online"]:
    raise RuntimeError("No merchant is currently online")

with ThreadPoolExecutor(max_workers=min(20, len(jobs))) as executor:
    futures = [executor.submit(submit_for_user, item) for item in jobs]
    for future in as_completed(futures):
        masked_cdk, task_ids = future.result()
        print(masked_cdk, task_ids)

生产代码还应捕获非 2xx 响应并按 code 分类处理。提交请求发生连接中断或读取超时时,先调用 /account 检查 URL 是否已存在,再决定是否重试。