Skip to content

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

HeaderTypeDescriptionExample
X-TimestampLongNotification timestamp (milliseconds)1737554400000
X-SignatureStringRequest signature (Base64-encoded). Algorithm: Base64(HMAC-SHA256(timestamp + "\n" + "POST" + "\n" + path + "\n" + Base64(SHA256(requestBody)), apiSecret))base64-hmac-sha256

Request Body Fields

FieldTypeDescription
acquiringOrderIdStringPlatform order ID
paymentOrderIdStringPayment order ID
merchantOrderIdStringYour merchant order ID
statusStringOrder status: PROCESSING or SUCCEEDED
orderAmountObjectOriginal order amount
cryptoPaymentAmountObjectExpected crypto payment amount
cryptoPaidAmountObjectActual crypto amount paid by customer
cryptoPaymentDetailObjectBlockchain transaction details
createdTimeStringOrder creation time (UTC, ISO 8601)
paidTimeStringPayment completion time (UTC, ISO 8601)

Signature Verification

  1. Extract X-Timestamp and X-Signature from the request headers
  2. Read the complete raw request body as a string (do not re-serialize)
  3. Compute:
    • bodySha256Base64 = Base64(SHA256(requestBody))
    • signContent = timestamp + "\n" + "POST" + "\n" + path + "\n" + bodySha256Base64
    • expectedSignature = Base64(HMAC-SHA256(signContent, apiSecret))
  4. Compare expectedSignature with X-Signature
  5. 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

AttemptDelay after previous failure
11 minute
25 minutes
315 minutes
430 minutes
51 hour
62 hours

After all retries are exhausted, query the order status manually using the Query Order API.

Examples

PROCESSING event:

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 event:

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. 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

HeaderTypeDescriptionExample
X-TimestampLongNotification timestamp (milliseconds)1706428835000
X-SignatureStringRequest signature (Base64-encoded). Algorithm: Base64(HMAC-SHA256(timestamp + "\n" + "POST" + "\n" + path + "\n" + Base64(SHA256(requestBody)), apiSecret))base64-hmac-sha256

Request Body Fields

FieldTypeDescriptionExample
refundOrderIdStringPSC refund order IDREF_20260128120001
merchantRefundOrderIdStringMerchant refund order IDREFUND_20260128_001
acquiringOrderIdStringOriginal payment order IDORDER_20260128_001
statusStringRefund status: SUCCEEDED / FAILED / CLOSEDSUCCEEDED
refundCryptoAmountObjectRefund crypto amount{"value": "100.50", "currency": "USDT"}
refundCryptoAmount.valueStringRefund amount100.50
refundCryptoAmount.currencyStringRefund currencyUSDT
refundTimeString (ISO 8601)Time the refund was completed2026-01-28T12:05:30
refundCryptoTxHashStringOn-chain transaction hash of the refund0xabc123...
refundNetworkStringBlockchain network used for the refundTRON
refundChannelStringRefund channelEXCHANGE
exchangeOrderIdStringExchange-side order IDBINANCE_ORDER_123
merchantDeductAmountObjectAmount deducted from merchant{"value": "99.50", "currency": "USDT"}
merchantDeductAmount.valueStringDeducted amount99.50
merchantDeductAmount.currencyStringDeducted currencyUSDT
exchangeTypeStringExchange typeBINANCE_PAY

Signature Verification

  1. Extract X-Timestamp and X-Signature from the request headers
  2. Read the complete raw request body as a string (do not re-serialize)
  3. Compute:
    • bodySha256Base64 = Base64(SHA256(requestBody))
    • signContent = timestamp + "\n" + "POST" + "\n" + path + "\n" + bodySha256Base64
    • expectedSignature = Base64(HMAC-SHA256(signContent, apiSecret))
  4. Compare expectedSignature with X-Signature
  5. 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:

json
{
  "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:

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 event:

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 event:

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"
}

Released under the MIT License.