Skip to content

代付回调通知接口

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

请求方式: POST

Content-Type: application/json


一、回调通知参数

参数名称参数变量名类型必填说明
签名signString参数加密后算法生成,签名规则见附录
友商唯一订单号tradeNoString(32)友商系统中的唯一订单号
订单总金额totalAmountNumber订单总金额以元为单位,精确到小数点后两位
平台订单号outTradeNoString(32)平台方支付平台里唯一的订单号
订单交易时间tradeTimeString(20)格式为 yyyy-MM-dd HH:mm:ss,例如:2015-01-01 12:45:52
交易状态tradeStatusInt0 = 审核中,1 = 审核成功,2 = 审核失败,3 = 代付中,4 = 代付已转账,1 和 4 都为成功状态
附加字段attachString(128)attach 参数列表中参数转换 json 串,base64 编码后的数据

attach 参数说明

attach 字段是一个 JSON 字符串的 Base64 编码,解码后包含以下参数:

参数名称参数变量名类型必填说明
姓名bankAccountString户主名称,如:张三
银行卡号bankCardString银行卡号
银行代码bankMarkString银行的简码,如:CCB
银行名称bankNameString银行的名称,如:建设银行
省份bankProvString如:台湾省
城市bankCityString如:台北市
开户支行bankBranchString如:建设银行台北XX支行

交易状态说明

状态值说明
0审核中
1审核成功
2审核失败
3代付中
4代付已转账

重要: 1(审核成功)和 4(代付已转账)都代表成功状态。


二、回调请求示例

json
{
  "sign": "3E541783111209F709ACDA0F4BFD17EA",
  "tradeNo": "ORDER20250115001",
  "totalAmount": "100.00",
  "outTradeNo": "PO20250115001001",
  "tradeTime": "2025-01-15 12:30:45",
  "tradeStatus": 4,
  "attach": "eyJiYW5rQWNjb3VudCI6IuWwj+eOi..."
}

三、商户响应要求

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

如果响应内容不包含 success,支付平台系统会认为通知失败,并在后续进行重试。

正确响应示例

方式一:直接返回文字

text
success

方式二:返回 JSON

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

四、PHP 回调处理示例

php
<?php
// 获取回调参数(POST 请求,JSON 格式)
$json = file_get_contents('php://input');
$params = json_decode($json, true);

if (!$params) {
    echo 'fail';
    exit;
}

// 保存 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) {
    // 签名验证通过
    $tradeStatus = $params['tradeStatus'];
    $tradeNo = $params['tradeNo'];
    $totalAmount = $params['totalAmount'];

    // 判断交易状态
    if ($tradeStatus == 1 || $tradeStatus == 4) {
        // 交易成功,处理订单业务逻辑
        // 例如:更新订单状态为成功,给用户账户加款等
        echo 'success';
    } elseif ($tradeStatus == 0 || $tradeStatus == 3) {
        // 处理中状态,暂不处理,等待后续状态通知
        echo 'success';
    } else {
        // 交易失败,处理订单业务逻辑
        echo 'success';
    }
} else {
    // 签名验证失败,不处理业务
    echo 'fail';
}

五、Java 回调处理示例

java
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.util.*;
import java.security.MessageDigest;

@RestController
@RequestMapping("/payout")
public class PayoutNotifyController {

    @PostMapping("/notify")
    public String notify(HttpServletRequest request) {
        try {
            // 读取请求体(JSON 格式)
            BufferedReader reader = request.getReader();
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
            String jsonBody = sb.toString();

            // 解析 JSON 参数
            Map<String, Object> paramMap = new com.fasterxml.jackson.databind.ObjectMapper()
                .readValue(jsonBody, Map.class);

            // 提取并移除 sign
            String receivedSign = (String) paramMap.remove("sign");

            // 按参数名 ASCII 字典序排序(使用 TreeMap)
            Map<String, Object> sortedParams = new TreeMap<>(paramMap);

            // 拼接字符串
            StringBuilder signStr = new StringBuilder();
            for (Map.Entry<String, Object> entry : sortedParams.entrySet()) {
                if (entry.getValue() != null && !entry.getValue().toString().isEmpty()) {
                    signStr.append(entry.getKey()).append("=")
                           .append(entry.getValue()).append("&");
                }
            }
            if (signStr.length() > 0) {
                signStr.deleteCharAt(signStr.length() - 1);
            }

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

            // 验证签名
            if (receivedSign != null && receivedSign.equals(calculatedSign)) {
                // 签名验证通过,处理业务逻辑
                return "success";
            } else {
                return "fail";
            }
        } catch (Exception e) {
            e.printStackTrace();
            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. 成功状态判断:代付成功状态为 1(审核成功)和 4(代付已转账)
  4. 返回要求:无论业务处理成功与否,都应返回 success 字符串防止重复通知
  5. 回调格式:代付回调为 JSON 格式,与代收回调的 form-data 格式不同
  6. 异步处理:建议将回调通知放入异步队列处理,避免超时
  7. attach 解码:如需使用 attach 中的银行信息,请先进行 Base64 解码