中文 English

泰达U选 API接口开发文档

版本:V1.0.3 - 更新时间:

目录

1. API开发文档简介

本文阅读对象:使用 泰达U选 商户自服务系统的技术架构师、研发工程师、系统运维工程师。通过本文档,商户可了解 泰达U选 接入的技术、接入的产品业务、接入的流程、接入规范等信息,以便于商户顺利完成接入工作。

2. 接入网关

网关地址:/getway.html

3. 统一下单接口

3.1 接口说明

提交方式:POST

3.2 请求参数

参数名称 参数含义 是否必填 参与签名 参数说明
merchantid 商户号 平台分配商户号
network 主网 主网 1: mainnet, 测试 2: test [用于调试,正式环境请使用 1]
orderid 订单号 商户订单号唯一, 字符长度20
create_time 提交时间 unix时间戳:1626373569
cashier 是否返回Json(整数) 1:返回json数据、2:跳转到收银台
notifyurl 异步通知地址 用于回调通知订单状态.(POST返回数据)
amount 订单金额(USD) 保留4小数,不四舍五入
productname 商品名称 商品描述信息
sign MD5签名 请查看签名算法

3.3 返回参数

参数名称 参数含义 参数说明
status 状态码 1:成功,0:失败
message 返回消息 状态说明
data 返回数据 包含订单信息的JSON对象
data.qrcode_url 二维码URL 收款二维码链接
data.account_address 收款地址 USDT收款钱包地址
data.amount 支付金额 实际需要支付的USDT金额
data.orderid 商户订单号 商户提交的订单号
data.platform_orderid 平台订单号 系统生成的唯一订单号(以PYS开头)
data.create_time 创建时间 订单创建时间
data.time_out 超时时间 订单有效期(秒)
data.pay_code 支付类型 1: 以太坊 ERC20 、2:波场 TRC20
data.cashier_url 收银台地址 当cashier=2时返回收银台跳转链接

3.4 请求示例

POST /getway.html HTTP/1.1
Content-Type: application/x-www-form-urlencoded

merchantid=1&network=1&orderid=TEST20230101000001&create_time=1626373569&cashier=1¬ifyurl=https://您的域名/notify.php&amount=10.0000&productname=测试商品&sign=A1B2C3D4E5F6G7H8I9J0

3.5 返回示例

当请求参数cashier=1时,返回JSON和二维码示例:

{
    "status": 1,
    "message": "订单创建成功",
    "data": {
        "qrcode": "data:image/png;base64",
        "account_address": "TFpS9NJ4Djm29RTmax3VonXL8HumgrC4zw",
        "amount": "10.69",
        "platform_orderid": "PYS2025032450595",
        "orderid": "202503201546059331",
        "create_time": "2025-03-20 15:46:05",
        "time_out": 1800,
        "pay_code": "2"
    }
}

当请求参数cashier=2时,返回收银台URL示例:

{
    "status": 1,
    "message": "订单创建成功",
    "data": {
        "cashier_url": "http://loc.weepay.com:8888/index/index/cashier.html?osn=202503201544321601",
        "amount": "10.78",
        "orderid": "202503201544321601",
        "create_time": "2025-03-20 15:44:32",
        "time_out": 1800,
        "pay_code": "2"
    }
}

返回参数说明:

4. 异步通知

提交方式:POST

接收到服务器点对点通讯时,在页面输出"success"(没有双引号,success字母小写),否则会重复3次发送点对点通知。

4.1 通知参数

参数名称 参数含义 是否必填 参与签名 参数说明
code 状态码 1:成功,0:失败
msg 消息 返回消息说明
data 数据对象 包含订单信息的JSON对象 - data非空字段参与签名
data.merchantid 商户编号 平台分配商户号
data.orderid 平台订单号 系统生成的唯一订单号(以PYS开头)
data.out_trade_id 商户订单号 商户提交的原始订单号
data.amount 订单金额 支付金额,单位USDT
data.poundage 手续费 手续费金额,单位USDT
data.status 订单状态 0:未支付,1:支付中,2:已支付,3:订单超时
sign MD5签名 请查看签名算法

4.2 通知示例

{
    "code": 1,
    "msg": "success",
    "data": {
        "merchantid": "1",
        "orderid": "PYS2025032450595",
        "out_trade_id": "202503241012158778",
        "amount": "10.7300",
        "poundage": "0.1073",
        "status": 2,
        "sign": "63FB0B1B2340AB0E0801FE0F249012E4"
    }
}
说明:订单状态(status)为2时,表示支付已成功。商户系统需要校验签名并处理业务逻辑,处理完成后返回"success"。

