Files
affiliate_dash/docs/乐玩开放平台接入文档-V1.2.md
T
yml2213andCursor f7f48d0feb 功能:接入乐玩开放平台双渠道路由,并修复渠道锁定与 Webhook 可靠性
在保持 DLC 默认零破坏的前提下,支持 priority/仅乐玩/仅 DLC 履约策略与后台 GUI 配置;同步补齐 Prepare 渠道落库、乐玩订单取消、Webhook 验签与失败重试。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-11 15:48:03 +08:00

102 KiB
Raw Blame History

开放平台接入文档

版本:V1.2

Base URL:https://shop.cloudtentacles.com

快速开始

先完成以下五步,再参考各接口的参数与示例。下单受理不等于发货成功,请以逐件结果为准。

Base URLhttps://shop.cloudtentacles.com

接口前缀/openapi/v1

鉴权协议HMAC-SHA256 / V1

数据格式UTF-8 JSON

  1. 开通权限登录用户必须实时具备 openapi:use,然后在开放平台创建应用。
  2. 配置来源 IP所有 POST 写请求必须命中应用 IP 白名单;GET 查询仍需签名,但不要求白名单。
  3. 签发密钥妥善保存 AccessKey 与仅显示一次的 SecretKey,调用时不使用浏览器 JWT。
  4. 查询并下单先读取商品的 accessMode、库存、发货方式与角色累计发货上限,只对 PURCHASE 商品幂等下单。
  5. 接收异步结果配置 HTTPS Webhook,并同时保留订单查询作为补偿手段。

V1 仅支持 LINK_BINDING 和 EMAIL_BINDING;CDK 商品不会出现在商品列表中,也不能下单。

统一响应包装

成功响应为 { data, requestId };失败响应为 { statusCode, error, message, code, requestId, timestamp, path }。响应头 X-Request-Id 与响应体的 requestId 一致。

应用 ID 在哪里查看

进入开放平台 → 应用密钥,在应用名称下方查看并复制应用 ID(applicationId);签发或轮换密钥的弹窗也会显示该应用 ID。

applicationId 标识应用,与 AccessKey 不同;同一应用可以有多个 AccessKey,但共用一个应用 ID。普通签名请求通过 X-OpenAPI-Access-Key 定位应用,无需另传 applicationId;收到 Webhook 后,按其 applicationId 选择对应应用的 WebhookSecret 验签。

管理接口中,应用列表的 data.items[].id、签发密钥响应的 data.applicationId 都是应用 ID;签发密钥响应的 data.id 是密钥记录 ID,不要混用。

密钥只保存在服务端

SecretKey 仅在签发时显示一次,只能保存在调用方服务端。不要放入网页、移动端、URL、日志或工单。

调用配额与重试

查询/分钟 下单请求/分钟 下单件数/分钟 绑定刷新/分钟
1800 300 600 600
  • 表中为单应用按每分钟折算的持续速率,另有短时突发保护。同一用户的多个应用共享用户总额度,全平台另有总量保护,不能把各应用额度直接相加。
  • 取消、账号退还及邮箱验证/重发各有独立限制,不占绑定刷新额度。
  • binding/action 与 binding/refresh 共用绑定刷新额度及单订单冷却,最快每 5 秒一次;优先使用 Webhook,查询用于补偿,不要对全部历史订单持续轮询。
  • 遇到 429 按响应头 Retry-After 等待并加入少量随机延迟;重试下单保留原 callerOrderId,但使用新的时间戳、nonce 和签名。
  • 下单请求每次计数;已经受理的相同幂等订单不重复消耗件数额度。入口配额不是渠道履约速度或硬件容量承诺。

鉴权与签名

每个请求都必须携带以下四个请求头。服务端会统一返回 X-Request-Id,请在报错和技术支持场景中保留。

请求头 规则
X-OpenAPI-Access-Key 应用签发的 AccessKey,16 至 64 位;首位为字母或数字,其余可含下划线和连字符
X-OpenAPI-Timestamp Unix 秒级时间戳,必须位于服务端时间 ±5 分钟内
X-OpenAPI-Nonce 每次请求唯一的 16 至 64 位字母、数字、下划线或连字符,首位必须是字母或数字
X-OpenAPI-Signature HMAC-SHA256 小写十六进制,可选带 v1= 前缀

六行待签名串

  1. HTTP_METHOD
  2. PATH
  3. RFC3986_QUERY
  4. SHA256_HEX_OF_RAW_BODY
  5. UNIX_TIMESTAMP_SECONDS
  6. NONCE
  • HTTP 方法必须转为大写。
  • PATH 是完整路径,不包含域名、查询串和片段,例如 /openapi/v1/orders。
  • 查询参数按 RFC 3986 编码,再按编码后的键和值使用码点顺序排序;重复键必须保留。
  • 规范化基于查询串解析后的键值对;空格统一编码为 %20,不使用 +。
  • 请求体摘要基于实际发送的原始字节;签名后不能再次格式化 JSON。
  • GET 或无请求体 POST 使用空字节摘要 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855。
  • 六行之间仅使用一个 LF 字符 \n,末尾不追加换行。

signature = hex(HMAC-SHA256(SecretKey, signingString))

Nonce 一旦被同一密钥接受,在防重放窗口内不可再次使用;重试必须生成新 Timestamp、Nonce 和 Signature。业务幂等由 callerOrderId 保证。

固定验签测试向量

以下值与服务端回归测试共用,可直接放入调用方单元测试,用于发现查询串规范化、原始请求体或 Webhook 验签的差异。

这是离线回归向量,固定密钥是公开测试数据,固定时间戳也已不适用于实时请求。不要将这些值用作真实应用凭据或发送到线上接口。

签名版本v1

固定时间戳1788566400

测试 AccessKeyak_test_fixed_20260905

测试 SecretKeysk_test_fixed_20260905

1. GET 查询

方法 / PATH GET /openapi/v1/products
原始查询串 pageSize=20&page=1
规范查询串 page=1&pageSize=20
Nonce nonce_get_20260905_fixed
请求体 SHA-256 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

原始请求体

(空字符串)

六行待签名串

GET
/openapi/v1/products
page=1&pageSize=20
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
1788566400
nonce_get_20260905_fixed

预期请求签名54ea478585e61230be670d664745c81ffb4907b87c8592bf082c9cc894232adf

2. POST JSON

方法 / PATH POST /openapi/v1/orders
原始查询串 (空字符串)
规范查询串 (空字符串)
Nonce nonce_post_20260905_fixed
请求体 SHA-256 146e687459b70c7fdf2781f72593306a8041aa0d5212207459dc71cd76e59032

原始请求体

{"callerOrderId":"DOC-20260905-001","roleId":"1148711973","items":[{"productId":"00000000-0000-4000-8000-000000000101","quantity":2}]}

六行待签名串

POST
/openapi/v1/orders

146e687459b70c7fdf2781f72593306a8041aa0d5212207459dc71cd76e59032
1788566400
nonce_post_20260905_fixed

预期请求签名1e1d3df46b885e76b1e992585215e488c29315e7207e62df4aa26b0124d04968

3. 重复查询参数

方法 / PATH GET /openapi/v1/orders
原始查询串 status=SUCCEEDED&keyword=%E4%BA%91%20%E8%A7%A6%E8%A7%92&status=FAILED
规范查询串 keyword=%E4%BA%91%20%E8%A7%A6%E8%A7%92&status=FAILED&status=SUCCEEDED
Nonce nonce_repeat_20260905_fixed
请求体 SHA-256 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

原始请求体

(空字符串)

六行待签名串

GET
/openapi/v1/orders
keyword=%E4%BA%91%20%E8%A7%A6%E8%A7%92&status=FAILED&status=SUCCEEDED
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
1788566400
nonce_repeat_20260905_fixed

预期请求签名c1c2be18372e3ac89e8042f3477fff8287126e31777a18a593c0cce7028626d8

4. POST 空请求体

方法 / PATH POST /openapi/v1/orders/00000000-0000-4000-8000-000000000201/account/release
原始查询串 (空字符串)
规范查询串 (空字符串)
Nonce nonce_empty_20260905_fixed
请求体 SHA-256 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

原始请求体

(空字符串)

六行待签名串

POST
/openapi/v1/orders/00000000-0000-4000-8000-000000000201/account/release

e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
1788566400
nonce_empty_20260905_fixed

预期请求签名167f6d7c116fc7e51b0d2418e65a0705f08076940025f2c013adbd8f27648908

Webhook 固定向量

Webhook Secret whsec_test_fixed_20260905
Timestamp 1788566400
Application ID 00000000-0000-4000-8000-000000000301
Event ID 00000000-0000-4000-8000-000000000302

Webhook 原始请求体

{"applicationId":"00000000-0000-4000-8000-000000000301","data":{"callerOrderId":"DOC-20260905-001","failedQuantity":1,"failureCodes":["SUPPLIER_REJECTED"],"orderId":"00000000-0000-4000-8000-000000000303","pendingQuantity":0,"releasedQuantity":0,"status":"PARTIAL_SUCCESS","succeededQuantity":1},"eventId":"00000000-0000-4000-8000-000000000302","occurredAt":"2026-09-05T00:00:00.000Z","orderVersion":7,"schemaVersion":"1.0","type":"ORDER_COMPLETED"}

Webhook 待签名字节

1788566400.00000000-0000-4000-8000-000000000302.{"applicationId":"00000000-0000-4000-8000-000000000301","data":{"callerOrderId":"DOC-20260905-001","failedQuantity":1,"failureCodes":["SUPPLIER_REJECTED"],"orderId":"00000000-0000-4000-8000-000000000303","pendingQuantity":0,"releasedQuantity":0,"status":"PARTIAL_SUCCESS","succeededQuantity":1},"eventId":"00000000-0000-4000-8000-000000000302","occurredAt":"2026-09-05T00:00:00.000Z","orderVersion":7,"schemaVersion":"1.0","type":"ORDER_COMPLETED"}

预期 Webhook 签名v1=ee3968ef467a4f9c19c0a33e7af1bb3118fab8d02c5ebf9cbd8001cc34ebe6be

查询商品

GET/openapi/v1/products

参数 类型与限制 说明
categoryId UUID v4,可选 包含指定分类及其后代分类
keyword 1–100 字符,可选 匹配商品名称或简介
page 1–10000,默认 1 页码
pageSize 1–100,默认 50 每页数量
  • 返回当前账号有查看权限、在售、有库存、媒体可用且渠道映射有效的 LINK / EMAIL 商品。
  • accessMode=VIEW 只能展示;只有 accessMode=PURCHASE 才能下单,服务端会实时复核。
  • effectiveUnitPrice 是当前账号实际支付的最小货币单位,使用十进制字符串防止精度丢失。
  • 当前 CREDIT 的 cnyCentRate=1,即 1 CREDIT = 人民币 1 分;下单只传数量,不回传或信任客户端价格。
  • 开放 API 下单后直接进入绑定与发货流程,不进入商城背包;其数量限制与商城单次购买上限相互独立。
  • roleDeliveryLimit 为同一 SKU + 经验证角色 ID 的全系统累计发货上限;null 表示不限,更换手机号不会重置。
  • thumbnailUrl 始终是可直接访问的绝对 HTTPS 地址,不会返回 /uploads/... 相对路径。
  • updatedAt 是基础商品记录的更新时间;productVersion 是对该应用所属用户实际可见投影(包括成交价)计算的 sha256: 版本指纹。

GET/openapi/v1/products/{id}

详情接口与列表使用完全相同的可见性、售卖窗口、库存、媒体与渠道映射条件;不存在或当前不可用统一返回 OPENAPI_PRODUCT_NOT_FOUND。

当前版本不支持 updatedSince 增量同步。请分页全量拉取,以 productVersion 判断单个商品投影是否变化;不要把 updatedAt 当成完整增量游标。

成功响应示例

{
  "data": {
    "items": [
      {
        "id": "11111111-1111-4111-8111-111111111111",
        "categoryId": "55555555-5555-4555-8555-555555555555",
        "categoryName": "永劫无间",
        "name": "示例商品",
        "summary": "商品简介",
        "thumbnailUrl": "https://shop.cloudtentacles.com/api/v1/media/commerce/example.webp",
        "stock": 500,
        "roleDeliveryLimit": 10,
        "deliveryType": "LINK_BINDING",
        "currency": {
          "code": "CREDIT",
          "name": "积分",
          "scale": 0,
          "cnyCentRate": 1
        },
        "effectiveUnitPrice": "1000",
        "accessMode": "PURCHASE",
        "updatedAt": "2026-09-05T00:00:00.000Z",
        "productVersion": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      }
    ],
    "total": 1,
    "page": 1,
    "pageSize": 50
  },
  "requestId": "44444444-4444-4444-8444-444444444444"
}

下单、并行与幂等

POST/openapi/v1/orders

请求限制

  • callerOrderId 长度 1–128,只能包含字母、数字、点、下划线、冒号和连字符。
  • roleId 长度 1–191,去除首尾空白,不能包含控制字符。
  • items 为 1–10 行;单请求最多包含 10 种不同商品,同一 productId 不允许重复。
  • 每行 quantity 必须是正整数且受 PostgreSQL INTEGER 存储边界限制;整单总量还会按下单件数风控策略校验。
  • 业务额度按“同一 SKU + 经验证角色 ID”累计发货上限计算;任一商品剩余额度不足时整单立即拒绝。
  • 角色累计额度在创建订单时原子预占;明确失败释放,结果未知时继续占用,避免并发绕过。
  • 一张订单内必须是同一个分类、同一种发货方式。

