最新公告
支付宝支付通道目前正在维护修复中,如需充值或下单请优先使用 USDT 支付,恢复正常后平台会第一时间通知。 客服不接受任何私下转账,请勿添加私人 TG 转账付款,谨防骗子冒充客服,所有操作请通过官方平台完成。 支付宝支付通道目前正在维护修复中,如需充值或下单请优先使用 USDT 支付,恢复正常后平台会第一时间通知。 客服不接受任何私下转账,请勿添加私人 TG 转账付款,谨防骗子冒充客服,所有操作请通过官方平台完成。

店铺共享 API 文档

通过 API 接口对接供货商商品,实现自动查询、库存同步、下单购买。

自动化交易

通过 API 自动完成商品查询、库存检查、下单购买

灵活定价

支持固定加价和百分比加价两种模式

库存同步

实时库存查询,支持缓存机制

安全认证

基于 MD5 签名的认证机制

API 凭证

你的凭证信息

app_id
登录用户后台 → 我的主页 → API 接口凭证
app_key
登录用户后台 → 我的主页 → API 接口凭证
妥善保管你的 app_key,不要泄露给他人。所有 API 请求都需要携带这两个参数。
基础 URL:https://你的域名/shared  |  传输方式:HTTP POST  |  数据格式:application/x-www-form-urlencoded

签名算法

所有 API 请求都需要携带 sign 参数,签名计算步骤如下:

  1. 将所有请求参数(包括 app_id 和 app_key)放入数组
  2. 移除 sign 字段(如果存在)
  3. 按照参数名字典序排序(ksort)
  4. 移除所有值为空字符串的参数
  5. 拼接成 URL 查询字符串,末尾追加 &key=你的app_key
  6. 对整个字符串进行 URL 解码
  7. 计算 MD5 值,即为签名
function generateSignature(array $data, string $appKey): string {
    unset($data['sign']);
    ksort($data);
    foreach ($data as $key => $val) {
        if ($val === '') unset($data[$key]);
    }
    return md5(urldecode(http_build_query($data) . "&key=" . $appKey));
}
import hashlib
from urllib.parse import urlencode, unquote

def generate_signature(data: dict, app_key: str) -> str:
    if 'sign' in data:
        del data['sign']
    sorted_data = {k: v for k, v in sorted(data.items()) if v != ''}
    sign_string = unquote(urlencode(sorted_data) + "&key=" + app_key)
    return hashlib.md5(sign_string.encode('utf-8')).hexdigest()
const crypto = require('crypto');

function generateSignature(data, appKey) {
    delete data.sign;
    const sortedKeys = Object.keys(data).sort();
    const params = [];
    sortedKeys.forEach(key => {
        if (data[key] !== '') {
            params.push(`${key}=${encodeURIComponent(data[key])}`);
        }
    });
    const signString = decodeURIComponent(params.join('&') + '&key=' + appKey);
    return crypto.createHash('md5').update(signString).digest('hex');
}

API 端点

POST /shared/authentication/connect

测试 API 连接是否正常,获取店铺名称和余额。

请求参数

参数类型必填说明
app_idstring必填商户 ID
app_keystring必填商户密钥
signstring必填签名

响应示例

{
    "code": 200,
    "msg": "success",
    "data": {
        "shopName": "示例店铺",
        "balance": 1000.50
    }
}
POST /shared/commodity/items

获取所有可供共享的商品列表(含分类结构)。

请求参数

参数类型必填说明
app_idstring必填商户 ID
app_keystring必填商户密钥
signstring必填签名

响应示例

{
    "code": 200,
    "data": [
        {
            "id": 1,
            "name": "游戏账号",
            "children": [
                {
                    "id": 101,
                    "code": "PROD001",
                    "name": "王者荣耀账号",
                    "description": "包含多个皮肤",
                    "price": 100.00,
                    "user_price": 95.00,
                    "factory_price": 90.00,
                    "cover": "https://your-domain.com/uploads/cover.jpg",
                    "delivery_way": 0,
                    "contact_type": 0,
                    "password_status": 0,
                    "sort": 0,
                    "config": "category[普通]=100.00\ncategory[高级]=200.00",
                    "widget": "[{\"cn\":\"区服\",\"name\":\"server\",\"type\":\"select\"}]",
                    "draft_status": 0,
                    "inventory_hidden": 0,
                    "only_user": 0,
                    "purchase_count": 0,
                    "minimum": 1,
                    "seckill_status": 0
                }
            ]
        }
    ]
}

