Python — API Checkout
請替換示例憑證
運行前請替換所有佔位符:
"your-api-secret"→ 商戶後台的實際 API Secret"MCH_20240101_ABC123"→ 您的實際商戶號
API Checkout 不使用 X-Api-Key,只需 X-Timestamp 和 X-Signature。
需要開通權限
API Checkout 為獨立產品,需單獨申請開通。請聯繫技術支持啟用。
依賴
bash
pip install requests flask簽名函數
API Checkout 使用與託管支付相同的簽名算法,但不包含 X-Api-Key。
python
import base64
import hashlib
import hmac
import json
import time
import requests
BASE_URL = "https://api.paystablecoin.global"
MERCHANT_ID = "MCH_20240101_ABC123"
API_SECRET = "your-api-secret"
def _hmac_base64(data: str) -> str:
sig = hmac.new(API_SECRET.encode(), data.encode(), hashlib.sha256).digest()
return base64.b64encode(sig).decode()
def sign_post(timestamp: str, path: str, body: str) -> str:
"""為 POST 請求生成簽名。"""
body_hash = base64.b64encode(hashlib.sha256(body.encode()).digest()).decode()
return _hmac_base64(f"{timestamp}\nPOST\n{path}\n{body_hash}")
def sign_get(timestamp: str, path: str) -> str:
"""為無查詢字符串的 GET 請求生成簽名。"""
return _hmac_base64(f"{timestamp}\nGET\n{path}")
def verify_webhook(timestamp: str, signature: str, raw_body: str) -> bool:
"""驗證 Webhook 簽名。"""
expected = _hmac_base64(f"{timestamp}\n{raw_body}")
return expected == signature第一步:獲取可用支付方式
渲染支付界面前必須在運行時調用此接口,禁止硬編碼幣種/網絡組合。
python
def get_payment_methods() -> dict:
timestamp = str(int(time.time() * 1000))
path = f"/api/v1/merchants/{MERCHANT_ID}/checkout/payment-methods"
headers = {
"X-Timestamp": timestamp,
"X-Signature": sign_get(timestamp, path)
}
response = requests.get(BASE_URL + path, headers=headers)
return response.json()
if __name__ == "__main__":
methods = get_payment_methods()
print(json.dumps(methods, indent=2))
# 每個支付方式包含:
# cryptoCurrency, network, networkDisplayName,
# minAmount, maxAmount, estimatedConfirmationTimeSec, displayOrder
# 按 displayOrder 升序排列後展示給用戶。第二步:創建 Checkout 訂單
客戶選擇幣種和網絡後,立即創建訂單。
python
def create_checkout_order(network: str, currency: str, amount: str) -> dict:
timestamp = str(int(time.time() * 1000))
path = f"/api/v1/merchants/{MERCHANT_ID}/checkout/orders"
# orderAmount.currency 必須為穩定幣:USDC、USDT 或 USD1
# network 必須與客戶選擇的支付方式匹配
body = json.dumps({
"merchantOrderId": f"ORDER_{timestamp}",
"orderAmount": {"value": amount, "currency": currency},
"paymentMethodType": "ON_CHAIN_TRANSFER",
"network": network,
"expiresAt": "2026-12-31T23:59:59Z",
"callbackUrl": "https://yoursite.com/webhook/checkout"
}, separators=(",", ":"))
headers = {
"X-Timestamp": timestamp,
"X-Signature": sign_post(timestamp, path, body),
"Content-Type": "application/json"
}
response = requests.post(BASE_URL + path, data=body, headers=headers)
return response.json()
if __name__ == "__main__":
result = create_checkout_order(network="tron", currency="USDC", amount="100.50")
print(json.dumps(result, indent=2))
# 展示給客戶:
# result["data"]["depositAddress"] — 文字 + 二維碼
# result["data"]["cryptoPaymentAmount"]["value"] — 需支付的精確金額
# result["data"]["networkDisplayName"] — 醒目展示(轉錯網絡=資產丟失)
# result["data"]["expiresAt"] — 倒計時依據
#
# 重要:展示 cryptoPaymentAmount,而非 orderAmount第三步:查詢 Checkout 訂單狀態
當 Webhook 未收到時作為備選方案。
python
def query_checkout_order(merchant_order_id: str) -> dict:
timestamp = str(int(time.time() * 1000))
path = f"/api/v1/merchants/{MERCHANT_ID}/checkout/orders/{merchant_order_id}"
headers = {
"X-Timestamp": timestamp,
"X-Signature": sign_get(timestamp, path)
}
response = requests.get(BASE_URL + path, headers=headers)
return response.json()
if __name__ == "__main__":
# 每 5-10 秒輪詢一次,直到狀態達到終態
result = query_checkout_order("ORDER_1738112345000")
status = result.get("data", {}).get("status")
print(f"狀態:{status}")
# 終態:SUCCEEDED、FAILED 或 CLOSED第四步:驗證 Webhook 簽名
API Checkout Webhook 要求嚴格響應:5 秒內返回 HTTP 200 + {"code":"00000"}。
python
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route("/webhook/checkout", methods=["POST"])
def checkout_webhook():
timestamp = request.headers.get("X-Timestamp", "")
signature = request.headers.get("X-Signature", "")
raw_body = request.get_data(as_text=True)
# 第一步:驗證簽名
if not verify_webhook(timestamp, signature, raw_body):
return jsonify({"error": "Invalid signature"}), 401
# 第二步:驗證時間戳在 5 分鐘內
if abs(int(time.time() * 1000) - int(timestamp)) > 300_000:
return jsonify({"error": "Timestamp expired"}), 401
# 第三步:5 秒內返回 HTTP 200 + {"code":"00000"}(必須)
# 異步處理業務邏輯
event = request.get_json(force=True)
acquiring_order_id = event.get("acquiringOrderId")
merchant_order_id = event.get("merchantOrderId")
# 使用任一 ID 進行去重——同一事件可能多次推送。
return jsonify({"code": "00000"})相關文檔
- API Checkout 整合指南 — 分步整合說明
- API Checkout 參考文檔 — 完整接口與字段說明
- 簽名算法 — 簽名計算詳解