处理模型

  • 不同订单且使用不同手机号时可并行;同一手机号的发货严格串行。
  • 同一 SKU 明确失败会停止其尚未开始的剩余件,其他 SKU 继续。
  • 已经开始的单件继续确认,因此完成顺序和 Webhook 顺序不保证与请求 items 顺序一致。
  • 若不同系统 SKU 映射到相同的上游 game + productId,为保证安全对账也会串行。
  • 渠道提交结果未知时按 2 / 4 / 8 / 16 / 32 秒进行五次对账;仍无法确认时进入人工异常处理,绝不自动重复发货。

请求体

{
  "callerOrderId": "merchant-order-20260824-0001",
  "roleId": "123456789",
  "items": [
    {
      "productId": "11111111-1111-4111-8111-111111111111",
      "quantity": 2
    },
    {
      "productId": "22222222-2222-4222-8222-222222222222",
      "quantity": 1
    }
  ]
}

LINK 待绑定响应

{
  "data": {
    "order": {
      "id": "33333333-3333-4333-8333-333333333333",
      "orderNo": "O...",
      "callerOrderId": "merchant-order-20260824-0001",
      "orderVersion": 3,
      "roleId": "123456789",
      "categoryId": "55555555-5555-4555-8555-555555555555",
      "deliveryType": "LINK_BINDING",
      "currency": {
        "code": "CREDIT",
        "name": "积分",
        "scale": 0,
        "cnyCentRate": 1
      },
      "orderedAmount": "4500",
      "settledAmount": "0",
      "releasedAmount": "0",
      "heldAmount": "4500",
      "feeAmount": "0",
      "quantity": 3,
      "succeededQuantity": 0,
      "failedQuantity": 0,
      "releasedQuantity": 0,
      "pendingQuantity": 3,
      "failureCodes": [],
      "status": "WAITING_BINDING",
      "bindingDeadline": "2026-08-24T12:30:00.000Z",
      "bindingAction": {
        "bindingRequired": true,
        "bindingType": "LINK_BINDING",
        "expiresAt": "2026-08-24T12:30:00.000Z",
        "bindingDeadline": "2026-08-24T12:30:00.000Z",
        "bindingUrl": "https://binding.example/game/link-demo",
        "bindingUrlIssuedAt": "2026-08-24T12:00:01.000Z",
        "bindingUrlExpiresAt": "2026-08-24T12:03:01.000Z",
        "bindingUrlExpired": false,
        "bindingActionEndpoint": null,
        "emailCodeSent": false,
        "emailCodeSentAt": null,
        "resendAvailableAt": null,
        "allocationInProgress": false,
        "bindingActionInProgress": false
      },
      "binding": {
        "status": "ACTIVE",
        "verified": false,
        "expiresAt": "2026-08-24T12:30:00.000Z",
        "roleId": null,
        "roleName": null,
        "roleArea": null
      },
      "failureCode": null,
      "failureReason": null,
      "review": null,
      "createdAt": "2026-08-24T12:00:00.000Z",
      "startedAt": null,
      "completedAt": null,
      "items": [
        {
          "id": "77777777-7777-4777-8777-777777777777",
          "productId": "11111111-1111-4111-8111-111111111111",
          "productName": "示例商品",
          "quantity": 2,
          "currency": {
            "code": "CREDIT",
            "name": "积分",
            "scale": 0,
            "cnyCentRate": 1
          },
          "effectiveUnitPrice": "1000",
          "grandTotal": "2000",
          "orderedAmount": "2000",
          "settledAmount": "0",
          "releasedAmount": "0",
          "heldAmount": "2000",
          "feeAmount": "0",
          "status": "QUEUED",
          "succeededQuantity": 0,
          "failedQuantity": 0,
          "releasedQuantity": 0,
          "pendingQuantity": 2,
          "failureCodes": [],
          "units": [
            {
              "sequence": 0,
              "status": "WAITING_BINDING",
              "supplierState": null,
              "failureCode": null,
              "failureReason": null,
              "completedAt": null
            },
            {
              "sequence": 1,
              "status": "WAITING_BINDING",
              "supplierState": null,
              "failureCode": null,
              "failureReason": null,
              "completedAt": null
            }
          ]
        },
        {
          "id": "88888888-8888-4888-8888-888888888888",
          "productId": "22222222-2222-4222-8222-222222222222",
          "productName": "示例商品 B",
          "quantity": 1,
          "currency": {
            "code": "CREDIT",
            "name": "积分",
            "scale": 0,
            "cnyCentRate": 1
          },
          "effectiveUnitPrice": "2500",
          "grandTotal": "2500",
          "orderedAmount": "2500",
          "settledAmount": "0",
          "releasedAmount": "0",
          "heldAmount": "2500",
          "feeAmount": "0",
          "status": "QUEUED",
          "succeededQuantity": 0,
          "failedQuantity": 0,
          "releasedQuantity": 0,
          "pendingQuantity": 1,
          "failureCodes": [],
          "units": [
            {
              "sequence": 0,
              "status": "WAITING_BINDING",
              "supplierState": null,
              "failureCode": null,
              "failureReason": null,
              "completedAt": null
            }
          ]
        }
      ]
    },
    "bindingAction": {
      "bindingRequired": true,
      "bindingType": "LINK_BINDING",
      "expiresAt": "2026-08-24T12:30:00.000Z",
      "bindingDeadline": "2026-08-24T12:30:00.000Z",
      "bindingUrl": "https://binding.example/game/link-demo",
      "bindingUrlIssuedAt": "2026-08-24T12:00:01.000Z",
      "bindingUrlExpiresAt": "2026-08-24T12:03:01.000Z",
      "bindingUrlExpired": false,
      "bindingActionEndpoint": null,
      "emailCodeSent": false,
      "emailCodeSentAt": null,
      "resendAvailableAt": null,
      "allocationInProgress": false,
      "bindingActionInProgress": false
    },
    "idempotentReplay": false
  },
  "requestId": "44444444-4444-4444-8444-444444444444"
}

callerOrderId 幂等规则

它在同一应用内永久不可复用。相同规范化请求重放会返回原订单,并将 idempotentReplay 设为 true;相同单号但请求内容不同返回 CALLER_ORDER_ID_CONFLICT。即使订单终态或超出列表查询窗口,该单号也不会释放给新订单。

收到冲突时禁止自动换号重发。先按原单号精确查单,核对角色、商品、数量和原业务请求;若仍属于原业务,则继续跟踪原单。只有明确确认是一次新的业务发货后,才使用新 callerOrderId。

下单超时时精确查单

调用 GET /openapi/v1/orders/by-caller-order-id?callerOrderId=...。该查询只能读取当前鉴权应用的订单,不受列表接口 90 天时间窗口限制。查到原单就继续跟踪;明确返回 OPENAPI_ORDER_NOT_FOUND 后,才可用原参数和原 callerOrderId 幂等重试。

金额、结算与资金流水

当前换算口径:1 CREDIT = 人民币 1 分,100 CREDIT = 人民币 1 元。所有金额均是最小单位的十进制字符串,请使用整数或 BigInt 计算,禁止浮点运算。

订单与商品行金额字段

字段 类型 口径
effectiveUnitPrice 十进制字符串 该商品行创建订单时的单价快照
grandTotal / orderedAmount 十进制字符串 原始下单金额;商品行为单价 × 数量
settledAmount 十进制字符串 发货成功并已结算的金额
releasedAmount 十进制字符串 FAILED、STOPPED、CANCELLED 单件已退回可用余额的金额;不能只乘 releasedQuantity
heldAmount 十进制字符串 结果尚未明确、仍被冻结的金额;订单终态必须为 "0"
feeAmount 十进制字符串 平台手续费;当前固定为 "0"

对订单和每个商品行都应满足 orderedAmount = settledAmount + releasedAmount + heldAmount。响应同时携带 currency.code、name、scale 和 cnyCentRate;未配置人民币换算的未来币种会返回 cnyCentRate=null,不得当作 1:1。

商品行的 releasedAmount = effectiveUnitPrice × (failedQuantity + releasedQuantity);settledAmount = effectiveUnitPrice × succeededQuantity,heldAmount = effectiveUnitPrice × pendingQuantity。整单金额分别汇总各行,不能用某一商品单价乘整单数量。

GET/openapi/v1/wallets

  • scope=OWNER_SHARED 表示这是应用所属用户的共享钱包,网页商城及同一用户的其他开放应用会共同改变余额。
  • availableBalance 为可用余额,heldBalance 包含所有业务中的冻结,totalBalance 为两者之和。

GET/openapi/v1/wallet-transactions

  • scope=APPLICATION_ORDERS 仅返回当前鉴权应用订单产生的 HOLD、SETTLE 和 RELEASE,不包含其他应用、网页商城、充值或赠送流水。
  • 可按 effect、currencyCode、startedAt / endedAt 筛选;默认 30 天,单次跨度最多 90 天。
  • 使用不透明 cursor 翻页;hasMore=true 时将 nextCursor 原样传给下一页,不要解析或自行构造。

钱包余额响应

{
  "data": {
    "scope": "OWNER_SHARED",
    "items": [
      {
        "currency": {
          "code": "CREDIT",
          "name": "积分",
          "scale": 0,
          "active": true,
          "cnyCentRate": 1
        },
        "availableBalance": "125000",
        "heldBalance": "4500",
        "totalBalance": "129500",
        "updatedAt": "2026-09-05T00:00:00.000Z"
      }
    ]
  },
  "requestId": "44444444-4444-4444-8444-444444444444"
}

当前应用订单资金流水

{
  "data": {
    "scope": "APPLICATION_ORDERS",
    "items": [
      {
        "id": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
        "effect": "SETTLE",
        "amount": "1000",
        "availableDelta": "0",
        "heldDelta": "-1000",
        "availableBalanceAfter": "125000",
        "heldBalanceAfter": "3500",
        "currency": {
          "code": "CREDIT",
          "name": "积分",
          "scale": 0,
          "active": true,
          "cnyCentRate": 1
        },
        "orderId": "33333333-3333-4333-8333-333333333333",
        "callerOrderId": "merchant-order-20260824-0001",
        "createdAt": "2026-09-05T00:00:00.000Z"
      }
    ],
    "hasMore": false,
    "nextCursor": null,
    "pageSize": 50,
    "startedAt": "2026-08-06T00:00:00.000Z",
    "endedAt": "2026-09-05T00:00:00.000Z"
  },
  "requestId": "44444444-4444-4444-8444-444444444444"
}

下单时系统优先按 roleId 匹配手机号最后一次实时绑定的角色,仍需重新登录并实时核对当前绑定。确认角色一致时 bindingRequired=false,订单直接排队;未匹配才返回绑定动作。

并发幂等下单时,bindingAction 可能暂时返回 allocationInProgress=true。请使用原 callerOrderId 和完全相同的请求体重试下单,但每次生成新的 Timestamp、Nonce 和 Signature;不要创建新业务单号。

重新进入页面与链接恢复

  1. 先调用 GET /openapi/v1/orders/{id},没有平台 UUID 时按原 callerOrderId 精确查单。data.bindingAction 是随订单保存的只读快照;GET 不生成新链接、不发送验证码、也不查询上游。
  2. 绑定链接有效期为 3 分钟,从后端取得链接的 bindingUrlIssuedAt 起算,以服务端返回的 bindingUrlExpiresAt 为准。订单仍为 WAITING_BINDING、租约和绑定窗口均未结束且链接未过期时,才展示链接或二维码。
  3. 链接缺失或过期时,调用 POST /openapi/v1/orders/{id}/binding/action,从 data.bindingAction 取得恢复结果;仍使用原订单。3 分钟内重复调用也复用同一链接,不重新请求渠道、不重置链接到期时间;到期后再次调用才获取新链接,没有自动后台刷新。
  4. 普通查单不生成新链接;已有链接时,相同 callerOrderId 的幂等重放也不替换链接,尚未完成初始化的原单仍可能生成首个链接。链接过期后返回 bindingUrl=null、bindingUrlExpired=true,保留签发和到期时间,并提供 bindingActionEndpoint 恢复入口。不要继续展示已过期二维码,也不要因此创建新业务订单。
  5. 链接自然到期不会主动增加 orderVersion;即使订单版本未变,也须根据 bindingUrlExpiresAt 停止展示旧链接。缓存命中不增加版本;实际重新生成时,预留和写入新链接会更新版本。
  6. allocationInProgress=true 表示账号还在分配;bindingActionInProgress=true 表示绑定链接仍在生成。请等冷却后查询原单或重试原动作,勿并发申请或换新订单号。
  7. EMAIL 已发送时恢复接口返回已有发送状态;需要重新发送时使用 email/resend。验证码业务冷却至少 60 秒,进行中的发送 / 验证会推迟 resendAvailableAt;即使倒计时已结束,仍须遵循额外 HTTP 429 的 Retry-After。
  8. 重新生成链接和重发邮箱验证码都不会延长 bindingDeadline 或租期。窗口过期后先查原单终态,确认未发货数量及冻结释放结果,再由业务决定是否新建发货单;不能直接自动换号。
