Async Callback
Overview
When order status changes (payment success, failure, close, refund success), DaxPay sends async notifications to the merchant's notifyUrl.
Two Notification Protocols
The platform picks a protocol based on the order's payment source. Request method, payload format, and verification differ:
| Protocol | protocol value | Applies to | Method | Format | Verification |
|---|---|---|---|---|---|
| System | system | Orders from standard Payment API (/unipay/*) | POST | JSON body | RSA (SHA256withRSA) |
| EasyPay | easy_pay | Orders from EasyPay plugin (/epay/*) | GET | URL query params | MD5 / RSA (depends on V1/V2) |
easy_pay uses GET
The EasyPay-compatible protocol (protocol=easy_pay) sends callbacks via GET + URL query params, not POST JSON. When integrating EasyPay, read params via request.getParameter(), not request.getReader().
Notification Flow (system protocol)
- Merchant provides
notifyUrlin the payment request. - Platform sends a POST request (JSON) to
notifyUrl. - Merchant verifies signature first, then processes business logic.
- Merchant returns
SUCCESS(case-insensitive; leading/trailing whitespace is trimmed). - If response is not
SUCCESS, HTTP non-2xx, or timeout, the system retries.
Retry Strategy
Fixed 16 retries with increasing intervals (implemented in NoticeRetryPolicy, scheduled via Artemis delayed queue):
| Retry # | Interval | Cumulative |
|---|---|---|
| 1 | 15s | 15s |
| 2 | 15s | 30s |
| 3 | 30s | 1min |
| 4 | 3min | 4min |
| 5 | 10min | 14min |
| 6 | 20min | 34min |
| 7~9 | 30min × 3 | ~2h4min |
| 10 | 60min | ~3h4min |
| 11~13 | 3h × 3 | ~12h4min |
| 14~16 | 6h × 3 | ~30h4min |
- Max retries: 16 (after which sending stops and the task is marked failed)
- Only auto-send path retries: Manual "resend notification" from the merchant panel does not auto-retry on failure — must be triggered again manually
- ACK logic: HTTP 2xx and body trimmed equals
SUCCESScase-insensitively; either missing triggers a retry
Callback Message Format
Method: POST Content-Type: application/json
Structure (DaxNoticeResult)
Extends DaxResult with event and merchant fields:
| Field | Type | Description |
|---|---|---|
| event | string | Notification event code (see below) |
| protocol | string | Notification protocol (system / easy_pay) |
| mchNo | string | Merchant number |
| appId | string | Application ID |
| code | int | Status code (0 = success) |
| msg | string | Message |
| data | object | Business data (order/refund snapshot) |
| sign | string | Platform RSA signature (Base64) |
| resTime | string | Notification time (UTC, ISO 8601) |
| reqId | string | Request ID |
Event Types (NoticeEventEnum)
| event | Description |
|---|---|
| pay.success | Payment successful |
| pay.fail | Payment failed |
| pay.close | Payment closed |
| refund.success | Refund successful |
| refund.close | Refund closed |
Example: Payment Success
{
"event": "pay.success",
"protocol": "system",
"mchNo": "M200000001",
"appId": "APP001",
"code": 0,
"msg": "success",
"data": {
"orderNo": "P2024120112345700001",
"bizOrderNo": "ORDER20241201001",
"tradeNo": "T2024120112345700001",
"amount": 100,
"realAmount": 100,
"status": "success",
"method": "wechat_qr",
"payTime": "2024-12-01 12:00:00",
"attach": "{\"orderId\": 123}"
},
"sign": "Base64Signature",
"resTime": "2024-12-01T04:00:00Z",
"reqId": "NTF20241201001"
}Example: Refund Success
{
"event": "refund.success",
"protocol": "system",
"mchNo": "M200000001",
"appId": "APP001",
"code": 0,
"msg": "success",
"data": {
"refundNo": "R2024120112345700001",
"bizRefundNo": "REFUND20241201001",
"bizOrderNo": "ORDER20241201001",
"amount": 100,
"status": "success",
"finishTime": "2024-12-01 14:00:00"
},
"sign": "Base64Signature",
"resTime": "2024-12-01T06:00:00Z",
"reqId": "NTF20241201002"
}Signature Verification
Merchant must verify the sign field to ensure the message is from DaxPay and untampered.
Steps
- Get all fields from the callback JSON.
- Remove
sign. - Exclude empty values.
- Sort by key in ASCII order.
- Join as
key1=value1&key2=value2. - Verify with platform public key using
SHA256withRSA.
Verify before processing
Always verify signature before processing business logic to prevent forged notifications.
See Signature for code examples.
Merchant Response
After successful verification and processing, return:
- HTTP status: 2xx (200-299)
- Body:
SUCCESS(case-insensitive; leading/trailing whitespace is trimmed)
SUCCESSNon-SUCCESS body, non-2xx HTTP, or timeout (15s) triggers a retry per the Retry Strategy above.