Skip to content

异步回调

更新时间:2026/7/26 21:23:52

概述

当订单状态发生变更时(如支付成功、支付失败、关闭、退款成功),DaxPay 系统会主动向商户在支付请求中传入的 notifyUrl 发送异步通知。

两套通知协议

平台根据订单的支付来源选择通知协议,请求方式、报文格式、验签方式均不同

协议protocol适用场景请求方式报文格式验签方式
系统协议system标准支付 API(/unipay/*)发起的订单POSTJSON bodyRSA(SHA256withRSA
易支付协议easy_pay易支付插件(/epay/*)发起的订单GETURL query 参数MD5 / RSA(取决于 V1/V2)

easy_pay 协议是 GET 请求

易支付兼容协议(protocol=easy_pay)的回调走 GET + URL query 参数不是 POST JSON。对接易支付时需用 request.getParameter() 接收,而非 request.getReader()

通知流程(system 协议)

  1. 商户在发起支付请求时传入 notifyUrl 参数。
  2. 系统处理完业务后,向 notifyUrl 发送 POST 请求(JSON 格式)。
  3. 商户系统接收通知,先验签,再处理业务逻辑
  4. 商户系统处理后,返回 SUCCESS 字符串(大小写不敏感,前后空格会被 trim)。
  5. 如果商户系统返回非 SUCCESS 或 HTTP 状态非 2xx 或超时未响应,系统按策略重试。

重试策略

固定 16 次重试,间隔递增(实现于 NoticeRetryPolicy,通过 Artemis 延时队列调度):

重试次数间隔累计耗时
115s15s
215s30s
330s1min
43min4min
510min14min
620min34min
7~930min × 3~2h4min
1060min~3h4min
11~133h × 3~12h4min
14~166h × 3~30h4min
  • 最大重试次数:16 次(超过后停止,标记为发送失败)
  • 仅自动发送路径会重试:商户在管理端「手动重发通知」失败后不会自动重试,需再次手动触发
  • ACK 判定:HTTP 状态 2xx 响应体 trim 后忽略大小写等于 SUCCESS,两者缺一即视为失败并重试

回调消息格式

请求方式:POST Content-Type:application/json

报文结构(DaxNoticeResult)

回调报文继承 DaxResult,增加事件与商户字段:

参数类型描述
eventstring通知事件码(见下方事件类型)
protocolstring通知协议(system / easy_pay
mchNostring商户号
appIdstring应用 ID
codeint状态码(0 = 成功)
msgstring提示信息
dataobject业务数据(订单/退款单快照)
signstring平台 RSA 签名(Base64)
resTimestring通知时间(UTC,ISO 8601)
reqIdstring请求 ID

事件类型(NoticeEventEnum)

event说明
pay.success支付成功
pay.fail支付失败
pay.close支付关闭
refund.success退款成功
refund.close退款关闭

回调示例:支付成功

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": "Base64签名值",
  "resTime": "2024-12-01T04:00:00Z",
  "reqId": "NTF20241201001"
}

回调示例:退款成功

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": "Base64签名值",
  "resTime": "2024-12-01T06:00:00Z",
  "reqId": "NTF20241201002"
}

签名验证

商户接收回调后,必须验证 sign 字段,确保消息由 DaxPay 平台发出且未被篡改。

验签步骤

  1. 获取回调 JSON 中的所有字段。
  2. 移除 sign 字段。
  3. 排除值为空的字段。
  4. 将剩余字段按 key 的 ASCII 码升序排列。
  5. 拼接为 key1=value1&key2=value2 格式。
  6. 使用平台公钥SHA256withRSA 验签。

先验签再处理业务

务必先验签通过后再处理业务逻辑,避免伪造通知导致数据不一致。

验签代码示例参见 签名机制

商户响应

验签通过且业务处理成功后,商户系统应返回:

  • HTTP 状态码:2xx(200-299)
  • 响应体:SUCCESS(大小写不敏感,前后空格会被自动 trim)
text
SUCCESS

返回非 SUCCESS 或 HTTP 非 2xx 或超时(15 秒),系统将按上述 重试策略 重新发送通知。

基于 GNU LGPL v3.0 协议开源