5. 订单查询接口

5.1 接口说明

提交方式:POST

地址:/query.html

5.2 请求参数

参数名称 参数含义 是否必填 参与签名 参数说明
merchantid 商户编号 平台分配的商户ID
orderid 商户订单号 商户系统的订单号
query_type 查询类型 payment: 支付订单, payout: 代付订单, 空值: 自动判断
sign MD5签名 请查看签名算法

5.3 返回参数

参数名称 参数含义 参数说明
orderid 平台订单号 系统生成的订单编号
out_trade_id 商户订单号 商户提交的订单号
amount 订单金额 单位:usdt
time_end 支付成功时间 格式:yyyy-mm-dd HH:ii:ss
trade_state 支付状态 支付订单:SUCCESS:支付成功,NOTPAY:未支付
代付订单:SUCCESS:代付成功,NOTPAY:未支付,PAYING:代付处理中,PAY_FAILED:代付失败
order_type 订单类型 payment:支付订单,payout:代付订单
status_text 状态描述 订单状态的中文描述

5.4 请求示例

POST /query.html HTTP/1.1
Content-Type: application/x-www-form-urlencoded

merchantid=1&orderid=TEST20230101000001&query_type=payment&sign=A1B2C3D4E5F6G7H8I9J0

5.5 返回示例

{
    "code": 1,
    "msg": "查询支付订单成功",
    "data": {
        "orderid": "PAY202301010001",
        "out_trade_id": "TEST20230101000001",
        "amount": "10.0000",
        "time_end": "2023-01-01 12:05:30",
        "trade_state": "SUCCESS",
        "order_type": "payment",
        "status_text": "已支付",
        "sign": "B2C3D4E5F6G7H8I9J0K1"
    }
}
订单状态说明:
  • 支付订单状态:
    • SUCCESS:订单支付成功
    • NOTPAY:订单未支付或支付失败
  • 代付订单状态:
    • SUCCESS:代付成功
    • NOTPAY:订单未支付
    • PAYING:代付处理中(表示订单状态为未支付、支付中或订单超时)
    • PAY_FAILED:代付失败(表示订单状态为代付失败或拒绝代付)

系统会自动判断订单类型,并在查询结果中返回order_type字段标识订单类型。如需查询指定类型的订单,可以在请求参数中添加query_type参数。

6. 代付接口

6.1 接口说明

提交方式:POST

地址:/payout.html

6.2 请求参数

参数名称 参数含义 是否必填 参与签名 参数说明
merchantid 商户编号 平台分配商户号
network 主网 主网 1: mainnet, 测试 2: ropsten
orderid 订单号 商户订单号唯一, 字符长度20
create_time 提交时间 unix时间戳:1626373569
pay_type 币种选择(整数) 币种选择 usdt: 1
notifyurl 服务端通知 服务端返回地址.(POST返回数据)
withdraw_query_url 代付二次确认URL 如果开启二次确认,则需要传入这个参数 商户需要是否二次确认,如果不需要则可以不传这个参数。
pay_code 支付类型(整数) 1: 以太坊 ERC20 、2:波场 TRC20
amount 订单金额 保留4小数,不四舍五入
address 代付地址 USDT收款地址(ERC20或TRC20)
sign MD5签名 请查看签名算法
version 接口版本号 '1.0.0'
withdraw_query_url 说明:

根据商户需要是否二次确认,如果不需要则可以不传这个参数。

如果需要二次确认,则需要传入一个回查URL地址,当商户提交代付订单API后会回查这个地址,并带入商户订单号,代付金额,代付地址。

{
    "orderId": "1234567890",
    "amount": "10.0000",
    "address": "TYour_TRC20_Address_Here"
}

对应返回值:

{
    "code": 10001,
    "message": "订单被恶意修改。"
}
或者
{
    "code": 10000,
    "message": "订单验证通过。"
}

如果订单验证通过,则继续执行代付操作。否则返回订单被恶意修改。并且代付订单不会进入平台进行任何处理。

二次确认回调地址需要商户自己提供,并且需要保证地址能够被公网访问。

二次确认回调地址需要支持POST请求,并返回JSON数据。

二次确认回调地址需要支持跨域请求。

二次确认回调地址需要支持HTTPS请求。

6.3 返回参数

