Skip to content

Async Callback

Updated: 7/26/26, 9:23:52 PM

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:

Protocolprotocol valueApplies toMethodFormatVerification
SystemsystemOrders from standard Payment API (/unipay/*)POSTJSON bodyRSA (SHA256withRSA)
EasyPayeasy_payOrders from EasyPay plugin (/epay/*)GETURL query paramsMD5 / 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)

  1. Merchant provides notifyUrl in the payment request.
  2. Platform sends a POST request (JSON) to notifyUrl.
  3. Merchant verifies signature first, then processes business logic.
  4. Merchant returns SUCCESS (case-insensitive; leading/trailing whitespace is trimmed).
  5. 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 #IntervalCumulative
115s15s
215s30s
330s1min
43min4min
510min14min
620min34min
7~930min × 3~2h4min
1060min~3h4min
11~133h × 3~12h4min
14~166h × 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 SUCCESS case-insensitively; either missing triggers a retry

Callback Message Format

Method: POST Content-Type: application/json

Structure (DaxNoticeResult)

Extends DaxResult with event and merchant fields:

FieldTypeDescription
eventstringNotification event code (see below)
protocolstringNotification protocol (system / easy_pay)
mchNostringMerchant number
appIdstringApplication ID
codeintStatus code (0 = success)
msgstringMessage
dataobjectBusiness data (order/refund snapshot)
signstringPlatform RSA signature (Base64)
resTimestringNotification time (UTC, ISO 8601)
reqIdstringRequest ID

Event Types (NoticeEventEnum)

eventDescription
pay.successPayment successful
pay.failPayment failed
pay.closePayment closed
refund.successRefund successful
refund.closeRefund closed

Example: Payment Success

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

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

  1. Get all fields from the callback JSON.
  2. Remove sign.
  3. Exclude empty values.
  4. Sort by key in ASCII order.
  5. Join as key1=value1&key2=value2.
  6. 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)
text
SUCCESS

Non-SUCCESS body, non-2xx HTTP, or timeout (15s) triggers a retry per the Retry Strategy above.

Released under the GNU LGPL v3.0