Webhook 通知
PSC 在支付事件發生時向您的服務器發送 Webhook 通知。創建訂單時提供 callbackUrl(支付 Webhook)或 notifyUrl(退款 Webhook),即可無需輪詢即時接收狀態更新。
1. 支付 Webhook
託管支付訂單狀態發生變更時,PSC 向您創建訂單時提供的 callbackUrl 發送 POST 請求。
觸發狀態
- PROCESSING — 在區塊鏈上檢測到支付交易
- SUCCEEDED — 支付確認並成功完成
Webhook 請求頭
| 請求頭 | 類型 | 說明 | 示例 |
|---|---|---|---|
X-Timestamp | Long | 通知時間戳(毫秒) | 1737554400000 |
X-Signature | String | 請求簽名(Base64 編碼)。算法:Base64(HMAC-SHA256(timestamp + "\n" + "POST" + "\n" + path + "\n" + Base64(SHA256(requestBody)), apiSecret)) | base64-hmac-sha256 |
Webhook Body 字段
| 字段 | 類型 | 說明 |
|---|---|---|
acquiringOrderId | String | 平台訂單 ID |
paymentOrderId | String | 支付訂單 ID |
merchantOrderId | String | 商戶訂單 ID |
status | String | 訂單狀態:PROCESSING 或 SUCCEEDED |
orderAmount | Object | 原始訂單金額 |
cryptoPaymentAmount | Object | 預期加密貨幣支付金額 |
cryptoPaidAmount | Object | 客戶實際支付的加密貨幣金額 |
cryptoPaymentDetail | Object | 區塊鏈交易詳情 |
createdTime | String | 訂單創建時間(UTC,ISO 8601) |
paidTime | String | 支付完成時間(UTC,ISO 8601) |
簽名驗證
- 從請求頭中獲取
X-Timestamp和X-Signature - 讀取完整的原始請求體字符串(不能反序列化再序列化)
- 計算:
bodySha256Base64 = Base64(SHA256(requestBody))signContent = timestamp + "\n" + "POST" + "\n" + path + "\n" + bodySha256Base64expectedSignature = Base64(HMAC-SHA256(signContent, apiSecret))
- 將
expectedSignature與X-Signature對比 - 驗證時間戳在 5 分鐘內有效,防止重放攻擊
響應要求
收到通知後,請立即返回 HTTP 200。請在返回 200 後異步處理業務邏輯,避免超時。
非 HTTP 200 的響應或超時均會觸發重試。
重試策略
| 次數 | 距上次失敗的延遲 |
|---|---|
| 1 | 1 分鐘 |
| 2 | 5 分鐘 |
| 3 | 15 分鐘 |
| 4 | 30 分鐘 |
| 5 | 1 小時 |
| 6 | 2 小時 |
所有重試均失敗後,請使用查詢訂單 API 手動查詢訂單狀態。
示例
PROCESSING 事件:
json
{
"acquiringOrderId": "ACQ20250121001",
"paymentOrderId": "PAY20250121001",
"merchantOrderId": "order-123456",
"status": "PROCESSING",
"orderAmount": {
"value": "99.99",
"currency": "USD"
},
"cryptoPaymentAmount": {
"value": "100.000000",
"currency": "USDT"
},
"cryptoPaidAmount": {
"value": "100.000000",
"currency": "USDT"
},
"cryptoPaymentDetail": {
"chain": "TRON",
"txHash": "0xabc123...",
"fromAddress": "T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb",
"toAddress": "TJRabPrwbZy45CUjUKdZLJ3WFzEKKE5Rmh",
"blockNumber": 12345678,
"confirmations": 3
},
"createdTime": "2025-01-21T10:30:00Z",
"paidTime": null
}SUCCEEDED 事件:
json
{
"acquiringOrderId": "ACQ20250121001",
"paymentOrderId": "PAY20250121001",
"merchantOrderId": "order-123456",
"status": "SUCCEEDED",
"orderAmount": {
"value": "99.99",
"currency": "USD"
},
"cryptoPaymentAmount": {
"value": "100.000000",
"currency": "USDT"
},
"cryptoPaidAmount": {
"value": "100.123456",
"currency": "USDT"
},
"cryptoPaymentDetail": {
"chain": "TRON",
"txHash": "0xabc123...",
"fromAddress": "T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb",
"toAddress": "TJRabPrwbZy45CUjUKdZLJ3WFzEKKE5Rmh",
"blockNumber": 12345678,
"confirmations": 20
},
"createdTime": "2025-01-21T10:30:00Z",
"paidTime": "2025-01-21T10:30:15Z"
}2. 退款 Webhook
當退款狀態發生變更時,PSC 向商戶創建退款時提供的 notifyUrl(或商戶默認通知地址)發送 POST 請求。
Webhook 請求頭
| 請求頭 | 類型 | 說明 | 示例 |
|---|---|---|---|
X-Timestamp | Long | 通知時間戳(毫秒) | 1706428835000 |
X-Signature | String | 請求簽名(Base64 編碼)。算法:Base64(HMAC-SHA256(timestamp + "\n" + "POST" + "\n" + path + "\n" + Base64(SHA256(requestBody)), apiSecret)) | base64-hmac-sha256 |
Webhook Body 字段
| 字段 | 類型 | 說明 | 示例 |
|---|---|---|---|
refundOrderId | String | PSC 退款單號 | REF_20260128120001 |
merchantRefundOrderId | String | 商戶退款單號 | REFUND_20260128_001 |
acquiringOrderId | String | 原支付單號 | ORDER_20260128_001 |
status | String | 退款狀態:SUCCEEDED / FAILED / CLOSED | SUCCEEDED |
refundCryptoAmount | Object | 退款加密貨幣金額 | {"value": "100.50", "currency": "USDT"} |
refundCryptoAmount.value | String | 退款金額 | 100.50 |
refundCryptoAmount.currency | String | 退款幣種 | USDT |
refundTime | String(ISO 8601) | 退款完成時間 | 2026-01-28T12:05:30 |
refundCryptoTxHash | String | 退款鏈上交易哈希 | 0xabc123... |
refundNetwork | String | 退款使用的區塊鏈網絡 | TRON |
refundChannel | String | 退款渠道 | EXCHANGE |
exchangeOrderId | String | 交易所側訂單號 | BINANCE_ORDER_123 |
merchantDeductAmount | Object | 商戶扣款金額 | {"value": "99.50", "currency": "USDT"} |
merchantDeductAmount.value | String | 扣款金額 | 99.50 |
merchantDeductAmount.currency | String | 扣款幣種 | USDT |
exchangeType | String | 交易所類型 | BINANCE_PAY |
簽名驗證
- 從請求頭中獲取
X-Timestamp和X-Signature - 讀取完整的原始請求體字符串(不能反序列化再序列化)
- 計算:
bodySha256Base64 = Base64(SHA256(requestBody))signContent = timestamp + "\n" + "POST" + "\n" + path + "\n" + bodySha256Base64expectedSignature = Base64(HMAC-SHA256(signContent, apiSecret))
- 將
expectedSignature與X-Signature對比 - 驗證時間戳在 5 分鐘內有效,防止重放攻擊
響應要求
收到 Webhook 通知後,請立即返回 HTTP 200。響應體內容不限,建議返回以下格式,但非強制:
json
{
"code": "00000",
"message": "Success"
}非 HTTP 200 的響應均視為失敗。請在返回 200 後異步處理業務邏輯,避免超時。
示例
SUCCEEDED 事件:
json
{
"refundOrderId": "REF_20260128120001",
"merchantRefundOrderId": "REFUND_20260128_001",
"acquiringOrderId": "ORDER_20260128_001",
"status": "SUCCEEDED",
"refundCryptoAmount": {
"value": "100.50",
"currency": "USDT"
},
"refundTime": "2026-01-28T12:05:30",
"refundCryptoTxHash": "0xabc123...",
"refundNetwork": "TRON",
"refundChannel": "EXCHANGE",
"exchangeOrderId": "BINANCE_ORDER_123",
"merchantDeductAmount": {
"value": "99.50",
"currency": "USDT"
},
"exchangeType": "BINANCE_PAY"
}FAILED 事件:
json
{
"refundOrderId": "REF_20260128120002",
"merchantRefundOrderId": "REFUND_20260128_002",
"acquiringOrderId": "ORDER_20260128_002",
"status": "FAILED",
"refundCryptoAmount": {
"value": "75.00",
"currency": "USDT"
},
"refundNetwork": "TRON",
"refundChannel": "EXCHANGE",
"exchangeOrderId": "BINANCE_ORDER_456",
"merchantDeductAmount": {
"value": "74.25",
"currency": "USDT"
},
"exchangeType": "BINANCE_PAY"
}CLOSED 事件:
json
{
"refundOrderId": "REF_20260128120003",
"merchantRefundOrderId": "REFUND_20260128_003",
"acquiringOrderId": "ORDER_20260128_003",
"status": "CLOSED",
"refundCryptoAmount": {
"value": "50.00",
"currency": "USDT"
},
"refundNetwork": "TRON",
"refundChannel": "EXCHANGE",
"exchangeOrderId": "BINANCE_ORDER_789",
"merchantDeductAmount": {
"value": "49.50",
"currency": "USDT"
},
"exchangeType": "BINANCE_PAY"
}相關文檔
- 託管支付整合指南 — 分步整合說明
- API 參考 — 完整接口與字段說明
- 簽名算法 — 簽名計算詳解
- Java — 託管支付 — Java Webhook 驗證示例
- Python — 託管支付 — Python Webhook 驗證示例