参数名称 参数含义 参数说明
code 结果代码 success:成功,0:失败
msg 结果消息 接口返回的消息说明
data 返回数据 JSON对象,包含订单详情
data.orderid 商户订单号 商户提交的原始订单号
data.platform_orderid 平台订单号 系统生成的平台内部订单号(以DFS开头)
data.amount 订单金额 代付金额,单位USDT
data.poundage 手续费 代付手续费,单位USDT
data.address 代付地址 USDT收款地址
data.create_time 创建时间 订单创建时间,格式:yyyy-mm-dd HH:ii:ss
data.network 主网 1: mainnet, 2: 测试网
data.pay_code 支付类型 ERC20: 以太坊, TRC20: 波场
data.pay_type 币种 USDT: USDT代币
data.status 订单状态 0:未支付, 1:处理中, 2:已支付, 3:订单超时, 4:失败, 5:拒绝
data.status_desc 状态描述 订单状态的文字描述

6.4 请求示例

POST /payout.html HTTP/1.1
Content-Type: application/x-www-form-urlencoded

merchantid=1&network=1&orderid=202503281035406411&create_time=1626373569&pay_type=1¬ifyurl=https://您的域名/notify.php&pay_code=2&amount=10.0000&address=TYour_TRC20_Address_Here&sign=A1B2C3D4E5F6G7H8I9J0&version=1.0.0

6.5 返回示例

{
    "code": "success",
    "msg": "代付订单提交成功",
    "data": {
        "orderid": "202503281035406411",
        "platform_orderid": "DFS2025032885781",
        "amount": "10.0000",
        "poundage": "1.00",
        "address": "TYour_TRC20_Address_Here",
        "create_time": "2025-03-28 10:35:40",
        "network": "1",
        "pay_code": "TRC20",
        "pay_type": "USDT",
        "status": 1,
        "status_desc": "代付处理中"
    }
}

6.6 代付异步通知

提交方式:POST

接收到服务器点对点通讯时,在页面输出"success"(没有双引号,success字母小写),否则会重复3次发送点对点通知。
参数名称 参数含义 是否必填 参与签名 参数说明
merchantid 商户编号 平台分配商户号
orderid 系统订单号 系统生成的唯一订单号(以DF开头)
out_trade_id 商户订单号 商户提交的原始订单号
amount 订单金额 代付金额,单位USDT
status 订单状态 0:未支付,1:支付中,2:已支付,3:订单超时,4:失败,5:拒绝
time 通知时间 unix时间戳
sign MD5签名 请查看签名算法

6.7 代付异步通知示例

                {
                    "code": 1,
                    "msg": "success",
                    "data": {
                        "merchantid": 1,
                        "orderid": "DFS2025032846917",
                        "out_trade_id": "202503281034025239",
                        "amount": "10.0000",
                        "transaction_id": "",
                        "status": 2,
                        "time": 1743944210,
                        "remark": "已支付",
                        "sign": "DE987378BAC0AC03976A79AD17060310"
                    }
                }
说明:订单状态(status)为2时,表示代付已成功。商户系统需要校验签名并处理业务逻辑,处理完成后返回"success"。
代付状态处理说明:
  • 状态 2 (已支付): 表示代付交易成功完成,可以更新订单为成功状态。
  • 状态 4 (失败) 或 状态 5 (拒绝): 表示代付失败,系统会自动退回商户余额,并产生对应的回调通知。
  • 状态 3 (订单超时): 可理解为处理中状态,此时会有人工介入处理。
  • 注意: code=0 不一定表示订单最终失败,请以 status 字段的值为准判断订单最终状态。

7. 余额查询接口

7.1 接口说明

提交方式:POST

地址:/balance.html

7.2 请求参数

参数名称 参数含义 是否必填 参与签名 参数说明
merchantid 商户编号 平台分配的商户ID
timestamp 时间戳 unix时间戳,5分钟内有效
sign MD5签名 请查看签名算法

7.3 返回参数

参数名称 参数含义 参数说明
code 状态码 1:成功,0:失败
msg 返回消息 状态说明
data 返回数据 包含余额信息的JSON对象
data.merchantid 商户ID 商户唯一标识
data.username 商户名称 商户账户名
data.balance 账户余额 商户当前可用余额,单位USDT
data.currency 货币类型 余额的货币类型,固定为USDT
data.query_time 查询时间 格式:yyyy-mm-dd HH:ii:ss

7.4 请求示例

POST /balance.html HTTP/1.1
Content-Type: application/x-www-form-urlencoded

merchantid=1&timestamp=1711603200&sign=A1B2C3D4E5F6G7H8I9J0

7.5 返回示例

