Skip to content

代收回调通知接口

说明: 根据支付下单接口中回调地址(noticeUrl),进行异步通知。

请求方式: POST

Content-Type: application/jsonapplication/x-www-form-urlencoded


一、回调通知参数

参数名称参数变量名类型必填说明
签名signString参数加密后算法生成,签名规则见附录
友商唯一订单号tradeNoString(50)友商系统中的唯一订单号
友商订单金额orderAmountNumber订单总金额以元为单位,精确到小数点后两位
平台实际支付金额totalAmountNumber(11.2)客户实际支付金额
支付平台订单号outTradeNoString(32)平台方支付平台里唯一的订单号
支付平台订单时间tradeTimeString(20)支付平台订单交易时间,格式为 yyyy-MM-dd HH:mm:ss,例如:2015-01-01 12:45:52
交易状态tradeStatusInt1 = 交易成功,0 = 交易失败,-1 = 交易中
附加字段attachString(30)附加参数(原样返回)
第三方单号otherTradeNoString(32)第三方平台单号

交易状态说明

状态值说明
1交易成功
0交易失败
-1交易中

二、回调请求示例

json
{
  "sign": "3E541783111209F709ACDA0F4BFD17EA",
  "tradeNo": "ORDER20250115001",
  "orderAmount": "100.00",
  "totalAmount": "100.00",
  "outTradeNo": "PO20250115001001",
  "tradeTime": "2025-01-15 12:30:45",
  "tradeStatus": 1,
  "attach": "eyAiYmF...",
  "otherTradeNo": "THIRD20250115001"
}

三、商户响应要求

当通知方式为服务器后台异步通知时,友商系统在收到通知并处理完成后,必须打印输出包含 success 这个字符串

如果响应内容不包含 success,支付平台系统会认为通知失败。

正确响应示例

**方式一:直接返回文字

text
success

**方式二:返回 JSON

json
{
  "code": 0,
  "message": "success"
}

四、回调签名验证

友商系统需要对回调数据进行签名验证,确保数据来源是支付平台。

验证步骤:

  1. 接收回调参数(排除 sign 字段)
  2. 将非空参数按参数名 ASCII 字典序排序
  3. 拼接成 key=value&key=value 格式
  4. 末尾拼接 &key=商户密钥
  5. 对整个字符串进行 MD5 加密并转大写
  6. 与传入的 sign 进行对比

PHP 验证示例

php
<?php
// 获取回调参数(POST请求)
$params = $_POST; // 或 $_GET,取决于请求方式

// 保存sign后用于验证
$receivedSign = $params['sign'];
unset($params['sign']);

// 按参数名ASCII字典序排序
ksort($params);

// 拼接字符串
$stringA = '';
foreach ($params as $k => $v) {
    if ($v !== '' && $v !== null) {
        $stringA .= $k . '=' . $v . '&';
    }
}
$stringA = rtrim($stringA, '&');

// 拼接密钥
$key = 'your_api_key_here';
$signStr = $stringA . '&key=' . $key;
$calculatedSign = strtoupper(md5($signStr));

// 验证签名
if ($receivedSign === $calculatedSign) {
    // 签名验证通过
    if ($params['tradeStatus'] == 1) {
        // 交易成功,处理订单业务逻辑
        // ...
        echo 'success';
    } else {
        // 交易失败或处理中
        echo 'success';
    }
} else {
    // 签名验证失败,不处理业务
    echo 'fail';
}

Java 验证示例

java
import java.util.*;
import java.security.MessageDigest;

@RequestMapping("/notify")
@ResponseBody
public String notify(HttpServletRequest request) {
    // 获取所有参数
    Map<String, String[]> parameterMap = request.getParameterMap();
    Map<String, String> params = new TreeMap<>();
    
    String receivedSign = null;
    for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
        String key = entry.getKey();
        String value = entry.getValue()[0];
        if ("sign".equals(key)) {
            receivedSign = value;
        } else if (value != null && !value.isEmpty()) {
            params.put(key, value);
        }
    }

    // 拼接字符串 (TreeMap已自动排序)
    StringBuilder sb = new StringBuilder();
    for (Map.Entry<String, String> entry : params.entrySet()) {
        if (entry.getValue() != null && !entry.getValue().isEmpty()) {
            sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
        }
    }
    if (sb.length() > 0) sb.deleteCharAt(sb.length() - 1);

    // 拼接密钥并生成签名
    String key = "your_api_key_here";
    String signStr = sb.toString() + "&key=" + key;
    String calculatedSign = md5(signStr).toUpperCase();

    // 验证签名
    if (receivedSign != null && receivedSign.equals(calculatedSign)) {
        // 签名验证通过,处理业务逻辑
        return "success";
    } else {
        return "fail";
    }
}

private static String md5(String input) throws Exception {
    MessageDigest md = MessageDigest.getInstance("MD5");
    byte[] digest = md.digest(input.getBytes("UTF-8"));
    StringBuilder hexString = new StringBuilder();
    for (byte b : digest) {
        String hex = Integer.toHexString(0xff & b);
        if (hex.length() == 1) hexString.append('0');
        hexString.append(hex);
    }
    return hexString.toString();
}

五、注意事项

  1. 幂等性:建议友商系统对同一订单号的回调通知做幂等处理,避免重复处理
  2. 签名验证:必须对所有回调通知进行签名验证,防止伪造请求
  3. 交易状态判断:tradeStatus=1 时才标记订单为成功状态
  4. 返回要求:无论业务处理成功与否,都应返回 success 字符串防止重复通知
  5. 业务处理:建议将回调通知放入异步队列处理,避免超时