TGX Account
通过 API 接口对接供货商商品,实现自动查询、库存同步、下单购买。
通过 API 自动完成商品查询、库存检查、下单购买
支持固定加价和百分比加价两种模式
实时库存查询,支持缓存机制
基于 MD5 签名的认证机制
https://你的域名/shared | 传输方式:HTTP POST | 数据格式:application/x-www-form-urlencoded
所有 API 请求都需要携带 sign 参数,签名计算步骤如下:
sign 字段(如果存在)&key=你的app_keyfunction 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 连接是否正常,获取店铺名称和余额。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| app_id | string | 必填 | 商户 ID |
| app_key | string | 必填 | 商户密钥 |
| sign | string | 必填 | 签名 |
{
"code": 200,
"msg": "success",
"data": {
"shopName": "示例店铺",
"balance": 1000.50
}
}
获取所有可供共享的商品列表(含分类结构)。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| app_id | string | 必填 | 商户 ID |
| app_key | string | 必填 | 商户密钥 |
| sign | string | 必填 | 签名 |
{
"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(商品数组)。
商品字段如下:
| 字段 | 类型 | 说明 |
|---|---|---|
| id | int | 商品 ID |
| code | string | 商品唯一标识码(购买时使用) |
| name | string | 商品名称 |
| description | string | 商品描述(可能包含 HTML) |
| price | float | 普通用户价格 |
| user_price | float | 会员价格 |
| factory_price | float | 成本价(你的拿货价) |
| cover | string | 商品封面图 URL |
| delivery_way | int | 发货方式:0=自动发货,1=手动发货 |
| contact_type | int | 联系方式类型:0=可选,1=必填邮箱,2=必填手机 |
| password_status | int | 是否需要查询密码:0=否,1=是 |
| sort | int | 排序权重 |
| config | string | 商品配置(INI 格式,含种类和价格,详见下方说明) |
| widget | string | 自定义表单字段(JSON 格式,详见下方说明) |
| draft_status | int | 是否支持预选:0=否,1=是 |
| inventory_hidden | int | 是否隐藏库存:0=显示,1=隐藏 |
| only_user | int | 是否仅会员可购买:0=所有人,1=仅会员 |
| purchase_count | int | 限购数量(0=不限购) |
| minimum | int | 最低购买数量(默认1) |
| seckill_status | int | 是否为秒杀商品:0=否,1=是 |
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 字段是一个 JSON 数组,定义了商品的自定义表单字段:
[
{"cn": "游戏区服", "name": "server", "type": "select"},
{"cn": "充值账号", "name": "account", "type": "input"}
]
| 属性 | 说明 |
|---|---|
| cn | 字段显示名称(中文标签) |
| name | 字段参数名(购买时作为额外参数传递) |
| type | 字段类型:input=文本输入,select=下拉选择 |
购买时将用户填写的值作为额外参数传递:
// 如果 widget 包含 {"name": "server", "cn": "游戏区服"}
// 则购买时额外传递:
$params['server'] = '艾欧尼亚';
根据商品 CODE 查询单个商品详情。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| app_id | string | 必填 | 商户 ID |
| app_key | string | 必填 | 商户密钥 |
| sharedCode | string | 必填 | 商品 CODE |
| sign | string | 必填 | 签名 |
响应格式同商品列表,仅返回匹配的商品。
查询指定商品的库存数量和详细信息。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| app_id | string | 必填 | 商户 ID |
| app_key | string | 必填 | 商户密钥 |
| sharedCode | string | 必填 | 商品 CODE |
| race | string | 可选 | 商品种类 |
| sign | string | 必填 | 签名 |
{
"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
}
}
| 字段 | 类型 | 说明 |
|---|---|---|
| count | int | 当前库存数量 |
| delivery_way | int | 发货方式:0=自动发货,1=手动发货 |
| draft_status | int | 是否支持预选:0=否,1=是 |
| price | float | 普通用户价格 |
| user_price | float | 会员价格 |
| factory_price | float | 成本价(你的拿货价) |
| config | string | 种类价格配置(INI 格式) |
| is_category | bool | 是否有多个种类(true 时需让用户选择种类后传 race 参数) |
检查指定数量的商品是否有足够库存。成功返回 200,库存不足返回 500。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| app_id | string | 必填 | 商户 ID |
| app_key | string | 必填 | 商户密钥 |
| shared_code | string | 必填 | 商品 CODE |
| num | int | 必填 | 购买数量 |
| card_id | int | 可选 | 预选的卡密 ID |
| race | string | 可选 | 商品种类 |
| sign | string | 必填 | 签名 |
购买商品并获取卡密信息。金额从你的账户余额中扣除。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| app_id | string | 必填 | 商户 ID |
| app_key | string | 必填 | 商户密钥 |
| shared_code | string | 必填 | 商品 CODE |
| num | int | 必填 | 购买数量 |
| request_no | string | 必填 | 请求唯一标识(防重复下单) |
| contact | string | 可选 | 联系方式 |
| card_id | int | 可选 | 预选的卡密 ID |
| device | int | 可选 | 设备类型 |
| password | string | 可选 | 商品密码 |
| race | string | 可选 | 商品种类 |
| sign | string | 必填 | 签名 |
request_no 用于防止重复下单,建议使用时间戳 + 随机数生成。如果商品有自定义表单字段(widget),需要作为额外参数传递。
{
"code": 200,
"data": {
"trade_no": "20250117123456789",
"secret": "账号:xxx\n密码:xxx",
"widget": {
"server": "艾欧尼亚"
},
"status": 2
}
}
| 字段 | 类型 | 说明 |
|---|---|---|
| trade_no | string | 订单号(可用于查询订单状态) |
| secret | string | 卡密信息(发货内容,自动发货时立即返回) |
| widget | object|null | 自定义表单数据(如有) |
| status | int | 订单状态:0=待支付,1=已支付待处理,2=已完成 |
$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'];
}
获取支持预选的商品的可选卡密列表。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| app_id | string | 必填 | 商户 ID |
| app_key | string | 必填 | 商户密钥 |
| sharedCode | string | 必填 | 商品 CODE |
| page | int | 必填 | 页码 |
| limit | int | 必填 | 每页数量 |
| race | string | 可选 | 商品种类 |
| sign | string | 必填 | 签名 |
{
"code": 200,
"data": {
"data": [
{ "id": 1001, "draft": "预览:账号等级100级" },
{ "id": 1002, "draft": "预览:账号等级80级" }
],
"total": 50
}
}
根据订单号查询订单状态和卡密信息。下单后使用此接口获取购买结果。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| app_id | string | 必填 | 商户 ID |
| app_key | string | 必填 | 商户密钥 |
| tradeNo | string | 必填 | 订单号(购买接口返回的 trade_no) |
| sign | string | 必填 | 签名 |
{
"code": 200,
"msg": "success",
"data": {
"status": 1,
"secret": "账号:xxx\n密码:xxx",
"widget": null
}
}
| 字段 | 类型 | 说明 |
|---|---|---|
| status | int | 订单状态:0=待支付,1=已完成,2=已关闭 |
| secret | string | 卡密内容(订单完成后返回) |
| widget | object|null | 自定义表单数据(如有) |
完整的 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'];
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']}")
排查步骤:
固定金额加价:最终价 = 原价 + 加价金额(例:原价 100 + 加价 10 = 110 元)
百分比加价:最终价 = 原价 × (1 + 百分比)(例:原价 100 × 1.2 = 120 元)
从商品信息的 widget 字段解析出表单配置(JSON),购买时将用户填写的值作为额外参数传递。
// 如果 widget 包含 {"name": "server", "cn": "游戏区服"}
// 则购买时额外传递:
$params['server'] = '艾欧尼亚';
商品种类在 config 字段中(INI 格式):
category[普通]=100.00
category[高级]=200.00
category[豪华]=300.00
购买时通过 race 参数传递用户选择的种类名称。
没有硬性限制,建议:查询接口每秒不超过 10 次,购买接口使用 request_no 防重复,库存查询建议开启缓存。
使用 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}}
可能原因:
建议先调用 /shared/authentication/connect 确认连接正常,再排查商品设置。
可能原因:
race 参数传递了正确的种类名称建议先调用 /shared/commodity/inventoryState 检查库存状态后再下单。
这种情况通常不会发生,系统使用 request_no 防止重复下单。如果遇到:
/shared/commodity/query 查询订单状态,确认订单是否实际已完成系统提供两种库存同步方式:
方式一 — 实时查询:每次用户访问商品页面时调用 /shared/commodity/inventory 获取最新库存。优点是数据准确,缺点是请求频繁。
方式二 — 定时缓存:开启库存同步后,系统自动每 300 秒刷新一次库存缓存。适合访问量大的场景,减少对供货商的请求压力。
建议:小流量用实时查询,大流量用定时缓存。
| code | 说明 |
|---|---|
| 200 | 请求成功 |
| 401 | 认证失败(app_id 或 app_key 错误、签名验证失败) |
| 404 | 资源不存在(商品不存在、订单不存在等) |
| 500 | 业务错误(具体原因见 msg 字段) |
| msg | 说明 |
|---|---|
| sign error | 签名验证失败,检查签名算法 |
| 库存不足 | 商品库存不够 |
| The request ID already exists | request_no 重复,换一个唯一标识 |
| 商品不存在 | shared_code 不正确 |