代付API文档

1. 概述

代付API为商户提供资金代付能力。商户通过HTTP POST方式提交代付申请,系统自动审核并调用第三方代付通道完成打款。代付结果通过异步通知方式返回给商户。

接口基础地址: https://您的域名/payment
请求方式: POST(除特别说明外)
数据格式: application/x-www-form-urlencoded
签名算法: MD5(大写)
前提条件:

2. 签名规则

代付API使用与支付API相同的MD5签名算法:

签名步骤:
1. 将所有非空参数按照参数名ASCII码从小到大排序(字典序)
2. 使用URL键值对格式拼接:key1=value1&key2=value2&...
3. 在拼接字符串末尾连接商户APIKEY:...&key=商户APIKEY
4. 对整个字符串进行MD5加密,得到32位大写签名值
注意:

各语言签名示例

PHP
Python
Java
Go
Node.js
function createSign($params, $apikey) {
    ksort($params);
    $md5str = "";
    foreach ($params as $key => $val) {
        if (!empty($val) && $key != 'pay_md5sign') {
            $md5str .= $key . "=" . $val . "&";
        }
    }
    return strtoupper(md5($md5str . "key=" . $apikey));
}
import hashlib

def create_sign(params, apikey):
    sorted_params = sorted(params.items())
    parts = [f'{k}={v}' for k, v in sorted_params if v and k != 'pay_md5sign']
    sign_str = '&'.join(parts) + '&key=' + apikey
    return hashlib.md5(sign_str.encode()).hexdigest().upper()
import java.security.MessageDigest;
import java.util.Map;
import java.util.TreeMap;

public static String createSign(Map params, String apikey)
        throws Exception {
    Map sortedParams = new TreeMap<>(params);
    StringBuilder sb = new StringBuilder();
    for (Map.Entry entry : sortedParams.entrySet()) {
        String val = entry.getValue();
        if (val != null && !val.isEmpty() && !"pay_md5sign".equals(entry.getKey())) {
            sb.append(entry.getKey()).append("=").append(val).append("&");
        }
    }
    sb.append("key=").append(apikey);
    MessageDigest md = MessageDigest.getInstance("MD5");
    byte[] digest = md.digest(sb.toString().getBytes("UTF-8"));
    StringBuilder hexString = new StringBuilder();
    for (byte b : digest) {
        hexString.append(String.format("%02X", b));
    }
    return hexString.toString();
}
import (
    "crypto/md5"
    "encoding/hex"
    "sort"
    "strings"
)

func createSign(params map[string]string, apikey string) string {
    keys := make([]string, 0, len(params))
    for k := range params {
        keys = append(keys, k)
    }
    sort.Strings(keys)
    parts := make([]string, 0)
    for _, k := range keys {
        v := params[k]
        if v != "" && k != "pay_md5sign" {
            parts = append(parts, k+"="+v)
        }
    }
    signStr := strings.Join(parts, "&") + "&key=" + apikey
    hash := md5.Sum([]byte(signStr))
    return strings.ToUpper(hex.EncodeToString(hash[:]))
}
const crypto = require('crypto');

function createSign(params, apikey) {
    const sortedKeys = Object.keys(params).sort();
    const parts = [];
    sortedKeys.forEach(key => {
        if (params[key] && key !== 'pay_md5sign') {
            parts.push(`${key}=${params[key]}`);
        }
    });
    const signStr = parts.join('&') + '&key=' + apikey;
    return crypto.createHash('md5')
        .update(signStr)
        .digest('hex')
        .toUpperCase();
}

3. 代付申请接口

POST /payment/dfpay/add

商户提交代付申请,系统创建代付订单并自动审核(如商户开启自动审核)。

请求参数