bindingAction 字段 类型 / 含义
bindingRequired / bindingType 是否仍需绑定,以及 LINK_BINDING / EMAIL_BINDING;非等待绑定状态的历史时间不代表仍允许操作
expiresAt / bindingDeadline ISO 8601 绝对时间;前者是手机号租期、未分配时为 null,后者是订单绑定窗口截止时间
bindingUrl / bindingUrlIssuedAt 当前可用的绑定入口和后端取得链接的时间,未生成时为 null;过期仅隐藏链接,保留时间;离开 WAITING_BINDING 后两者隐藏为 null
bindingUrlExpiresAt ISO 8601 到期时间,为 bindingUrlIssuedAt 加 180 秒;没有链接或不再等待绑定时为 null。expiresAt 是租约截止时间,不能代替链接 TTL
bindingUrlExpired / bindingActionEndpoint 链接是否已过期;仅 LINK 链接已过期且当前仍可绑定时提供恢复动作的 POST 路径,其余情况为 null。过期判断不会自动向渠道请求新链接,租约或绑定窗口结束后不能恢复
emailCodeSent / emailCodeSentAt 是否已有成功发送记录及其绝对时间;未知时间为 null,不代表验证码仍有效
resendAvailableAt 按已保存状态推导的最早业务重发时间(ISO 8601 或 null);并非所有限流的通过承诺
allocationInProgress / bindingActionInProgress 分别表示账号分配中、LINK 动作生成中;依据订单版本更新显示状态

所有时间均为带时区的绝对时间,客户端自行计算倒计时。bindingUrl 来自上游返回的 HTTPS redirectUrl,调用方应直接展示该地址;平台不会把它替换为管理页面。上游可能使用跳转入口,最终游戏直链和固定域名范围尚未获得明确保证,不能自行拼接域名。链接 3 分钟有效期、30 分钟租约和绑定校验有效期相互独立。

LINK_BINDING

  1. 读取创建订单响应中的 bindingAction.bindingUrl、bindingUrlExpiresAt 和租约 expiresAt。
  2. 将链接或二维码展示给最终用户,等待其完成绑定。
  3. 调用 POST /orders/{id}/binding/refresh 刷新最新角色。
  4. matched=true 后自动进入发货;不匹配时展示返回的角色信息并允许再次刷新。

EMAIL_BINDING

  1. 创建订单会尝试发送验证码,并返回 emailCodeSent 或发送中状态。
  2. 用户收到验证码后调用 POST /orders/{id}/email/verify,请求体为 { "code": "123456" }。
  3. 只有返回角色与订单 roleId 一致才会自动进入发货。
  4. 确需重发时调用 POST /orders/{id}/email/resend,并遵循服务端限频。

绑定窗口最长 30 分钟。超时后系统自动取消未发放商品、释放冻结资产和库存,并推送 BINDING_TIMEOUT。

  • BINDING_UPDATED 表示通过 LINK 显式刷新或 EMAIL 验证获得了新绑定快照;BINDING_VERIFIED 表示最新角色已与订单 roleId 匹配。
  • 首次按已绑定 roleId 直接匹配时也会产生验证事件。平台当前不会在绑定窗口内无限制主动轮询上游;调用方完成 LINK 绑定后仍应调用 refresh,EMAIL 流程则提交 verify。

完整绑定响应示例

LINK 查单:重新进入时读取已保存链接

GET /openapi/v1/orders/{id}

按 callerOrderId 精确查单返回相同结构。GET 的 data 直接是订单,不套 order;这里不会请求上游。

{
  "data": {
    "id": "33333333-3333-4333-8333-333333333333",
    "orderNo": "O...",
    "callerOrderId": "merchant-order-20260824-0001",
    "orderVersion": 3,
    "roleId": "123456789",
    "categoryId": "55555555-5555-4555-8555-555555555555",
    "deliveryType": "LINK_BINDING",
    "currency": {
      "code": "CREDIT",
      "name": "积分",
      "scale": 0,
      "cnyCentRate": 1
    },
    "orderedAmount": "4500",
    "settledAmount": "0",
    "releasedAmount": "0",
    "heldAmount": "4500",
    "feeAmount": "0",
    "quantity": 3,
    "succeededQuantity": 0,
    "failedQuantity": 0,
    "releasedQuantity": 0,
    "pendingQuantity": 3,
    "failureCodes": [],
    "status": "WAITING_BINDING",
    "bindingDeadline": "2026-08-24T12:30:00.000Z",
    "bindingAction": {
      "bindingRequired": true,
      "bindingType": "LINK_BINDING",
      "expiresAt": "2026-08-24T12:30:00.000Z",
      "bindingDeadline": "2026-08-24T12:30:00.000Z",
      "bindingUrl": "https://binding.example/game/link-demo",
      "bindingUrlIssuedAt": "2026-08-24T12:00:01.000Z",
      "bindingUrlExpiresAt": "2026-08-24T12:03:01.000Z",
      "bindingUrlExpired": false,
      "bindingActionEndpoint": null,
      "emailCodeSent": false,
      "emailCodeSentAt": null,
      "resendAvailableAt": null,
      "allocationInProgress": false,
      "bindingActionInProgress": false
    },
    "binding": {
      "status": "ACTIVE",
      "verified": false,
      "expiresAt": "2026-08-24T12:30:00.000Z",
      "roleId": null,
      "roleName": null,
      "roleArea": null
    },
    "failureCode": null,
    "failureReason": null,
    "review": null,
    "createdAt": "2026-08-24T12:00:00.000Z",
    "startedAt": null,
    "completedAt": null,
    "items": [
      {
        "id": "77777777-7777-4777-8777-777777777777",
        "productId": "11111111-1111-4111-8111-111111111111",
        "productName": "示例商品",
        "quantity": 2,
        "currency": {
          "code": "CREDIT",
          "name": "积分",
          "scale": 0,
          "cnyCentRate": 1
        },
        "effectiveUnitPrice": "1000",
        "grandTotal": "2000",
        "orderedAmount": "2000",
        "settledAmount": "0",
        "releasedAmount": "0",
        "heldAmount": "2000",
        "feeAmount": "0",
        "status": "QUEUED",
        "succeededQuantity": 0,
        "failedQuantity": 0,
        "releasedQuantity": 0,
        "pendingQuantity": 2,
        "failureCodes": [],
        "units": [
          {
            "sequence": 0,
            "status": "WAITING_BINDING",
            "supplierState": null,
            "failureCode": null,
            "failureReason": null,
            "completedAt": null
          },
          {
            "sequence": 1,
            "status": "WAITING_BINDING",
            "supplierState": null,
            "failureCode": null,
            "failureReason": null,
            "completedAt": null
          }
        ]
      },
      {
        "id": "88888888-8888-4888-8888-888888888888",
        "productId": "22222222-2222-4222-8222-222222222222",
        "productName": "示例商品 B",
        "quantity": 1,
        "currency": {
          "code": "CREDIT",
          "name": "积分",
          "scale": 0,
          "cnyCentRate": 1
        },
        "effectiveUnitPrice": "2500",
        "grandTotal": "2500",
        "orderedAmount": "2500",
        "settledAmount": "0",
        "releasedAmount": "0",
        "heldAmount": "2500",
        "feeAmount": "0",
        "status": "QUEUED",
        "succeededQuantity": 0,
        "failedQuantity": 0,
        "releasedQuantity": 0,
        "pendingQuantity": 1,
        "failureCodes": [],
        "units": [
          {
            "sequence": 0,
            "status": "WAITING_BINDING",
            "supplierState": null,
            "failureCode": null,
            "failureReason": null,
            "completedAt": null
          }
        ]
      }
    ]
  },
  "requestId": "44444444-4444-4444-8444-444444444444"
}

LINK 查单:链接已过期,使用原单恢复

GET /openapi/v1/orders/{id}

链接过期后不返回旧 URL,保留签发与到期时间,并提供 bindingActionEndpoint;只读查询不会调用渠道生成新链接。

{
  "data": {
    "id": "33333333-3333-4333-8333-333333333333",
    "orderNo": "O...",
    "callerOrderId": "merchant-order-20260824-0001",
    "orderVersion": 3,
    "roleId": "123456789",
    "categoryId": "55555555-5555-4555-8555-555555555555",
    "deliveryType": "LINK_BINDING",
    "currency": {
      "code": "CREDIT",
      "name": "积分",
      "scale": 0,
      "cnyCentRate": 1
    },
    "orderedAmount": "4500",
    "settledAmount": "0",
    "releasedAmount": "0",
    "heldAmount": "4500",
    "feeAmount": "0",
    "quantity": 3,
    "succeededQuantity": 0,
    "failedQuantity": 0,
    "releasedQuantity": 0,
    "pendingQuantity": 3,
    "failureCodes": [],
    "status": "WAITING_BINDING",
    "bindingDeadline": "2026-08-24T12:30:00.000Z",
    "bindingAction": {
      "bindingRequired": true,
      "bindingType": "LINK_BINDING",
      "expiresAt": "2026-08-24T12:30:00.000Z",
      "bindingDeadline": "2026-08-24T12:30:00.000Z",
      "bindingUrl": null,
      "bindingUrlIssuedAt": "2026-08-24T12:00:01.000Z",
      "bindingUrlExpiresAt": "2026-08-24T12:03:01.000Z",
      "bindingUrlExpired": true,
      "bindingActionEndpoint": "/openapi/v1/orders/33333333-3333-4333-8333-333333333333/binding/action",
      "emailCodeSent": false,
      "emailCodeSentAt": null,
      "resendAvailableAt": null,
      "allocationInProgress": false,
      "bindingActionInProgress": false
    },
    "binding": {
      "status": "ACTIVE",
      "verified": false,
      "expiresAt": "2026-08-24T12:30:00.000Z",
      "roleId": null,
      "roleName": null,
      "roleArea": null
    },
    "failureCode": null,
    "failureReason": null,
    "review": null,
    "createdAt": "2026-08-24T12:00:00.000Z",
    "startedAt": null,
    "completedAt": null,
    "items": [
      {
        "id": "77777777-7777-4777-8777-777777777777",
        "productId": "11111111-1111-4111-8111-111111111111",
        "productName": "示例商品",
        "quantity": 2,
        "currency": {
          "code": "CREDIT",
          "name": "积分",
          "scale": 0,
          "cnyCentRate": 1
        },
        "effectiveUnitPrice": "1000",
        "grandTotal": "2000",
        "orderedAmount": "2000",
        "settledAmount": "0",
        "releasedAmount": "0",
        "heldAmount": "2000",
        "feeAmount": "0",
        "status": "QUEUED",
        "succeededQuantity": 0,
        "failedQuantity": 0,
        "releasedQuantity": 0,
        "pendingQuantity": 2,
        "failureCodes": [],
        "units": [
          {
            "sequence": 0,
            "status": "WAITING_BINDING",
            "supplierState": null,
            "failureCode": null,
            "failureReason": null,
            "completedAt": null
          },
          {
            "sequence": 1,
            "status": "WAITING_BINDING",
            "supplierState": null,
            "failureCode": null,
            "failureReason": null,
            "completedAt": null
          }
        ]
      },
      {
        "id": "88888888-8888-4888-8888-888888888888",
        "productId": "22222222-2222-4222-8222-222222222222",
        "productName": "示例商品 B",
        "quantity": 1,
        "currency": {
          "code": "CREDIT",
          "name": "积分",
          "scale": 0,
          "cnyCentRate": 1
        },
        "effectiveUnitPrice": "2500",
        "grandTotal": "2500",
        "orderedAmount": "2500",
        "settledAmount": "0",
        "releasedAmount": "0",
        "heldAmount": "2500",
        "feeAmount": "0",
        "status": "QUEUED",
        "succeededQuantity": 0,
        "failedQuantity": 0,
        "releasedQuantity": 0,
        "pendingQuantity": 1,
        "failureCodes": [],
        "units": [
          {
            "sequence": 0,
            "status": "WAITING_BINDING",
            "supplierState": null,
            "failureCode": null,
            "failureReason": null,
            "completedAt": null
          }
        ]
      }
    ]
  },
  "requestId": "44444444-4444-4444-8444-444444444444"
}

LINK 有效期内再次获取:复用原链接

POST /openapi/v1/orders/{id}/binding/action

3 分钟内复用同一链接、签发时间和到期时间,不请求渠道;订单版本不会因为读取缓存而增加。

