开始与兼容性 / Getting Started
兼容承诺:原有五个接口、方法与必填字段不变。所有 v1.1 请求字段均为可选;旧调用方只传 access_token 或 qr 时保持原行为。
自动化调用方建议为每次业务操作生成稳定的 client_request_id。网络超时后先按该字段恢复查询,再决定是否重发原始 POST;同一键与相同内容重发不会二次扣费。
Use a stable client request ID for every business operation. After an ambiguous response, recover by that ID before retrying the original POST.
认证与数据约定 / Authentication
X-API-Key: gcs_YOUR_API_KEY Authorization: Bearer gcs_YOUR_API_KEY
- 二选一传入认证头;
Bearer区分大小写。 - 每个响应都有
X-Request-ID;每个 API Key 每 UTC 分钟最多 120 次请求。 - 金额是固定两位小数的字符串,例如
"1.00";单位始终为U。 - 时间为 UTC RFC 3339;未发生的时间和空结果返回 JSON
null。 client_request_id最长 128 字符,匹配^[A-Za-z0-9][A-Za-z0-9._:@+=-]{0,127}$。- 错误体始终包含机器码
error和说明message。
余额与当前费用 / Balance
/balance{
"available": "25.50",
"locked": "1.00",
"unit": "U",
"extraction_fee": "1.00",
"order_fee": "1.00",
"channels": [
{"link_type":"gcash","link_type_label":"GCash","scan_fee":"1.00","extract_fee":"1.00"}
]
}
extraction_fee 与 order_fee 保留为默认兼容字段;按渠道计价请读取 channels 中对应 link_type 的 scan_fee 或 extract_fee。本地可在提交前判断余额是否足够;服务端仍会在创建事务中锁定用户余额并做最终检查。
提链任务 / Extractions
/extractionsRequest
{
"link_type": "upi",
"access_token": "FULL_ACCESS_TOKEN",
"client_request_id": "local-batch-email-hash-001",
"email": "user@example.com",
"metadata": {"batch_id":"20260807_001","channel":"gc-plus"},
"callback_url": "https://hooks.example.com/gc-plus/callback",
"callback_secret": "optional-secret"
}
| 字段 | 必填 | 约束与用途 |
|---|---|---|
link_type | 否 | pix / upi / ideal / kakao_pay / paypal / ph_short / gcash;默认 gcash。 |
access_token | 是 | 去除首尾空白后 20-8192 字符;只转发,原文不落库、不记录日志。 |
client_request_id | 否 | 幂等与本地恢复键;同一 API 用户内唯一。 |
email | 否 | 有效邮箱,最长 320 字符。 |
metadata | 否 | JSON 对象,编码后不超过 16 KiB。 |
callback_url | 否 | 最长 2048 字符的绝对 HTTP/HTTPS URL,必须使用公网主机;回环、私网和链路本地地址会被拒绝。 |
callback_secret | 否 | 最长 512 字节,必须同时提供 URL;加密存储且永不返回。 |
202 First creation · 200 Replay
{
"task_no": "01K20B2CDEFG3HJ4KM5NP6QRST",
"link_type": "upi",
"client_request_id": "local-batch-email-hash-001",
"linked_order_no": null,
"status": "QUEUED",
"stage": "queued",
"long_url": null,
"copy_paste": null,
"image_url_png": null,
"error": null,
"message": null,
"fee": "1.00",
"unit": "U",
"idempotent_replay": false,
"created_at": "2026-08-07T08:20:00Z",
"started_at": "2026-08-07T08:20:01Z",
"finished_at": null,
"expires_at": "2026-08-07T08:30:00Z"
}
IDEMPOTENCY 服务端仅保存 link_type 与 trim 后 AT 的 SHA-256 指纹。同键同类型、同 AT 返回已有任务和 HTTP 200,不重复扣费;类型或 AT 不同返回 409 idempotency_conflict。
/extractions/{task_no}返回同一任务对象,但不含 idempotent_replay。成功时至少有 long_url 或 copy_paste;如果提链服务宣称成功但两者都为空,任务以 no_url_returned 失败并退款。
二维码订单 / Orders
/ordersContent-Type: multipart/form-data qr=@payment-qr.png link_type=upi client_request_id=local-batch-email-order-001 task_no=01K20B2CDEFG3HJ4KM5NP6QRST email=user@example.com callback_url=https://hooks.example.com/gc-plus/callback callback_secret=optional-secret
仅 qr 必填。link_type 允许值同提链接口,默认 gcash。支持 PNG、JPEG、WebP、GIF,最大 10 MiB。task_no 只能关联当前 API 用户、且 link_type 相同的提链任务,并且一个提链任务最多关联一个订单。未显式提供回调字段时,订单继承关联提链任务的回调配置。
201 First creation · 200 Replay
{
"order_no": "01K20A1BCDEF2GH3JK4MNP5QRS",
"link_type": "upi",
"task_no": "01K20B2CDEFG3HJ4KM5NP6QRST",
"client_request_id": "local-batch-email-order-001",
"status": "QUEUED",
"stage": "queued",
"error": null,
"message": null,
"fee": "1.00",
"unit": "U",
"idempotent_replay": false,
"queued_at": "2026-08-07T08:20:00Z",
"claimed_at": null,
"completed_at": null,
"expires_at": "2026-08-07T08:30:00Z"
}
IDEMPOTENCY 指纹来自 link_type 与二维码原始字节。同键同类型、同内容返回已有订单和 HTTP 200;类型或内容不同返回 409 idempotency_conflict。重放不会上传第二份持久对象或重复冻结费用。
/orders/{order_no}完成的关联订单返回 task_no、status: "COMPLETED" 和 stage: "scan_completed";对应提链任务会返回该订单的 linked_order_no。
本地恢复查询 / Recovery
/extractions/by-client-request/{client_request_id}/orders/by-client-request/{client_request_id}本地断线、超时或重启后,先调用恢复接口。找到资源就继续等待或处理回调;只有收到 404 时才使用原始内容和同一个 client_request_id 重发 POST。
回调与验签 / Webhooks
- 状态或阶段变化与 callback outbox 在同一数据库事务提交。
- 任意 HTTP
2xx表示接收成功;HTTP 重定向不会被跟随。 - 首次失败后至少重试 5 次:约 1 秒、5 秒、30 秒、2 分钟、10 分钟。
- 每个事件的所有尝试使用同一个
X-Request-ID;接收方应按它去重。 - 提供 secret 时发送
X-Signature,值为小写十六进制 HMAC-SHA256,无sha256=前缀。
expected = hex_lower( HMAC_SHA256(callback_secret, exact_raw_json_body) ) constant_time_compare(request.headers["X-Signature"], expected)
必须先读取原始 body 并验签,再解析 JSON。重新序列化 JSON 会改变字节序列,导致签名不匹配。业务处理应按 X-Request-ID 幂等,并尽快返回 2xx。
Extraction callback
{
"type": "extraction.updated",
"task_no": "01K20B2CDEFG3HJ4KM5NP6QRST",
"client_request_id": "local-batch-email-hash-001",
"status": "SUCCESS",
"stage": "url_ready",
"long_url": "https://example.com/gcash/result",
"copy_paste": "https://example.com/gcash/result",
"image_url_png": null,
"error": null,
"message": null,
"fee": "1.00",
"unit": "U",
"created_at": "2026-08-07T08:20:00Z",
"started_at": "2026-08-07T08:20:01Z",
"finished_at": "2026-08-07T08:20:08Z",
"expires_at": "2026-08-07T08:30:00Z"
}
Order callback
{
"type": "order.updated",
"order_no": "01K20A1BCDEF2GH3JK4MNP5QRS",
"task_no": "01K20B2CDEFG3HJ4KM5NP6QRST",
"client_request_id": "local-batch-email-order-001",
"status": "COMPLETED",
"stage": "scan_completed",
"error": null,
"message": null,
"fee": "1.00",
"unit": "U",
"queued_at": "2026-08-07T08:20:00Z",
"claimed_at": "2026-08-07T08:21:00Z",
"completed_at": "2026-08-07T08:22:00Z",
"expires_at": "2026-08-07T08:30:00Z"
}
状态与阶段 / Status and Stage
Extraction
| Status | Stage | 含义与资金 |
|---|---|---|
CREATED | created | 本地创建并冻结费用,外部提交中。 |
QUEUED | queued | 外部已接受,费用冻结。 |
RUNNING | running | 提链处理中,费用冻结。 |
SUCCESS | url_ready | URL 可用,费用扣除。 |
FAILED | failed | 失败,费用退回。 |
EXPIRED | expired | 任务超时,费用退回。 |
Order
| Status | Stage | 含义与资金 |
|---|---|---|
QUEUED | queued | 等待领取,费用冻结。 |
CLAIMED | claimed | 工人已领取。 |
CLAIMED | waiting_scan | 二维码已成功打开,等待扫码完成。 |
COMPLETED | scan_completed | 扫码完成,费用扣除。 |
CANCELLED | scan_failed | 扫码失败,费用退回。 |
EXPIRED | expired | 二维码或订单超时,费用退回。 |
REFUNDED | refunded | 管理员原子退款完成。 |
订单 stage 枚举还保留 cancelled,用于后续显式取消流程。
稳定错误码 / Errors
{
"error": "no_url_returned",
"message": "Extraction completed but returned no usable URL"
}
| HTTP | 错误码 |
|---|---|
| 400 | invalid_request, invalid_access_token, qr_required, invalid_qr, invalid_client_request_id, invalid_email, invalid_metadata, invalid_callback_url, invalid_callback_secret |
| 401 | invalid_api_key |
| 402 | insufficient_balance |
| 403 | ip_not_allowed, user_disabled |
| 404 | not_found, task_not_found |
| 409 | idempotency_conflict, task_already_linked, qr_expired |
| 415 | unsupported_image |
| 429 | rate_limit_exceeded |
| 500 | internal_error |
| 502 | extraction_submit_failed |
| 503 | extractor_unavailable,或二维码存储暂时不可用时的 internal_error |
资源终态的 error 还可能为:no_url_returned、extraction_failed、extraction_timeout、scan_failed、qr_expired、order_timeout。
gc-plus 推荐链路 / Workflow
注册成功 → GET /balance 检查 extraction_fee → POST /extractions(稳定 client_request_id + callback) → 结果不明确时按 client_request_id 恢复查询 → 等待 SUCCESS / url_ready,打开 long_url 或 copy_paste → 页面出现二维码后截图 → GET /balance 检查 order_fee → POST /orders(新的 client_request_id + task_no) → 结果不明确时按 client_request_id 恢复查询 → 等待 COMPLETED / scan_completed → 刷新页面确认开通,关闭窗口,标记 gc-plus 成功
正常进度优先消费 callback;漏回调或本地重启后使用恢复 GET。GET 可安全重试;POST 仅在复用同一个 client_request_id 和原始内容时安全重试。