Skip to content

站点对接 Open API 文档

更新时间:2026-04-01

本文档用于对接开发,覆盖 Dujiao-Next 站点间串货接口:/api/v1/upstream/*


1. 基础信息

1.1 Base URL

https://<B站域名>/api/v1/upstream

示例:https://api.example.com/api/v1/upstream

1.2 统一响应结构

成功响应:

json
{
  "ok": true,
  ...
}

失败响应:

json
{
  "ok": false,
  "error_code": "error_code_here",
  "error_message": "human readable message"
}

1.3 当前实现约束(重要)

  • 支付成功后的采购为异步执行,不阻断终端用户支付流程。
  • 对接商品(存在启用上游映射)必须等待上游回调,不允许本地手工/自动交付。
  • 本地取消前必须先执行上游取消;若上游返回不可取消,本地必须拒绝取消与退款。
  • 上游采购失败时,调用方应将订单标记为异常待处理,不应自动做本地取消/退款关闭。

2. 鉴权与签名

2.1 必需请求头

Header说明
Dujiao-Next-Api-KeyAPI Key(由 B 站用户在前台"API 权限"中生成)
Dujiao-Next-TimestampUnix 秒级时间戳
Dujiao-Next-SignatureHMAC-SHA256 签名(hex 小写)

2.2 签名算法

签名串(signString)按如下格式拼接:

{METHOD}\n{PATH}\n{TIMESTAMP}\n{BODY_MD5}

各部分说明:

占位符说明
{METHOD}HTTP 方法大写,如 GETPOST
{PATH}请求路径(不含域名和查询参数),如 /api/v1/upstream/products
{TIMESTAMP}与请求头 Dujiao-Next-Timestamp 一致
{BODY_MD5}请求体的 MD5 哈希(hex 小写)。无 body 时为空字符串的 MD5:d41d8cd98f00b204e9800998ecf8427e

最终签名:

signature = hex_lower(hmac_sha256(signString, api_secret))

2.3 签名伪代码示例

python
import hashlib, hmac, time

api_key = "your_api_key"
api_secret = "your_api_secret"
method = "POST"
path = "/api/v1/upstream/orders"
body = b'{"sku_id":1,"quantity":1}'
timestamp = str(int(time.time()))

body_md5 = hashlib.md5(body).hexdigest()
sign_string = f"{method}\n{path}\n{timestamp}\n{body_md5}"
signature = hmac.new(
    api_secret.encode(), sign_string.encode(), hashlib.sha256
).hexdigest()

headers = {
    "Dujiao-Next-Api-Key": api_key,
    "Dujiao-Next-Timestamp": timestamp,
    "Dujiao-Next-Signature": signature,
    "Content-Type": "application/json",
}

2.4 签名校验规则

  • 时间戳允许偏差 ±60 秒
  • API Key 必须处于"已批准且启用"状态。
  • 所属用户账号必须处于正常状态。

3. 接口清单

方法路径说明
POST/ping连通性检查
GET/categories拉取分类列表
GET/products拉取商品列表
GET/products/:id拉取商品详情
POST/orders创建采购单(钱包自动扣款)
GET/orders/:id查询采购单
POST/orders/:id/cancel取消采购单

回调接口(B 站 → A 站):

方法路径说明
POST/upstream/callbackB 站向 A 站推送订单状态变更与交付内容

4. 核心接口说明

4.1 POST /ping

连通性检查,同时返回对接账户的钱包余额、币种与会员等级。

请求: 无需请求体。

响应字段:

字段类型说明
okboolean是否连通
site_namestring站点名称
protocol_versionstring协议版本,当前为 "1.0"
user_idnumber对接用户 ID
balancestring钱包可用余额
currencystring站点币种(如 CNYUSD
member_levelobject|null用户会员等级信息(无会员等级时为 null

member_level 子对象:

字段类型说明
idnumber会员等级 ID
nameobject多语言等级名称,如 {"zh-CN":"黄金会员","en":"Gold"}
slugstring等级标识
iconstring等级图标 URL

响应示例:

json
{
  "ok": true,
  "site_name": "My Shop",
  "protocol_version": "1.0",
  "user_id": 42,
  "balance": "1000.00",
  "currency": "CNY",
  "member_level": {
    "id": 1,
    "name": { "zh-CN": "黄金会员", "en": "Gold" },
    "slug": "gold",
    "icon": "https://example.com/gold.png"
  }
}

4.2 GET /categories

拉取上游分类列表。

请求: 无需请求体和查询参数。

响应字段:

字段类型说明
okboolean是否成功
categoriesarray分类数组(见下方 Category 结构)

Category 结构

字段类型说明
idnumber分类 ID
parent_idnumber父分类 ID(0 表示一级分类)
slugstring分类唯一标识
nameobject多语言名称,如 {"zh-CN":"游戏充值","en":"Game Top-up"}
iconstring分类图标 URL
sort_ordernumber排序权重(降序)

响应示例:

json
{
  "ok": true,
  "categories": [
    {
      "id": 1,
      "parent_id": 0,
      "slug": "game-topup",
      "name": { "zh-CN": "游戏充值", "en": "Game Top-up" },
      "icon": "",
      "sort_order": 10
    },
    {
      "id": 2,
      "parent_id": 1,
      "slug": "steam",
      "name": { "zh-CN": "Steam", "en": "Steam" },
      "icon": "",
      "sort_order": 5
    }
  ]
}

分类层级

分类最多支持两层:一级分类(parent_id = 0)和二级分类(parent_id 指向一级分类)。商品只能关联到末级分类。


4.3 GET /products

拉取上游商品列表(仅返回已上架商品)。

Query 参数:

参数类型必填默认说明
pagenumber1页码
page_sizenumber20每页条数(1~100)

响应字段:

字段类型说明
okboolean是否成功
itemsarray商品数组(见下方 Product 结构)
totalnumber总数
pagenumber当前页码
page_sizenumber每页条数

Product 结构

字段类型说明
idnumber商品 ID(下单时通过 SKU ID 引用)
slugstringURL Slug
titleobject多语言标题,如 {"zh-CN":"标题","en":"Title"}
descriptionobject多语言描述
contentobject多语言详情内容
seo_metaobjectSEO 元信息
imagesstring[]图片 URL 列表
tagsstring[]标签列表
price_amountstring商品实际售价(若有会员价则为会员价)
original_pricestring原价(仅当存在会员折扣时返回,omitempty
member_pricestring会员价(仅当存在会员折扣时返回,omitempty
fulfillment_typestring交付类型:auto(自动发货)/ manual(人工交付)
manual_form_schemaobject人工交付表单结构(manual 类型商品需要买家填写的表单)
is_activeboolean是否上架
category_idnumber分类 ID
skusarraySKU 列表(见下方 SKU 结构)
created_atstring创建时间(ISO 8601)
updated_atstring更新时间(ISO 8601)

SKU 结构

字段类型说明
idnumberSKU ID(下单时使用此 ID
sku_codestringSKU 编码
spec_valuesobject规格值,如 {"颜色":"红色","容量":"128GB"}
price_amountstringSKU 实际售价(若有会员价则为会员价)
original_pricestring原价(仅当存在会员折扣时返回,omitempty
member_pricestring会员价(仅当存在会员折扣时返回,omitempty
stock_statusstring库存状态:unlimited / in_stock / low_stock / out_of_stock
stock_quantitynumber库存数量(-1 = 无限)
is_activeboolean是否启用

库存状态说明

  • unlimited:无限库存(stock_quantity = -1
  • in_stock:充足(> 20
  • low_stock:低库存(1 ~ 20
  • out_of_stock:售罄(0

响应示例:

json
{
  "ok": true,
  "items": [
    {
      "id": 1,
      "slug": "example-product",
      "title": { "zh-CN": "示例商品", "en": "Example Product" },
      "description": { "zh-CN": "这是一个示例" },
      "content": {},
      "seo_meta": {},
      "images": ["https://example.com/img1.jpg"],
      "tags": ["hot"],
      "price_amount": "7.90",
      "original_price": "9.90",
      "member_price": "7.90",
      "fulfillment_type": "auto",
      "manual_form_schema": null,
      "is_active": true,
      "category_id": 1,
      "skus": [
        {
          "id": 1,
          "sku_code": "DEFAULT",
          "spec_values": {},
          "price_amount": "7.90",
          "original_price": "9.90",
          "member_price": "7.90",
          "stock_status": "in_stock",
          "stock_quantity": 100,
          "is_active": true
        }
      ],
      "created_at": "2026-03-01T12:00:00Z",
      "updated_at": "2026-03-01T12:00:00Z"
    }
  ],
  "total": 1,
  "page": 1,
  "page_size": 20
}

4.4 GET /products/:id

按商品 ID 查询详情。

路径参数:

参数类型说明
idnumber商品 ID

响应字段:

字段类型说明
okboolean是否成功
productobjectProduct 结构(同 4.3 节)

错误码:

error_codeHTTP 状态码说明
product_not_found404商品不存在
product_unavailable404商品已下架

4.5 POST /orders

创建采购单。系统会自动使用对接用户的钱包余额支付,无需额外支付流程。

请求体:

字段类型必填说明
sku_idnumberSKU ID(来自商品列表中的 skus[].id
quantitynumber购买数量,必须 ≥ 1
manual_form_dataobject人工交付表单数据(当商品为 manual 类型时必填)
downstream_order_nostring下游订单号(建议全局唯一,用于幂等和回调匹配)
trace_idstring调用方追踪 ID
callback_urlstring回调通知地址(必须为公网可访问的 HTTP/HTTPS 地址)

幂等性

同一个 API Key + 同一个 downstream_order_no 不允许重复创建,重复提交会返回已有订单的信息。

成功响应(ok: true):

字段类型说明
okbooleantrue
order_idnumber上游订单 ID
order_nostring上游订单号
statusstring订单状态(通常为 paid
amountstring订单金额
currencystring币种

成功响应示例:

json
{
  "ok": true,
  "order_id": 101,
  "order_no": "DJ20260301120000ABCD",
  "status": "paid",
  "amount": "9.90",
  "currency": "CNY"
}

业务失败(HTTP 200,ok: false):

json
{
  "ok": false,
  "order_id": 101,
  "order_no": "DJ20260301120000ABCD",
  "status": "canceled",
  "error_code": "payment_failed",
  "error_message": "wallet payment failed: insufficient balance"
}

说明:当钱包余额不足或支付失败时,系统自动取消订单并返回 ok: false

错误码汇总:

error_codeHTTP 状态码说明
bad_request400请求参数无效
invalid_callback_url400回调地址非法(内网/localhost 等)
sku_unavailable400SKU 不存在或已下架
product_unavailable400商品不可用
insufficient_balance402钱包余额不足
insufficient_stock409库存不足
payment_failed200钱包支付失败(ok: false

4.6 GET /orders/:id

查询采购单详情。

路径参数:

参数类型说明
idnumber订单 ID(来自创建订单时返回的 order_id

响应字段:

字段类型说明
okboolean是否成功
order_idnumber订单 ID
order_nostring订单号
statusstring订单状态
amountstring订单金额
currencystring币种
itemsarray订单商品列表(见下表)
fulfillmentobject交付信息(已交付时才有,见下表)

items[] 字段:

字段类型说明
product_idnumber商品 ID
sku_idnumberSKU ID
titleobject多语言标题
quantitynumber数量
unit_pricestring单价
total_pricestring小计
fulfillment_typestring交付类型

fulfillment 字段(仅 status 为 delivered 时出现):

字段类型说明
typestring交付类型(auto / manual
statusstring交付状态(delivered
payloadstring交付内容(卡密等纯文本)
delivery_dataobject结构化交付数据
delivered_atstring交付时间(ISO 8601)

响应示例:

json
{
  "ok": true,
  "order_id": 101,
  "order_no": "DJ20260301120000ABCD",
  "status": "completed",
  "amount": "9.90",
  "currency": "CNY",
  "items": [
    {
      "product_id": 1,
      "sku_id": 1,
      "title": { "zh-CN": "示例商品" },
      "quantity": 1,
      "unit_price": "9.90",
      "total_price": "9.90",
      "fulfillment_type": "auto"
    }
  ],
  "fulfillment": {
    "type": "auto",
    "status": "delivered",
    "payload": "ABCD-EFGH-1234-5678",
    "delivery_data": null,
    "delivered_at": "2026-03-01T12:01:00Z"
  }
}

4.7 POST /orders/:id/cancel

取消采购单。

路径参数:

参数类型说明
idnumber订单 ID

请求: 无需请求体。

成功响应:

字段类型说明
okbooleantrue
order_idnumber订单 ID
order_nostring订单号
statusstring取消后的状态(canceled

错误码:

error_codeHTTP 状态码说明
order_not_found404订单不存在
cancel_not_allowed409当前状态不可取消(已支付/已完成/已交付等)

5. 回调接口

5.1 POST /api/v1/upstream/callback

B 站主动向 A 站推送订单状态变更与交付内容。

回调触发时机:

  • 订单状态变更(已支付 → 已完成、已取消等)
  • 交付内容生成(自动发卡、手动交付完成)

回调鉴权方式:

B 站使用 A 站的对接连接中配置的 api_key / api_secret 进行签名,A 站用同样的算法验证。

请求头:

Header说明
Dujiao-Next-Api-KeyA 站在"对接连接"中设置的 API Key
Dujiao-Next-TimestampUnix 秒级时间戳
Dujiao-Next-SignatureHMAC-SHA256 签名

请求体:

字段类型必填说明
eventstring事件类型
order_idnumberB 站订单 ID
order_nostringB 站订单号
downstream_order_nostringA 站本地订单号(用于匹配采购单)
statusstring订单状态
fulfillmentobject交付信息(见下表)
timestampnumber事件时间戳

fulfillment 子对象:

字段类型说明
typestring交付类型(auto / manual
statusstring交付状态(delivered
payloadstring交付内容(卡密、文本等)
delivery_dataobject结构化交付数据
delivered_atstring交付时间(ISO 8601)

回调状态映射:

上游 statusA 站处理为
delivered / completeddelivered(触发本地交付)
canceledcanceled(触发本地取消)
其他透传原值

回调成功响应:

json
{ "ok": true, "message": "received" }

回调失败响应:

json
{ "ok": false, "message": "reason" }

6. 订单状态说明

状态说明
pending_payment待支付
paid已支付(等待交付)
fulfilling履约中(正在处理交付)
partially_delivered部分交付
delivered已交付
completed已完成
canceled已取消

7. 错误码汇总

7.1 鉴权错误(HTTP 401 / 403)

error_code说明
missing_auth_headers缺少签名请求头
invalid_timestamp时间戳格式错误
timestamp_expired时间戳过期(超过 ±60 秒)
invalid_signature签名验证失败
invalid_api_keyAPI Key 无效或已禁用
user_disabledAPI Key 所属用户已被封禁

7.2 业务错误

error_codeHTTP 状态码说明
bad_request400请求参数无效
invalid_callback_url400回调地址非法
sku_unavailable400SKU 不存在或不可用
product_unavailable400商品不存在或不可用
product_not_found404商品不存在
order_not_found404订单不存在
insufficient_balance402钱包余额不足
insufficient_stock409库存不足
cancel_not_allowed409订单不可取消
payment_failed200钱包支付失败(ok: false
internal_error500服务端内部错误

8. 对接流程与最佳实践

8.1 标准对接流程

1. B 站用户开通 API 权限,生成 API Key / Secret
2. A 站管理员创建"对接连接",填写 B 站地址与凭证
3. A 站调用 POST /ping 测试连通性
4. A 站调用 GET /products 拉取商品列表
5. A 站在管理后台建立商品映射(本地商品 → 上游商品/SKU)
6. 终端用户在 A 站下单支付 → A 站异步调用 POST /orders 向 B 站采购
7. B 站处理订单并交付 → 通过回调通知 A 站
8. A 站根据回调更新本地订单状态

8.2 幂等与重试

  • downstream_order_no 作为幂等键:相同 API Key + 相同 downstream_order_no 只会创建一次订单。
  • 建议对所有写操作实现指数退避重试(如 1s → 2s → 4s)。
  • 查询接口可轮询但应控制频率。

8.3 回调可靠性

  • B 站在创建订单时传入 callback_url,交付/状态变更时 B 站会主动推送。
  • 建议 A 站同时实现轮询兜底:定期调用 GET /orders/:id 检查订单状态。
  • 回调可能因网络问题失败,A 站应做好幂等处理。

8.4 安全要求

  • callback_url 不允许指向内网地址(localhost、127.0.0.1、私有 IP 段等)。
  • 签名使用 HMAC-SHA256,Secret 仅在创建时展示一次,请妥善保管。
  • 建议所有通信使用 HTTPS。

9. 签名验证示例

Go

go
package main

import (
    "crypto/hmac"
    "crypto/md5"
    "crypto/sha256"
    "encoding/hex"
    "fmt"
    "time"
)

func sign(secret, method, path string, timestamp int64, body []byte) string {
    h := md5.New()
    h.Write(body)
    bodyMD5 := hex.EncodeToString(h.Sum(nil))
    signString := fmt.Sprintf("%s\n%s\n%d\n%s", method, path, timestamp, bodyMD5)
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write([]byte(signString))
    return hex.EncodeToString(mac.Sum(nil))
}

func main() {
    secret := "your_api_secret"
    method := "POST"
    path := "/api/v1/upstream/orders"
    body := []byte(`{"sku_id":1,"quantity":1}`)
    ts := time.Now().Unix()

    sig := sign(secret, method, path, ts, body)
    fmt.Println("Signature:", sig)
    fmt.Println("Timestamp:", ts)
}

Python

python
import hashlib, hmac, time, json, requests

api_key = "your_api_key"
api_secret = "your_api_secret"
base_url = "https://api.example.com"

def sign(secret: str, method: str, path: str, timestamp: int, body: bytes) -> str:
    body_md5 = hashlib.md5(body).hexdigest()
    sign_string = f"{method}\n{path}\n{timestamp}\n{body_md5}"
    return hmac.new(
        secret.encode(), sign_string.encode(), hashlib.sha256
    ).hexdigest()

def api_request(method: str, path: str, body: dict = None):
    ts = int(time.time())
    body_bytes = json.dumps(body).encode() if body else b""
    sig = sign(api_secret, method, path, ts, body_bytes)
    headers = {
        "Dujiao-Next-Api-Key": api_key,
        "Dujiao-Next-Timestamp": str(ts),
        "Dujiao-Next-Signature": sig,
        "Content-Type": "application/json",
    }
    resp = requests.request(method, base_url + path, headers=headers, data=body_bytes)
    return resp.json()

# 测试连通
print(api_request("POST", "/api/v1/upstream/ping"))

# 拉取商品
print(api_request("GET", "/api/v1/upstream/products"))

# 创建订单
print(api_request("POST", "/api/v1/upstream/orders", {
    "sku_id": 1,
    "quantity": 1,
    "downstream_order_no": "A-20260301-001",
    "callback_url": "https://a-site.example.com/api/v1/upstream/callback",
}))

PHP

php
<?php
function dujiaoSign(string $secret, string $method, string $path, int $timestamp, string $body): string {
    $bodyMD5 = md5($body);
    $signString = "{$method}\n{$path}\n{$timestamp}\n{$bodyMD5}";
    return hash_hmac('sha256', $signString, $secret);
}

$apiKey = 'your_api_key';
$apiSecret = 'your_api_secret';
$baseUrl = 'https://api.example.com';

$method = 'POST';
$path = '/api/v1/upstream/orders';
$body = json_encode(['sku_id' => 1, 'quantity' => 1, 'downstream_order_no' => 'A-001']);
$timestamp = time();
$signature = dujiaoSign($apiSecret, $method, $path, $timestamp, $body);

$ch = curl_init($baseUrl . $path);
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => $body,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Content-Type: application/json',
        "Dujiao-Next-Api-Key: {$apiKey}",
        "Dujiao-Next-Timestamp: {$timestamp}",
        "Dujiao-Next-Signature: {$signature}",
    ],
]);
$response = curl_exec($ch);
curl_close($ch);
echo $response;

Released under the MIT License.