TopUP 开放 API
第 1 版。"接口详情"里的每个字段都由自动测试和实际接口逐一核对,文档和接口不会不一致。
概览
- 网络和币种:只支持 TRON 上的 USDT(TRC20)。金额用字符串表示,最多 6 位小数,例如 "100.5"。
- 接口地址:
https://{BASE_URL}/api/v1/,所有路径都以 "/" 结尾。不带结尾斜杠的请求会被跳转,有些 HTTP 工具在 POST 被跳转时会出错。 - 按客户收款:你的每个客户有一个永久地址(发到账通知)。开启订单模式后,可以给客户创建订单,系统自动把到账匹配到订单(发订单匹配通知)。
- 到账达到 19 个确认(大约 1 分钟)后才算入账,一般在付款后 2 分钟内发出回调。
- 每一笔提币都经过人工审核。工作时间为东八区 8:00–23:00:工作时间内的申请 1 小时内处理,其余的顺延到下一个工作时间。
| 接口 | 用途 |
|---|---|
POSTorders/ | 创建订单 |
GETorders/ | 查询订单 |
GETorders/list/ | 订单列表 |
POSTorders/match/ | 手动匹配 |
POSTorders/unmatch/ | 解除匹配 |
POSTcustomers/ | 新建或更新客户 |
GETcustomers/ | 查询客户 |
GETcustomers/list/ | 客户列表 |
GETcustomers/stats/ | 客户统计 |
GETbalance/ | 余额 |
GETledger/ | 账本明细 |
GETdeposits/ | 到账明细 |
POSTwithdrawals/ | 发起提币 |
GETwithdrawals/ | 查询提币 |
POSTwithdrawals/cancel/ | 取消提币 |
POSTpayouts/batch/ | 提交批量打款 |
GETpayouts/batch/ | 查询批量打款 |
请求签名
每个请求都要带上三个请求头。Secret 在商户后台的"API 设置"里生成,只显示一次,请只保存在你的服务器上。
| Header | 内容 |
|---|---|
X-TopUP-Key | 你的 API Key |
X-TopUP-Timestamp | Unix 时间戳(秒),和服务器时间相差不能超过 5 分钟 |
X-TopUP-Signature | hex( HMAC-SHA256( Secret, 时间戳 + "\n" + 请求方法 + "\n" + 路径和查询参数 + "\n" + hex(SHA-256(请求体)) ) );GET 请求的请求体为空 |
Idempotency-Key | 每个 POST 请求都必须带上(8 到 128 位,可用字母、数字和 _ . : -,用 UUID 即可)。用同一个 Key 和同样的内容重试,会返回第一次的结果,不会重复创建(保留 24 小时)。失败的请求不占用这个 Key。 |
Node.js
import crypto from 'node:crypto';
const BASE = process.env.TOPUP_BASE || 'https://{BASE_URL}/api/v1/';
const KEY = process.env.TOPUP_KEY, SECRET = process.env.TOPUP_SECRET;
async function topup(method, path, body) {
const ts = Math.floor(Date.now() / 1000).toString();
const raw = body ? JSON.stringify(body) : '';
const bodyHash = crypto.createHash('sha256').update(raw).digest('hex');
const url = new URL(path, BASE);
const toSign = [ts, method, url.pathname + url.search, bodyHash].join('\n');
const sig = crypto.createHmac('sha256', SECRET).update(toSign).digest('hex');
const res = await fetch(url, {
method,
headers: {
'content-type': 'application/json',
'X-TopUP-Key': KEY, 'X-TopUP-Timestamp': ts, 'X-TopUP-Signature': sig,
...(method === 'POST' ? { 'Idempotency-Key': crypto.randomUUID() } : {}),
},
body: raw || undefined,
});
return res.json();
}
const order = await topup('POST', 'orders/', { customer_id: 'user_88213', merchant_order_no: `ACME-${Date.now()}`, amount: '100.00' });
console.log(order.pay_url);
Python
import hashlib, hmac, json, os, time, uuid, requests
BASE = os.environ.get("TOPUP_BASE_URL", "https://{BASE_URL}")
KEY, SECRET = os.environ["TOPUP_KEY"], os.environ["TOPUP_SECRET"].encode()
def topup(method, path, body=None):
ts = str(int(time.time()))
raw = json.dumps(body, separators=(",", ":")) if body else ""
body_hash = hashlib.sha256(raw.encode()).hexdigest()
to_sign = "\n".join([ts, method, "/api/v1/" + path, body_hash])
sig = hmac.new(SECRET, to_sign.encode(), hashlib.sha256).hexdigest()
headers = {"content-type": "application/json", "X-TopUP-Key": KEY,
"X-TopUP-Timestamp": ts, "X-TopUP-Signature": sig}
if method == "POST":
headers["Idempotency-Key"] = str(uuid.uuid4())
return requests.request(method, BASE + "/api/v1/" + path, data=raw or None, headers=headers).json()
print(topup("GET", "balance/"))
订单收款
POST /api/v1/orders/
{
"customer_id": "user_88213", // required: your own ID for this customer
"customer_name": "Lin Trading", // optional, used only if the customer is new
"customer_email": "lin@lintrading.example",
"merchant_order_no": "ACME-88513",
"amount": "100.00"
}{
"order_no": "ORD-20260929-0413",
"merchant_order_no": "ACME-88513",
"customer_id": "user_88213",
"amount": "100.00",
"matched": "0.00",
"address": "TMa7yK2d9Qp4XvB8sN1cF6rH3wZ5jL0eUt", // the customer's permanent address
"network": "TRON",
"token": "USDT",
"status": "pending",
"created_at": "2026-09-29T09:00:00.000Z",
"expires_at": "2026-09-29T09:30:00.000Z",
"deposits": [],
"pay_url": "https://{BASE_URL}/pay/ORD-20260929-0413/"
}- customer_id 必填;新的 customer_id 会自动创建客户和永久地址。merchant_order_no 在你的账户里不能重复。
- 过期时间和匹配阈值由平台为你的账户设置(在"API 设置"里可以看到)。创建订单需要开启订单模式,否则返回 403 order_mode_disabled。
- 可以把 pay_url 发给付款方,也可以自己展示收款地址。
- 如果创建时就匹配到了这个客户之前的到账,返回的状态可能已经是 completed,见"匹配规则"。
客户
POST /api/v1/customers/
{ "customer_id": "user_88213", "name": "Lin Trading", "email": "lin@lintrading.example" }
→ {
"customer_id": "user_88213", "name": "Lin Trading", "email": "lin@lintrading.example", "remark": "",
"address": "TXk8fQ2m9sR7ZcY1hN4vB6pW3aLd5Z3wQe",
"total": "0.00", "count": 0, "fees": "0.00", "unmatched": "0.00",
"last_payment_at": null, "created_at": "2026-09-11T02:10:00.000Z"
}- 每个客户一个永久地址,转到这个地址的钱都算这个客户的,不论有没有订单。customer_id 不能修改。
- 低于 1 USDT 的付款不入账、不匹配、不发回调,也不会出现在你的到账明细和导出里(这类付款通常是垃圾"灰尘"转账)。
- customers/stats/ 返回某个时间段的收款合计、笔数、手续费和未匹配金额。
匹配规则
匹配只是把到账和订单关联起来,不会改变余额;只在同一个客户内部进行,两个方向都会触发:
- 订单在先、到账在后:到账匹配给"加上这笔后累计落在下限 L 和上限 H 之间"的未完成订单;都不满足时,匹配最早创建的未完成订单。
- 到账在先、订单在后:从回看时间内最早的未匹配到账开始累计,达到 L 且不超过 H 时匹配,否则一笔都不匹配。
- 状态:累计 < L 为 partial(继续等待),L ≤ 累计 ≤ H 为 completed,累计 > H 为 overpaid;过期时还没达到 L 的订单变为 expired(没有收到)或 expired_partial。
- 过期后才到的钱记为未匹配,需要时用 orders/match/ 手动匹配。尽量不要让同一个客户同时有两个金额接近的未完成订单。
余额与账本
GET balance/ 返回 { "available": "48215.40", "frozen": "1002.00" }。账本只追加,每条记录的类型为 deposit、fee_in、freeze、unfreeze、withdraw、fee_out 之一。列表用 cursor 翻页。
提币
POST /api/v1/withdrawals/
{
"kind": "payout", // "payout" = to your customer, "cashout" = to your registered wallet
"customer_id": "user_88213", // optional, for payouts
"to": "TXk8fQ2m9sR7ZcY1hN4vB6pW3aLd5Z3wQe",
"amount": "1000.00",
"merchant_ref": "PAYOUT-7781"
}
→ {
"withdrawal_no": "WD-20260929-7F3A21", "kind": "payout", "customer_id": "user_88213",
"to": "TXk8fQ2m9sR7ZcY1hN4vB6pW3aLd5Z3wQe", "amount": "1000.00", "fee": "2.00", // 1002.00 is frozen
"status": "pending", "txid": null, "reason": null, "merchant_ref": "PAYOUT-7781", "source": "api",
"created_at": "2026-09-29T09:12:00.000Z"
}- API 提币需要先在"API 设置"里设置 IP 白名单;没有白名单时返回 403 ip_not_allowed。
- 提交时冻结"金额 + 手续费",被驳回或取消时解冻。
- 状态:pending → approved → broadcast → completed,或 rejected / cancelled / failed。每次变化都发 withdrawal.updated 回调。
回调
POST https://your-server.example/callback
X-TopUP-Timestamp: 1790665800
X-TopUP-Signature: 5f1c…
{
"id": "evt_9f2a02",
"type": "order.matched", // deposit | order.matched | withdrawal.updated
"created_at": "2026-09-29T09:10:03Z",
"data": {
"order_no": "ORD-20260929-0409",
"merchant_order_no": "ACME-88509",
"customer_id": "user_88213",
"status": "completed", // pending | partial | completed | overpaid | expired | expired_partial
"amount": "500.00",
"matched": "500.00",
"created_at": "2026-09-29T09:05:00.000Z",
"expires_at": "2026-09-29T09:35:00.000Z",
"deposits": [{ "id": "1042", "txid": "a3c1…", "amount": "500.00", "matched_by": "deposit", "time": "2026-09-29T09:10:00.000Z" }],
"manual": false
}
}签名:hex( HMAC-SHA256( Secret, 时间戳 + "\n" + 原始请求体 ) )。收到后请返回任意 2xx 状态码;否则我们在第一次发送之后的 1 分钟、5 分钟、30 分钟、2 小时、6 小时、12 小时、24 小时重试,然后停止,你可以在商户后台手动重发。同一个事件可能收到不止一次,请按 id 去重。
import crypto from 'node:crypto';
// Verify a TopUP callback. rawBody must be the exact request body you received,
// not an object you parsed and serialised again.
export function verifyCallback(secret, timestamp, signature, rawBody) {
const expected = crypto.createHmac('sha256', secret).update(`${timestamp}\n${rawBody}`).digest('hex');
const a = Buffer.from(expected), b = Buffer.from(String(signature || ''));
const fresh = Math.abs(Date.now() / 1000 - Number(timestamp)) <= 300;
return fresh && a.length === b.length && crypto.timingSafeEqual(a, b);
}
// Express example:
// app.post('/callback', express.raw({ type: 'application/json' }), (req, res) => {
// if (!verifyCallback(SECRET, req.get('X-TopUP-Timestamp'), req.get('X-TopUP-Signature'), req.body)) return res.sendStatus(401);
// const event = JSON.parse(req.body); // the same event may arrive more than once: deduplicate by event.id
// res.sendStatus(200);
// });
回调内容
| 字段 | 类型 | 说明 |
|---|---|---|
id | 字符串 | 事件编号,请按它去重 |
type | 字符串 | deposit、order.matched、withdrawal.updated 之一 |
created_at | 时间 | 事件产生时间 |
data | object | 事件内容,见下表 |
deposit
客户地址收到 1 USDT 及以上的付款并入账。data 的字段:
| 字段 | 类型 | 说明 |
|---|---|---|
customer_id | 字符串 | 你的客户标识 |
chain | 字符串 | 付款所在的链:eth、bsc 或 tron |
address | 字符串 | 收款地址 |
txid | 字符串 | 链上交易哈希 |
amount | 金额 | 到账金额 |
fee | 金额 | 收款手续费 |
credited | 金额 | 计入余额的金额 |
order_no | 字符串 | 匹配到的订单(可能为 null) |
time | 时间 | 链上付款时间 |
order.matched
订单因为匹配或解除匹配发生变化。data 的字段:
| 字段 | 类型 | 说明 |
|---|---|---|
order_no | 字符串 | TopUP 生成的订单号 |
merchant_order_no | 字符串 | 你的订单号 |
customer_id | 字符串 | 你的客户标识 |
amount | 金额 | 订单金额 |
matched | 金额 | 已经匹配到这个订单的到账合计 |
status | 字符串 | pending、partial、completed、overpaid、expired、expired_partial 之一(见"匹配规则") |
created_at | 时间 | 创建时间 |
expires_at | 时间 | 过期时间 |
deposits | 数组 | 匹配到这个订单的到账(OrderDeposit 对象) |
manual | 布尔值 | 手动匹配或解除匹配时为 true |
withdrawal.updated
提币创建或状态变化。data 的字段:
| 字段 | 类型 | 说明 |
|---|---|---|
withdrawal_no | 字符串 | 提币单号 |
kind | 字符串 | payout(付给你的客户)或 cashout(提到你登记的钱包) |
chain | 字符串 | 付款所在的链:eth、bsc 或 tron |
batch_no | 字符串 | 所属的批量打款批次号;单笔提币为 null(可能为 null) |
customer_id | 字符串 | 代付对应的客户(可能为 null) |
to | 字符串 | 收款地址(所在链的地址) |
amount | 金额 | 提币金额 |
fee | 金额 | 提币手续费(提交时冻结"金额 + 手续费") |
status | 字符串 | pending、approved、broadcast、completed、rejected、cancelled、failed 之一 |
txid | 字符串 | 广播后的链上交易哈希(可能为 null) |
reason | 字符串 | 驳回或失败的原因(可能为 null) |
merchant_ref | 字符串 | 你的备注编号(可能为 null) |
source | 字符串 | api(开放 API)或 web(商户后台) |
created_at | 时间 | 创建时间 |
接口详情
金额是 USDT 的字符串(例如 "100.50"),时间是 UTC 的 ISO 8601 格式。列表接口返回 { items, next_cursor }:把 next_cursor 作为 cursor 传入,取下一页。
OrderDeposit
订单里 deposits 数组的每一项。
| 字段 | 类型 | 说明 |
|---|---|---|
id | 字符串 | 到账编号 |
txid | 字符串 | 链上交易哈希 |
amount | 金额 | 到账金额 |
matched_by | 字符串 | 匹配方式:deposit(先建单后到账)、order(先到账后建单)、manual(手动匹配) |
time | 时间 | 链上付款时间 |
POST/api/v1/orders/ · 创建订单
请求体(JSON)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
customer_id | 字符串 | 是 | 你给客户的标识,1 到 128 位,可用字母、数字和 _ . @ -;新的标识会自动创建客户(不带地址:地址在客户发起充值时按链生成) |
merchant_order_no | 字符串 | 是 | 你的订单号,1 到 64 位,可用字母、数字、_ 和 -;在你的账户里不能重复 |
amount | 金额 | 是 | 订单金额(USDT),最多 6 位小数,例如 "100.5" |
customer_name | 字符串 | 否 | 只在新建客户时使用 |
customer_email | 字符串 | 否 | 只在新建客户时使用 |
chain | 字符串 | 否 | eth 或 bsc:付款人只能用这条链付款,创建订单时生成客户在这条链的地址。不传时由付款人在付款页选择(推荐),选了才生成那条链的地址 |
返回
| 字段 | 类型 | 说明 |
|---|---|---|
order_no | 字符串 | TopUP 生成的订单号 |
merchant_order_no | 字符串 | 你的订单号 |
customer_id | 字符串 | 你的客户标识 |
amount | 金额 | 订单金额 |
matched | 金额 | 已经匹配到这个订单的到账合计 |
status | 字符串 | pending、partial、completed、overpaid、expired、expired_partial 之一(见"匹配规则") |
created_at | 时间 | 创建时间 |
expires_at | 时间 | 过期时间 |
deposits | 数组 | 匹配到这个订单的到账(OrderDeposit 对象) |
address | 字符串 | 付款地址:指定了链时是这条链的地址;没有指定时为 null(付款人在付款页选链)(可能为 null) |
network | 字符串 | address 所在的网络:ETH、BSC 或 TRON;没有指定链时为 null(可能为 null) |
chain | 字符串 | 创建订单时指定的链(eth 或 bsc);null 表示付款人在付款页自己选(可能为 null) |
addresses | 数组 | 这个客户已经生成过的地址:[{ chain, address }](指定了链时只给这条链)。ETH 和 BSC 是同一串地址,只按选定的网络转账(可能为 null) |
token | 字符串 | 固定为 USDT |
pay_url | 字符串 | 这个订单的付款页面 |
可能的错误:order_mode_disabled、duplicate_merchant_order_no、chain_not_enabled、invalid_param
GET/api/v1/orders/ · 查询订单
查询参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
order_no | 字符串 | 否 | 订单号(和 merchant_order_no 二选一) |
merchant_order_no | 字符串 | 否 | 你的订单号 |
返回
| 字段 | 类型 | 说明 |
|---|---|---|
order_no | 字符串 | TopUP 生成的订单号 |
merchant_order_no | 字符串 | 你的订单号 |
customer_id | 字符串 | 你的客户标识 |
amount | 金额 | 订单金额 |
matched | 金额 | 已经匹配到这个订单的到账合计 |
status | 字符串 | pending、partial、completed、overpaid、expired、expired_partial 之一(见"匹配规则") |
created_at | 时间 | 创建时间 |
expires_at | 时间 | 过期时间 |
deposits | 数组 | 匹配到这个订单的到账(OrderDeposit 对象) |
address | 字符串 | 付款地址:指定了链时是这条链的地址;没有指定时为 null(付款人在付款页选链)(可能为 null) |
network | 字符串 | address 所在的网络:ETH、BSC 或 TRON;没有指定链时为 null(可能为 null) |
chain | 字符串 | 创建订单时指定的链(eth 或 bsc);null 表示付款人在付款页自己选(可能为 null) |
addresses | 数组 | 这个客户已经生成过的地址:[{ chain, address }](指定了链时只给这条链)。ETH 和 BSC 是同一串地址,只按选定的网络转账(可能为 null) |
token | 字符串 | 固定为 USDT |
pay_url | 字符串 | 这个订单的付款页面 |
可能的错误:not_found
GET/api/v1/orders/list/ · 订单列表
查询参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
status | 字符串 | 否 | 按状态筛选 |
customer_id | 字符串 | 否 | 按客户筛选 |
cursor | 字符串 | 否 | 上一页返回的 next_cursor |
limit | 整数 | 否 | 每页条数,1 到 100,默认 50 |
返回:{ items, next_cursor },每一项的字段:
| 字段 | 类型 | 说明 |
|---|---|---|
order_no | 字符串 | 订单号 |
merchant_order_no | 字符串 | 你的订单号 |
customer_id | 字符串 | 你的客户标识 |
amount | 金额 | 订单金额 |
matched | 金额 | 已匹配合计 |
status | 字符串 | 订单状态 |
created_at | 时间 | 创建时间 |
expires_at | 时间 | 过期时间 |
POST/api/v1/orders/match/ · 手动匹配
只改变到账和订单的关联,不改变余额;会发送 order.matched 回调(manual 为 true)。
请求体(JSON)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
order_no | 字符串 | 是 | 订单号 |
deposit_id | 字符串 | 是 | 到账编号(来自 deposits/;同一个客户、还没有匹配) |
返回
| 字段 | 类型 | 说明 |
|---|---|---|
order_no | 字符串 | TopUP 生成的订单号 |
merchant_order_no | 字符串 | 你的订单号 |
customer_id | 字符串 | 你的客户标识 |
amount | 金额 | 订单金额 |
matched | 金额 | 已经匹配到这个订单的到账合计 |
status | 字符串 | pending、partial、completed、overpaid、expired、expired_partial 之一(见"匹配规则") |
created_at | 时间 | 创建时间 |
expires_at | 时间 | 过期时间 |
deposits | 数组 | 匹配到这个订单的到账(OrderDeposit 对象) |
可能的错误:not_found、cannot_match
POST/api/v1/orders/unmatch/ · 解除匹配
请求体(JSON)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
order_no | 字符串 | 是 | 订单号 |
deposit_id | 字符串 | 是 | 当前匹配在这个订单上的到账编号 |
返回
| 字段 | 类型 | 说明 |
|---|---|---|
order_no | 字符串 | TopUP 生成的订单号 |
merchant_order_no | 字符串 | 你的订单号 |
customer_id | 字符串 | 你的客户标识 |
amount | 金额 | 订单金额 |
matched | 金额 | 已经匹配到这个订单的到账合计 |
status | 字符串 | pending、partial、completed、overpaid、expired、expired_partial 之一(见"匹配规则") |
created_at | 时间 | 创建时间 |
expires_at | 时间 | 过期时间 |
deposits | 数组 | 匹配到这个订单的到账(OrderDeposit 对象) |
可能的错误:not_found、not_matched
POST/api/v1/customers/ · 新建或更新客户
没有传的字段保持不变;这里不生成地址:每条链的地址在客户第一次在这条链充值时生成,之后不会改变。
请求体(JSON)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
customer_id | 字符串 | 是 | 你给客户的标识(规则同"创建订单"),之后不能修改 |
name | 字符串 | 否 | 名称,最多 200 个字符 |
email | 字符串 | 否 | 邮箱 |
remark | 字符串 | 否 | 备注,最多 500 个字符 |
返回
| 字段 | 类型 | 说明 |
|---|---|---|
customer_id | 字符串 | 你的客户标识 |
name | 字符串 | 名称 |
email | 字符串 | 邮箱(加密保存) |
remark | 字符串 | 备注 |
address | 字符串 | 永久收款地址(TRON,旧字段;只有原来就有 TRON 地址的老客户才有,其他为 null;请改用 addresses)(可能为 null) |
addresses | 数组 | 每条链的永久收款地址:[{ chain, address }],chain 是 eth、bsc 或 tron。在客户发起充值时按链生成(订单指定了链,或付款人在付款页选了链),新客户为空;ETH 和 BSC 是同一串地址 |
total | 金额 | 累计入账的到账金额 |
count | 整数 | 入账的到账笔数 |
fees | 金额 | 累计收款手续费 |
unmatched | 金额 | 已入账但没有匹配订单的金额 |
last_payment_at | 时间 | 最后一次付款时间(可能为 null) |
created_at | 时间 | 创建时间 |
可能的错误:invalid_param
GET/api/v1/customers/ · 查询客户
查询参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
customer_id | 字符串 | 是 | 你的客户标识 |
返回
| 字段 | 类型 | 说明 |
|---|---|---|
customer_id | 字符串 | 你的客户标识 |
name | 字符串 | 名称 |
email | 字符串 | 邮箱(加密保存) |
remark | 字符串 | 备注 |
address | 字符串 | 永久收款地址(TRON,旧字段;只有原来就有 TRON 地址的老客户才有,其他为 null;请改用 addresses)(可能为 null) |
addresses | 数组 | 每条链的永久收款地址:[{ chain, address }],chain 是 eth、bsc 或 tron。在客户发起充值时按链生成(订单指定了链,或付款人在付款页选了链),新客户为空;ETH 和 BSC 是同一串地址 |
total | 金额 | 累计入账的到账金额 |
count | 整数 | 入账的到账笔数 |
fees | 金额 | 累计收款手续费 |
unmatched | 金额 | 已入账但没有匹配订单的金额 |
last_payment_at | 时间 | 最后一次付款时间(可能为 null) |
created_at | 时间 | 创建时间 |
可能的错误:not_found
GET/api/v1/customers/list/ · 客户列表
查询参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
q | 字符串 | 否 | 按客户标识、名称或邮箱搜索(部分匹配,不区分大小写),或按地址精确搜索 |
cursor | 字符串 | 否 | 上一页返回的 next_cursor |
limit | 整数 | 否 | 每页条数,1 到 100,默认 50 |
返回:{ items, next_cursor },每一项的字段:
| 字段 | 类型 | 说明 |
|---|---|---|
customer_id | 字符串 | 你的客户标识 |
name | 字符串 | 名称 |
email | 字符串 | 邮箱(加密保存) |
remark | 字符串 | 备注 |
address | 字符串 | 永久收款地址(TRON,旧字段;只有原来就有 TRON 地址的老客户才有,其他为 null;请改用 addresses)(可能为 null) |
addresses | 数组 | 每条链的永久收款地址:[{ chain, address }],chain 是 eth、bsc 或 tron。在客户发起充值时按链生成(订单指定了链,或付款人在付款页选了链),新客户为空;ETH 和 BSC 是同一串地址 |
total | 金额 | 累计入账的到账金额 |
count | 整数 | 入账的到账笔数 |
fees | 金额 | 累计收款手续费 |
unmatched | 金额 | 已入账但没有匹配订单的金额 |
last_payment_at | 时间 | 最后一次付款时间(可能为 null) |
created_at | 时间 | 创建时间 |
GET/api/v1/customers/stats/ · 客户统计
查询参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
customer_id | 字符串 | 是 | 你的客户标识 |
from | 时间 | 否 | 开始时间(ISO 8601),默认从最早开始 |
to | 时间 | 否 | 结束时间(ISO 8601),默认到现在 |
返回
| 字段 | 类型 | 说明 |
|---|---|---|
customer_id | 字符串 | 你的客户标识 |
from | 时间 | 统计开始时间 |
to | 时间 | 统计结束时间 |
total | 金额 | 期间入账的到账金额 |
count | 整数 | 入账笔数 |
fees | 金额 | 收款手续费 |
unmatched | 金额 | 没有匹配订单的金额 |
last_payment_at | 时间 | 期间最后一次付款时间(可能为 null) |
payouts | 金额 | 已完成的给这个客户的代付金额 |
payout_count | 整数 | 已完成的代付笔数 |
可能的错误:not_found、invalid_param
GET/api/v1/balance/ · 余额
返回
| 字段 | 类型 | 说明 |
|---|---|---|
available | 金额 | 可用余额 |
frozen | 金额 | 提币中冻结的金额 |
currency | 字符串 | 固定为 USDT |
GET/api/v1/ledger/ · 账本明细
查询参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
customer_id | 字符串 | 否 | 按客户筛选 |
cursor | 字符串 | 否 | 上一页返回的 next_cursor |
limit | 整数 | 否 | 每页条数,1 到 100,默认 50 |
返回:{ items, next_cursor },每一项的字段:
| 字段 | 类型 | 说明 |
|---|---|---|
id | 字符串 | 记录编号,也用于翻页 |
type | 字符串 | deposit、fee_in、freeze、unfreeze、withdraw、fee_out 之一 |
amount | 金额 | 变动金额:正数增加,负数减少(withdraw、fee_out 变动的是冻结余额) |
available_after | 金额 | 这条记录之后的可用余额 |
frozen_after | 金额 | 这条记录之后的冻结余额 |
ref | 字符串 | 关联的到账编号或提币单号 |
customer_id | 字符串 | 关联的客户(可能为 null) |
created_at | 时间 | 记录时间 |
GET/api/v1/deposits/ · 到账明细
查询参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
customer_id | 字符串 | 否 | 按客户筛选 |
matched | 字符串 | 否 | "false" 表示已入账但没有匹配订单;"true" 表示已匹配 |
cursor | 字符串 | 否 | 上一页返回的 next_cursor |
limit | 整数 | 否 | 每页条数,1 到 100,默认 50 |
返回:{ items, next_cursor },每一项的字段:
| 字段 | 类型 | 说明 |
|---|---|---|
id | 字符串 | 到账编号(手动匹配时使用) |
customer_id | 字符串 | 你的客户标识 |
chain | 字符串 | 付款所在的链:eth、bsc 或 tron |
address | 字符串 | 收款地址 |
txid | 字符串 | 链上交易哈希 |
amount | 金额 | 到账金额 |
fee | 金额 | 收款手续费(最多等于到账金额) |
credited | 金额 | 计入余额的金额(到账金额减手续费) |
result | 字符串 | 固定为 credited。低于 1 USDT 的付款不入账,也不会列出 |
order_no | 字符串 | 匹配到的订单(可能为 null) |
matched_by | 字符串 | deposit、order、manual 之一(可能为 null) |
time | 时间 | 链上付款时间 |
POST/api/v1/withdrawals/ · 发起提币
需要先设置 IP 白名单;提交后立即冻结"金额 + 手续费";每一笔都经过人工审核。
请求体(JSON)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
kind | 字符串 | 是 | payout(付给你的客户)或 cashout(提到开户时登记的钱包) |
chain | 字符串 | 否 | eth、bsc 或 tron(不传是 tron) |
to | 字符串 | 是 | 收款地址:以太坊、BSC 是 0x 地址(大小写混合的必须校验和正确),TRON 是 T 开头的地址;cashout 必须是这条链上登记过的钱包 |
amount | 金额 | 是 | 金额(USDT),最多 6 位小数 |
customer_id | 字符串 | 否 | 代付时填写收款的客户 |
merchant_ref | 字符串 | 否 | 你的备注编号,最多 64 个字符 |
返回
| 字段 | 类型 | 说明 |
|---|---|---|
withdrawal_no | 字符串 | 提币单号 |
kind | 字符串 | payout(付给你的客户)或 cashout(提到你登记的钱包) |
chain | 字符串 | 付款所在的链:eth、bsc 或 tron |
batch_no | 字符串 | 所属的批量打款批次号;单笔提币为 null(可能为 null) |
customer_id | 字符串 | 代付对应的客户(可能为 null) |
to | 字符串 | 收款地址(所在链的地址) |
amount | 金额 | 提币金额 |
fee | 金额 | 提币手续费(提交时冻结"金额 + 手续费") |
status | 字符串 | pending、approved、broadcast、completed、rejected、cancelled、failed 之一 |
txid | 字符串 | 广播后的链上交易哈希(可能为 null) |
reason | 字符串 | 驳回或失败的原因(可能为 null) |
merchant_ref | 字符串 | 你的备注编号(可能为 null) |
source | 字符串 | api(开放 API)或 web(商户后台) |
created_at | 时间 | 创建时间 |
可能的错误:ip_not_allowed、invalid_address、address_not_registered、insufficient_balance、invalid_param
GET/api/v1/withdrawals/ · 查询提币
查询参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
withdrawal_no | 字符串 | 是 | 提币单号 |
返回
| 字段 | 类型 | 说明 |
|---|---|---|
withdrawal_no | 字符串 | 提币单号 |
kind | 字符串 | payout(付给你的客户)或 cashout(提到你登记的钱包) |
chain | 字符串 | 付款所在的链:eth、bsc 或 tron |
batch_no | 字符串 | 所属的批量打款批次号;单笔提币为 null(可能为 null) |
customer_id | 字符串 | 代付对应的客户(可能为 null) |
to | 字符串 | 收款地址(所在链的地址) |
amount | 金额 | 提币金额 |
fee | 金额 | 提币手续费(提交时冻结"金额 + 手续费") |
status | 字符串 | pending、approved、broadcast、completed、rejected、cancelled、failed 之一 |
txid | 字符串 | 广播后的链上交易哈希(可能为 null) |
reason | 字符串 | 驳回或失败的原因(可能为 null) |
merchant_ref | 字符串 | 你的备注编号(可能为 null) |
source | 字符串 | api(开放 API)或 web(商户后台) |
created_at | 时间 | 创建时间 |
可能的错误:not_found
POST/api/v1/withdrawals/cancel/ · 取消提币
请求体(JSON)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
withdrawal_no | 字符串 | 是 | 提币单号;只能取消待审核(pending)的提币。批量打款里的单不能单笔取消,只能整批取消还没审核的部分 |
返回
| 字段 | 类型 | 说明 |
|---|---|---|
withdrawal_no | 字符串 | 提币单号 |
kind | 字符串 | payout(付给你的客户)或 cashout(提到你登记的钱包) |
chain | 字符串 | 付款所在的链:eth、bsc 或 tron |
batch_no | 字符串 | 所属的批量打款批次号;单笔提币为 null(可能为 null) |
customer_id | 字符串 | 代付对应的客户(可能为 null) |
to | 字符串 | 收款地址(所在链的地址) |
amount | 金额 | 提币金额 |
fee | 金额 | 提币手续费(提交时冻结"金额 + 手续费") |
status | 字符串 | pending、approved、broadcast、completed、rejected、cancelled、failed 之一 |
txid | 字符串 | 广播后的链上交易哈希(可能为 null) |
reason | 字符串 | 驳回或失败的原因(可能为 null) |
merchant_ref | 字符串 | 你的备注编号(可能为 null) |
source | 字符串 | api(开放 API)或 web(商户后台) |
created_at | 时间 | 创建时间 |
可能的错误:not_found、invalid_state、batch_item_cancel
POST/api/v1/payouts/batch/ · 提交批量打款
需要先设置 IP 白名单。有一笔不对整个请求返回 422 invalid_rows,error.rows 列出 [{ index, line, code, errors }](index 从 0 起,line = index + 1;code 是这一笔的第一个问题,errors 列出全部问题),不冻结任何金额;全部没问题时一次冻结每笔的"金额 + 手续费",每一笔都经过人工审核。网络费不另收。这个接口的请求体上限是 256 KB。
请求体(JSON)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
items | 数组 | 是 | 最多 500 笔:[{ chain, to, amount, customer_id?, merchant_ref? }](chain 是 eth、bsc 或 tron;amount 是 USDT,最多 6 位小数) |
返回
| 字段 | 类型 | 说明 |
|---|---|---|
batch_no | 字符串 | 批次号 |
source | 字符串 | api(开放 API)或 web(商户后台上传表格) |
status | 字符串 | pending(还有没审核的)、approved、partially_rejected(部分被驳回)、rejected、completed、cancelled 之一 |
count | 整数 | 收下的笔数 |
total | 金额 | 金额合计(不含手续费) |
created_at | 时间 | 创建时间 |
items | 数组 | 逐笔:[{ withdrawal_no, chain, to, amount, fee, status, txid, reason, customer_id, merchant_ref }],按提交顺序 |
service | object | 处理时段(东八区 8:00–23:00):{ open },不在时段内另给 next_open_at(下一个 8:00) |
可能的错误:ip_not_allowed、too_many_items、invalid_rows、insufficient_balance、invalid_param
GET/api/v1/payouts/batch/ · 查询批量打款
查询参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
batch_no | 字符串 | 是 | 批次号 |
返回
| 字段 | 类型 | 说明 |
|---|---|---|
batch_no | 字符串 | 批次号 |
source | 字符串 | api(开放 API)或 web(商户后台上传表格) |
status | 字符串 | pending(还有没审核的)、approved、partially_rejected(部分被驳回)、rejected、completed、cancelled 之一 |
count | 整数 | 收下的笔数 |
total | 金额 | 金额合计(不含手续费) |
created_at | 时间 | 创建时间 |
items | 数组 | 逐笔:[{ withdrawal_no, chain, to, amount, fee, status, txid, reason, customer_id, merchant_ref }],按提交顺序 |
可能的错误:not_found
错误码
出错时返回 { "error": { "code": "…", "message": "…", "field": "…" } }。
| HTTP | code | 含义 |
|---|---|---|
| 400 | idempotency_key_required | POST 请求没有带 Idempotency-Key(8 到 128 位,可用字母、数字和 _ . : -) |
| 400 | invalid_param | 请求体不是合法的 JSON |
| 401 | invalid_signature | 没有签名、签名错误或 API Key 不存在 |
| 401 | timestamp_expired | 时间戳和服务器相差超过 5 分钟 |
| 403 | merchant_disabled | 账户已停用 |
| 403 | order_mode_disabled | 你的账户没有开启订单模式 |
| 403 | ip_not_allowed | 提币请求不是来自白名单 IP(或没有设置白名单) |
| 404 | not_found | 接口不存在,或你的账户里没有这条记录 |
| 409 | duplicate_merchant_order_no | 商户订单号已经存在 |
| 409 | idempotency_conflict | 同一个 Idempotency-Key 用在了不同的请求上 |
| 409 | request_in_progress | 同一个 Idempotency-Key 的请求还在处理中,请稍后重试 |
| 409 | cannot_match | 这笔到账不能匹配到这个订单(不是同一个客户、已经匹配过、没有入账或订单已关闭) |
| 409 | not_matched | 这笔到账没有匹配在这个订单上 |
| 409 | chain_not_enabled | 这条链还没有开放分配新地址(TRON 在后续版本开放) |
| 409 | invalid_state | 当前状态不允许这个操作(例如取消已经不是待审核的提币) |
| 409 | batch_item_cancel | 这笔属于批量打款,不能单笔取消,请整批取消(只取消还没审核的部分) |
| 413 | too_large | 请求体超过 32 KB(POST payouts/batch/ 是 256 KB) |
| 422 | invalid_param | 参数缺失或格式错误,见 error.field |
| 422 | invalid_address | 不是这条链的合法地址 |
| 422 | too_many_items | 一批超过 500 笔 |
| 422 | invalid_rows | 批量打款里有不对的行,见 error.rows = [{ index, line, code, errors }] |
| 422 | address_not_registered | cashout 的地址不是登记过的钱包 |
| 422 | insufficient_balance | 金额加手续费超过了可用余额 |
| 429 | rate_limited | 每秒超过 20 个请求,请稍后重试 |
| 503 | wallet_not_initialized | 服务还没有准备好 |
| 500 | internal_error | 服务器内部错误,可以用同一个 Idempotency-Key 重试 |
沙箱
暂时不提供沙箱。试用阶段请在正式环境用小金额(例如 1 USDT)联调。