参数名 必填 类型 说明
mchid 是 String 商户号(如10001)
out_trade_no 是 String 商户代付订单号(唯一)
money 是 String 代付金额(元,如100.00)
type_key_pix 是 String PIX Key类型(CPF/CNPJ/EMAIL/PHONE)
cpf 是 String PIX Key值(收款人CPF/CNPJ/邮箱/手机号)
description 是 String 收款人姓名
notify_url 是 String 代付结果异步通知地址
pay_md5sign 是 String MD5签名(32位大写)
code 否 String 代付渠道代码(默认tresory)
client_name 否 String 收款人附加信息
client_email 否 String 收款人邮箱
client_document 否 String 收款人证件号
extends 否 String 扩展字段(Base64编码的JSON,渠道扩展字段)

参与签名的参数

所有非空请求参数(除 pay_md5sign 外)均参与签名。

成功响应

{
    "status": "200",
    "msg": "代付申请成功",
    "data": {
        "transaction_id": "A0101120000123456789"  // 系统代付订单号
    }
}

失败响应

{
    "status": "error",
    "msg": "错误描述",
    "data": []
}

常见错误

错误信息 说明
代付API未开启!系统未开启代付功能
商户未开启此功能!商户未开启df_api
请求来源域名与报备域名不一致!请求域名不匹配df_domain
IP地址与报备IP不一致!请求IP不匹配df_ip
节假日暂时无法提款!当天为节假日
提款已关闭!系统提款配置未开启
不在提现时间不在允许的提现时间段
单笔最低/最大提款额度金额超出限额
超出商户当日提款次数/额度触发日限
存在重复订单号!out_trade_no重复
签名验证失败签名错误
余额不足商户余额不足以支付代付金额

4. 代付查询接口

POST /payment/dfpay/query

商户主动查询代付订单状态。

请求参数

参数名 必填 类型 说明
mchid 是 String 商户号
out_trade_no 是 String 商户代付订单号
pay_md5sign 是 String MD5签名

参与签名的参数

mchid, out_trade_no

成功响应

{
    "status": "success",
    "msg": "请求成功",
    "mchid": "10001",
    "out_trade_no": "202401010001",       // 商户订单号
    "amount": "100.00",                   // 代付金额
    "transaction_id": "A0101120000...",   // 系统代付订单号
    "refCode": "1",                       // 状态码(见下表)
    "refMsg": "成功",                     // 状态描述
    "success_time": "2024-01-01 12:00:00",// 成功时间(仅成功时返回)
    "sign": "MD5签名"                      // 响应签名
}

交易不存在响应

{
    "status": "error",
    "msg": "请求成功",
    "refCode": "7",
    "refMsg": "交易不存在"
}

5. 代付状态码

refCode refMsg 说明
1成功代付已成功打款
2失败代付失败
3处理中/待确认代付正在处理或待确认
4待处理代付申请已提交,等待处理
5审核驳回代付申请被驳回
6待审核代付申请等待审核
7交易不存在订单号不存在
8未知状态异常状态
状态流转:
待审核(6) → 待处理(4) → 处理中(3) → 成功(1) 或 失败(2)
待审核(6) → 审核驳回(5)

6. 异步通知

代付处理完成后,系统会向商户提交的 notify_url 发送POST请求通知代付结果。

重要:商户接收到通知后,需返回字符串 success,否则系统会重复通知。

通知参数

参数名 类型 说明
mchidString商户号
out_trade_noString商户订单号
transaction_idString系统代付订单号
amountString代付金额
refCodeString状态码(1=成功 2=失败)
refMsgString状态描述
success_timeString成功时间(成功时返回)
signStringMD5签名

各语言通知处理示例

PHP
Python
Java
Go
Node.js
// 接收代付异步通知
$params = $_POST;

// 验证签名
$apikey = '商户APIKEY';
$signParams = [
    'mchid'           => $params['mchid'],
    'out_trade_no'    => $params['out_trade_no'],
    'transaction_id'  => $params['transaction_id'],
    'amount'          => $params['amount'],
    'refCode'         => $params['refCode'],
    'refMsg'          => $params['refMsg'],
];
if (isset($params['success_time'])) {
    $signParams['success_time'] = $params['success_time'];
}
$sign = createSign($signParams, $apikey);