{
    "code": 1,
    "msg": "查询余额成功",
    "data": {
        "merchantid": "1",
        "username": "merchant_demo",
        "balance": "1000.0000",
        "currency": "USDT",
        "query_time": "2025-03-28 10:35:40"
    }
}
说明:因安全考虑,余额查询接口添加了时间戳验证,请确保您的服务器时间正确同步,时间戳有效期为5分钟。

8. 签名算法

签名生成的通用步骤如下:

第一步,设所有发送或者接收到的数据为数组M,将数组M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1+value1+key2+value2…)拼接成字符串stringA。

第二步,在stringA最后拼接上应用private_key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。

示例:

原始参数:

{
    "amount": "300.0000",
    "callbackurl": "http://www.baidu.com/callbackurl.html",
    "cashier": "2",
    "create_time": "1626376050",
    "default_currency": "cny",
    "merchantid": "2",
    "network": "1",
    "notifyurl": "http://www.baidu.com/notifyurl.html",
    "orderid": "out2021071587481",
    "pay_code": "1",
    "pay_type": "1",
    "productname": "测试商品",
    "version": "1.0.0"
}

私钥:

K0d8F434vUjVc88vxkDxmC0z8cj0UZ

第一步:按照参数名ASCII码从小到大排序

amount300.0000callbackurlhttp://www.baidu.com/callbackurl.htmlcashier2create_time1626376050default_currencycnymerchantid2network1notifyurlhttp://www.baidu.com/notifyurl.htmlorderidout2021071587481pay_code1pay_type1productname测试商品version1.0.0

第二步:拼接私钥

stringSignTemp="amount300.0000callbackurlhttp://www.baidu.com/callbackurl.htmlcashier2create_time1626376050default_currencycnymerchantid2network1notifyurlhttp://www.baidu.com/notifyurl.htmlorderidout2021071587481pay_code1pay_type1productname测试商品version1.0.0K0d8F434vUjVc88vxkDxmC0z8cj0UZ"

第三步:生成MD5并转大写

sign=MD5(stringSignTemp).toUpperCase()

Java代码示例:

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

public class WeePaySignUtil {
    private static final String CHARSET = "UTF-8";
    
    /**
     * 生成签名
     * @param params 请求参数
     * @param privateKey 商户私钥
     * @return 签名结果
     */
    public static String generateSign(Map params, String privateKey) {
        try {
            // 1. 过滤空值和sign参数
            Map filteredParams = new TreeMap<>();
            for (Map.Entry entry : params.entrySet()) {
                if (entry.getValue() != null && !entry.getValue().isEmpty() 
                    && !"sign".equals(entry.getKey())) {
                    filteredParams.put(entry.getKey(), entry.getValue());
                }
            }
            
            // 2. 按照参数名ASCII码从小到大排序
            StringBuilder stringA = new StringBuilder();
            for (Map.Entry entry : filteredParams.entrySet()) {
                stringA.append(entry.getKey()).append(entry.getValue());
            }
            
            // 3. 拼接私钥
            String stringSignTemp = stringA.toString() + privateKey;
            
            // 4. MD5加密并转大写
            return md5(stringSignTemp).toUpperCase();
        } catch (Exception e) {
            throw new RuntimeException("生成签名失败", e);
        }
    }
    