{
  "data": {
    "order": {
      "id": "33333333-3333-4333-8333-333333333333",
      "orderNo": "O...",
      "callerOrderId": "merchant-order-20260824-0001",
      "orderVersion": 3,
      "roleId": "123456789",
      "categoryId": "55555555-5555-4555-8555-555555555555",
      "deliveryType": "LINK_BINDING",
      "currency": {
        "code": "CREDIT",
        "name": "积分",
        "scale": 0,
        "cnyCentRate": 1
      },
      "orderedAmount": "4500",
      "settledAmount": "0",
      "releasedAmount": "0",
      "heldAmount": "4500",
      "feeAmount": "0",
      "quantity": 3,
      "succeededQuantity": 0,
      "failedQuantity": 0,
      "releasedQuantity": 0,
      "pendingQuantity": 3,
      "failureCodes": [],
      "status": "WAITING_BINDING",
      "bindingDeadline": "2026-08-24T12:30:00.000Z",
      "bindingAction": {
        "bindingRequired": true,
        "bindingType": "LINK_BINDING",
        "expiresAt": "2026-08-24T12:30:00.000Z",
        "bindingDeadline": "2026-08-24T12:30:00.000Z",
        "bindingUrl": "https://binding.example/game/link-demo",
        "bindingUrlIssuedAt": "2026-08-24T12:00:01.000Z",
        "bindingUrlExpiresAt": "2026-08-24T12:03:01.000Z",
        "bindingUrlExpired": false,
        "bindingActionEndpoint": null,
        "emailCodeSent": false,
        "emailCodeSentAt": null,
        "resendAvailableAt": null,
        "allocationInProgress": false,
        "bindingActionInProgress": false
      },
      "binding": {
        "status": "ACTIVE",
        "verified": false,
        "expiresAt": "2026-08-24T12:30:00.000Z",
        "roleId": null,
        "roleName": null,
        "roleArea": null
      },
      "failureCode": null,
      "failureReason": null,
      "review": null,
      "createdAt": "2026-08-24T12:00:00.000Z",
      "startedAt": null,
      "completedAt": null,
      "items": [
        {
          "id": "77777777-7777-4777-8777-777777777777",
          "productId": "11111111-1111-4111-8111-111111111111",
          "productName": "示例商品",
          "quantity": 2,
          "currency": {
            "code": "CREDIT",
            "name": "积分",
            "scale": 0,
            "cnyCentRate": 1
          },
          "effectiveUnitPrice": "1000",
          "grandTotal": "2000",
          "orderedAmount": "2000",
          "settledAmount": "0",
          "releasedAmount": "0",
          "heldAmount": "2000",
          "feeAmount": "0",
          "status": "QUEUED",
          "succeededQuantity": 0,
          "failedQuantity": 0,
          "releasedQuantity": 0,
          "pendingQuantity": 2,
          "failureCodes": [],
          "units": [
            {
              "sequence": 0,
              "status": "WAITING_BINDING",
              "supplierState": null,
              "failureCode": null,
              "failureReason": null,
              "completedAt": null
            },
            {
              "sequence": 1,
              "status": "WAITING_BINDING",
              "supplierState": null,
              "failureCode": null,
              "failureReason": null,
              "completedAt": null
            }
          ]
        },
        {
          "id": "88888888-8888-4888-8888-888888888888",
          "productId": "22222222-2222-4222-8222-222222222222",
          "productName": "示例商品 B",
          "quantity": 1,
          "currency": {
            "code": "CREDIT",
            "name": "积分",
            "scale": 0,
            "cnyCentRate": 1
          },
          "effectiveUnitPrice": "2500",
          "grandTotal": "2500",
          "orderedAmount": "2500",
          "settledAmount": "0",
          "releasedAmount": "0",
          "heldAmount": "2500",
          "feeAmount": "0",
          "status": "QUEUED",
          "succeededQuantity": 0,
          "failedQuantity": 0,
          "releasedQuantity": 0,
          "pendingQuantity": 1,
          "failureCodes": [],
          "units": [
            {
              "sequence": 0,
              "status": "WAITING_BINDING",
              "supplierState": null,
              "failureCode": null,
              "failureReason": null,
              "completedAt": null
            }
          ]
        }
      ]
    },
    "bindingAction": {
      "bindingRequired": true,
      "bindingType": "LINK_BINDING",
      "expiresAt": "2026-08-24T12:30:00.000Z",
      "bindingDeadline": "2026-08-24T12:30:00.000Z",
      "bindingUrl": "https://binding.example/game/link-demo",
      "bindingUrlIssuedAt": "2026-08-24T12:00:01.000Z",
      "bindingUrlExpiresAt": "2026-08-24T12:03:01.000Z",
      "bindingUrlExpired": false,
      "bindingActionEndpoint": null,
      "emailCodeSent": false,
      "emailCodeSentAt": null,
      "resendAvailableAt": null,
      "allocationInProgress": false,
      "bindingActionInProgress": false
    }
  },
  "requestId": "44444444-4444-4444-8444-444444444444"
}

LINK 到期后恢复绑定动作成功

POST /openapi/v1/orders/{id}/binding/action

链接已到期后用户主动调用才重新获取上游绑定地址;等待中的并发请求通过 bindingActionInProgress 表示。示例 URL 是不可用于真实绑定的占位地址。

{
  "data": {
    "order": {
      "id": "33333333-3333-4333-8333-333333333333",
      "orderNo": "O...",
      "callerOrderId": "merchant-order-20260824-0001",
      "orderVersion": 4,
      "roleId": "123456789",
      "categoryId": "55555555-5555-4555-8555-555555555555",
      "deliveryType": "LINK_BINDING",
      "currency": {
        "code": "CREDIT",
        "name": "积分",
        "scale": 0,
        "cnyCentRate": 1
      },
      "orderedAmount": "4500",
      "settledAmount": "0",
      "releasedAmount": "0",
      "heldAmount": "4500",
      "feeAmount": "0",
      "quantity": 3,
      "succeededQuantity": 0,
      "failedQuantity": 0,
      "releasedQuantity": 0,
      "pendingQuantity": 3,
      "failureCodes": [],
      "status": "WAITING_BINDING",
      "bindingDeadline": "2026-08-24T12:30:00.000Z",
      "bindingAction": {
        "bindingRequired": true,
        "bindingType": "LINK_BINDING",
        "expiresAt": "2026-08-24T12:30:00.000Z",
        "bindingDeadline": "2026-08-24T12:30:00.000Z",
        "bindingUrl": "https://binding.example/game/link-demo",
        "bindingUrlIssuedAt": "2026-08-24T12:03:02.000Z",
        "bindingUrlExpiresAt": "2026-08-24T12:06:02.000Z",
        "bindingUrlExpired": false,
        "bindingActionEndpoint": null,
        "emailCodeSent": false,
        "emailCodeSentAt": null,
        "resendAvailableAt": null,
        "allocationInProgress": false,
        "bindingActionInProgress": false
      },
      "binding": {
        "status": "ACTIVE",
        "verified": false,
        "expiresAt": "2026-08-24T12:30:00.000Z",
        "roleId": null,
        "roleName": null,
        "roleArea": null
      },
      "failureCode": null,
      "failureReason": null,
      "review": null,
      "createdAt": "2026-08-24T12:00:00.000Z",
      "startedAt": null,
      "completedAt": null,
      "items": [
        {
          "id": "77777777-7777-4777-8777-777777777777",
          "productId": "11111111-1111-4111-8111-111111111111",
          "productName": "示例商品",
          "quantity": 2,
          "currency": {
            "code": "CREDIT",
            "name": "积分",
            "scale": 0,
            "cnyCentRate": 1
          },
          "effectiveUnitPrice": "1000",
          "grandTotal": "2000",
          "orderedAmount": "2000",
          "settledAmount": "0",
          "releasedAmount": "0",
          "heldAmount": "2000",
          "feeAmount": "0",
          "status": "QUEUED",
          "succeededQuantity": 0,
          "failedQuantity": 0,
          "releasedQuantity": 0,
          "pendingQuantity": 2,
          "failureCodes": [],
          "units": [
            {
              "sequence": 0,
              "status": "WAITING_BINDING",
              "supplierState": null,
              "failureCode": null,
              "failureReason": null,
              "completedAt": null
            },
            {
              "sequence": 1,
              "status": "WAITING_BINDING",
              "supplierState": null,
              "failureCode": null,
              "failureReason": null,
              "completedAt": null
            }
          ]
        },
        {
          "id": "88888888-8888-4888-8888-888888888888",
          "productId": "22222222-2222-4222-8222-222222222222",
          "productName": "示例商品 B",
          "quantity": 1,
          "currency": {
            "code": "CREDIT",
            "name": "积分",
            "scale": 0,
            "cnyCentRate": 1
          },
          "effectiveUnitPrice": "2500",
          "grandTotal": "2500",
          "orderedAmount": "2500",
          "settledAmount": "0",
          "releasedAmount": "0",
          "heldAmount": "2500",
          "feeAmount": "0",
          "status": "QUEUED",
          "succeededQuantity": 0,
          "failedQuantity": 0,
          "releasedQuantity": 0,
          "pendingQuantity": 1,
          "failureCodes": [],
          "units": [
            {
              "sequence": 0,
              "status": "WAITING_BINDING",
              "supplierState": null,
              "failureCode": null,
              "failureReason": null,
              "completedAt": null
            }
          ]
        }
      ]
    },
    "bindingAction": {
      "bindingRequired": true,
      "bindingType": "LINK_BINDING",
      "expiresAt": "2026-08-24T12:30:00.000Z",
      "bindingDeadline": "2026-08-24T12:30:00.000Z",
      "bindingUrl": "https://binding.example/game/link-demo",
      "bindingUrlIssuedAt": "2026-08-24T12:03:02.000Z",
      "bindingUrlExpiresAt": "2026-08-24T12:06:02.000Z",
      "bindingUrlExpired": false,
      "bindingActionEndpoint": null,
      "emailCodeSent": false,
      "emailCodeSentAt": null,
      "resendAvailableAt": null,
      "allocationInProgress": false,
      "bindingActionInProgress": false
    }
  },
  "requestId": "44444444-4444-4444-8444-444444444444"
}

LINK 刷新:角色匹配,开始排队

POST /openapi/v1/orders/{id}/binding/refresh

binding.matched=true 后订单进入 QUEUED;仍须等待发货结果。非等待绑定状态不再返回可使用的绑定 URL。

{
  "data": {
    "order": {
      "id": "33333333-3333-4333-8333-333333333333",
      "orderNo": "O...",
      "callerOrderId": "merchant-order-20260824-0001",
      "orderVersion": 6,
      "roleId": "123456789",
      "categoryId": "55555555-5555-4555-8555-555555555555",
      "deliveryType": "LINK_BINDING",
      "currency": {
        "code": "CREDIT",
        "name": "积分",
        "scale": 0,
        "cnyCentRate": 1
      },
      "orderedAmount": "4500",
      "settledAmount": "0",
      "releasedAmount": "0",
      "heldAmount": "4500",
      "feeAmount": "0",
      "quantity": 3,
      "succeededQuantity": 0,
      "failedQuantity": 0,
      "releasedQuantity": 0,
      "pendingQuantity": 3,
      "failureCodes": [],
      "status": "QUEUED",
      "bindingDeadline": "2026-08-24T12:30:00.000Z",
      "bindingAction": {
        "bindingRequired": false,
        "bindingType": "LINK_BINDING",
        "expiresAt": "2026-08-24T12:30:00.000Z",
        "bindingDeadline": "2026-08-24T12:30:00.000Z",
        "bindingUrl": null,
        "bindingUrlIssuedAt": null,
        "bindingUrlExpiresAt": null,
        "bindingUrlExpired": false,
        "bindingActionEndpoint": null,
        "emailCodeSent": false,
        "emailCodeSentAt": null,
        "resendAvailableAt": null,
        "allocationInProgress": false,
        "bindingActionInProgress": false
      },
      "binding": {
        "status": "ACTIVE",
        "verified": true,
        "expiresAt": "2026-08-24T12:30:00.000Z",
        "roleId": "123456789",
        "roleName": "示例角色",
        "roleArea": "示例区服"
      },
      "failureCode": null,
      "failureReason": null,
      "review": null,
      "createdAt": "2026-08-24T12:00:00.000Z",
      "startedAt": "2026-08-24T12:02:00.000Z",
      "completedAt": null,
      "items": [
        {
          "id": "77777777-7777-4777-8777-777777777777",
          "productId": "11111111-1111-4111-8111-111111111111",
          "productName": "示例商品",
          "quantity": 2,
          "currency": {
            "code": "CREDIT",
            "name": "积分",
            "scale": 0,
            "cnyCentRate": 1
          },
          "effectiveUnitPrice": "1000",
          "grandTotal": "2000",
          "orderedAmount": "2000",
          "settledAmount": "0",
          "releasedAmount": "0",
          "heldAmount": "2000",
          "feeAmount": "0",
          "status": "QUEUED",
          "succeededQuantity": 0,
          "failedQuantity": 0,
          "releasedQuantity": 0,
          "pendingQuantity": 2,
          "failureCodes": [],
          "units": [
            {
              "sequence": 0,
              "status": "QUEUED",
              "supplierState": null,
              "failureCode": null,
              "failureReason": null,
              "completedAt": null
            },
            {
              "sequence": 1,
              "status": "QUEUED",
              "supplierState": null,
              "failureCode": null,
              "failureReason": null,
              "completedAt": null
            }
          ]
        },
        {
          "id": "88888888-8888-4888-8888-888888888888",
          "productId": "22222222-2222-4222-8222-222222222222",
          "productName": "示例商品 B",
          "quantity": 1,
          "currency": {
            "code": "CREDIT",
            "name": "积分",
            "scale": 0,
            "cnyCentRate": 1
          },
          "effectiveUnitPrice": "2500",
          "grandTotal": "2500",
          "orderedAmount": "2500",
          "settledAmount": "0",
          "releasedAmount": "0",
          "heldAmount": "2500",
          "feeAmount": "0",
          "status": "QUEUED",
          "succeededQuantity": 0,
          "failedQuantity": 0,
          "releasedQuantity": 0,
          "pendingQuantity": 1,
          "failureCodes": [],
          "units": [
            {
              "sequence": 0,
              "status": "QUEUED",
              "supplierState": null,
              "failureCode": null,
              "failureReason": null,
              "completedAt": null
            }
          ]
        }
      ]
    },
    "binding": {
      "bindingRequired": false,
      "matched": true,
      "binding": {
        "userBound": true,
        "roleId": "123456789",
        "roleName": "示例角色",
        "roleArea": "示例区服"
      }
    }
  },
  "requestId": "44444444-4444-4444-8444-444444444444"
}

