Webhook Notifications
PSC sends webhook notifications to your server when payment events occur. Provide a callbackUrl (for payment webhooks) or notifyUrl (for refund webhooks) to receive real-time status updates without polling.
1. Payment Webhook
When a Hosted Payment order status changes, PSC sends a POST request to the callbackUrl you provided when creating the order.
Triggered Statuses
- PROCESSING — Payment transaction detected on the blockchain
- SUCCEEDED — Payment confirmed and completed
Request Headers
| Header | Type | Description | Example |
|---|---|---|---|
X-Timestamp | Long | Notification timestamp (milliseconds) | 1737554400000 |
X-Signature | String | Request signature (Base64-encoded). Algorithm: Base64(HMAC-SHA256(timestamp + "\n" + "POST" + "\n" + path + "\n" + Base64(SHA256(requestBody)), apiSecret)) | base64-hmac-sha256 |
Request Body Fields
| Field | Type | Description |
|---|---|---|
acquiringOrderId | String | Platform order ID |
paymentOrderId | String | Payment order ID |
merchantOrderId | String | Your merchant order ID |
status | String | Order status: PROCESSING or SUCCEEDED |
orderAmount | Object | Original order amount |
cryptoPaymentAmount | Object | Expected crypto payment amount |
cryptoPaidAmount | Object | Actual crypto amount paid by customer |
cryptoPaymentDetail | Object | Blockchain transaction details |
createdTime | String | Order creation time (UTC, ISO 8601) |
paidTime | String | Payment completion time (UTC, ISO 8601) |
Signature Verification
- Extract
X-TimestampandX-Signaturefrom the request headers - Read the complete raw request body as a string (do not re-serialize)
- Compute:
bodySha256Base64 = Base64(SHA256(requestBody))signContent = timestamp + "\n" + "POST" + "\n" + path + "\n" + bodySha256Base64expectedSignature = Base64(HMAC-SHA256(signContent, apiSecret))
- Compare
expectedSignaturewithX-Signature - Verify the timestamp is within 5 minutes to prevent replay attacks
Response Requirements
Return HTTP 200 immediately upon receiving the notification. Process any business logic asynchronously after returning 200 to avoid timeouts.
Any response other than HTTP 200, or a timeout, will trigger a retry.
Retry Schedule
| Attempt | Delay after previous failure |
|---|---|
| 1 | 1 minute |
| 2 | 5 minutes |
| 3 | 15 minutes |
| 4 | 30 minutes |
| 5 | 1 hour |
| 6 | 2 hours |
After all retries are exhausted, query the order status manually using the Query Order API.
Examples
PROCESSING event:
{
"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 event:
{
"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. Refund Webhook
When a refund status changes, PSC sends a POST request to the notifyUrl provided when creating the refund (or the merchant's default notify URL).
Request Headers
| Header | Type | Description | Example |
|---|---|---|---|
X-Timestamp | Long | Notification timestamp (milliseconds) | 1706428835000 |
X-Signature | String | Request signature (Base64-encoded). Algorithm: Base64(HMAC-SHA256(timestamp + "\n" + "POST" + "\n" + path + "\n" + Base64(SHA256(requestBody)), apiSecret)) | base64-hmac-sha256 |
Request Body Fields
| Field | Type | Description | Example |
|---|---|---|---|
refundOrderId | String | PSC refund order ID | REF_20260128120001 |
merchantRefundOrderId | String | Merchant refund order ID | REFUND_20260128_001 |
acquiringOrderId | String | Original payment order ID | ORDER_20260128_001 |
status | String | Refund status: SUCCEEDED / FAILED / CLOSED | SUCCEEDED |
refundCryptoAmount | Object | Refund crypto amount | {"value": "100.50", "currency": "USDT"} |
refundCryptoAmount.value | String | Refund amount | 100.50 |
refundCryptoAmount.currency | String | Refund currency | USDT |
refundTime | String (ISO 8601) | Time the refund was completed | 2026-01-28T12:05:30 |
refundCryptoTxHash | String | On-chain transaction hash of the refund | 0xabc123... |
refundNetwork | String | Blockchain network used for the refund | TRON |
refundChannel | String | Refund channel | EXCHANGE |
exchangeOrderId | String | Exchange-side order ID | BINANCE_ORDER_123 |
merchantDeductAmount | Object | Amount deducted from merchant | {"value": "99.50", "currency": "USDT"} |
merchantDeductAmount.value | String | Deducted amount | 99.50 |
merchantDeductAmount.currency | String | Deducted currency | USDT |
exchangeType | String | Exchange type | BINANCE_PAY |
Signature Verification
- Extract
X-TimestampandX-Signaturefrom the request headers - Read the complete raw request body as a string (do not re-serialize)
- Compute:
bodySha256Base64 = Base64(SHA256(requestBody))signContent = timestamp + "\n" + "POST" + "\n" + path + "\n" + bodySha256Base64expectedSignature = Base64(HMAC-SHA256(signContent, apiSecret))
- Compare
expectedSignaturewithX-Signature - Verify the timestamp is within 5 minutes to prevent replay attacks
Response Requirements
Return HTTP 200 immediately upon receiving the notification. The response body is not strictly required — the following is recommended but not mandatory:
{
"code": "00000",
"message": "Success"
}Any response other than HTTP 200 will be treated as a failure. Process any business logic asynchronously after returning 200 to avoid timeouts.
Examples
SUCCEEDED event:
{
"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 event:
{
"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 event:
{
"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"
}Related Documentation
- Hosted Payments Integration Guide — Step-by-step integration walkthrough
- API Reference — Full endpoint and field specification
- Signature Algorithm — Signature calculation details
- Java — Hosted Payments — Java webhook verification example
- Python — Hosted Payments — Python webhook verification example