    /**
     * MD5加密
     * @param text 待加密文本
     * @return 加密结果
     */
    private static String md5(String text) throws Exception {
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] bytes = md.digest(text.getBytes(CHARSET));
        return bytesToHex(bytes);
    }
    
    /**
     * 字节数组转16进制字符串
     */
    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02x", b));
        }
        return sb.toString();
    }
    
    /**
     * 使用示例
     */
    public static void main(String[] args) {
        // 配置信息
        String merchantId = "1";
        String privateKey = "K0d8F434vUjVc88vxkDxmC0z8cj0UZ";
        
        // 构建请求参数
        Map params = new HashMap<>();
        params.put("merchantid", merchantId);
        params.put("network", "1");
        params.put("orderid", "TEST" + System.currentTimeMillis());
        params.put("create_time", String.valueOf(System.currentTimeMillis() / 1000));
        params.put("cashier", "1");
        params.put("notifyurl", "https://您的域名/notify.php");
        params.put("amount", "10.0000");
        params.put("productname", "测试商品");
        
        // 生成签名
        String sign = generateSign(params, privateKey);
        params.put("sign", sign);
        
        // 打印请求参数
        System.out.println("请求参数:");
        for (Map.Entry entry : params.entrySet()) {
            System.out.println(entry.getKey() + " = " + entry.getValue());
        }
        
        // 发送HTTP请求示例
        try {
            String response = sendHttpRequest(params);
            System.out.println("响应结果:" + response);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 发送HTTP请求示例
     */
    private static String sendHttpRequest(Map params) throws Exception {
        // 这里使用HttpClient发送请求,需要添加相关依赖
        // 实际项目中建议使用OkHttp或Apache HttpClient等HTTP客户端
        StringBuilder urlParams = new StringBuilder();
        for (Map.Entry entry : params.entrySet()) {
            if (urlParams.length() > 0) {
                urlParams.append("&");
            }
            urlParams.append(entry.getKey()).append("=")
                    .append(java.net.URLEncoder.encode(entry.getValue(), CHARSET));
        }
        
        // 这里仅作示例,实际项目中需要根据具体情况实现
        return "HTTP请求实现代码";
    }
}
Java示例说明:
  • 示例代码使用了TreeMap确保参数按ASCII码排序
  • 使用UTF-8编码处理字符串
  • 提供了完整的签名生成流程
  • 包含了参数过滤、排序、拼接、MD5加密等步骤
  • 提供了main方法作为使用示例
注意事项:
  • 实际项目中需要添加适当的异常处理
  • 建议使用线程安全的实现方式
  • HTTP请求部分需要根据实际项目选择合适的HTTP客户端
  • 建议添加参数验证和日志记录

9. 示例代码

9.1 PHP代码示例

/**
 * 生成签名
 * 
 * @param array $data 请求参数
 * @param string $privateKey 商户私钥
 * @return array 包含签名和签名字符串
 */
function makeSign($data, $privateKey)
{
    // 除去数组中的空值和签名参数
    $para_filter = array();
    foreach ($data as $key => $val) {
        if ($key == "sign" || $val === "" || $val === null) {
            continue;
        }
        $para_filter[$key] = $val;
    }

    // 按照键名字典序排序
    ksort($para_filter);
    reset($para_filter);

    // 拼接字符串
    $str_sign = "";
    foreach ($para_filter as $key => $val) {
        $str_sign .= $key . $val;
    }

    // 添加私钥
    $str_sign .= $privateKey;

    // MD5加密并转大写
    $sign = strtoupper(md5($str_sign));

    return [
        'sign'     => $sign,
        'str_sign' => $str_sign
    ];
}

9.2 下单请求示例

// 配置信息
$config = [
    'merchant_id' => 1,
    'private_key' => 'your_private_key',
    'notify_url'  => 'https://您的域名/notify.php',
    'gateway_url' => 'https://支付域名/getway.html',
];

// 组装下单参数
$timestamp = time();
$orderid   = date('YmdHis') . mt_rand(1000, 9999);

$params = [
    'merchantid'  => $config['merchant_id'],
    'network'     => 1,
    'orderid'     => $orderid,
    'create_time' => $timestamp,
    'cashier'     => 1,
    'notifyurl'   => $config['notify_url'],
    'amount'      => '10.0000',
    'productname' => '测试商品',
];

// 生成签名
$sign_data = makeSign($params, $config['private_key']);
$params['sign'] = $sign_data['sign'];

// 发起HTTP请求
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $config['gateway_url']);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);

// 处理响应
$result = json_decode($response, true);
print_r($result);

9.3 异步通知处理示例

// 获取所有请求参数
$data = $_POST;

// 获取商户信息(实际应用中应该从数据库获取)
$private_key = 'your_private_key';

// 验证签名
$sign = $data['sign'];
unset($data['sign']);

// 签名验证
$sign_data = makeSign($data, $private_key);
if ($sign_data['sign'] === $sign) {
    // 验证成功,处理订单逻辑
    // ...
    
    // 返回成功标识
    echo 'success';
} else {
    // 签名验证失败
    echo 'fail';
}

9.4 余额查询示例

// 配置信息
$config = [
    'merchant_id' => 1,
    'private_key' => 'your_private_key',
    'balance_url' => 'https://支付域名/balance.html',
];

// 组装参数
$timestamp = time();

$params = [
    'merchantid'  => $config['merchant_id'],
    'timestamp'   => $timestamp,
];

// 生成签名
$sign_data = makeSign($params, $config['private_key']);
$params['sign'] = $sign_data['sign'];

// 发起HTTP请求
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $config['balance_url']);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);

// 处理响应
$result = json_decode($response, true);
if ($result['code'] == 1) {
    echo "余额查询成功:" . $result['data']['balance'] . " " . $result['data']['currency'] . "\n";
} else {
    echo "查询失败:" . $result['msg'] . "\n";
}

print_r($result);