REST API · v1
商品下单 API 接入文档
使用带「商品下单」权限的 dy_sk_ Key 和统一预付余额,按当前用户等级价查询商品、检查数量并购买兑换码。 本文给出可直接使用的请求、响应、幂等恢复和错误处理规则。
https://api.dingyue.app/shop/v1Bearer dy_sk_...快速开始
- 登录后在个人中心的「API 中心」为余额充值(单笔 $2–$10,000,通过 Taria Pay 以 USDT/USDC 支付,确认后自动到账),再创建密钥并勾选「商品下单」权限(可与「AI 代理」同时勾选)。密钥只会完整显示一次。
- 服务端调用
GET /products,取得商品 ID 和当前等级价。 - 为自己的业务订单持久化一个唯一
externalOrderId。 - 调用
POST /orders;请求结果不确定时复用同一参数重试。 - 把每个
code和响应中的redeemUrl一起交付给最终用户。
鉴权
除 GET /pricing、OpenAPI 和 llms.txt 外,所有接口都需要:
Authorization: Bearer dy_sk_your_api_key权限在创建密钥时勾选:shop:read 始终包含,可读取商品与余额;下单、订单列表和订单详情都需要 shop:order;ai:proxy 对应 AI 代理,可与 shop:order 同时勾选。只勾了 AI 代理的 Key 无法访问订单数据。
权限创建后不能修改:旧 Key 不能追加 shop:order,需要时请吊销后重建。带商品下单权限的 Key 可直接消耗账户余额,只能保存在后端服务、服务器密钥或本地环境变量中。
接口一览
| 方法 | 路径 | 所需权限 | 用途 |
|---|---|---|---|
| GET | /pricing | 无需鉴权 | 公开价目表:已开放商品的 Lv1–Lv5 等级价与等级门槛 |
| GET | /products | shop:read | 查询已开放商品、当前等级价与履约状态 |
| GET | /products/:id/availability?qty=N | shop:read | 实时检查指定数量是否可购 |
| GET | /balance | shop:read | 查询统一 API 余额、预留金额与等级 |
| POST | /topups | shop:read | 发起余额充值,返回 USDT/USDC 付款链接 paymentUrl |
| GET | /topups?limit=N | shop:read | 按时间倒序列出本账户的充值记录 |
| GET | /topups/:id | shop:read | 查询充值状态;轮询到 credited 即已到账 |
| POST | /orders | shop:order | 从余额购买兑换码;externalOrderId 幂等 |
| GET | /orders | shop:order | 按游标查询订单,可用 externalOrderId 过滤;不返回 codes |
| GET | /orders/:id | shop:order | 查询订单详情;已交付订单返回 codes |
金额全部使用十进制字符串,例如 "21.99",不要用二进制浮点数自行计算或比较。
商品与报价
查询当前等级价
curl https://api.dingyue.app/shop/v1/products \
-H "Authorization: Bearer dy_sk_your_api_key"{
"level": {
"level": 2,
"scoreMicrocents": 10000000000,
"score": "100.00",
"nextLevel": {
"level": 3,
"thresholdMicrocents": 50000000000,
"remainingMicrocents": 40000000000,
"remaining": "400.00"
}
},
"products": [{
"id": "prod_chatgpt_plus",
"slug": "chatgpt-plus",
"name": "ChatGPT Plus",
"price": "21.99",
"currency": "USD",
"inStock": true,
"stockMode": "on_demand",
"stock": null,
"availabilityStatus": "available",
"deliveryMode": "automatic"
}]
}price是当前 API Key 所属账户的最终单价,不是供货成本。availabilityStatus描述当前履约状态;下单前仍应检查目标数量。- 客户端应保存商品 ID,不要用商品名称作为稳定标识。
检查指定数量
curl "https://api.dingyue.app/shop/v1/products/prod_chatgpt_plus/availability?qty=2" \
-H "Authorization: Bearer dy_sk_your_api_key"{
"productId": "prod_chatgpt_plus",
"qty": 2,
"available": true,
"unitPrice": "21.99",
"currency": "USD",
"stockMode": "on_demand",
"availabilityStatus": "available",
"deliveryMode": "automatic",
"checkedAt": 1787600000
}单次数量范围为 1–1000。可购性是调用时刻的结果,不构成长期库存或 SLA 保证。
余额与等级价格
curl https://api.dingyue.app/shop/v1/balance \
-H "Authorization: Bearer dy_sk_your_api_key"{
"balance": "125.0000",
"reserved": "0.0000",
"available": "125.0000",
"currency": "USD",
"level": {
"level": 2,
"scoreMicrocents": 12500000000,
"score": "125.00",
"nextLevel": {
"level": 3,
"thresholdMicrocents": 50000000000,
"remainingMicrocents": 37500000000,
"remaining": "375.00"
}
}
}available 等于余额减去正在处理订单的预留金额;创建订单时以可用余额为准。
| 等级 | 等级分门槛 |
|---|---|
| Lv1 | $0 |
| Lv2 | $100 |
| Lv3 | $500 |
| Lv4 | $2,000 |
| Lv5 | $10,000 |
等级分随 API 账户正向充值累计,只升不降;购买商品和消耗 AI API 余额不会降级。每个商品都可以配置独立的 Lv1–Lv5 价格。
余额充值
任何 Key(每把 Key 都带 shop:read)都可以为自己所属的账户充值,余额与 AI API 共用。
- 调用
POST /topups,金额为美元十进制字符串,范围2.00–10000.00。 - 在浏览器打开返回的
paymentUrl,用 USDT/USDC 付款(1 USD = 1 USDT/USDC)。付款页约 60 分钟后过期。 - 轮询
GET /topups/:id,直到status变为credited(其他终态为failed、expired)。 - 调用
GET /balance查看新余额。到账金额同时计入等级分。
curl -X POST https://api.dingyue.app/shop/v1/topups \
-H "Authorization: Bearer dy_sk_your_api_key" \
-H "Content-Type: application/json" \
-d '{"amount": "25.00"}'{
"topup": {
"id": "atp_xxxxxxxxxxxx",
"status": "pending",
"amount": "25.00",
"currency": "USD",
"paymentUrl": "https://checkout.example.com/...",
"transfer": {
"address": "0x0000000000000000000000000000000000000000",
"amount": "25.00",
"currencies": ["USDT", "USDC"],
"chains": [
{
"chainId": 8453, "name": "Base", "currencies": ["USDC", "USDT"],
"tokens": [
{ "symbol": "USDC", "tokenAddress": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913", "decimals": 6, "amountMinor": "25000000" },
{ "symbol": "USDT", "tokenAddress": "0xfde4c96c8593536e31f229ea8f37b2ada2699bb2", "decimals": 6, "amountMinor": "25000000" }
]
},
{
"chainId": 56, "name": "BNB Smart Chain", "currencies": ["USDT"],
"tokens": [
{ "symbol": "USDT", "tokenAddress": "0x55d398326f99059ff775485246999027b3197955", "decimals": 18, "amountMinor": "25000000000000000000" }
]
}
],
"expiresAt": 1787600900
},
"expiresAt": 1787600900,
"createdAt": 1787600000,
"creditedAt": null
}
}curl https://api.dingyue.app/shop/v1/topups/atp_xxxxxxxxxxxx \
-H "Authorization: Bearer dy_sk_your_api_key"paymentUrl只在充值待付款且未过期时返回;到账、失败或过期后为null。- 每个账户每分钟最多创建 5 笔充值,同时计入账户级每分钟请求限额。未付款时请继续使用原 paymentUrl,不要反复创建新充值。
- 可能的错误:
400 invalid_input(金额无效)、404 not_found、409 account_not_linked、429 rate_limited、502 payment_unavailable、503 recharge_unavailable。
热钱包自动付款
响应中的 transfer 不为 null 时,可以不打开付款页,直接从自己的热钱包转账:
POST /topups,读取transfer.address、transfer.amount和transfer.chains。- 在
transfer.chains中选一条链,再在该链的tokens中选一种代币,从热钱包对其tokenAddress调用一次 ERC-20transfer(transfer.address, amountMinor)。 - 轮询
GET /topups/:id,直到status为credited。
amountMinor已按该代币在该链上的decimals换算成最小单位整数:同样 25.00 USD,在 Base(6 位小数)是"25000000",在 BNB Smart Chain(18 位小数)是"25000000000000000000"。不要把一条链的amountMinor用到另一条链或另一种代币上。- 某条链没有
tokens字段时,按该链currencies自行查该chainId上 USDT/USDC 的合约地址和 decimals,一笔转出恰好transfer.amount。 - 该地址只属于这一笔充值,不要用于其他充值,也不要重复转账。
- 一笔转出准确金额:少付超过 0.5% 不会入账;多付不会自动退回。
- 只能使用列出的链和代币,且必须是 ERC-20 转账。转错网络、转错代币或转链的原生币(ETH/BNB)都不会入账,需要联系客服人工处理。
- 不要在
transfer.expiresAt(创建后约 60 分钟)之后转账。 - Gas 由付款方承担。热钱包私钥只保存在你方,任何情况下都不要发给我们。
- 无法预备收款地址时
transfer为null;到账、失败或过期后也变为null,此时请使用paymentUrl。
创建订单
POST /orders 会从统一 API 余额扣款并返回兑换码。该请求可能产生真实费用。
| 字段 | 要求 | 说明 |
|---|---|---|
| externalOrderId | 必填,1–64 字符 | 你方唯一订单号,也是幂等键 |
| productId | 必填 | 来自 GET /products |
| qty | 必填,整数 1–1000 | 购买数量 |
| expectedUnitPrice | 推荐,最多两位小数 | 锁定用户单价,变化时拒绝下单 |
curl -X POST https://api.dingyue.app/shop/v1/orders \
-H "Authorization: Bearer dy_sk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"externalOrderId": "merchant-order-20260825-001",
"productId": "prod_chatgpt_plus",
"qty": 2,
"expectedUnitPrice": "21.99"
}'{
"orderId": "aord_xxxxxxxxxxxx",
"externalOrderId": "merchant-order-20260825-001",
"productId": "prod_chatgpt_plus",
"productName": "ChatGPT Plus",
"qty": 2,
"unitPrice": "21.99",
"amount": "43.98",
"currency": "USD",
"status": "delivered",
"codes": ["CODE-ONE", "CODE-TWO"],
"redeemUrl": "https://redeem.example.com",
"errorCode": null,
"createdAt": 1787600000,
"deliveredAt": 1787600002,
"reused": false
}reused=false表示本次首次处理;幂等重放成功时为 true。- 成功响应的 code 数量应与 qty 完全一致。
- 兑换网址可能调整,必须使用每笔响应中的 redeemUrl,不要在客户端硬编码。
prod_chatgpt_plus的码只能兑换到当前为 Free 的 ChatGPT 个人账号;已有 Plus、Go 等付费套餐的账号兑换会被拒绝(account_plan_not_free,码不会被占用)。已订阅账号请选用商品列表中的已订阅账号版本(如已开放)。
0.01 USD 真实扣款测试商品
测试商品 ID 为 prod_api_order_test。它不出现在 GET /products 和 GET /pricing 中,请直接使用此 ID。
- 正式环境真实扣款,所有等级均为 0.01 USD/件,不是免费沙箱。
- 只支持
qty=1,其他数量返回400 api_test_quantity。 - 返回的是测试码,不含任何实际权益,不能交付给最终用户;兑换站会拒绝测试码。
- 可用于验证余额扣款、发码、订单查询和 externalOrderId 幂等。重试规则与正式订单相同:复用原 externalOrderId 不会重复扣款,换新 ID 会再下一单并再次扣款。
- 下单前可调用
GET /products/prod_api_order_test/availability?qty=1检查可购性。
curl -X POST https://api.dingyue.app/shop/v1/orders \
-H "Authorization: Bearer dy_sk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"externalOrderId": "integration-test-001",
"productId": "prod_api_order_test",
"qty": 1,
"expectedUnitPrice": "0.01"
}'查询订单与恢复未知结果
按你方订单号查找
curl "https://api.dingyue.app/shop/v1/orders?externalOrderId=merchant-order-20260825-001&limit=1" \
-H "Authorization: Bearer dy_sk_your_api_key"列表支持 limit=1..100、不透明 cursor 和 externalOrderId 过滤(去掉首尾空格后 1–64 字符,否则返回 400 bad_request)。列表不返回兑换码;需要取码时调用订单详情。
查询详情
curl https://api.dingyue.app/shop/v1/orders/aord_xxxxxxxxxxxx \
-H "Authorization: Bearer dy_sk_your_api_key"当状态为 delivered 时,详情会返回 codes 和 redeemUrl。
订单状态
processing:供应结果仍可能在确认,保留余额预留;复用原请求重试。retryable:已确认可安全重试,预留已释放;仍复用原 externalOrderId。errorCode给出上次失败原因,取值为out_of_stock、supplier_price_changed或supplier_unavailable。delivered:已扣款并交付,可从详情恢复兑换码。failed / refunded:订单已关闭;如需重新购买,使用新的 externalOrderId。
完整 Node.js 示例
以下示例会读取实时商品价格并创建一笔真实余额订单。运行前请确认商品、数量和账户余额。
const BASE_URL = "https://api.dingyue.app/shop/v1";
const API_KEY = process.env.DINGYUE_API_KEY;
if (!API_KEY) throw new Error("Missing DINGYUE_API_KEY");
async function request(path, init = {}) {
const response = await fetch(BASE_URL + path, {
...init,
headers: {
Authorization: `Bearer ${API_KEY}`,
...(init.body ? { "Content-Type": "application/json" } : {}),
...init.headers,
},
});
const data = await response.json();
if (!response.ok) {
throw Object.assign(new Error(data.message || data.error), {
status: response.status,
code: data.error,
details: data.details,
});
}
return data;
}
const catalog = await request("/products");
const product = catalog.products[0];
if (!product) throw new Error("No API product is currently available");
// Persist this value with your own order before sending the request.
// If the response is lost, retry with the SAME value, productId and qty.
const externalOrderId = `merchant-${crypto.randomUUID()}`;
const order = await request("/orders", {
method: "POST",
body: JSON.stringify({
externalOrderId,
productId: product.id,
qty: 1,
expectedUnitPrice: product.price,
}),
});
for (const code of order.codes) {
console.log({ code, redeemUrl: order.redeemUrl });
}生产代码应先把 externalOrderId 与本地业务订单写入数据库,再发出 POST;不要只保存在进程内存中。
错误处理与安全重试
错误响应使用稳定的 error 码。业务逻辑应按错误码和 HTTP 状态分支,不要依赖可变的 message 文案。
{
"error": "price_changed",
"message": "Product price changed; refresh the quote and retry",
"details": {
"expectedUnitPrice": "21.99",
"actualUnitPrice": "22.49"
}
}| HTTP | 错误码 | 处理方式 |
|---|---|---|
| 400 | invalid_input | POST /orders 或 POST /topups 请求体无效(充值金额须在 2.00–10000.00 之间);修正请求后再调用,不要原样重试 |
| 400 | bad_request | 路径或查询参数无效(qty、limit、cursor、externalOrderId 过滤);修正后再调用 |
| 400 | api_test_quantity | 0.01 测试商品(prod_api_order_test)只能 qty=1 |
| 401 | unauthorized | API Key 缺失、无效、已撤销或账户不可用 |
| 402 | insufficient_balance | 可用余额不足;用 POST /topups 或个人中心充值后复用原 externalOrderId |
| 402 | key_daily_limit_exceeded | 该 Key 当日消费达到日上限;等 UTC 零点重置或在个人中心调高限额 |
| 403 | insufficient_scope | 当前 Key 没有接口所需权限;订单接口需要创建时勾选了「商品下单」的 Key,旧 Key 不能追加权限 |
| 403 | key_order_limit_exceeded | 订单金额超过该 Key 的单笔上限;减少数量或在个人中心调高限额 |
| 404 | not_found | 商品、订单或充值记录不存在,或不属于当前账户 |
| 409 | price_changed | 用户售价变化;重新读取商品,确认新价格后用新的 externalOrderId 和新的 expectedUnitPrice 下单 |
| 409 | idempotency_conflict | 同一 externalOrderId 被用于不同商品或数量 |
| 409 | out_of_stock | 当前数量暂不可履约(下单和可购性检查都可能返回);稍后重新检查 |
| 409 | supplier_price_changed | 供货成本超过保护线(下单和可购性检查都可能返回);等待平台更新报价 |
| 409 | account_not_linked | POST /topups:该 API 账户没有已注册的所有者;请登录个人中心充值 |
| 429 | rate_limited | 账户级每分钟请求数超限,与 AI 代理(/proxy/v1/*)共用同一计数;POST /topups 另限每分钟 5 次。按 Retry-After 头(同 details.retryAfterSeconds)等待后重试 |
| 500 | server_error | 服务内部错误;POST /orders 复用原 externalOrderId 退避重试,其他情况记录时间和请求参数后联系支持 |
| 502 | payment_unavailable | POST /topups:付款页创建失败;稍后重试 |
| 503 | order_in_progress | 已有请求正在处理;按 details.retryAfterSeconds 等待并复用原 externalOrderId |
| 503 | order_commit_pending | 供应结果正在确认;必须复用原 externalOrderId 重试 |
| 503 | supplier_unavailable | 供货服务暂不可用;POST 重试必须复用原 externalOrderId |
| 503 | supplier_not_configured | 平台尚未配置供货密钥;不要循环重试,联系平台处理 |
| 503 | ordering_paused | 平台已暂停新下单;不要循环重试。进行中的订单仍可复用原 externalOrderId 恢复 |
| 503 | recharge_unavailable | POST /topups:充值暂不可用;稍后重试 |
安全建议
- 不要把 API Key 或兑换码写入前端代码、公开仓库、分析事件或普通日志。
- externalOrderId 不要包含姓名、邮箱、手机号等个人信息。
- 仅向订单所属用户展示 codes,并把 code 与 redeemUrl 一起交付。
- 如果怀疑 API Key 泄露,请立即在 API Hub 撤销并重新生成。