LINK 刷新:角色不匹配,继续等待绑定

POST /openapi/v1/orders/{id}/binding/refresh

HTTP 成功但 binding.matched=false;展示实际返回的角色并提示换绑,不得当作发货成功。

{
  "data": {
    "order": {
      "id": "33333333-3333-4333-8333-333333333333",
      "orderNo": "O...",
      "callerOrderId": "merchant-order-20260824-0001",
      "orderVersion": 4,
      "roleId": "123456789",
      "categoryId": "55555555-5555-4555-8555-555555555555",
      "deliveryType": "LINK_BINDING",
      "currency": {
        "code": "CREDIT",
        "name": "积分",
        "scale": 0,
        "cnyCentRate": 1
      },
      "orderedAmount": "4500",
      "settledAmount": "0",
      "releasedAmount": "0",
      "heldAmount": "4500",
      "feeAmount": "0",
      "quantity": 3,
      "succeededQuantity": 0,
      "failedQuantity": 0,
      "releasedQuantity": 0,
      "pendingQuantity": 3,
      "failureCodes": [],
      "status": "WAITING_BINDING",
      "bindingDeadline": "2026-08-24T12:30:00.000Z",
      "bindingAction": {
        "bindingRequired": true,
        "bindingType": "LINK_BINDING",
        "expiresAt": "2026-08-24T12:30:00.000Z",
        "bindingDeadline": "2026-08-24T12:30:00.000Z",
        "bindingUrl": "https://binding.example/game/link-demo",
        "bindingUrlIssuedAt": "2026-08-24T12:00:01.000Z",
        "bindingUrlExpiresAt": "2026-08-24T12:03:01.000Z",
        "bindingUrlExpired": false,
        "bindingActionEndpoint": null,
        "emailCodeSent": false,
        "emailCodeSentAt": null,
        "resendAvailableAt": null,
        "allocationInProgress": false,
        "bindingActionInProgress": false
      },
      "binding": {
        "status": "ACTIVE",
        "verified": false,
        "expiresAt": "2026-08-24T12:30:00.000Z",
        "roleId": "987654321",
        "roleName": "其他角色",
        "roleArea": "示例区服"
      },
      "failureCode": null,
      "failureReason": null,
      "review": null,
      "createdAt": "2026-08-24T12:00:00.000Z",
      "startedAt": null,
      "completedAt": null,
      "items": [
        {
          "id": "77777777-7777-4777-8777-777777777777",
          "productId": "11111111-1111-4111-8111-111111111111",
          "productName": "示例商品",
          "quantity": 2,
          "currency": {
            "code": "CREDIT",
            "name": "积分",
            "scale": 0,
            "cnyCentRate": 1
          },
          "effectiveUnitPrice": "1000",
          "grandTotal": "2000",
          "orderedAmount": "2000",
          "settledAmount": "0",
          "releasedAmount": "0",
          "heldAmount": "2000",
          "feeAmount": "0",
          "status": "QUEUED",
          "succeededQuantity": 0,
          "failedQuantity": 0,
          "releasedQuantity": 0,
          "pendingQuantity": 2,
          "failureCodes": [],
          "units": [
            {
              "sequence": 0,
              "status": "WAITING_BINDING",
              "supplierState": null,
              "failureCode": null,
              "failureReason": null,
              "completedAt": null
            },
            {
              "sequence": 1,
              "status": "WAITING_BINDING",
              "supplierState": null,
              "failureCode": null,
              "failureReason": null,
              "completedAt": null
            }
          ]
        },
        {
          "id": "88888888-8888-4888-8888-888888888888",
          "productId": "22222222-2222-4222-8222-222222222222",
          "productName": "示例商品 B",
          "quantity": 1,
          "currency": {
            "code": "CREDIT",
            "name": "积分",
            "scale": 0,
            "cnyCentRate": 1
          },
          "effectiveUnitPrice": "2500",
          "grandTotal": "2500",
          "orderedAmount": "2500",
          "settledAmount": "0",
          "releasedAmount": "0",
          "heldAmount": "2500",
          "feeAmount": "0",
          "status": "QUEUED",
          "succeededQuantity": 0,
          "failedQuantity": 0,
          "releasedQuantity": 0,
          "pendingQuantity": 1,
          "failureCodes": [],
          "units": [
            {
              "sequence": 0,
              "status": "WAITING_BINDING",
              "supplierState": null,
              "failureCode": null,
              "failureReason": null,
              "completedAt": null
            }
          ]
        }
      ]
    },
    "binding": {
      "bindingRequired": true,
      "matched": false,
      "binding": {
        "userBound": true,
        "roleId": "987654321",
        "roleName": "其他角色",
        "roleArea": "示例区服"
      }
    }
  },
  "requestId": "44444444-4444-4444-8444-444444444444"
}

EMAIL 查单:已发送验证码与重发冷却

GET /openapi/v1/orders/{id}

resendAvailableAt 是当前持久化发送 / 验证状态推导的最早业务重试时间,不代表请求必能通过全部 HTTP 限流。

{
  "data": {
    "id": "33333333-3333-4333-8333-333333333333",
    "orderNo": "O...",
    "callerOrderId": "merchant-order-20260824-0001",
    "orderVersion": 3,
    "roleId": "123456789",
    "categoryId": "55555555-5555-4555-8555-555555555555",
    "deliveryType": "EMAIL_BINDING",
    "currency": {
      "code": "CREDIT",
      "name": "积分",
      "scale": 0,
      "cnyCentRate": 1
    },
    "orderedAmount": "4500",
    "settledAmount": "0",
    "releasedAmount": "0",
    "heldAmount": "4500",
    "feeAmount": "0",
    "quantity": 3,
    "succeededQuantity": 0,
    "failedQuantity": 0,
    "releasedQuantity": 0,
    "pendingQuantity": 3,
    "failureCodes": [],
    "status": "WAITING_BINDING",
    "bindingDeadline": "2026-08-24T12:30:00.000Z",
    "bindingAction": {
      "bindingRequired": true,
      "bindingType": "EMAIL_BINDING",
      "expiresAt": "2026-08-24T12:30:00.000Z",
      "bindingDeadline": "2026-08-24T12:30:00.000Z",
      "bindingUrl": null,
      "bindingUrlIssuedAt": null,
      "bindingUrlExpiresAt": null,
      "bindingUrlExpired": false,
      "bindingActionEndpoint": null,
      "emailCodeSent": true,
      "emailCodeSentAt": "2026-08-24T12:00:01.000Z",
      "resendAvailableAt": "2026-08-24T12:01:01.000Z",
      "allocationInProgress": false,
      "bindingActionInProgress": false
    },
    "binding": {
      "status": "ACTIVE",
      "verified": false,
      "expiresAt": "2026-08-24T12:30:00.000Z",
      "roleId": null,
      "roleName": null,
      "roleArea": null
    },
    "failureCode": null,
    "failureReason": null,
    "review": null,
    "createdAt": "2026-08-24T12:00:00.000Z",
    "startedAt": null,
    "completedAt": null,
    "items": [
      {
        "id": "77777777-7777-4777-8777-777777777777",
        "productId": "11111111-1111-4111-8111-111111111111",
        "productName": "示例商品",
        "quantity": 2,
        "currency": {
          "code": "CREDIT",
          "name": "积分",
          "scale": 0,
          "cnyCentRate": 1
        },
        "effectiveUnitPrice": "1000",
        "grandTotal": "2000",
        "orderedAmount": "2000",
        "settledAmount": "0",
        "releasedAmount": "0",
        "heldAmount": "2000",
        "feeAmount": "0",
        "status": "QUEUED",
        "succeededQuantity": 0,
        "failedQuantity": 0,
        "releasedQuantity": 0,
        "pendingQuantity": 2,
        "failureCodes": [],
        "units": [
          {
            "sequence": 0,
            "status": "WAITING_BINDING",
            "supplierState": null,
            "failureCode": null,
            "failureReason": null,
            "completedAt": null
          },
          {
            "sequence": 1,
            "status": "WAITING_BINDING",
            "supplierState": null,
            "failureCode": null,
            "failureReason": null,
            "completedAt": null
          }
        ]
      },
      {
        "id": "88888888-8888-4888-8888-888888888888",
        "productId": "22222222-2222-4222-8222-222222222222",
        "productName": "示例商品 B",
        "quantity": 1,
        "currency": {
          "code": "CREDIT",
          "name": "积分",
          "scale": 0,
          "cnyCentRate": 1
        },
        "effectiveUnitPrice": "2500",
        "grandTotal": "2500",
        "orderedAmount": "2500",
        "settledAmount": "0",
        "releasedAmount": "0",
        "heldAmount": "2500",
        "feeAmount": "0",
        "status": "QUEUED",
        "succeededQuantity": 0,
        "failedQuantity": 0,
        "releasedQuantity": 0,
        "pendingQuantity": 1,
        "failureCodes": [],
        "units": [
          {
            "sequence": 0,
            "status": "WAITING_BINDING",
            "supplierState": null,
            "failureCode": null,
            "failureReason": null,
            "completedAt": null
          }
        ]
      }
    ]
  },
  "requestId": "44444444-4444-4444-8444-444444444444"
}

EMAIL 获取 / 恢复动作成功

POST /openapi/v1/orders/{id}/binding/action

已发送时返回已有动作;首次发送未完成时可以恢复。明确重发仍使用 email/resend。

{
  "data": {
    "order": {
      "id": "33333333-3333-4333-8333-333333333333",
      "orderNo": "O...",
      "callerOrderId": "merchant-order-20260824-0001",
      "orderVersion": 3,
      "roleId": "123456789",
      "categoryId": "55555555-5555-4555-8555-555555555555",
      "deliveryType": "EMAIL_BINDING",
      "currency": {
        "code": "CREDIT",
        "name": "积分",
        "scale": 0,
        "cnyCentRate": 1
      },
      "orderedAmount": "4500",
      "settledAmount": "0",
      "releasedAmount": "0",
      "heldAmount": "4500",
      "feeAmount": "0",
      "quantity": 3,
      "succeededQuantity": 0,
      "failedQuantity": 0,
      "releasedQuantity": 0,
      "pendingQuantity": 3,
      "failureCodes": [],
      "status": "WAITING_BINDING",
      "bindingDeadline": "2026-08-24T12:30:00.000Z",
      "bindingAction": {
        "bindingRequired": true,
        "bindingType": "EMAIL_BINDING",
        "expiresAt": "2026-08-24T12:30:00.000Z",
        "bindingDeadline": "2026-08-24T12:30:00.000Z",
        "bindingUrl": null,
        "bindingUrlIssuedAt": null,
        "bindingUrlExpiresAt": null,
        "bindingUrlExpired": false,
        "bindingActionEndpoint": null,
        "emailCodeSent": true,
        "emailCodeSentAt": "2026-08-24T12:00:01.000Z",
        "resendAvailableAt": "2026-08-24T12:01:01.000Z",
        "allocationInProgress": false,
        "bindingActionInProgress": false
      },
      "binding": {
        "status": "ACTIVE",
        "verified": false,
        "expiresAt": "2026-08-24T12:30:00.000Z",
        "roleId": null,
        "roleName": null,
        "roleArea": null
      },
      "failureCode": null,
      "failureReason": null,
      "review": null,
      "createdAt": "2026-08-24T12:00:00.000Z",
      "startedAt": null,
      "completedAt": null,
      "items": [
        {
          "id": "77777777-7777-4777-8777-777777777777",
          "productId": "11111111-1111-4111-8111-111111111111",
          "productName": "示例商品",
          "quantity": 2,
          "currency": {
            "code": "CREDIT",
            "name": "积分",
            "scale": 0,
            "cnyCentRate": 1
          },
          "effectiveUnitPrice": "1000",
          "grandTotal": "2000",
          "orderedAmount": "2000",
          "settledAmount": "0",
          "releasedAmount": "0",
          "heldAmount": "2000",
          "feeAmount": "0",
          "status": "QUEUED",
          "succeededQuantity": 0,
          "failedQuantity": 0,
          "releasedQuantity": 0,
          "pendingQuantity": 2,
          "failureCodes": [],
          "units": [
            {
              "sequence": 0,
              "status": "WAITING_BINDING",
              "supplierState": null,
              "failureCode": null,
              "failureReason": null,
              "completedAt": null
            },
            {
              "sequence": 1,
              "status": "WAITING_BINDING",
              "supplierState": null,
              "failureCode": null,
              "failureReason": null,
              "completedAt": null
            }
          ]
        },
        {
          "id": "88888888-8888-4888-8888-888888888888",
          "productId": "22222222-2222-4222-8222-222222222222",
          "productName": "示例商品 B",
          "quantity": 1,
          "currency": {
            "code": "CREDIT",
            "name": "积分",
            "scale": 0,
            "cnyCentRate": 1
          },
          "effectiveUnitPrice": "2500",
          "grandTotal": "2500",
          "orderedAmount": "2500",
          "settledAmount": "0",
          "releasedAmount": "0",
          "heldAmount": "2500",
          "feeAmount": "0",
          "status": "QUEUED",
          "succeededQuantity": 0,
          "failedQuantity": 0,
          "releasedQuantity": 0,
          "pendingQuantity": 1,
          "failureCodes": [],
          "units": [
            {
              "sequence": 0,
              "status": "WAITING_BINDING",
              "supplierState": null,
              "failureCode": null,
              "failureReason": null,
              "completedAt": null
            }
          ]
        }
      ]
    },
    "bindingAction": {
      "bindingRequired": true,
      "bindingType": "EMAIL_BINDING",
      "expiresAt": "2026-08-24T12:30:00.000Z",
      "bindingDeadline": "2026-08-24T12:30:00.000Z",
      "bindingUrl": null,
      "bindingUrlIssuedAt": null,
      "bindingUrlExpiresAt": null,
      "bindingUrlExpired": false,
      "bindingActionEndpoint": null,
      "emailCodeSent": true,
      "emailCodeSentAt": "2026-08-24T12:00:01.000Z",
      "resendAvailableAt": "2026-08-24T12:01:01.000Z",
      "allocationInProgress": false,
      "bindingActionInProgress": false
    }
  },
  "requestId": "44444444-4444-4444-8444-444444444444"
}

