收款 API 文件

下載 Markdown 原始檔

支付API文檔

1. 創建訂單 API

介面訊息

請求參數

參數名 類型 必填 說明
mid int 商戶ID
orderid string 商戶訂單號,唯一標識
amount float 訂單金額(法幣)
notifyurl string 支付結果通知URL
sign string 簽章,請參考文檔末尾簽章方法

回應格式

成功回應

{
    "status": "ok",
    "orderid": "ORDER123456",
    "address": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
    "uamount": "0.12",
    "url": "https://demosite.com/pay?id=abc123def456"
}

錯誤回應

{
    "status": "error",
    "message": "系統繁忙,請稍後再試!"
}

回應欄位說明

欄位名 類型 說明
status string 回應狀態:ok-成功,error-失敗
orderid string 商戶訂單號
address string 錢包地址
uamount string 需要支付的USDT數量
url string 支付頁面URL,請將使用者浏覽器跳轉到此地址。或者商戶自行展示上述錢包和金額訊息。
message string 錯誤訊息(僅失敗時回傳)

注意事項

  1. 訂單號唯一性:每個商戶的訂單號必須唯一。
  2. 金額精度:支援最多2位小數
  3. 通知URL:必須是可訪问的HTTPS地址
  4. 金額調整:在交易繁忙時,系統會自動調整USDT支付金額(最多+0.09)
  5. 來源 IP 白名單:管理後台可為商戶設定收款 API IP 白名單;設定後,僅白名單中的 IP 可建立收款訂單。

2. 查詢訂單狀態 API

介面訊息

請求參數

參數名 類型 必填 說明
mid string 商戶ID
orderid string 商戶訂單號
sign string 簽章,請參考文檔末尾簽章方法

回應格式

成功回應

{
    "status": "ok",
    "orderid": "ORDER123456",
    "status_code": 0,
    "amount": 100.00,
    "uamount": 0.12,
    "updatetime": 1640995200
}

錯誤回應

{
    "status": "error",
    "message": "訂單不存在"
}

回應欄位說明

欄位名 類型 說明
orderid string 商戶訂單號
status_code int 訂單狀態:0-未支付,1-已支付
amount float 訂單金額(法幣)
uamount float USDT金額
updatetime int 更新時間戳(status_code=0時為創建時間,status=1時為成功時間)
status string 回應狀態:ok-成功,error-失敗
message string 錯誤訊息(僅失敗時回傳)

注意事項

  1. 訂單查詢:只能查詢本商戶的訂單
  2. 時間戳:updatetime欄位根據訂單狀態回傳不同時間
    • status=0:回傳訂單創建時間
    • status=1:回傳訂單成功時間

3. 支付回調通知

回調機制說明

當訂單支付成功時,系統會主動向商戶提供的notifyurl發送POST請求通知支付結果。

回調參數

參數名 類型 說明
orderid string 商戶訂單號
amount float 訂單金額(法幣)
uamount float USDT支付金額
created_time int 訂單創建時間戳
success_time int USDT轉帳時間
sign string 平台以 RSA-SHA256 生成的 Base64 簽章

回調回應要求

商戶收到回調後,必須回傳HTTP狀態碼200,表示成功接收。如果回傳非200狀態碼,系統會认為回調失敗並進行重試。 通知收妥後務請回應成功訊息,避免系統徒勞的多次重試堵塞通道。

成功回應示例:

success

回調失敗重試機制

4. 簽章規則

RSA-SHA256 簽章

商戶在後台創建商戶資料時上傳 RSA 公鑰(PEM / PKIX),並自行安全保管配對私鑰。所有 API 請求由商戶私鑰使用 RSA-SHA256(PKCS#1 v1.5 + SHA-256)簽章;平台用該商戶公鑰驗簽。平台會自動生成自身金鑰對,後台“平台公鑰”介面回傳公鑰;商戶必須儲存該公鑰並用它驗證回調。

  1. 將除 sign 外的參數按參數名 ASCII 升序排序。
  2. 拼為 key=value&key=value,原始值不做 URL 编碼。
  3. 對 UTF-8 原文計算 SHA-256,並以 RSA PKCS#1 v1.5 簽章。
  4. 將簽章字節作標準 Base64 编碼,放入 sign 參數。

簽章示例

原始參數:
mid=1001
orderid=ORDER123456
amount=100.00
notifyurl=https://example.com/notify

排序後:
amount=100.00&mid=1001&notifyurl=https://example.com/notify&orderid=ORDER123456

以上排序後的字符串即簽章原文;用商戶 RSA 私鑰簽章並 Base64 编碼。

注意事項

簽章核心邏輯

以下示例先用實際業務欄位構建請求參數與簽章原文,privateKey 為商戶私鑰;將得到的 sign 放回參數集合後送出。回調驗簽時改用平台公鑰和對應語言的驗簽 API。

$params = ['mid'=>'1001', 'orderid'=>'ORDER123456', 'amount'=>'100.00', 'notifyurl'=>'https://example.com/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":"ORDER123456", "amount":"100.00", "notifyurl":"https://example.com/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, "&")
hash := sha256.Sum256([]byte(source))
raw, _ := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hash[:])
params["sign"] = base64.StdEncoding.EncodeToString(raw)
Map<String,String> params = new TreeMap<>();
params.put("mid", "1001"); params.put("orderid", "ORDER123456"); params.put("amount", "100.00"); params.put("notifyurl", "https://example.com/notify");
String source = params.entrySet().stream().map(e -> e.getKey()+"="+e.getValue()).collect(Collectors.joining("&"));
Signature signer = Signature.getInstance("SHA256withRSA");
signer.initSign(privateKey); signer.update(source.getBytes(StandardCharsets.UTF_8));
params.put("sign", Base64.getEncoder().encodeToString(signer.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);