if ($sign === $params['sign']) {
    if ($params['refCode'] == '1') {
        // 代付成功
        updateWithdrawStatus($params['out_trade_no'], 'success');
    } elseif ($params['refCode'] == '2') {
        // 代付失败
        updateWithdrawStatus($params['out_trade_no'], 'failed');
    }
    echo 'success';
} else {
    echo 'fail';
}
from flask import Flask, request

app = Flask(__name__)

@app.route('/df_notify', methods=['POST'])
def df_notify():
    params = request.form.to_dict()
    apikey = '商户APIKEY'

    # 参与签名的参数
    sign_params = {
        'mchid': params.get('mchid', ''),
        'out_trade_no': params.get('out_trade_no', ''),
        'transaction_id': params.get('transaction_id', ''),
        'amount': params.get('amount', ''),
        'refCode': params.get('refCode', ''),
        'refMsg': params.get('refMsg', ''),
    }
    if params.get('success_time'):
        sign_params['success_time'] = params['success_time']

    # 验证签名
    check_sign = create_sign(sign_params, apikey)
    if check_sign != params.get('sign', ''):
        return 'fail'

    if params.get('refCode') == '1':
        # 代付成功
        update_withdraw_status(params['out_trade_no'], 'success')
    elif params.get('refCode') == '2':
        # 代付失败
        update_withdraw_status(params['out_trade_no'], 'failed')

    return 'success'
// Spring Boot 控制器示例
@RestController
public class DfNotifyController {

    private String apikey = "商户APIKEY";

    @PostMapping("/df_notify")
    public String dfNotify(@RequestBody Map params) {
        // 参与签名的参数
        Map signParams = new TreeMap<>();
        signParams.put("mchid", params.get("mchid"));
        signParams.put("out_trade_no", params.get("out_trade_no"));
        signParams.put("transaction_id", params.get("transaction_id"));
        signParams.put("amount", params.get("amount"));
        signParams.put("refCode", params.get("refCode"));
        signParams.put("refMsg", params.get("refMsg"));
        if (params.containsKey("success_time")) {
            signParams.put("success_time", params.get("success_time"));
        }

        // 验证签名
        String checkSign = createSign(signParams, apikey);
        if (!checkSign.equals(params.get("sign"))) {
            return "fail";
        }

        if ("1".equals(params.get("refCode"))) {
            // 代付成功
            updateWithdrawStatus(params.get("out_trade_no"), "success");
        } else if ("2".equals(params.get("refCode"))) {
            // 代付失败
            updateWithdrawStatus(params.get("out_trade_no"), "failed");
        }

        return "success";
    }
}
package main

import (
    "net/http"
)

func dfNotify(w http.ResponseWriter, r *http.Request) {
    r.ParseForm()
    params := make(map[string]string)
    for k := range r.PostForm {
        params[k] = r.PostForm.Get(k)
    }

    apikey := "商户APIKEY"

    // 参与签名的参数
    signParams := map[string]string{
        "mchid":          params["mchid"],
        "out_trade_no":   params["out_trade_no"],
        "transaction_id": params["transaction_id"],
        "amount":         params["amount"],
        "refCode":        params["refCode"],
        "refMsg":         params["refMsg"],
    }
    if _, ok := params["success_time"]; ok {
        signParams["success_time"] = params["success_time"]
    }

    // 验证签名
    checkSign := createSign(signParams, apikey)
    if checkSign != params["sign"] {
        w.Write([]byte("fail"))
        return
    }

    if params["refCode"] == "1" {
        // 代付成功
        updateWithdrawStatus(params["out_trade_no"], "success")
    } else if params["refCode"] == "2" {
        // 代付失败
        updateWithdrawStatus(params["out_trade_no"], "failed")
    }

    w.Write([]byte("success"))
}
const express = require('express');
const app = express();