字段说明

外层数组为分类列表,每个分类包含 id(分类ID)、name(分类名)和 children(商品数组)。

商品字段如下:

字段类型说明
idint商品 ID
codestring商品唯一标识码(购买时使用)
namestring商品名称
descriptionstring商品描述(可能包含 HTML)
pricefloat普通用户价格
user_pricefloat会员价格
factory_pricefloat成本价(你的拿货价)
coverstring商品封面图 URL
delivery_wayint发货方式:0=自动发货,1=手动发货
contact_typeint联系方式类型:0=可选,1=必填邮箱,2=必填手机
password_statusint是否需要查询密码:0=否,1=是
sortint排序权重
configstring商品配置(INI 格式,含种类和价格,详见下方说明)
widgetstring自定义表单字段(JSON 格式,详见下方说明)
draft_statusint是否支持预选:0=否,1=是
inventory_hiddenint是否隐藏库存:0=显示,1=隐藏
only_userint是否仅会员可购买:0=所有人,1=仅会员
purchase_countint限购数量(0=不限购)
minimumint最低购买数量(默认1)
seckill_statusint是否为秒杀商品:0=否,1=是

config 字段详解

config 字段使用 INI 格式描述商品种类及其对应价格:

category[普通]=100.00
category[高级]=200.00
category[豪华]=300.00

解析方法(PHP):

$config = parse_ini_string($item['config']);
// 结果:['category' => ['普通' => '100.00', '高级' => '200.00', '豪华' => '300.00']]
// 每个 key 为种类名称,value 为该种类价格
// 购买时通过 race 参数传递用户选择的种类名称

widget 字段详解

widget 字段是一个 JSON 数组,定义了商品的自定义表单字段:

[
    {"cn": "游戏区服", "name": "server", "type": "select"},
    {"cn": "充值账号", "name": "account", "type": "input"}
]
属性说明
cn字段显示名称(中文标签)
name字段参数名(购买时作为额外参数传递)
type字段类型:input=文本输入,select=下拉选择

购买时将用户填写的值作为额外参数传递:

// 如果 widget 包含 {"name": "server", "cn": "游戏区服"}
// 则购买时额外传递:
$params['server'] = '艾欧尼亚';
POST /shared/commodity/item

根据商品 CODE 查询单个商品详情。

请求参数

参数类型必填说明
app_idstring必填商户 ID
app_keystring必填商户密钥
sharedCodestring必填商品 CODE
signstring必填签名

响应格式同商品列表,仅返回匹配的商品。

POST /shared/commodity/inventory

查询指定商品的库存数量和详细信息。

请求参数

参数类型必填说明
app_idstring必填商户 ID
app_keystring必填商户密钥
sharedCodestring必填商品 CODE
racestring可选商品种类
signstring必填签名

响应示例

{
    "code": 200,
    "data": {
        "count": 150,
        "delivery_way": 0,
        "draft_status": 0,
        "price": 100.00,
        "user_price": 95.00,
        "factory_price": 90.00,
        "config": "category[普通]=100.00\ncategory[高级]=200.00",
        "is_category": true
    }
}

字段说明

字段类型说明
countint当前库存数量
delivery_wayint发货方式:0=自动发货,1=手动发货
draft_statusint是否支持预选:0=否,1=是
pricefloat普通用户价格
user_pricefloat会员价格
factory_pricefloat成本价(你的拿货价)
configstring种类价格配置(INI 格式)
is_categorybool是否有多个种类(true 时需让用户选择种类后传 race 参数)
注意:当接口返回 "商品不存在" 时,可能是商品已下架或暂时缺货,请联系客服确认商品状态。
POST /shared/commodity/inventoryState

