REST API · v1
商品下单 API 接入文档
使用专用的 dy_sk_ 商品下单 Key 和统一预付余额,按当前用户等级价查询商品、检查数量并购买兑换码。 本文给出可直接使用的请求、响应、幂等恢复和错误处理规则。
Base URL
https://api.dingyue.app/shop/v1Authentication
Bearer dy_sk_...Machine readable
快速开始
- 登录 API Hub,充值余额并显式选择“商品下单 Key”。密钥只会完整显示一次。
- 服务端调用
GET /products,取得商品 ID 和当前等级价。 - 为自己的业务订单持久化一个唯一
externalOrderId。 - 调用
POST /orders;请求结果不确定时复用同一参数重试。 - 把每个
code和响应中的redeemUrl一起交付给最终用户。
最重要的规则:发送下单请求前先保存 externalOrderId。任何超时、断网或 5xx 重试都必须使用相同 externalOrderId、productId 和 qty,不能生成新值。
鉴权
除 OpenAPI 和 llms.txt 外,所有接口都需要:
Authorization: Bearer dy_sk_your_api_keyshop:read 可读取商品与余额;下单、订单列表和订单详情都需要 shop:order。普通 AI Key 无法访问订单数据。
商品下单 Key 可直接消耗账户余额,只能保存在后端服务、服务器密钥或本地环境变量中。
接口一览
| 方法 | 路径 | 所需权限 | 用途 |
|---|---|---|---|
| GET | /products | shop:read | 查询已开放商品、当前等级价与履约状态 |
| GET | /products/:id/availability?qty=N | shop:read | 实时检查指定数量是否可购 |
| GET | /balance | shop:read | 查询统一 API 余额、预留金额与等级 |
| 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 价格。
创建订单
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,不要在客户端硬编码。
查询订单与恢复未知结果
按你方订单号查找
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。列表不返回兑换码;需要取码时调用订单详情。
查询详情
curl https://api.dingyue.app/shop/v1/orders/aord_xxxxxxxxxxxx \
-H "Authorization: Bearer dy_sk_your_api_key"当状态为 delivered 时,详情会返回 codes 和 redeemUrl。
订单状态
processing:供应结果仍可能在确认,保留余额预留;复用原请求重试。retryable:已确认可安全重试;仍复用原 externalOrderId。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 / bad_request | 请求字段、数量或游标无效;修正请求后再调用 |
| 401 | unauthorized | API Key 缺失、无效、已撤销或账户不可用 |
| 403 | insufficient_scope | 当前 Key 没有接口所需权限;创建专用商品下单 Key |
| 403 | key_order_limit_exceeded | 订单金额超过该 Key 的单笔上限;减少数量或在个人中心调高限额 |
| 402 | insufficient_balance | 可用余额不足;充值后复用原 externalOrderId |
| 402 | key_daily_limit_exceeded | 该 Key 当日消费达到日上限;等 UTC 零点重置或在个人中心调高限额 |
| 404 | not_found | 商品或订单不存在,或不属于当前账户 |
| 409 | price_changed | 用户售价变化;重新读取商品并确认新价格 |
| 409 | idempotency_conflict | 同一 externalOrderId 被用于不同商品或数量 |
| 409 | out_of_stock | 当前数量暂不可履约;稍后重新检查可购性 |
| 409 | supplier_price_changed | 供货成本超过保护线,等待平台更新报价 |
| 429 | rate_limited | 账户级请求过快;指数退避后重试 |
| 503 | supplier_unavailable | 供货服务暂不可用;POST 重试必须复用原 externalOrderId |
| 503 | supplier_not_configured | 平台尚未配置供货密钥;不要循环重试,联系平台处理 |
| 503 | order_in_progress | 已有请求正在处理;按 details.retryAfterSeconds 等待并复用原 externalOrderId |
| 503 | order_commit_pending | 供应结果正在确认;必须复用原 externalOrderId 重试 |
| 500 | server_error | 服务内部错误;记录时间和请求参数后联系支持 |
对 POST /orders 而言,即使无法判断供应端是否已经扣款,也可以安全地用原 externalOrderId 和相同参数重试。平台会恢复同一订单,不会创建第二笔采购。
安全建议
- 不要把 API Key 或兑换码写入前端代码、公开仓库、分析事件或普通日志。
- externalOrderId 不要包含姓名、邮箱、手机号等个人信息。
- 仅向订单所属用户展示 codes,并把 code 与 redeemUrl 一起交付。
- 如果怀疑 API Key 泄露,请立即在 API Hub 撤销并重新生成。