EMAIL 验证成功且角色匹配

POST /openapi/v1/orders/{id}/email/verify

请求体 { "code": "123456" } 仅为格式示例,应填写实际收到的验证码;成功后进入 QUEUED。

{
  "data": {
    "order": {
      "id": "33333333-3333-4333-8333-333333333333",
      "orderNo": "O...",
      "callerOrderId": "merchant-order-20260824-0001",
      "orderVersion": 6,
      "roleId": "123456789",
      "categoryId": "55555555-5555-4555-8555-555555555555",
      "deliveryType": "EMAIL_BINDING",
      "currency": {
        "code": "CREDIT",
        "name": "积分",
        "scale": 0,
        "cnyCentRate": 1
      },
      "orderedAmount": "4500",
      "settledAmount": "0",
      "releasedAmount": "0",
      "heldAmount": "4500",
      "feeAmount": "0",
      "quantity": 3,
      "succeededQuantity": 0,
      "failedQuantity": 0,
      "releasedQuantity": 0,
      "pendingQuantity": 3,
      "failureCodes": [],
      "status": "QUEUED",
      "bindingDeadline": "2026-08-24T12:30:00.000Z",
      "bindingAction": {
        "bindingRequired": false,
        "bindingType": "EMAIL_BINDING",
        "expiresAt": "2026-08-24T12:30:00.000Z",
        "bindingDeadline": "2026-08-24T12:30:00.000Z",
        "bindingUrl": null,
        "bindingUrlIssuedAt": null,
        "bindingUrlExpiresAt": null,
        "bindingUrlExpired": false,
        "bindingActionEndpoint": null,
        "emailCodeSent": true,
        "emailCodeSentAt": "2026-08-24T12:00:01.000Z",
        "resendAvailableAt": "2026-08-24T12:01:01.000Z",
        "allocationInProgress": false,
        "bindingActionInProgress": false
      },
      "binding": {
        "status": "ACTIVE",
        "verified": true,
        "expiresAt": "2026-08-24T12:30:00.000Z",
        "roleId": "123456789",
        "roleName": "示例角色",
        "roleArea": "示例区服"
      },
      "failureCode": null,
      "failureReason": null,
      "review": null,
      "createdAt": "2026-08-24T12:00:00.000Z",
      "startedAt": "2026-08-24T12:02:00.000Z",
      "completedAt": null,
      "items": [
        {
          "id": "77777777-7777-4777-8777-777777777777",
          "productId": "11111111-1111-4111-8111-111111111111",
          "productName": "示例商品",
          "quantity": 2,
          "currency": {
            "code": "CREDIT",
            "name": "积分",
            "scale": 0,
            "cnyCentRate": 1
          },
          "effectiveUnitPrice": "1000",
          "grandTotal": "2000",
          "orderedAmount": "2000",
          "settledAmount": "0",
          "releasedAmount": "0",
          "heldAmount": "2000",
          "feeAmount": "0",
          "status": "QUEUED",
          "succeededQuantity": 0,
          "failedQuantity": 0,
          "releasedQuantity": 0,
          "pendingQuantity": 2,
          "failureCodes": [],
          "units": [
            {
              "sequence": 0,
              "status": "QUEUED",
              "supplierState": null,
              "failureCode": null,
              "failureReason": null,
              "completedAt": null
            },
            {
              "sequence": 1,
              "status": "QUEUED",
              "supplierState": null,
              "failureCode": null,
              "failureReason": null,
              "completedAt": null
            }
          ]
        },
        {
          "id": "88888888-8888-4888-8888-888888888888",
          "productId": "22222222-2222-4222-8222-222222222222",
          "productName": "示例商品 B",
          "quantity": 1,
          "currency": {
            "code": "CREDIT",
            "name": "积分",
            "scale": 0,
            "cnyCentRate": 1
          },
          "effectiveUnitPrice": "2500",
          "grandTotal": "2500",
          "orderedAmount": "2500",
          "settledAmount": "0",
          "releasedAmount": "0",
          "heldAmount": "2500",
          "feeAmount": "0",
          "status": "QUEUED",
          "succeededQuantity": 0,
          "failedQuantity": 0,
          "releasedQuantity": 0,
          "pendingQuantity": 1,
          "failureCodes": [],
          "units": [
            {
              "sequence": 0,
              "status": "QUEUED",
              "supplierState": null,
              "failureCode": null,
              "failureReason": null,
              "completedAt": null
            }
          ]
        }
      ]
    },
    "binding": {
      "bindingRequired": false,
      "matched": true,
      "binding": {
        "userBound": true,
        "roleId": "123456789",
        "roleName": "示例角色",
        "roleArea": "示例区服"
      }
    }
  },
  "requestId": "44444444-4444-4444-8444-444444444444"
}

EMAIL 重发成功

POST /openapi/v1/orders/{id}/email/resend

data.binding 返回发送操作结果;最新冷却绝对时间位于 data.order.bindingAction.resendAvailableAt。

{
  "data": {
    "order": {
      "id": "33333333-3333-4333-8333-333333333333",
      "orderNo": "O...",
      "callerOrderId": "merchant-order-20260824-0001",
      "orderVersion": 4,
      "roleId": "123456789",
      "categoryId": "55555555-5555-4555-8555-555555555555",
      "deliveryType": "EMAIL_BINDING",
      "currency": {
        "code": "CREDIT",
        "name": "积分",
        "scale": 0,
        "cnyCentRate": 1
      },
      "orderedAmount": "4500",
      "settledAmount": "0",
      "releasedAmount": "0",
      "heldAmount": "4500",
      "feeAmount": "0",
      "quantity": 3,
      "succeededQuantity": 0,
      "failedQuantity": 0,
      "releasedQuantity": 0,
      "pendingQuantity": 3,
      "failureCodes": [],
      "status": "WAITING_BINDING",
      "bindingDeadline": "2026-08-24T12:30:00.000Z",
      "bindingAction": {
        "bindingRequired": true,
        "bindingType": "EMAIL_BINDING",
        "expiresAt": "2026-08-24T12:30:00.000Z",
        "bindingDeadline": "2026-08-24T12:30:00.000Z",
        "bindingUrl": null,
        "bindingUrlIssuedAt": null,
        "bindingUrlExpiresAt": null,
        "bindingUrlExpired": false,
        "bindingActionEndpoint": null,
        "emailCodeSent": true,
        "emailCodeSentAt": "2026-08-24T12:02:00.000Z",
        "resendAvailableAt": "2026-08-24T12:03:00.000Z",
        "allocationInProgress": false,
        "bindingActionInProgress": false
      },
      "binding": {
        "status": "ACTIVE",
        "verified": false,
        "expiresAt": "2026-08-24T12:30:00.000Z",
        "roleId": null,
        "roleName": null,
        "roleArea": null
      },
      "failureCode": null,
      "failureReason": null,
      "review": null,
      "createdAt": "2026-08-24T12:00:00.000Z",
      "startedAt": null,
      "completedAt": null,
      "items": [
        {
          "id": "77777777-7777-4777-8777-777777777777",
          "productId": "11111111-1111-4111-8111-111111111111",
          "productName": "示例商品",
          "quantity": 2,
          "currency": {
            "code": "CREDIT",
            "name": "积分",
            "scale": 0,
            "cnyCentRate": 1
          },
          "effectiveUnitPrice": "1000",
          "grandTotal": "2000",
          "orderedAmount": "2000",
          "settledAmount": "0",
          "releasedAmount": "0",
          "heldAmount": "2000",
          "feeAmount": "0",
          "status": "QUEUED",
          "succeededQuantity": 0,
          "failedQuantity": 0,
          "releasedQuantity": 0,
          "pendingQuantity": 2,
          "failureCodes": [],
          "units": [
            {
              "sequence": 0,
              "status": "WAITING_BINDING",
              "supplierState": null,
              "failureCode": null,
              "failureReason": null,
              "completedAt": null
            },
            {
              "sequence": 1,
              "status": "WAITING_BINDING",
              "supplierState": null,
              "failureCode": null,
              "failureReason": null,
              "completedAt": null
            }
          ]
        },
        {
          "id": "88888888-8888-4888-8888-888888888888",
          "productId": "22222222-2222-4222-8222-222222222222",
          "productName": "示例商品 B",
          "quantity": 1,
          "currency": {
            "code": "CREDIT",
            "name": "积分",
            "scale": 0,
            "cnyCentRate": 1
          },
          "effectiveUnitPrice": "2500",
          "grandTotal": "2500",
          "orderedAmount": "2500",
          "settledAmount": "0",
          "releasedAmount": "0",
          "heldAmount": "2500",
          "feeAmount": "0",
          "status": "QUEUED",
          "succeededQuantity": 0,
          "failedQuantity": 0,
          "releasedQuantity": 0,
          "pendingQuantity": 1,
          "failureCodes": [],
          "units": [
            {
              "sequence": 0,
              "status": "WAITING_BINDING",
              "supplierState": null,
              "failureCode": null,
              "failureReason": null,
              "completedAt": null
            }
          ]
        }
      ]
    },
    "binding": {
      "sent": true,
      "inProgress": false,
      "sentAt": "2026-08-24T12:02:00.000Z"
    }
  },
  "requestId": "44444444-4444-4444-8444-444444444444"
}

EMAIL 业务冷却尚未结束

{
  "statusCode": 409,
  "error": "ConflictException",
  "message": "验证码发送过于频繁,请60秒后再试",
  "code": "EMAIL_CODE_RATE_LIMITED",
  "requestId": "44444444-4444-4444-8444-444444444444",
  "timestamp": "2026-08-24T12:02:30.000Z",
  "path": "/openapi/v1/orders/33333333-3333-4333-8333-333333333333/email/resend"
}

绑定窗口已过期

{
  "statusCode": 409,
  "error": "ConflictException",
  "message": "绑定已超时",
  "code": "BINDING_TIMEOUT",
  "requestId": "44444444-4444-4444-8444-444444444444",
  "timestamp": "2026-08-24T12:30:01.000Z",
  "path": "/openapi/v1/orders/33333333-3333-4333-8333-333333333333/binding/refresh"
}

订单状态与部分成功

订单状态 含义
WAITING_BINDING 等待 LINK 或 EMAIL 绑定完成,最长 30 分钟
QUEUED 绑定已验证,等待发货 Worker 领取
PROCESSING 至少一件商品正在渠道处理
RECONCILING 渠道提交结果未知,正在按 2 / 4 / 8 / 16 / 32 秒进行五次对账
REVIEW_REQUIRED 渠道记录无法唯一确认,需要管理员人工对账
SUCCEEDED 全部单件发货成功并已结算
PARTIAL_SUCCESS 部分成功;成功部分正常结算,其余已失败或释放
FAILED 没有单件成功,失败或未发部分已按规则释放
CANCELLED 调用方取消或绑定超时自动取消

以逐件结果为最终依据

订单及每个 item 都包含 quantity、succeededQuantity、failedQuantity、releasedQuantity、pendingQuantity 和去重后的 failureCodes;商品行另有 units。不要把 HTTP 下单成功理解为发货成功。

部分成功不会整单回滚

成功单件正常结算;明确失败或被停止的未发单件释放冻结金额并恢复库存。当前不支持退款。

数量互斥与守恒

四类结果数量互斥:一件商品只能计入成功、明确失败、未执行释放、尚未确定中的一类。failedQuantity 不包含 releasedQuantity;但这两类对应的资金都会计入 releasedAmount。

整单和每个商品行均满足 quantity = succeededQuantity + failedQuantity + releasedQuantity + pendingQuantity。订单各数量等于商品行对应字段之和;终态 pendingQuantity=0。failureCodes 是诊断码集合,不是件数,不能靠码的个数计算失败数量。