检查指定数量的商品是否有足够库存。成功返回 200,库存不足返回 500。

请求参数

参数类型必填说明
app_idstring必填商户 ID
app_keystring必填商户密钥
shared_codestring必填商品 CODE
numint必填购买数量
card_idint可选预选的卡密 ID
racestring可选商品种类
signstring必填签名
POST /shared/commodity/trade

购买商品并获取卡密信息。金额从你的账户余额中扣除。

请求参数

参数类型必填说明
app_idstring必填商户 ID
app_keystring必填商户密钥
shared_codestring必填商品 CODE
numint必填购买数量
request_nostring必填请求唯一标识(防重复下单)
contactstring可选联系方式
card_idint可选预选的卡密 ID
deviceint可选设备类型
passwordstring可选商品密码
racestring可选商品种类
signstring必填签名
request_no 用于防止重复下单,建议使用时间戳 + 随机数生成。如果商品有自定义表单字段(widget),需要作为额外参数传递。

响应示例

{
    "code": 200,
    "data": {
        "trade_no": "20250117123456789",
        "secret": "账号:xxx\n密码:xxx",
        "widget": {
            "server": "艾欧尼亚"
        },
        "status": 1
    }
}

响应字段说明

字段类型说明
trade_nostring订单号(可用于查询订单状态)
secretstring卡密信息(发货内容,自动发货时立即返回)
widgetobject|null自定义表单数据(如有)
statusint订单状态:0=未支付,1=已支付
注意:手动发货的商品(delivery_way=1),status 为 1(已支付)但 secret 为空,需要等供货商手动发货后通过订单查询接口获取卡密。

代码示例

$data = [
    'app_id' => $appId,  'app_key' => $appKey,
    'shared_code' => 'PROD001',  'num' => 1,  'race' => '普通',
    'request_no' => mt_rand(100,999) . date("ymdHis") . mt_rand(100,999),
    'contact' => '',  'card_id' => 0,  'device' => 0,  'password' => ''
];
$data['sign'] = generateSignature($data, $appKey);

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $domain . '/shared/commodity/trade');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);

if ($result['code'] == 200) {
    echo "订单号:" . $result['data']['trade_no'];
    echo "卡密:" . $result['data']['secret'];
}
POST /shared/commodity/draftCard

获取支持预选的商品的可选卡密列表。

请求参数

参数类型必填说明
app_idstring必填商户 ID
app_keystring必填商户密钥
sharedCodestring必填商品 CODE
pageint必填页码
limitint必填每页数量
racestring可选商品种类
signstring必填签名

响应示例

{
    "code": 200,
    "data": {
        "data": [
            { "id": 1001, "draft": "预览:账号等级100级" },
            { "id": 1002, "draft": "预览:账号等级80级" }
        ],
        "total": 50
    }
}
POST /shared/commodity/query

根据订单号查询订单状态和卡密信息。下单后使用此接口获取购买结果。

请求参数

参数类型必填说明
app_idstring必填商户 ID
app_keystring必填商户密钥
tradeNostring必填订单号(购买接口返回的 trade_no)
signstring必填签名

响应示例

{
    "code": 200,
    "msg": "success",
    "data": {
        "status": 1,
        "delivery_status": 1,
        "secret": "账号:xxx\n密码:xxx",
        "widget": null
    }
}

响应字段说明

字段类型说明
statusint订单状态:0=未支付,1=已支付
delivery_statusint发货状态:0=未发货,1=已发货
secretstring卡密内容(已发货后返回)
widgetobject|null自定义表单数据(如有)
提示:建议在调用购买接口后,轮询此接口获取订单结果。手动发货的商品 status 可能不会立即变为 1。

PHP SDK

完整的 PHP 集成类,复制即用。

class SharedAPIClient {
    private $domain, $appId, $appKey;

    public function __construct($domain, $appId, $appKey) {
        $this->domain = rtrim($domain, '/');
        $this->appId = $appId;
        $this->appKey = $appKey;
    }

