代付API为商户提供资金代付能力。商户通过HTTP POST方式提交代付申请,系统自动审核并调用第三方代付通道完成打款。代付结果通过异步通知方式返回给商户。
https://您的域名/payment代付API使用与支付API相同的MD5签名算法:
key1=value1&key2=value2&......&key=商户APIKEYpay_md5sign 参数本身不参与签名&key=APIKEY 结尾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();
}
/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重复 |
| 签名验证失败 | 签名错误 |
| 余额不足 | 商户余额不足以支付代付金额 |
/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": "交易不存在"
}
| refCode | refMsg | 说明 |
|---|---|---|
| 1 | 成功 | 代付已成功打款 |
| 2 | 失败 | 代付失败 |
| 3 | 处理中/待确认 | 代付正在处理或待确认 |
| 4 | 待处理 | 代付申请已提交,等待处理 |
| 5 | 审核驳回 | 代付申请被驳回 |
| 6 | 待审核 | 代付申请等待审核 |
| 7 | 交易不存在 | 订单号不存在 |
| 8 | 未知状态 | 异常状态 |
代付处理完成后,系统会向商户提交的 notify_url 发送POST请求通知代付结果。
success,否则系统会重复通知。
| 参数名 | 类型 | 说明 |
|---|---|---|
| mchid | String | 商户号 |
| out_trade_no | String | 商户订单号 |
| transaction_id | String | 系统代付订单号 |
| amount | String | 代付金额 |
| refCode | String | 状态码(1=成功 2=失败) |
| refMsg | String | 状态描述 |
| success_time | String | 成功时间(成功时返回) |
| sign | String | MD5签名 |
// 接收代付异步通知
$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');
});
<?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模块逻辑生成