逐件状态 units[].status 唯一归属字段 含义
WAITING_BINDING pendingQuantity 尚未完成绑定
QUEUED pendingQuantity 已排队,尚未提交渠道
SUBMITTING pendingQuantity 已越过提交边界,渠道是否受理可能尚未明确
SUBMITTED pendingQuantity 已取得渠道发货单号,等待结果
PROCESSING pendingQuantity 渠道正在处理
RECONCILING pendingQuantity 结果未知,正在查询渠道记录核实
REVIEW_REQUIRED pendingQuantity 等待管理员人工对账;不能视为失败或补发
SUCCEEDED succeededQuantity 已确认成功并结算
FAILED failedQuantity 已明确失败且资金和额度已释放;也可能是在提交前预检中明确失败
STOPPED releasedQuantity 尚未执行的单件被停止,资金和额度已释放
CANCELLED releasedQuantity 尚未执行的单件被取消,资金和额度已释放

部分成功完整查单:成功 1 件、明确失败 1 件、停止释放 1 件

原价合计 3000 分,结算 1000 分,退回 2000 分;最多可考虑补发 2 件,但需先处理失败原因并重新通过角色额度校验。

{
  "data": {
    "id": "33333333-3333-4333-8333-333333333333",
    "orderNo": "O...",
    "callerOrderId": "merchant-order-20260824-0001",
    "orderVersion": 9,
    "roleId": "123456789",
    "categoryId": "55555555-5555-4555-8555-555555555555",
    "deliveryType": "LINK_BINDING",
    "currency": {
      "code": "CREDIT",
      "name": "积分",
      "scale": 0,
      "cnyCentRate": 1
    },
    "orderedAmount": "3000",
    "settledAmount": "1000",
    "releasedAmount": "2000",
    "heldAmount": "0",
    "feeAmount": "0",
    "quantity": 3,
    "succeededQuantity": 1,
    "failedQuantity": 1,
    "releasedQuantity": 1,
    "pendingQuantity": 0,
    "failureCodes": [
      "SKU_STOPPED_AFTER_FAILURE",
      "SUPPLIER_REJECTED"
    ],
    "status": "PARTIAL_SUCCESS",
    "bindingDeadline": "2026-08-24T12:30:00.000Z",
    "bindingAction": {
      "bindingRequired": false,
      "bindingType": "LINK_BINDING",
      "expiresAt": "2026-08-24T12:30:00.000Z",
      "bindingDeadline": "2026-08-24T12:30:00.000Z",
      "bindingUrl": null,
      "bindingUrlIssuedAt": null,
      "bindingUrlExpiresAt": null,
      "bindingUrlExpired": false,
      "bindingActionEndpoint": null,
      "emailCodeSent": false,
      "emailCodeSentAt": null,
      "resendAvailableAt": null,
      "allocationInProgress": false,
      "bindingActionInProgress": false
    },
    "binding": {
      "status": "QUARANTINED",
      "verified": true,
      "expiresAt": "2026-08-24T12:30:00.000Z",
      "roleId": "123456789",
      "roleName": "示例角色",
      "roleArea": "示例区服"
    },
    "failureCode": null,
    "failureReason": null,
    "review": null,
    "createdAt": "2026-08-24T12:00:00.000Z",
    "startedAt": "2026-08-24T12:02:00.000Z",
    "completedAt": "2026-08-24T12:10:00.000Z",
    "items": [
      {
        "id": "77777777-7777-4777-8777-777777777777",
        "productId": "11111111-1111-4111-8111-111111111111",
        "productName": "示例商品",
        "quantity": 3,
        "currency": {
          "code": "CREDIT",
          "name": "积分",
          "scale": 0,
          "cnyCentRate": 1
        },
        "effectiveUnitPrice": "1000",
        "grandTotal": "3000",
        "orderedAmount": "3000",
        "settledAmount": "1000",
        "releasedAmount": "2000",
        "heldAmount": "0",
        "feeAmount": "0",
        "status": "PARTIAL_SUCCESS",
        "succeededQuantity": 1,
        "failedQuantity": 1,
        "releasedQuantity": 1,
        "pendingQuantity": 0,
        "failureCodes": [
          "SKU_STOPPED_AFTER_FAILURE",
          "SUPPLIER_REJECTED"
        ],
        "units": [
          {
            "sequence": 0,
            "status": "SUCCEEDED",
            "supplierState": 4,
            "failureCode": null,
            "failureReason": null,
            "completedAt": "2026-08-24T12:09:00.000Z"
          },
          {
            "sequence": 1,
            "status": "FAILED",
            "supplierState": null,
            "failureCode": "SUPPLIER_REJECTED",
            "failureReason": "渠道明确拒绝该件",
            "completedAt": "2026-08-24T12:10:00.000Z"
          },
          {
            "sequence": 2,
            "status": "STOPPED",
            "supplierState": null,
            "failureCode": "SKU_STOPPED_AFTER_FAILURE",
            "failureReason": "同商品前件失败,停止尚未执行的单件",
            "completedAt": "2026-08-24T12:10:00.000Z"
          }
        ]
      }
    ]
  },
  "requestId": "44444444-4444-4444-8444-444444444444"
}

PARTIAL_SUCCESS 后如何补发

  • 只能对原订单中已明确失败或已释放的数量补发;先根据 failureCodes 处理原因,再创建一张使用**新 callerOrderId**的新订单。
  • 原订单已是终态,不能再整单取消,也不能在原单上追加数量或由调用方改写结果。
  • 角色累计发货上限计入已成功数量和结果尚未明确的预占数量;已明确失败或释放的数量不再占用,新单仍会在事务内重新校验。

REVIEW_REQUIRED 由平台管理员处理

调用方不需要、也不能自行选择渠道记录。平台管理员登录 /open-platform 后,通过“开放订单”人工对账;运营目标为 15 分钟内响应、2 小时内完成处理,但这是人工服务目标,不是自动完成或确定时限承诺。订单详情的 review.enteredAt、review.updatedAt 和 review.expectedResolutionAt 分别表示首次进入时间、最近更新时间和目标完成时间,离开人工状态后仍保留供追溯。处理期间不会自动重发、判定成功或退款,请通过 Webhook 和查单接口继续跟踪。

开放 API 订单终态自动退回手机号

SUCCEEDED、PARTIAL_SUCCESS、FAILED 或 CANCELLED 终态会自动退回开放 API 订单占用的手机号,先进入隔离池,默认 10 分钟后复查绑定信息再进入后续冷却或可用流程。同分类最后绑定角色一致的正常退还账号,指定角色申请可提前复用;使用中、有在途订单、存在异常或正在复核的账号不可抢占,复用后仍需实时核对当前绑定。这不跳过申请配额,也不清除普通分配的冷却。RECONCILING 和 REVIEW_REQUIRED 不是终态,不会提前退回。这不改变网页端手动发货“用户手动退还或等租期到期”的原有规则。

接口清单、查询、取消与账号释放

所有路径均需签名。点击路径旁的复制按钮,可直接复制完整接口路径。

查单与回调共用 orderVersion

订单响应直接返回 orderVersion,它与该订单 Webhook 顶层版本同源。低版本结果丢弃,高版本按实际提供字段更新;相同版本的查单可以补全金额、绑定和逐件结果。Webhook 是事件摘要,字段随事件类型变化,不能用它覆盖完整订单对象或把缺失数量默认成 0。

绑定信息是该订单的持久化快照,不会随账号退回后被其他人使用而变成别人的角色。GET 只读;动作恢复需显式调用 binding/action。旧版客户端发现响应缺少 orderVersion 时不能按 0 合并,应升级契约处理并重新查询。

GET/openapi/v1/products

请求输入

categoryId?、keyword?、page?、pageSize?

响应内容

商品分页列表、库存、角色累计发货上限、发货方式和当前用户成交单价

查询当前可见且可售的单个商品

GET/openapi/v1/products/{id}

请求输入

路径参数 id(UUID v4)

响应内容

与商品列表单行相同的完整投影,含 updatedAt / productVersion

查询应用所属用户的共享钱包余额

GET/openapi/v1/wallets

请求输入

无

响应内容

可用、冻结、合计余额及币种口径

查询当前应用订单产生的资金流水

GET/openapi/v1/wallet-transactions

请求输入

effect?、currencyCode?、startedAt?、endedAt?、cursor?、pageSize?

响应内容

HOLD / SETTLE / RELEASE 及可用、冻结余额变化

幂等创建聚合订单;完成绑定后自动开始逐件发货

POST/openapi/v1/orders

请求输入

callerOrderId、roleId、items[{ productId, quantity }]

响应内容

order、bindingAction、idempotentReplay

查询单笔订单、绑定信息、商品汇总和逐件结果

GET/openapi/v1/orders/{id}

请求输入

路径参数 id(UUID v4)

响应内容

含 orderVersion、完整数量/金额/逐件结果和只读 bindingAction 的订单快照;不会调用上游或发送验证码

按调用方订单号精确查单,用于下单超时补偿

GET/openapi/v1/orders/by-caller-order-id

请求输入

查询参数 callerOrderId(1–128 字符)

响应内容

不受 90 天列表窗口限制的单笔订单快照

查询当前应用最近最多 90 天的订单记录

GET/openapi/v1/orders

请求输入

startedAt?、endedAt?、callerOrderId?、page?、pageSize?

响应内容

订单分页列表

重新进入时显式获取或恢复原订单绑定动作

POST/openapi/v1/orders/{id}/binding/action

请求输入

无请求体;与 binding/refresh 共用绑定刷新额度

响应内容

order、bindingAction;可能调用上游生成链接或恢复邮箱动作,不创建新订单

POST/openapi/v1/orders/{id}/binding/refresh

请求输入

无请求体

响应内容

order、binding

提交 EMAIL 绑定验证码;匹配成功即进入发货

POST/openapi/v1/orders/{id}/email/verify

请求输入

{ code: string },4 至 8 位数字

响应内容

order、binding

在允许的状态和频率内重新发送 EMAIL 绑定验证码

POST/openapi/v1/orders/{id}/email/resend

请求输入

无请求体

响应内容

order、binding

所有单件均未越过渠道提交边界时取消整单

POST/openapi/v1/orders/{id}/cancel

请求输入

无请求体

响应内容

取消后的订单快照

订单终态后幂等确认退回(终态已会自动退回)

POST/openapi/v1/orders/{id}/account/release

请求输入

无请求体

响应内容

order、lease

  • 订单列表的 startedAt / endedAt 使用 ISO 8601;结束时间必须晚于开始时间,单次跨度不超过 90 天。
  • 列表不传时间时默认查询截至当前时间的最近 90 天;pageSize 最大 100。按 callerOrderId 精确查单不受该窗口限制。
  • 只要任意单件越过渠道提交边界,整单取消就会返回 ORDER_NOT_CANCELLABLE。
  • 开放 API 订单终态已会自动退回手机号并默认进入 10 分钟隔离复查;同分类原角色符合安全条件时可提前申请复用,普通分配仍等待复核与冷却。account/release 是幂等兜底和主动确认接口,重复调用不会重复退回。

Webhook 事件与验签

投递约定

  • 回调地址必须为公网 HTTPS,系统会拒绝本地、私网、保留地址和不安全重定向。
  • Webhook Secret 与 API SecretKey 是两套独立密钥,首次配置或轮换时同样只显示一次,不能混用。
  • 采用至少一次投递;接收方必须以 eventId 建立唯一键去重,共享一个回调入口的多应用再以 applicationId 路由到对应鉴权。
  • 验签时要求请求头与请求体的 eventId 一致,并拒绝超出本地允许时钟窗口的时间戳。
  • 订单事件的 orderVersion 从 1 开始、按订单单调递增,与查单 data.orderVersion 共用。低版本丢弃,高版本更新已提供字段,相同版本查单可补全细节;回调摘要不能整单覆盖。不同订单之间不保证顺序;WEBHOOK_TEST 的版本为 null。
  • schemaVersion 表示回调请求体结构版本;当前为 1.0。数量字段与整单/商品行 failureCodes 可用于提醒和可补发判断,最终仍以查单结果为准。
  • 验签成功并持久化事件后快速返回任意 2xx;非 2xx、网络错误或超时会重试。
  • 单次请求体不超过 256 KiB,Content-Type 为 application/json; charset=utf-8,绝对超时为 10 秒;回调响应体请保持在 64 KiB 内。
  • 最多投递 8 次(首次 + 7 次自动重试),失败后依次等待 1 分钟、5 分钟、15 分钟、1 小时、3 小时、6 小时、12 小时;耗尽后进入 DEAD,可在开放平台人工重放。
  • Webhook 只负责通知,订单查询接口是断点恢复和漏通知补偿依据。

回调请求头

X-OpenAPI-Application-Id

当前事件所属开放应用,与请求体 applicationId 一致

X-OpenAPI-Event-Id

固定事件 ID,同时用于幂等去重

X-OpenAPI-Schema-Version

回调请求体结构版本,当前为 1.0

X-OpenAPI-Timestamp

本次投递的 Unix 秒级时间戳

X-OpenAPI-Signature

v1= + HMAC-SHA256 hex

timestamp + "." + eventId + "." + rawBody