    private function generateSignature($data) {
        unset($data['sign']);
        ksort($data);
        foreach ($data as $key => $val) {
            if ($val === '') unset($data[$key]);
        }
        return md5(urldecode(http_build_query($data) . "&key=" . $this->appKey));
    }

    private function post($url, $params = []) {
        $data = array_merge($params, ['app_id' => $this->appId, 'app_key' => $this->appKey]);
        $data['sign'] = $this->generateSignature($data);

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $this->domain . $url);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

        $response = curl_exec($ch);
        $error = curl_error($ch);
        curl_close($ch);

        if ($error) throw new Exception("CURL Error: " . $error);
        $result = json_decode($response, true);
        if (!$result || $result['code'] != 200)
            throw new Exception($result['msg'] ?? 'Unknown error');
        return $result['data'] ?? [];
    }

    public function connect()      { return $this->post('/shared/authentication/connect'); }
    public function getItems()     { return $this->post('/shared/commodity/items'); }
    public function getItem($code) { return $this->post('/shared/commodity/item', ['sharedCode' => $code]); }

    public function getInventory($code, $race = '') {
        return $this->post('/shared/commodity/inventory', ['sharedCode' => $code, 'race' => $race]);
    }

    public function checkInventory($code, $num, $cardId = 0, $race = '') {
        return $this->post('/shared/commodity/inventoryState', [
            'shared_code' => $code, 'num' => $num, 'card_id' => $cardId, 'race' => $race
        ]);
    }

    public function purchase($code, $num, $options = []) {
        return $this->post('/shared/commodity/trade', array_merge([
            'shared_code' => $code, 'num' => $num,
            'request_no' => mt_rand(100,999) . date("ymdHis") . mt_rand(100,999),
            'contact' => '', 'card_id' => 0, 'device' => 0, 'password' => '', 'race' => ''
        ], $options));
    }

    public function getDraftCards($code, $page = 1, $limit = 20, $race = '') {
        return $this->post('/shared/commodity/draftCard', [
            'sharedCode' => $code, 'page' => $page, 'limit' => $limit, 'race' => $race
        ]);
    }
}

// 使用示例
$client = new SharedAPIClient('https://供货商域名', '你的app_id', '你的app_key');

$info = $client->connect();
echo "店铺:" . $info['shopName'] . "  余额:¥" . $info['balance'];

$order = $client->purchase('PROD001', 1, ['race' => '普通']);
echo "卡密:" . $order['secret'];

Python SDK

import requests, hashlib, time, random
from urllib.parse import urlencode, unquote

class SharedAPIClient:
    def __init__(self, domain, app_id, app_key):
        self.domain = domain.rstrip('/')
        self.app_id = app_id
        self.app_key = app_key

    def _sign(self, data):
        if 'sign' in data: del data['sign']
        sorted_data = {k: v for k, v in sorted(data.items()) if v != ''}
        return hashlib.md5(
            unquote(urlencode(sorted_data) + "&key=" + self.app_key).encode()
        ).hexdigest()

    def post(self, url, params=None):
        data = {**(params or {}), 'app_id': self.app_id, 'app_key': self.app_key}
        data['sign'] = self._sign(data)
        r = requests.post(f"{self.domain}{url}", data=data, timeout=30).json()
        if r.get('code') != 200: raise Exception(r.get('msg', 'Unknown error'))
        return r.get('data', {})

    def connect(self):    return self.post('/shared/authentication/connect')
    def get_items(self):  return self.post('/shared/commodity/items')

    def get_inventory(self, code, race=''):
        return self.post('/shared/commodity/inventory', {'sharedCode': code, 'race': race})

    def purchase(self, code, num, **opts):
        return self.post('/shared/commodity/trade', {
            'shared_code': code, 'num': num, 'contact': '', 'card_id': 0,
            'device': 0, 'password': '', 'race': '',
            'request_no': f"{random.randint(100,999)}{int(time.time())}{random.randint(100,999)}",
            **opts
        })

# 使用示例
client = SharedAPIClient('https://供货商域名', '你的app_id', '你的app_key')
info = client.connect()
print(f"店铺:{info['shopName']}  余额:¥{info['balance']}")

