Tài liệu tích hợp
Trang này chưa có bản Tiếng Việt; bên dưới là bản English.

Callbacks

When a payout has a result or a collection has been paid, we POST the result to the callback URL you gave. Store it first, then reply success.

Callbacks only go to public addresses: a callback URL whose host is an internal or local IP (127.0.0.1, 10.x, 192.168.x and the like) is refused when you submit it; a domain that resolves to such an address is not called when the callback is due, which counts as a failed attempt and is retried as usual.

Payout callback std1

Sent once each time the status becomes SUCCESS or FAILED. A reversal (REVERSED) is not sent as a callback; you see it by querying. If you gave no notifyUrl when applying, nothing is sent and you query instead.

ItemValue
MethodPOST
Content-Typeapplication/x-www-form-urlencoded; charset=UTF-8
URLYour URL is used as given; a query string on it stays in the URL and is not signed
Timeoutsconnect 5 s, read 15 s
Redirectsnot followed: give the final URL
HTTPS certificatenot verified (self-signed certificates work)
Counted as receivedHTTP 200–299 and the body, trimmed, equals success ignoring case (one leading BOM is removed first)
Retries without successafter the first attempt, again after 1, 2, 5, 10, 30, 60 minutes: 7 attempts in total, then it stops
Newer statusonce a newer status is sent for the same payout, pending retries of the older one are dropped

Fields

ParameterPresent whenDescription
mchIdalwaysMerchant ID
mchOrderNoalwaysYour order number
payoutNoalwaysPlatform payout number
amountalwaysPayout amount (minor units)
feealwaysFee (minor units), fixed when accepted
currencyalwaysCurrency
statusalwaysPayout status, see Status below
createTimealwaysAccepted at, Beijing time
finishTimewhen status is FAILED / SUCCESSWhen it entered the current status
failMsgwhen status is FAILEDCancellation reason
remarkalwaysYour note from the application
signalwaysSignature

Example

Example key (fake):0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef;Example merchant ID:DEMO0001

Callback body

mchId=DEMO0001&mchOrderNo=ORDER-20260917-0001&payoutNo=P20260917120000000001&amount=10000&fee=200&currency=CNY&status=SUCCESS&createTime=2026-09-17+12%3A00%3A00&finishTime=2026-09-17+12%3A05%3A30&remark=%E6%B5%8B%E8%AF%95&sign=f6b9b82e927528961584530bcd383b5dc360d3b453f7ba195fb41c986f519e61

String to sign

amount=10000&createTime=2026-09-17 12:00:00&currency=CNY&fee=200&finishTime=2026-09-17 12:05:30&mchId=DEMO0001&mchOrderNo=ORDER-20260917-0001&payoutNo=P20260917120000000001&remark=测试&status=SUCCESS

sign

f6b9b82e927528961584530bcd383b5dc360d3b453f7ba195fb41c986f519e61

What you do

  1. Verify using the POST body only. Do not use a framework's merged parameter map (Java getParameterMap, PHP $_POST/$_REQUEST, Go r.Form, ASP.NET Request.Form, Flask request.values, Express express.urlencoded()): it mixes in the query string of the URL.
  2. Verify the signature, check that mchId is yours, then deduplicate on payoutNo + status.
  3. Accept only forward changes for the same payout: PROCESSING < SUCCESS = FAILED < REVERSED. Callbacks may arrive late or twice; the query result is authoritative.
  4. Store it, then reply HTTP 200 with the body success. PHP files must not start with a BOM, and nothing else may be printed around success.

Collection callback compat1 (legacy)

Sent when an order is paid; also after the operator completes an order by hand, and when you query with executeNotify=true. Exactly as in the old system:

ItemValue
MethodPOST
Content-Typeapplication/x-www-form-urlencoded
Timeoutsconnect 5 s, read 15 s
Redirectsnot followed: give the final URL
HTTPS certificatenot verified (self-signed certificates work)
Counted as receivedthe body, trimmed, equals success ignoring case; HTTP 400 and above counts as failed
Retries without successafter the first attempt, again after 1, 2, 3, 4, 5 minutes: 6 attempts in total, then it stops
Orderfixed parameter order (the order of the table below), not alphabetical

Fields

OrderParameterDescription
1incomeAmount received (minor units); differs from amount when the channel re-priced the order
2payOrderIdYour order number
3amountOrdered amount (minor units)
4mchIdMerchant ID
5productIdFor the official product ID code, please contact customer service
6mchOrderNoYour order number
7paySuccTimeSuccess time in milliseconds; empty if none
8signSignature (MD5; empty values are not signed)
9channelOrderNoUpstream order number
10backTypeAlways 2
11param1param1 from the order
12param2Payer name
13channelAttachUpstream extra data
14statusOrder status: 2 paid, 3 completed

Example

Example key (fake):demo-key-NOT-REAL-do-not-use-0001

Your notifyUrl

https://merchant.example.com/notify

Sent to

https://merchant.example.com/notify

Callback body

income=10000&payOrderId=ORDER20260918001&amount=10000&mchId=20000001&productId=8001&mchOrderNo=ORDER20260918001&paySuccTime=1789704005000&sign=9447B44CF966CB47A625CADF63C539A8&channelOrderNo=UP20260918000123&backType=2&param1=shop-7&param2=%E5%BC%A0%E4%B8%89&channelAttach=&status=2

String to sign

amount=10000&backType=2&channelOrderNo=UP20260918000123&income=10000&mchId=20000001&mchOrderNo=ORDER20260918001&param1=shop-7&param2=张三&payOrderId=ORDER20260918001&paySuccTime=1789704005000&productId=8001&status=2&key=demo-key-NOT-REAL-do-not-use-0001

If your callback URL already contains ?, the old system's way of building the request is kept: your query string moves to the start of the body and our first parameter follows it directly (e.g. shop=7?income=10000&…). The old system always sent it like this, so if you could receive it before, you still can:

Example key (fake):demo-key-NOT-REAL-do-not-use-0001

Your notifyUrl

https://merchant.example.com/notify?shop=7

Sent to

https://merchant.example.com/notify

Callback body

shop=7?income=10000&payOrderId=ORDER20260918001&amount=10000&mchId=20000001&productId=8001&mchOrderNo=ORDER20260918001&paySuccTime=1789704005000&sign=9447B44CF966CB47A625CADF63C539A8&channelOrderNo=UP20260918000123&backType=2&param1=shop-7&param2=%E5%BC%A0%E4%B8%89&channelAttach=&status=2

What you do

  1. Verify the signature with the parameters received (MD5, see Signatures).
  2. Deduplicate on your order number: the same order may arrive more than once (re-sends, or a re-send triggered by a query with executeNotify=true).
  3. Store it, then reply success.

Callback source IPs

Our callbacks come from these IPs: 47.242.183.76. If you allow-list callback sources, add them. If you moved from the old system, do not remove the old system's IPs yet: orders placed before the switch are still called back from the old system.

The User-Agent request header of every callback is always WebhookClient/1.0 (payout and pay-in callbacks alike). If your firewall or CDN filters by User-Agent, allow it. It only identifies the sender; verify authenticity with the signature.