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.
| Item | Value |
|---|---|
| Method | POST |
| Content-Type | application/x-www-form-urlencoded; charset=UTF-8 |
| URL | Your URL is used as given; a query string on it stays in the URL and is not signed |
| Timeouts | connect 5 s, read 15 s |
| Redirects | not followed: give the final URL |
| HTTPS certificate | not verified (self-signed certificates work) |
| Counted as received | HTTP 200–299 and the body, trimmed, equals success ignoring case (one leading BOM is removed first) |
| Retries without success | after the first attempt, again after 1, 2, 5, 10, 30, 60 minutes: 7 attempts in total, then it stops |
| Newer status | once a newer status is sent for the same payout, pending retries of the older one are dropped |
Fields
| Parameter | Present when | Description |
|---|---|---|
mchId | always | Merchant ID |
mchOrderNo | always | Your order number |
payoutNo | always | Platform payout number |
amount | always | Payout amount (minor units) |
fee | always | Fee (minor units), fixed when accepted |
currency | always | Currency |
status | always | Payout status, see Status below |
createTime | always | Accepted at, Beijing time |
finishTime | when status is FAILED / SUCCESS | When it entered the current status |
failMsg | when status is FAILED | Cancellation reason |
remark | always | Your note from the application |
sign | always | Signature |
Example
Example key (fake):0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef;Example merchant ID:DEMO0001
Callback body
mchId=DEMO0001&mchOrderNo=ORDER-20260917-0001&payoutNo=P20260917120000000001&amount=10000&fee=200¤cy=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¤cy=CNY&fee=200&finishTime=2026-09-17 12:05:30&mchId=DEMO0001&mchOrderNo=ORDER-20260917-0001&payoutNo=P20260917120000000001&remark=测试&status=SUCCESS
sign
f6b9b82e927528961584530bcd383b5dc360d3b453f7ba195fb41c986f519e61What you do
- Verify using the POST body only. Do not use a framework's merged parameter map (Java
getParameterMap, PHP$_POST/$_REQUEST, Gor.Form, ASP.NETRequest.Form, Flaskrequest.values, Expressexpress.urlencoded()): it mixes in the query string of the URL. - Verify the signature, check that
mchIdis yours, then deduplicate onpayoutNo+status. - Accept only forward changes for the same payout:
PROCESSING<SUCCESS=FAILED<REVERSED. Callbacks may arrive late or twice; the query result is authoritative. - Store it, then reply HTTP 200 with the body
success. PHP files must not start with a BOM, and nothing else may be printed aroundsuccess.
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:
| Item | Value |
|---|---|
| Method | POST |
| Content-Type | application/x-www-form-urlencoded |
| Timeouts | connect 5 s, read 15 s |
| Redirects | not followed: give the final URL |
| HTTPS certificate | not verified (self-signed certificates work) |
| Counted as received | the body, trimmed, equals success ignoring case; HTTP 400 and above counts as failed |
| Retries without success | after the first attempt, again after 1, 2, 3, 4, 5 minutes: 6 attempts in total, then it stops |
| Order | fixed parameter order (the order of the table below), not alphabetical |
Fields
| Order | Parameter | Description |
|---|---|---|
| 1 | income | Amount received (minor units); differs from amount when the channel re-priced the order |
| 2 | payOrderId | Your order number |
| 3 | amount | Ordered amount (minor units) |
| 4 | mchId | Merchant ID |
| 5 | productId | For the official product ID code, please contact customer service |
| 6 | mchOrderNo | Your order number |
| 7 | paySuccTime | Success time in milliseconds; empty if none |
| 8 | sign | Signature (MD5; empty values are not signed) |
| 9 | channelOrderNo | Upstream order number |
| 10 | backType | Always 2 |
| 11 | param1 | param1 from the order |
| 12 | param2 | Payer name |
| 13 | channelAttach | Upstream extra data |
| 14 | status | Order 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¶m1=shop-7¶m2=%E5%BC%A0%E4%B8%89&channelAttach=&status=2
String to sign
amount=10000&backType=2&channelOrderNo=UP20260918000123&income=10000&mchId=20000001&mchOrderNo=ORDER20260918001¶m1=shop-7¶m2=张三&payOrderId=ORDER20260918001&paySuccTime=1789704005000&productId=8001&status=2&key=demo-key-NOT-REAL-do-not-use-0001If 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¶m1=shop-7¶m2=%E5%BC%A0%E4%B8%89&channelAttach=&status=2What you do
- Verify the signature with the parameters received (MD5, see Signatures).
- 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). - 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.