order = client.purchase('PROD001', 1, race='普通')
print(f"卡密:{order['secret']}")

常见问题

如何获取 app_id 和 app_key?
登录用户后台 → 我的主页 → API 接口凭证,即可看到你的 app_id 和 app_key。
签名验证总是失败怎么办?

排查步骤:

  1. 确认 app_key 没有多余的空格
  2. 确认参数按字典序排序(ksort)
  3. 确认空值参数已被移除
  4. 确认字符串做了 URL 解码(urldecode)后再 MD5
  5. 确认签名字段本身没有参与签名计算
如何设置加价?

固定金额加价:最终价 = 原价 + 加价金额(例:原价 100 + 加价 10 = 110 元)

百分比加价:最终价 = 原价 × (1 + 百分比)(例:原价 100 × 1.2 = 120 元)

如何处理自定义表单(widget)?

从商品信息的 widget 字段解析出表单配置(JSON),购买时将用户填写的值作为额外参数传递。

// 如果 widget 包含 {"name": "server", "cn": "游戏区服"}
// 则购买时额外传递:
$params['server'] = '艾欧尼亚';
商品有多个种类怎么处理?

商品种类在 config 字段中(INI 格式):

category[普通]=100.00
category[高级]=200.00
category[豪华]=300.00

购买时通过 race 参数传递用户选择的种类名称。

API 有频率限制吗?

没有硬性限制,建议:查询接口每秒不超过 10 次,购买接口使用 request_no 防重复,库存查询建议开启缓存。

如何测试 API 是否正常?

使用 curl 快速测试:

curl -X POST "https://你的域名/shared/authentication/connect" \
  -d "app_id=你的ID" \
  -d "app_key=你的KEY" \
  -d "sign=计算得到的签名"

预期返回:{"code":200,"msg":"success","data":{"shopName":"xxx","balance":xxx}}

商品列表返回为空?

可能原因:

  1. 供货商没有开启 API 共享的商品(需供货商在后台开启商品的 API 共享状态)
  2. 商品已下架(status 不是上架状态)
  3. 商品所属分类被禁用
  4. app_id 或 app_key 不正确,连接的不是目标店铺

建议先调用 /shared/authentication/connect 确认连接正常,再排查商品设置。

购买时提示"库存不足"?

可能原因:

  1. 自动发货商品:供货商的卡密已售完,需要供货商补充库存
  2. 种类选择错误:如果商品有多个种类(config 中有 category),确保 race 参数传递了正确的种类名称
  3. 库存同步延迟:如果开启了库存缓存,可能存在短暂的缓存延迟(默认 300 秒刷新)

建议先调用 /shared/commodity/inventoryState 检查库存状态后再下单。

购买失败但余额被扣除了?

这种情况通常不会发生,系统使用 request_no 防止重复下单。如果遇到:

  1. 先通过 /shared/commodity/query 查询订单状态,确认订单是否实际已完成
  2. 手动发货的商品(delivery_way=1)可能处于"已支付待处理"状态,等待供货商发货即可
  3. 如确认异常,请联系供货商处理退款
如何实现库存同步?

系统提供两种库存同步方式:

方式一 — 实时查询:每次用户访问商品页面时调用 /shared/commodity/inventory 获取最新库存。优点是数据准确,缺点是请求频繁。

方式二 — 定时缓存:开启库存同步后,系统自动每 300 秒刷新一次库存缓存。适合访问量大的场景,减少对供货商的请求压力。

建议:小流量用实时查询,大流量用定时缓存。

错误码

HTTP 状态码始终为 200,错误通过 JSON 响应体中的 code 字段区分。
code说明
200请求成功
0请求失败(具体原因见 msg 字段)

常见错误信息

msg说明
商户ID不存在app_id 不正确
密钥错误签名验证失败,检查签名算法
库存不足商品库存不够
The request ID already existsrequest_no 重复,换一个唯一标识
商品不存在shared_code 不正确或商品已下架
 安全提示:妥善保管你的 app_key,不要在前端代码中暴露。建议使用 HTTPS 传输,所有 API 调用应在服务端完成。