const apikey = '商户APIKEY';

app.post('/df_notify', (req, res) => {
    const params = req.body;

    // 参与签名的参数
    const signParams = {
        mchid: params.mchid,
        out_trade_no: params.out_trade_no,
        transaction_id: params.transaction_id,
        amount: params.amount,
        refCode: params.refCode,
        refMsg: params.refMsg,
    };
    if (params.success_time) {
        signParams.success_time = params.success_time;
    }

    // 验证签名
    const checkSign = createSign(signParams, apikey);
    if (checkSign !== params.sign) {
        return res.send('fail');
    }

    if (params.refCode === '1') {
        // 代付成功
        updateWithdrawStatus(params.out_trade_no, 'success');
    } else if (params.refCode === '2') {
        // 代付失败
        updateWithdrawStatus(params.out_trade_no, 'failed');
    }

    res.send('success');
});

7. 完整代码示例

各语言代付申请示例

PHP
Python
Java
Go
Node.js
<?php
/**
 * 代付申请示例
 */

// 商户配置
$mchid   = '10001';       // 商户号
$apikey  = '您的APIKEY';   // 商户APIKEY
$gateway = 'https://您的域名/payment/dfpay/add';

// 代付参数
$params = [
    'mchid'         => $mchid,
    'out_trade_no'  => 'DF' . date('YmdHis') . rand(1000, 9999),
    'money'         => '100.00',
    'type_key_pix'  => 'CPF',
    'cpf'           => '12345678901',       // 收款人CPF
    'description'   => '张三',               // 收款人姓名
    'notify_url'    => 'https://您的域名/df_notify.php',
    'code'          => 'tresory',           // 代付渠道
    'client_email'  => 'zhangsan@email.com',
    'client_document' => '12345678901',
];

// 生成签名
$params['pay_md5sign'] = createSign($params, $apikey);

// 发送请求
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $gateway);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$response = curl_exec($ch);
curl_close($ch);

// 解析响应
$result = json_decode($response, true);
if ($result['status'] == '200') {
    echo '代付申请成功,系统订单号:' . $result['data']['transaction_id'];
} else {
    echo '代付申请失败:' . $result['msg'];
}

/**
 * 代付查询示例
 */
function queryDfpay($mchid, $apikey, $out_trade_no) {
    $params = [
        'mchid'        => $mchid,
        'out_trade_no' => $out_trade_no,
    ];
    $params['pay_md5sign'] = createSign($params, $apikey);

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, 'https://您的域名/payment/dfpay/query');
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    $response = curl_exec($ch);
    curl_close($ch);

    return json_decode($response, true);
}

/**
 * 生成MD5签名
 */
function createSign($params, $apikey) {
    ksort($params);
    $md5str = "";
    foreach ($params as $key => $val) {
        if (!empty($val) && $key != 'pay_md5sign') {
            $md5str .= $key . "=" . $val . "&";
        }
    }
    return strtoupper(md5($md5str . "key=" . $apikey));
}
?>
import hashlib
import requests
import time
import random

# 商户配置
mchid = '10001'
apikey = '您的APIKEY'
gateway = 'https://您的域名/payment/dfpay/add'

# 代付参数
params = {
    'mchid': mchid,
    'out_trade_no': 'DF' + time.strftime('%Y%m%d%H%M%S') + str(random.randint(1000, 9999)),
    'money': '100.00',
    'type_key_pix': 'CPF',
    'cpf': '12345678901',
    'description': '张三',
    'notify_url': 'https://您的域名/df_notify.py',
    'code': 'tresory',
    'client_email': 'zhangsan@email.com',
    'client_document': '12345678901',
}

