付款 API 文件
付款服務會依付款訂單將 USDT 下發至指定的 TRON 地址,並在鏈上結果確認後通知商戶。商戶請使用下列 API 路徑建立與查詢付款訂單;如需取消待下發之付款訂單,請聯繫平台客服或管理員處理。
⚠️ 重要提示:付款(代付)功能強制要求使用 RSA-SHA256 簽章演算法(不支援 MD5)。商戶啟用代付功能必須在後台登記商戶 RSA 公鑰,並使用配對私鑰對每個請求簽章。商戶私鑰均不得提供給前端或第三方。
1. 建立付款訂單
介面
POST /payout/create
請求參數
| 參數 | 類型 | 必填 | 說明 |
|---|---|---|---|
mid |
string | 是 | 商戶 ID。 |
orderid |
string | 是 | 商戶付款訂單號;同一商戶下必須唯一。 |
amount |
decimal | 是 | 系統結算幣種金額(依系統設定,預設 TWD 新台幣),必須大於 0,最多 2 位小數。 |
toaddress |
string | 是 | TRON/TRC20 收款地址,必須為 34 位、以 T 開頭。 |
notifyurl |
string | 是 | 付款結果通知地址,必須是有效的 http 或 https URL。 |
sign |
string | 是 | 請求簽章,規則見下文。 |
服務會在建立時按照當前系統匯率換算 uamount,並將該值固定到訂單中;之後即使修改匯率,也不會影響已建立訂單。
成功回應
{
"status": "ok",
"orderid": "PAYOUT_20260726_001",
"amount": 100,
"uamount": 13.88,
"toaddress": "TVArfDmKDux1LSQYavBTEgnyhaF5Ta3xBU",
"created_time": 1785050247
}
失敗回應
{
"status": "error",
"message": "商戶訂單號已存在"
}
常見失敗原因:參數缺失、TRON 地址格式錯誤、通知地址格式錯誤、簽章錯誤、來源 IP 不在商戶付款 API 白名單內、當前匯率不可用、同一商戶訂單號重複,或同一錢包地址 5 分鐘內發送相同金額。
2. 查詢付款訂單
介面
POST /payout/checkorder
請求參數
| 參數 | 類型 | 必填 | 說明 |
|---|---|---|---|
mid |
string | 是 | 商戶 ID。 |
orderid |
string | 是 | 建立付款訂單時使用的商戶訂單號。 |
sign |
string | 是 | 請求簽章。 |
成功回應
{
"status": "ok",
"orderid": "PAYOUT_20260726_001",
"status_code": 0,
"amount": 100,
"uamount": 13.88,
"toaddress": "TVArfDmKDux1LSQYavBTEgnyhaF5Ta3xBU",
"updatetime": 1785050247
}
status_code 含義:
| 值 | 狀態 | 類型 | 說明 |
|---|---|---|---|
0 |
待下發 | 處理中 | 訂單已建立,等待出款服務領取。 |
1 |
已發送 | 終態 | 鏈上轉帳成功並已確認。 |
2 |
已取消 | 終態 | 訂單已由平台管理員或客服手動取消。 |
3 |
下發中 | 處理中 | 出款服務已領取並鎖定訂單,準備進行廣播。 |
4 |
已送出 | 處理中 | 交易已廣播至區塊鏈,正在等待鏈上打包回執。 |
5 |
下發失敗 | 終態 | 廣播失敗或鏈上執行失敗。 |
6 |
待人工核查 | 異常掛起 | 下發請求結果無法判定(如逾時或連線中斷);須人工確認鏈上結果。 |
📌 終態說明:只有進入終態(
1已發送、2已取消、5下發失敗)的訂單才會觸發商戶非同步通知回呼;處理中狀態(0、3、4)與異常掛起(6)不會發送通知。
付款結果通知回呼
付款訂單進入終態後,payoutworker 會以 application/x-www-form-urlencoded
向建立訂單時傳入的 notifyurl 發起 HTTP POST 通知回呼。終態包括已發送、已取消和下發失敗。狀態 6(待人工核查)不會發起通知,避免在下發結果無法判定時錯誤通知商戶。
通知會按原入款通知服務相同的規則重試:HTTP 狀態碼為 200 即視為成功;其他狀態碼或網路錯誤會指數退避重試,最多 9 次。
通知回呼參數
| 參數 | 類型 | 說明 |
|---|---|---|
orderid |
string | 商戶付款訂單號。 |
amount |
string | 系統結算幣種訂單金額,固定 2 位小數。 |
uamount |
string | 實際下發 USDT 金額,固定 2 位小數。 |
created_time |
string | 訂單建立時間,Unix 秒級時間戳。 |
success_time |
string | 訂單進入終態的時間,Unix 秒級時間戳。欄位名為相容原通知服務而保留。 |
status |
string | 終態:1 已發送、2 已取消、5 下發失敗。 |
txid |
string | TRON 交易 ID;取消或送出前失敗時為空。 |
sign |
string | 通知簽章,見下方規則。 |
通知回呼簽章
除 sign 外的所有通知參數按參數名 ASCII 升序排列,拼接為
key=value&...(不做 URL 編碼)。
平台使用 RSA PKCS#1 v1.5 + SHA-256 簽章後將標準 Base64 結果放入 sign,商戶使用在後台獲取的平台 RSA 公鑰驗證通知簽章。
簽章原文範例(已發送):
amount=100.00&created_time=1785050247&orderid=PAYOUT_20260726_001&status=1&success_time=1785050301&txid=交易ID&uamount=13.89
商戶收到並驗簽成功後應盡快回傳 HTTP 200;無需等待鏈上確認,因為付款服務僅會在鏈上回執已確認終態後發送此通知。
3. 簽章規則
付款 API 強制使用 RSA-SHA256 簽章演算法:
- 取請求內除
sign外的所有參數。 - 按參數名 ASCII 升序排序並拼成
key=value&key=value,不要 URL 編碼。 - 用商戶 RSA 私鑰執行
SHA256withRSA(RSA PKCS#1 v1.5),將結果標準 Base64 編碼為sign;平台用後台登記的商戶 RSA 公鑰驗簽,通知由平台私鑰簽章、商戶用平台公鑰驗簽。
建立訂單時待簽章原文範例:
amount=100.00&mid=1001¬ifyurl=https://merchant.example.com/payout/notify&orderid=PAYOUT_20260726_001&toaddress=TVArfDmKDux1LSQYavBTEgnyhaF5Ta3xBU
查詢訂單時待簽章原文範例:
mid=1001&orderid=PAYOUT_20260726_001
簽章核心邏輯
以下範例從建立付款訂單的業務欄位構建排序後的簽章原文;請求使用商戶私鑰簽章,付款通知使用平台公鑰驗簽。
$params = ['mid'=>'1001', 'orderid'=>'PAYOUT_001', 'amount'=>'100.00', 'toaddress'=>'T...', 'notifyurl'=>'https://merchant.example/notify'];
ksort($params, SORT_STRING);
$source = implode('&', array_map(fn($k) => $k.'='.$params[$k], array_keys($params)));
openssl_sign($source, $raw, $privateKey, OPENSSL_ALGO_SHA256);
$params['sign'] = base64_encode($raw);
params := map[string]string{"mid":"1001", "orderid":"PAYOUT_001", "amount":"100.00", "toaddress":"T...", "notifyurl":"https://merchant.example/notify"}
keys := make([]string, 0, len(params)); for k := range params { keys = append(keys, k) }; sort.Strings(keys)
parts := make([]string, 0, len(keys)); for _, k := range keys { parts = append(parts, k+"="+params[k]) }; source := strings.Join(parts, "&")
digest := sha256.Sum256([]byte(source))
raw, _ := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, digest[:])
params["sign"] = base64.StdEncoding.EncodeToString(raw)
Map<String,String> params = new TreeMap<>();
params.put("mid", "1001"); params.put("orderid", "PAYOUT_001"); params.put("amount", "100.00"); params.put("toaddress", "T..."); params.put("notifyurl", "https://merchant.example/notify");
String source = params.entrySet().stream().map(e -> e.getKey()+"="+e.getValue()).collect(Collectors.joining("&"));
Signature s = Signature.getInstance("SHA256withRSA");
s.initSign(privateKey); s.update(source.getBytes(StandardCharsets.UTF_8));
params.put("sign", Base64.getEncoder().encodeToString(s.sign()));
通知回呼驗簽核心邏輯
取出通知中的 sign 並從參數集合移除,其餘參數排序後拼接。platformPublicKey 為後台展示的平台公鑰。
$signature = base64_decode($params['sign']); unset($params['sign']); ksort($params, SORT_STRING);
$source = implode('&', array_map(fn($k) => $k.'='.$params[$k], array_keys($params)));
$valid = openssl_verify($source, $signature, $platformPublicKey, OPENSSL_ALGO_SHA256) === 1;
signature, _ := base64.StdEncoding.DecodeString(params["sign"]); delete(params, "sign")
keys := make([]string, 0, len(params)); for k := range params { keys = append(keys, k) }; sort.Strings(keys)
parts := make([]string, 0, len(keys)); for _, k := range keys { parts = append(parts, k+"="+params[k]) }; digest := sha256.Sum256([]byte(strings.Join(parts, "&")))
err := rsa.VerifyPKCS1v15(platformPublicKey, crypto.SHA256, digest[:], signature)
byte[] signature = Base64.getDecoder().decode(params.remove("sign"));
String source = params.entrySet().stream().sorted(Map.Entry.comparingByKey()).map(e -> e.getKey()+"="+e.getValue()).collect(Collectors.joining("&"));
Signature verifier = Signature.getInstance("SHA256withRSA");
verifier.initVerify(platformPublicKey); verifier.update(source.getBytes(StandardCharsets.UTF_8));
boolean valid = verifier.verify(signature);