签名对象是原始 UTF-8 请求体字节,不是解析后再序列化的 JSON;签名请求头固定以 v1= 开头。

LINK 显式刷新或 EMAIL 验证取得绑定快照时发送 BINDING_UPDATED;角色匹配并进入 QUEUED 时发送 BINDING_VERIFIED。当前没有后台主动轮询绑定变化,调用方完成绑定后仍需执行 refresh 或 verify。

事件 触发时机
ORDER_CREATED 订单已受理
BINDING_UPDATED 显式刷新或 EMAIL 验证后获得了新的绑定快照
BINDING_VERIFIED 最新角色与订单 roleId 匹配,订单进入发货队列
UNIT_SUCCEEDED 单件发货成功且整单尚未结束
UNIT_FAILED 单件发货失败且整单尚未结束
ORDER_COMPLETED 整单到达成功、部分成功或失败终态
ORDER_REVIEW_REQUIRED 订单需要管理员人工对账
ORDER_RECONCILIATION_RESUMED 管理员完成选择后恢复自动处理
BINDING_TIMEOUT 30 分钟内未完成绑定,订单已自动取消
ORDER_CANCELLED 订单被调用方取消

事件请求体

{
  "schemaVersion": "1.0",
  "applicationId": "00000000-0000-4000-8000-000000000301",
  "eventId": "66666666-6666-4666-8666-666666666666",
  "orderVersion": 7,
  "type": "ORDER_COMPLETED",
  "occurredAt": "2026-08-24T12:10:00.000Z",
  "data": {
    "orderId": "33333333-3333-4333-8333-333333333333",
    "orderNo": "O...",
    "callerOrderId": "merchant-order-20260824-0001",
    "status": "PARTIAL_SUCCESS",
    "succeededQuantity": 2,
    "failedQuantity": 1,
    "releasedQuantity": 0,
    "pendingQuantity": 0,
    "failureCodes": [
      "SUPPLIER_REJECTED"
    ],
    "changedUnitId": "99999999-9999-4999-8999-999999999999",
    "items": [
      {
        "productId": "11111111-1111-4111-8111-111111111111",
        "status": "SUCCEEDED",
        "quantity": 2,
        "succeededQuantity": 2,
        "failedQuantity": 0,
        "releasedQuantity": 0,
        "failureCodes": []
      },
      {
        "productId": "22222222-2222-4222-8222-222222222222",
        "status": "FAILED",
        "quantity": 1,
        "succeededQuantity": 0,
        "failedQuantity": 1,
        "releasedQuantity": 0,
        "failureCodes": [
          "SUPPLIER_REJECTED"
        ]
      }
    ]
  }
}

Node.js 验签

import { createHmac, timingSafeEqual } from 'node:crypto'

// rawBody 必须是在 JSON 解析前保留的原始请求体字节。
function verifyWebhook({ rawBody, timestamp, eventId, applicationId, schemaVersion, signature, secret, now = Date.now() }) {
  if (!/^(0|[1-9]\d{0,12})$/.test(timestamp)) return false
  if (Math.abs(Number(timestamp) * 1000 - now) > 5 * 60 * 1000) return false
  if (!/^[A-Za-z0-9][A-Za-z0-9_-]{7,127}$/.test(eventId)) return false
  const payload = Buffer.concat([Buffer.from(timestamp + '.' + eventId + '.', 'utf8'), rawBody])
  const expected = createHmac('sha256', secret).update(payload).digest('hex')
  const actual = signature.startsWith('v1=') ? signature.slice(3) : ''
  const verified = /^[a-fA-F0-9]{64}$/.test(actual) &&
    timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(actual, 'hex'))
  if (!verified) return false
  // 只在验签后解析 JSON,并校验请求头和请求体身份一致。
  try {
    const body = JSON.parse(rawBody.toString('utf8'))
    return body.eventId === eventId && body.applicationId === applicationId &&
      body.schemaVersion === schemaVersion
  } catch { return false }
}

// 验签成功后以 eventId 做唯一键;重复事件直接返回 2xx。
// 查单与回调共用 orderVersion:低版本丢弃,高版本合并已提供字段。
// 相同版本的查单可补全逐件/金额/绑定细节;回调摘要不能覆盖完整订单对象。
// WEBHOOK_TEST 的 orderVersion 为 null,不参与订单版本比较。

错误码与安全重试

HTTP code 含义 处理建议
400 HTTP_400 请求体、查询参数、UUID 或字段格式未通过校验 按 message 修正参数后,使用新的签名信息重试
400 DUPLICATE_PRODUCT_LINE 同一 SKU 在 items 中重复出现 修正参数后重试
400 MIXED_CATEGORY_OR_DELIVERY_TYPE 订单混入不同分类或不同发货方式 拆分订单
400 MIXED_CURRENCY 一张聚合订单混入了不同币种 按币种拆分订单
400 INVALID_QUERY_RANGE 订单或资金流水查询时间无效,或单次跨度超过 90 天 修正时间范围
400 INVALID_CURSOR 资金流水翻页游标无效 丢弃自行构造的游标,从首页重新查询
401 OPENAPI_CREDENTIAL_INVALID 凭据、时间戳、Nonce、签名或写请求 IP 校验失败 检查接入配置;服务端刻意不暴露具体鉴权原因
403 OPENAPI_PERMISSION_REVOKED 应用所属用户已不再具备 openapi:use 联系管理员恢复权限
403 PRODUCT_ACCESS_DENIED 商品可以查看,但当前用户没有购买权限 只对 accessMode=PURCHASE 的商品下单
404 OPENAPI_PRODUCT_NOT_FOUND / OPENAPI_ORDER_NOT_FOUND / HTTP_404 商品或当前应用下的订单不存在 检查资源 ID
503 OPENAPI_PRODUCT_MEDIA_URL_INVALID 商品图片未能安全解析为绝对 HTTPS 地址 停止展示该商品并联系平台管理员
409 CALLER_ORDER_ID_CONFLICT 相同 callerOrderId 已用于不同的请求内容 禁止自动换号重发。先按原 callerOrderId 精确查单,核对角色、商品、数量和原业务请求;确认属于一次新的业务发货后才使用新单号
409 ROLE_DELIVERY_LIMIT_EXCEEDED 同一 SKU + 经验证角色 ID 的全系统累计发货额度不足 减少发货数量或更换真实角色;更换手机号无法规避
409 PRODUCT_NOT_ON_SALE / PRODUCT_MEDIA_NOT_READY / CURRENCY_DISABLED 商品或币种在下单前已变为不可用 重新查询 products,不要对原商品盲目重试
409 PRODUCT_CHANNEL_MAPPING_UNAVAILABLE / PRODUCT_SUPPLY_PRICE_UNSAFE / OPENAPI_CDK_UNSUPPORTED / PRICE_OVERFLOW 商品渠道映射或供货价不可用,或商品不在 V1 支持范围 停止自动重试并联系平台管理员
409 INSUFFICIENT_STOCK / INSUFFICIENT_BALANCE 库存或应用所属用户余额不足 补足后使用同一 callerOrderId 重试
409 NO_VIRTUAL_ACCOUNT_AVAILABLE / VIRTUAL_ACCOUNT_LIMIT_REACHED 当前没有可租用账号,或用户在该分类达到租用上限 本单会取消;容量恢复后使用新 callerOrderId 下单
409 BINDING_TIMEOUT / BINDING_ROLE_MISMATCH 绑定已超时,或最新绑定角色与订单 roleId 不一致 超时后新建订单;不一致时重新绑定
409 EMAIL_CODE_RATE_LIMITED EMAIL 验证码发送过于频繁 查询原单的 bindingAction.resendAvailableAt,等冷却结束后再重发;额外 HTTP 限流仍须遵循 429 的 Retry-After
409 EMAIL_CODE_SEND_IN_PROGRESS / EMAIL_VERIFICATION_IN_PROGRESS 同一订单正在发送验证码或提交验证,两个渠道操作会互斥 先查询订单状态;仍等待绑定时,等处理窗口结束后再重试,期间不要盲目 resend
409 EMAIL_BINDING_RESULT_UNKNOWN 验证码提交结果不确定,系统未盲目重复绑定 查询订单;必要时调用 resend
409 BINDING_LEASE_NOT_READY / OPENAPI_ORDER_BINDING_STATE_CHANGED / OPENAPI_ORDER_NOT_WAITING_BINDING 手机号分配未完成、已失效,或订单绑定状态已发生并发变化 先查询订单;仍在等待绑定时再幂等重试原操作
409 OPENAPI_ORDER_SUPPLIER_SNAPSHOT_INCONSISTENT / OPENAPI_ORDER_SNAPSHOT_SUPPLIER_UNAVAILABLE / OPENAPI_BINDING_LEASE_SUPPLIER_MISMATCH 订单不可变渠道快照或已分配账号与渠道不一致 停止自动重试,保留 requestId 并联系平台管理员
409 OPENAPI_LEASE_RELEASE_NOT_ALLOWED 订单尚未进入终态,不能调用幂等退回确认 等订单进入终态;系统会自动退回,release 仅作兜底
409 ORDER_NOT_CANCELLABLE 至少一件商品已经越过渠道提交边界或订单已终态 不可整单取消,继续查询最终结果
429 OPENAPI_RATE_LIMITED 超过应用级、同用户共享、全平台、单订单操作、下单件数或预认证防护阈值,或触发短时突发保护 优先遵循 Retry-After;未提供时指数退避并加入抖动
5xx ORDER_FINANCIAL_STATE_INCONSISTENT / ORDER_CURRENCY_INCONSISTENT / ORDER_QUANTITY_STATE_INCONSISTENT / ORDER_BINDING_STATE_INCONSISTENT / HTTP_500 订单金额、币种、数量或绑定快照无法确认时返回 503,其他服务异常可能返回 500;响应包含 requestId 保留原 callerOrderId 查询,保存 requestId 联系平台;不能将异常当作未发货、盲目补发或自动换新单号

错误响应结构

{
  "statusCode": 409,
  "error": "ConflictException",
  "message": "调用方订单号已用于不同请求",
  "code": "CALLER_ORDER_ID_CONFLICT",
  "requestId": "44444444-4444-4444-8444-444444444444",
  "timestamp": "2026-08-24T12:00:00.000Z",
  "path": "/openapi/v1/orders"
}

所有 HTTP 429 响应都会携带 Retry-After(秒)。必须先按该值等待,再使用新 Timestamp / Nonce / Signature 重试;不要立即并发放大。

不要因网络超时自动生成新订单号。保留 callerOrderId,先查询订单状态;确认未受理时再使用相同参数幂等重试。

完整 Node.js 签名与请求示例

示例要求 Node.js 18+,仅应运行在可信服务端。请通过安全的环境变量或密钥管理服务注入密钥。

联调环境与覆盖范围

当前尚未提供独立隔离沙箱的地址和联调账号。固定验签向量只用于离线算法回归,开放平台的测试回调只验证连通性与签名,均不能替代免绑定、LINK / EMAIL、部分成功、超时已受理、重复 / 乱序回调和人工对账的完整联调。

独立沙箱及可控制的异常场景需另行建设,请联系平台协调联调安排;实际游戏绑定、真实邮箱验证码和渠道最终结果还需供应商测试资源验证。本文的业务响应均为结构示例,不是已完成真实联调的证明。

Node.js 18+

import { createHash, createHmac, randomBytes } from 'node:crypto'

const baseUrl = 'https://shop.cloudtentacles.com'
const accessKey = process.env.OPENAPI_ACCESS_KEY
const secretKey = process.env.OPENAPI_SECRET_KEY
if (!accessKey || !secretKey) throw new Error('缺少开放平台密钥')
const method = 'POST'
const path = '/openapi/v1/orders'
const query = new URLSearchParams()
const rawBody = JSON.stringify({ callerOrderId: 'demo-001', roleId: '123456789',
  items: [{ productId: '11111111-1111-4111-8111-111111111111', quantity: 1 }] })
const timestamp = String(Math.floor(Date.now() / 1000))
const nonce = randomBytes(18).toString('base64url')

// RFC 3986 编码后再按编码后的键和值排序,重复键不能合并。
const encode = (value) => encodeURIComponent(value).replace(/[!'()*]/g,
  (char) => '%' + char.charCodeAt(0).toString(16).toUpperCase())
const canonicalQuery = [...query.entries()]
  .map(([key, value]) => [encode(key), encode(value)])
  .sort(([ak, av], [bk, bv]) => ak < bk ? -1 : ak > bk ? 1 : av < bv ? -1 : av > bv ? 1 : 0)
  .map(([key, value]) => key + '=' + value).join('&')
const bodyHash = createHash('sha256').update(Buffer.from(rawBody, 'utf8')).digest('hex')
const signingString = [method, path, canonicalQuery, bodyHash, timestamp, nonce].join('\n')
const signature = createHmac('sha256', secretKey).update(signingString).digest('hex')

const response = await fetch(baseUrl + path, {
  method,
  headers: {
    'content-type': 'application/json; charset=utf-8',
    'x-openapi-access-key': accessKey,
    'x-openapi-timestamp': timestamp,
    'x-openapi-nonce': nonce,
    'x-openapi-signature': signature,
  },
  body: rawBody,
})
console.log(response.status, await response.json())