Skip to content

Callbacks ​

When a pay-in is received, or a payout finishes (success or refused), the platform sends a POST request to your callback URL.

Callback URL ​

OrderURL used
Pay-incallbackUrl passed when creating the order; otherwise the pay-in callback URL in the merchant console
PayoutcallbackUrl passed when creating the order; otherwise the payout callback URL in the merchant console

The URL must be a publicly reachable https address. Only payouts created through the OpenAPI get callbacks; payouts submitted manually in the merchant console don't.

Request ​

http
POST /your/callback/path HTTP/1.1
Content-Type: application/json

{"encryptedData":"Base64 string"}

encryptedData is JSON encrypted with your public key (PKCS#1 v1.5). Decrypt it with your private key.

Pay-in callback ​

json
{
  "OrderNo": "PI1845290017342758912",
  "MerchantNo": "your merchant number",
  "MerchantOrderNo": "A20261013001",
  "PayTime": "2026-10-13T12:00:00",
  "OrderState": 6,
  "Quantity": 13.7,
  "FiatCurrency": 100.0
}
FieldTypeDescription
OrderNostringPlatform order number
MerchantNostringMerchant number
MerchantOrderNostringYour merchant order number
PayTimestringWhen the customer paid, UTC+8, yyyy-MM-ddTHH:mm:ss
OrderStateintAlways 6 (Released: the platform has confirmed the money and credited the USDT to your balance)
QuantitynumberUSDT amount of the order (before fees)
FiatCurrencynumberFiat amount (the order amount)

Payout callback ​

json
{
  "OrderNo": "PO1845290017342758913",
  "MerchantNo": "your merchant number",
  "MerchantOrderNo": "PO20261013001",
  "PayTime": "2026-10-13T12:30:00",
  "state": 4
}
FieldTypeDescription
OrderNostringPlatform payout number
MerchantNostringMerchant number
MerchantOrderNostringYour merchant order number
PayTimestring | nullWhen it finished, UTC+8
stateint4 succeeded; 5 refused (the frozen USDT has been refunded)

Field name casing

Callback fields start with a capital letter (OrderNo), while the payout state is lowercase state — unlike the camelCase API responses. Parse field names case-insensitively. Numbers in callbacks are not rounded; Get order is the source of truth.

What to reply ​

After processing, reply with HTTP 2xx and a body of either:

  • plain text success (or ok, case-insensitive, no surrounding spaces or newlines), or
  • JSON {"code":200}

Anything else (timeouts, non-2xx, other content) counts as a failure and the platform will retry.

Retries ​

The first callback is sent within 30 seconds after the order completes. Failed callbacks are retried at these intervals, at most 5 attempts in total:

AttemptAfter the previous one
1within 30 seconds of completion
23 minutes
39 minutes
427 minutes
581 minutes

After 5 failures the platform stops. You can still see the order in the merchant console or with Get order. A failed callback does not mean the payment failed.

Required checks ​

Callbacks are not signed

Callbacks are only encrypted with your public key — they are not signed. Anyone who has your public key can forge a "paid" callback, so never credit a customer based on the callback content alone.

Handle each callback in this order (the sample code does all of this):

  1. Decrypt it; if that fails, reply with a failure.
  2. Check the merchant number is yours.
  3. Deduplicate: process the same OrderNo + state only once. The platform retries, and your own processing may be interrupted before you reply success. Put a unique index on "platform order number + state" and record it in the same database transaction as the crediting.
  4. Verify with Get order using MerchantOrderNo, and trust its result:
    • pay-in: the state must be 6 (Released) or 3 (Success), and the amount must equal the callback's FiatCurrency and the amount of the order in your own system;
    • payout: the state must equal the callback's state, and the amount must equal the payout amount in your own system.
  5. Only then credit / update your order, and finally reply success.

Also limit the request body size (a few KB is enough), accept POST only, and don't log full request bodies.