# 生成签名
def create_sign(params, apikey):
    sorted_params = sorted(params.items())
    sign_str = '&'.join([f'{k}={v}' for k, v in sorted_params if v and k != 'pay_md5sign'])
    sign_str += '&key=' + apikey
    return hashlib.md5(sign_str.encode()).hexdigest().upper()

params['pay_md5sign'] = create_sign(params, apikey)

# 发送请求
response = requests.post(gateway, data=params)
result = response.json()

if result.get('status') == '200':
    print('代付申请成功,系统订单号:', result['data']['transaction_id'])
else:
    print('代付申请失败:', result.get('msg'))

# 代付查询
def query_dfpay(mchid, apikey, out_trade_no):
    params = {
        'mchid': mchid,
        'out_trade_no': out_trade_no,
    }
    params['pay_md5sign'] = create_sign(params, apikey)
    response = requests.post(
        'https://您的域名/payment/dfpay/query',
        data=params
    )
    return response.json()
import java.io.*;
import java.net.*;
import java.security.MessageDigest;
import java.util.*;

public class DfPayDemo {

    public static void main(String[] args) throws Exception {
        String mchid = "10001";
        String apikey = "您的APIKEY";
        String gateway = "https://您的域名/payment/dfpay/add";

        // 代付参数
        Map<String, String> params = new HashMap<>();
        params.put("mchid", mchid);
        params.put("out_trade_no", "DF" + System.currentTimeMillis());
        params.put("money", "100.00");
        params.put("type_key_pix", "CPF");
        params.put("cpf", "12345678901");
        params.put("description", "张三");
        params.put("notify_url", "https://您的域名/df_notify");
        params.put("code", "tresory");

        // 生成签名
        params.put("pay_md5sign", createSign(params, apikey));

        // 发送POST请求
        StringBuilder postData = new StringBuilder();
        for (Map.Entry<String, String> entry : params.entrySet()) {
            if (postData.length() > 0) postData.append("&");
            postData.append(entry.getKey())
                    .append("=")
                    .append(URLEncoder.encode(entry.getValue(), "UTF-8"));
        }

        URL url = new URL(gateway);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("POST");
        conn.setDoOutput(true);
        conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
        conn.getOutputStream().write(postData.toString().getBytes("UTF-8"));

        BufferedReader reader = new BufferedReader(
            new InputStreamReader(conn.getInputStream(), "UTF-8")
        );
        StringBuilder response = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {
            response.append(line);
        }
        reader.close();
        System.out.println("响应:" + response);
    }

    // MD5签名
    public static String createSign(Map<String, String> params, String apikey) throws Exception {
        TreeMap<String, String> sorted = new TreeMap<>(params);
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, String> entry : sorted.entrySet()) {
            if (entry.getValue() != null && !entry.getValue().isEmpty()
                && !entry.getKey().equals("pay_md5sign")) {
                sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
            }
        }
        sb.append("key=").append(apikey);
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] digest = md.digest(sb.toString().getBytes("UTF-8"));
        StringBuilder hex = new StringBuilder();
        for (byte b : digest) {
            hex.append(String.format("%02X", b));
        }
        return hex.toString();
    }
}
package main

import (
    "crypto/md5"
    "encoding/hex"
    "fmt"
    "io"
    "net/http"
    "net/url"
    "sort"
    "strings"
    "time"
)

func main() {
    mchid := "10001"
    apikey := "您的APIKEY"
    gateway := "https://您的域名/payment/dfpay/add"

    // 代付参数
    params := url.Values{}
    params.Set("mchid", mchid)
    params.Set("out_trade_no", "DF"+time.Now().Format("20060102150405"))
    params.Set("money", "100.00")
    params.Set("type_key_pix", "CPF")
    params.Set("cpf", "12345678901")
    params.Set("description", "张三")
    params.Set("notify_url", "https://您的域名/df_notify")
    params.Set("code", "tresory")
    params.Set("client_email", "zhangsan@email.com")
    params.Set("client_document", "12345678901")

    // 生成签名
    sign := createSign(params, apikey)
    params.Set("pay_md5sign", sign)

    // 发送请求
    resp, err := http.PostForm(gateway, params)
    if err != nil {
        fmt.Println("请求失败:", err)
        return
    }
    defer resp.Body.Close()

    body, _ := io.ReadAll(resp.Body)
    fmt.Println("响应:", string(body))
}

