商户 API 文档
USDT-TRC20 支付网关对外开放的服务器端接入 API(/api/v1/)。
所有接口要求商户使用 HMAC-SHA256 进行签名认证,
请求与响应均为 JSON 格式。
适用对象:集成 USDT 收款能力的商户系统服务端(不适用浏览器端直接调用 ——
出于密钥安全考虑,
api_secret 不应出现在任何前端代码或 CDN 资源中)。
接入地址
https://api2.iqingchuan.com/api/v1https://api2.iqingchuan.com/api/v1(测试/生产共用同一 API 入口)application/json; charset=utf-8签名认证
所有 /api/v1/ 接口都需要 4 个签名头 + 1 个身份头。
商户在管理端「设置」页生成 api_key(公钥,用于识别身份)
与 api_secret(私钥,用于签名)。api_secret
一旦丢失只能重置,平台不存储明文。
必填请求头
| 头 | 说明 |
|---|---|
X-Api-Key | 商户 api_key(注册时由平台生成) |
X-Timestamp | Unix 秒级时间戳,误差须在 ±300 秒内 |
X-Nonce | 随机字符串(建议 16+ 字节),10 分钟内不可重用 |
X-Signature | HMAC-SHA256 签名(hex 小写),见下方算法 |
签名算法
msg = timestamp + "\n" + nonce + "\n" + METHOD + "\n" + path + "\n" + body signature = HMAC_SHA256(api_secret, msg).hexdigest()
说明:
METHOD大写:GET/POSTpath包含 query string(若有),例如/api/v1/order/query?platform_order_no=PAY...body为原始 JSON 字符串,GET 请求为空字符串("")- 签名结果用 小写 hex 编码
常见踩坑:
- GET 请求的
body必须是空字符串"",不能传null或{} path必须包含 query string,且顺序与编码后实际发送的一致(服务端按request.url.path + ? + request.url.query验签)- 服务器时间偏差若 > 5 分钟会返回
TIMESTAMP_EXPIRED,建议使用 NTP - 同一
nonce10 分钟内不可重用,否则返回NONCE_REPLAY(这是重试机制的关键)
返回结构
成功时统一返回:
{
"code": 0,
"message": "ok",
"data": { ... }
}
失败时 code 为业务错误码字符串,message 为人可读描述,
HTTP 状态码同步返回(401/403/404/400/500)。
错误码一览
| code | HTTP | 含义 |
|---|---|---|
MISSING_AUTH_HEADERS | 401 | 签名头缺失 |
INVALID_TIMESTAMP | 401 | 时间戳格式错误 |
TIMESTAMP_EXPIRED | 401 | 时间戳误差超过 5 分钟 |
NONCE_REPLAY | 401 | nonce 10 分钟内已被使用 |
INVALID_API_KEY | 401 | api_key 不存在 |
INVALID_SIGNATURE | 401 | 签名校验失败 |
MERCHANT_UNAVAILABLE | 403 | 商户账号被禁用 |
BLACKLISTED | 403 | 商户或 IP 在黑名单 |
IP_NOT_ALLOWED | 403 | 客户端 IP 不在白名单 |
PARAM_REQUIRED | 400 | 必填参数缺失 |
INVALID_MERCHANT_ORDER_NO | 400 | 订单号非法 |
INVALID_CLIENT_USER_ID | 400 | 客户 ID 非法 |
NOTIFY_URL_REQUIRED | 400 | 未提供 notify_url 且商户未设置默认地址 |
INVALID_MODE | 400 | confirmation_mode 取值非法 |
INVALID_EXPIRE | 400 | expire_minutes 应在 5–1440 之间 |
AMOUNT_POOL_EXHAUSTED | 400 | 该地址与金额组合已满负载(可重试或换地址) |
MERCHANT_ORDER_DUPLICATE | 409 | 订单号已存在(同 merchant_order_no) |
ORDER_NOT_FOUND | 404 | 订单不存在 |
ORDER_STATUS_INVALID | 400 | 只有 CREATED 状态可取消 |
INTERNAL_ERROR | 500 | 服务端内部错误 |
POST /order/create 下单
POST /order/create
创建一笔 USDT 收款订单。返回订单号、收款地址、应付金额、二维码内容。
同一 merchant_order_no 重复请求会幂等返回原订单。
请求参数(body)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
merchant_order_no | string(1-128) | * | 商户侧订单号,商户系统内唯一 |
amount | string(精度 ≤ 6) | * | 收款金额,字符串以保留精度(如 "10.00") |
client_user_id | string(1-128) | * | 商户侧用户标识,用于风控 |
notify_url | string(≤500) | 入账回调地址;留空使用商户默认回调 | |
client_ip | string(≤45) | 下单客户 IP,留空由服务端从头部识别 | |
client_fingerprint | string(≤64) | 设备指纹,风控辅助 | |
confirmation_mode | "fast"|"standard"|"smart" | 确认数模式;smart 时 ≤50 USDT 自动 0 确认 | |
expire_minutes | int(5-1440) | 订单过期时间,默认 30 分钟 | |
extra | object | 透传字段,会在回调中原样返回 |
响应
{
"code": 0,
"message": "ok",
"data": {
"platform_order_no": "PAY202608110001",
"merchant_order_no": "MYORDER_001",
"base_amount": "10.00",
"pay_amount": "10.001234",
"pay_address": "TAbc...",
"status": "CREATED",
"confirmation_mode": "smart",
"required_confirmations": 0,
"expire_at": 1723353600,
"created_at": 1723351980,
"qr_code_content": "tron:TAbc...?amount=10.001234&token=TR7..."
}
}
<?php
$apiKey = 'YOUR_API_KEY';
$apiSecret = 'YOUR_API_SECRET';
$baseUrl = 'https://api2.iqingchuan.com';
$path = '/api/v1/order/create';
$body = json_encode([
'merchant_order_no' => 'MYORDER_'.time(),
'amount' => '10.00',
'client_user_id' => 'user_123',
'notify_url' => 'https://yoursite.com/cb',
]);
$ts = (string) time();
$nonce = bin2hex(random_bytes(16));
$msg = $ts."\n".$nonce."\nPOST\n".$path."\n".$body;
$sig = hash_hmac('sha256', $msg, $apiSecret);
$ch = curl_init($baseUrl.$path);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $body,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
"X-Api-Key: $apiKey",
"X-Timestamp: $ts",
"X-Nonce: $nonce",
"X-Signature: $sig",
],
]);
echo curl_exec($ch);
// Java(后端,使用 OkHttp + Jackson)
String apiKey = "YOUR_API_KEY";
String apiSecret = "YOUR_API_SECRET";
String baseUrl = "https://api2.iqingchuan.com";
String path = "/api/v1/order/create";
Map<String,Object> params = new HashMap<>();
params.put("merchant_order_no", "MYORDER_" + System.currentTimeMillis());
params.put("amount", "10.00");
params.put("client_user_id", "user_123");
params.put("notify_url", "https://yoursite.com/cb");
ObjectMapper mapper = new ObjectMapper();
String body = mapper.writeValueAsString(params);
String ts = String.valueOf(System.currentTimeMillis() / 1000);
String nonce = UUID.randomUUID().toString().replace("-", "");
String msg = ts + "\n" + nonce + "\nPOST\n" + path + "\n" + body;
String sig = HmacUtils.hmacSha256Hex(apiSecret, msg);
Request req = new Request.Builder()
.url(baseUrl + path)
.post(RequestBody.create(body, MediaType.parse("application/json")))
.addHeader("X-Api-Key", apiKey)
.addHeader("X-Timestamp", ts)
.addHeader("X-Nonce", nonce)
.addHeader("X-Signature", sig)
.build();
OkHttpClient client = new OkHttpClient();
try (Response resp = client.newCall(req).execute()) {
System.out.println(resp.body().string());
}
import time, uuid, json, hmac, hashlib, requests
API_KEY = "YOUR_API_KEY"
API_SECRET = "YOUR_API_SECRET"
BASE_URL = "https://api2.iqingchuan.com"
PATH = "/api/v1/order/create"
body = json.dumps({
"merchant_order_no": "MYORDER_" + str(int(time.time())),
"amount": "10.00",
"client_user_id": "user_123",
"notify_url": "https://yoursite.com/cb",
}, separators=(",", ":"))
ts = str(int(time.time()))
nonce = uuid.uuid4().hex
msg = f"{ts}\n{nonce}\nPOST\n{PATH}\n{body}"
sig = hmac.new(API_SECRET.encode(), msg.encode(), hashlib.sha256).hexdigest()
resp = requests.post(
BASE_URL + PATH,
data=body,
headers={
"Content-Type": "application/json",
"X-Api-Key": API_KEY,
"X-Timestamp": ts,
"X-Nonce": nonce,
"X-Signature": sig,
},
timeout=15,
)
print(resp.json())
// Node.js(使用原生 https + crypto,无需第三方依赖)
const crypto = require("crypto");
const https = require("https");
const API_KEY = "YOUR_API_KEY";
const API_SECRET = "YOUR_API_SECRET";
const PATH = "/api/v1/order/create";
const bodyObj = {
merchant_order_no: "MYORDER_" + Date.now(),
amount: "10.00",
client_user_id: "user_123",
notify_url: "https://yoursite.com/cb",
};
const body = JSON.stringify(bodyObj);
const ts = Math.floor(Date.now() / 1000).toString();
const nonce = crypto.randomBytes(16).toString("hex");
const msg = `${ts}\n${nonce}\nPOST\n${PATH}\n${body}`;
const sig = crypto.createHmac("sha256", API_SECRET).update(msg).digest("hex");
const req = https.request({
host: "api2.iqingchuan.com",
path: PATH,
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Api-Key": API_KEY,
"X-Timestamp": ts,
"X-Nonce": nonce,
"X-Signature": sig,
},
}, (res) => {
let chunks = "";
res.on("data", c => chunks += c);
res.on("end", () => console.log(JSON.parse(chunks)));
});
req.write(body); req.end();
GET /order/query 查单
GET /order/query
根据 platform_order_no 或 merchant_order_no 查询订单。
查询参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
platform_order_no | string | 平台订单号(以 PAY 开头) | |
merchant_order_no | string | 商户订单号 |
两个参数至少传一个。
响应
同 /order/create 响应格式。
<?php
$apiKey = 'YOUR_API_KEY';
$apiSecret = 'YOUR_API_SECRET';
$baseUrl = 'https://api2.iqingchuan.com';
$query = http_build_query(['platform_order_no' => 'PAY202608110001']);
$path = '/api/v1/order/query?'.$query;
$body = ''; // GET 请求 body 必须为空字符串
$ts = (string) time();
$nonce = bin2hex(random_bytes(16));
$msg = $ts."\n".$nonce."\nGET\n".$path."\n".$body;
$sig = hash_hmac('sha256', $msg, $apiSecret);
$ch = curl_init($baseUrl.$path);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"X-Api-Key: $apiKey",
"X-Timestamp: $ts",
"X-Nonce: $nonce",
"X-Signature: $sig",
],
]);
echo curl_exec($ch);
String path = "/api/v1/order/query?platform_order_no=PAY202608110001";
String body = "";
String ts = String.valueOf(System.currentTimeMillis() / 1000);
String nonce = UUID.randomUUID().toString().replace("-", "");
String msg = ts + "\n" + nonce + "\nGET\n" + path + "\n" + body;
String sig = HmacUtils.hmacSha256Hex(apiSecret, msg);
Request req = new Request.Builder()
.url("https://api2.iqingchuan.com" + path)
.get()
.addHeader("X-Api-Key", apiKey)
.addHeader("X-Timestamp", ts)
.addHeader("X-Nonce", nonce)
.addHeader("X-Signature", sig)
.build();
OkHttpClient client = new OkHttpClient();
try (Response resp = client.newCall(req).execute()) {
System.out.println(resp.body().string());
}
import time, uuid, hmac, hashlib, requests
API_KEY = "YOUR_API_KEY"
API_SECRET = "YOUR_API_SECRET"
PATH = "/api/v1/order/query"
QUERY = "platform_order_no=PAY202608110001"
FULL_PATH = f"{PATH}?{QUERY}"
body = "" # GET 签名 body 必须是空字符串
ts = str(int(time.time()))
nonce = uuid.uuid4().hex
msg = f"{ts}\n{nonce}\nGET\n{FULL_PATH}\n{body}"
sig = hmac.new(API_SECRET.encode(), msg.encode(), hashlib.sha256).hexdigest()
resp = requests.get(
"https://api2.iqingchuan.com" + FULL_PATH,
headers={
"X-Api-Key": API_KEY,
"X-Timestamp": ts,
"X-Nonce": nonce,
"X-Signature": sig,
},
timeout=15,
)
print(resp.json())
const crypto = require("crypto");
const https = require("https");
const API_KEY = "YOUR_API_KEY";
const API_SECRET = "YOUR_API_SECRET";
const QUERY = "platform_order_no=PAY202608110001";
const FULL_PATH = "/api/v1/order/query?" + QUERY;
const body = "";
const ts = Math.floor(Date.now() / 1000).toString();
const nonce = crypto.randomBytes(16).toString("hex");
const msg = `${ts}\n${nonce}\nGET\n${FULL_PATH}\n${body}`;
const sig = crypto.createHmac("sha256", API_SECRET).update(msg).digest("hex");
https.get({
host: "api2.iqingchuan.com",
path: FULL_PATH,
headers: {
"X-Api-Key": API_KEY,
"X-Timestamp": ts,
"X-Nonce": nonce,
"X-Signature": sig,
},
}, (res) => {
let chunks = "";
res.on("data", c => chunks += c);
res.on("end", () => console.log(JSON.parse(chunks)));
});
POST /order/cancel 取消
POST /order/cancel
取消一笔尚未入账的订单(只能取消 CREATED 状态的订单)。
取消后金额配额会回滚,同款金额可被新订单重新占用。
请求参数(body)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
platform_order_no | string | * | 平台订单号 |
响应
{ "code": 0, "message": "已取消", "data": { "platform_order_no": "PAY...", "status": "CANCELLED" } }
<?php
$path = '/api/v1/order/cancel';
$body = json_encode(['platform_order_no' => 'PAY202608110001']);
$ts = (string) time();
$nonce = bin2hex(random_bytes(16));
$msg = $ts."\n".$nonce."\nPOST\n".$path."\n".$body;
$sig = hash_hmac('sha256', $msg, $apiSecret);
// 头部同 /order/create 示例,curl 用 POST 发送 $body
// body 仅含 platform_order_no,其它头部同 /order/create
body = json.dumps({"platform_order_no": "PAY202608110001"})
# 头部构造与 /order/create 一致,仅 path 与 body 不同
// body 仅含 platform_order_no,头部构造与 /order/create 一致
GET /order/list 列表
GET /order/list
分页拉取当前商户的订单列表。可选按状态筛选。
查询参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
status | string | 订单状态:CREATED / PAID / CONFIRMED / SETTLED / EXPIRED / CANCELLED | |
page | int ≥ 1 | 页码,默认 1 | |
page_size | int 1-100 | 每页数量,默认 20,最大 100 |
响应
{
"code": 0, "message": "ok",
"data": {
"total": 42, "page": 1, "page_size": 20,
"items": [
{ "platform_order_no": "PAY...", "status": "PAID", "pay_amount": "10.001234", ... }
]
}
}
签名方式同 /order/query(GET,body 为空字符串)。
GET /ping 调试
GET /ping
连通性测试。验证签名正确后,返回当前商户号、套餐状态与服务端时间戳。 可用于 SDK 接入自检。
{ "code": 0, "message": "pong", "data": { "merchant_no": "M001", "email": "merchant@x.com", "package_status": "active", "server_time": 1723351980 } }
POST /echo 调试
POST /echo
回显测试。服务端原样回传你发的 JSON body,用于校验序列化是否一致。
// 请求 body:任意 JSON
{ "hello": "world", "n": 1 }
// 响应
{ "code": 0, "message": "ok", "data": { "received": { "hello": "world", "n": 1 }, "from": "M001" } }