// 代付查询
func queryDfpay(mchid, apikey, outTradeNo string) (string, error) {
    params := url.Values{}
    params.Set("mchid", mchid)
    params.Set("out_trade_no", outTradeNo)
    params.Set("pay_md5sign", createSign(params, apikey))

    resp, err := http.PostForm("https://您的域名/payment/dfpay/query", params)
    if err != nil {
        return "", err
    }
    defer resp.Body.Close()

    body, err := io.ReadAll(resp.Body)
    return string(body), err
}

// MD5签名方法
func createSign(params url.Values, apikey string) string {
    keys := make([]string, 0)
    for k := range params {
        keys = append(keys, k)
    }
    sort.Strings(keys)

    parts := make([]string, 0)
    for _, k := range keys {
        v := params.Get(k)
        if v != "" && k != "pay_md5sign" {
            parts = append(parts, k+"="+v)
        }
    }

    signStr := strings.Join(parts, "&") + "&key=" + apikey
    hash := md5.Sum([]byte(signStr))
    return strings.ToUpper(hex.EncodeToString(hash[:]))
}
const crypto = require('crypto');
const https = require('https');

// 商户配置
const mchid = '10001';
const apikey = '您的APIKEY';
const gateway = '您的域名';
const path = '/payment/dfpay/add';

// 代付参数
const params = {
    mchid: mchid,
    out_trade_no: 'DF' + Date.now(),
    money: '100.00',
    type_key_pix: 'CPF',
    cpf: '12345678901',
    description: '张三',
    notify_url: 'https://您的域名/df_notify',
    code: 'tresory',
    client_email: 'zhangsan@email.com',
    client_document: '12345678901',
};

// 生成签名
function createSign(params, apikey) {
    const sortedKeys = Object.keys(params).sort();
    const parts = [];
    sortedKeys.forEach(key => {
        if (params[key] && key !== 'pay_md5sign') {
            parts.push(`${key}=${params[key]}`);
        }
    });
    const signStr = parts.join('&') + '&key=' + apikey;
    return crypto.createHash('md5').update(signStr).digest('hex').toUpperCase();
}

params.pay_md5sign = createSign(params, apikey);

// 发送请求
const postData = new URLSearchParams(params).toString();

const options = {
    hostname: gateway,
    path: path,
    method: 'POST',
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Content-Length': Buffer.byteLength(postData)
    }
};

const req = https.request(options, (res) => {
    let data = '';
    res.on('data', (chunk) => {
        data += chunk;
    });
    res.on('end', () => {
        console.log('响应:', data);
    });
});

req.on('error', (e) => {
    console.error('请求失败:', e);
});

req.write(postData);
req.end();

// 代付查询
function queryDfpay(mchid, apikey, outTradeNo) {
    const params = {
        mchid: mchid,
        out_trade_no: outTradeNo,
    };
    params.pay_md5sign = createSign(params, apikey);

    const postData = new URLSearchParams(params).toString();
    const options = {
        hostname: gateway,
        path: '/payment/dfpay/query',
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
            'Content-Length': Buffer.byteLength(postData)
        }
    };

    return new Promise((resolve, reject) => {
        const req = https.request(options, (res) => {
            let data = '';
            res.on('data', (chunk) => { data += chunk; });
            res.on('end', () => resolve(data));
        });
        req.on('error', reject);
        req.write(postData);
        req.end();
    });
}

代付API文档 v1.0 | 基于payment模块逻辑生成

查看支付API文档 →