快速开始
1
获取商户参数
登录商户后台获取 merchant_no 和 api_key
2
构造请求参数
按接口规范拼接请求参数,并按签名算法生成 sign
3
发起 API 请求
POST JSON/表单 到对应接口地址,处理返回结果
接口基本信息
| 项目 | 说明 |
|---|---|
| 请求方式 | POST 表单提交(application/x-www-form-urlencoded) |
| 字符编码 | UTF-8 |
| 签名算法 | MD5(详见签名算法章节) |
| 数据格式 | 请求:key=value 表单格式 | 响应:JSON |
| 基础 URL | https://<domain> |
POST 创建订单
/pay/create — 商户发起支付,创建一笔待支付订单
请求参数
| 参数名 | 必填 | 类型/规则 | 说明 |
|---|---|---|---|
merchant_id |
必填 | string / alphaNum |
商户号,后台分配的 merchant_no,最长 20 位 |
out_trade_no |
必填 | string / alphaDash |
商户订单号,最长 32 位 — 字母、数字、下划线、破折号 |
amount |
必填 | number / >= 0.01 |
订单金额,单位:元(保留两位小数) |
type |
必填 | string |
支付类型编码,如 alipay、wxpay |
sign |
必填 | string / 32 位 |
MD5 签名,详见签名算法 |
notify_url |
必填 | string / URL |
异步通知地址,支付成功后系统将 POST 通知此地址 |
subject |
可选 | string |
订单标题/商品名称,默认 支付订单 |
return_url |
可选 | string / URL |
同步跳转地址,支付完成后浏览器自动跳转 |
user_ip |
可选 | string / IP |
客户真实 IP, client_ip |
响应参数
| 参数名 | 类型 | 说明 |
|---|---|---|
code | int | 状态码:0 成功,1 失败 |
msg | string | 提示信息 |
data.system_no | string | 系统订单号 |
data.out_trade_no | string | 商户订单号(原样回传) |
data.amount | float | 订单金额 |
data.cashier_url | string | 收银台地址,将用户跳转至此 URL 进行支付 |
请求示例
# POST 表单方式
curl -X POST https://<domain>/pay/create \
-d "merchant_id=M10086" \
-d "out_trade_no=T202605060001" \
-d "amount=1.00" \
-d "type=alipay" \
-d "subject=测试商品" \
-d "notify_url=https://<domain>/notify" \
-d "sign=3b5d7c9e8f1a2b4c6d0e8f1a2b4c6d0e"
// PHP 示例 — 使用 cURL <?php $params = [ 'merchant_id' => 'M10086', 'out_trade_no' => 'T202605060001', 'amount' => '1.00', 'type' => 'alipay', 'subject' => '测试商品', 'notify_url' => 'https://<domain>/notify', ]; // 计算签名 ksort($params); $signStr = urldecode(http_build_query($params)) . '&key={your_api_key}'; $params['sign'] = md5($signStr); $ch = curl_init('https://<domain>/pay/create'); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $params); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $result = curl_exec($ch); curl_close($ch); $data = json_decode($result, true); print_r($data);
成功响应示例
✓ 创建成功
{
"code": 0,
"msg": "创建成功",
"data": {
"system_no": "SYS2026050600010001",
"out_trade_no": "T202605060001",
"amount": 1.00,
"cashier_url": "https://<domain>/pay/show/SYS2026050600010001"
}
}
✗ 请求失败
{
"code": 1,
"msg": "签名验证失败",
"data": {
"local_sign_str": "amount=1.00&merchant_id=M10086&...&key=****",
"local_sign": "3b5d7c9e8f1a2b4c6d0e8f1a2b4c6d0e",
"request_sign": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6"
}
}
POST 查询订单
/pay/query — 根据商户订单号查询订单状态
请求参数
| 参数名 | 必填 | 类型/规则 | 说明 |
|---|---|---|---|
merchant_id |
必填 | string / alphaNum |
商户号,最长 20 位 |
out_trade_no |
必填 | string / alphaDash |
商户订单号,最长 32 位 |
sign |
必填 | string / 32 位 |
MD5 签名,详见签名算法 |
响应参数
| 参数名 | 类型 | 说明 |
|---|---|---|
code | int | 状态码:0 成功,1 失败 |
msg | string | 提示信息 |
data.system_no | string | 系统订单号 |
data.out_trade_no | string | 商户订单号(原样回传) |
data.trade_no | string | 支付平台流水号(未支付时为空) |
data.amount | float | 订单金额 |
data.status | int | 订单状态码:0=创建成功 1=等待支付 2=支付成功 3=退款 4=关闭 |
data.subject | string | 订单标题 |
data.pay_time | int | 支付时间戳(未支付时为 0) |
请求示例
# POST 表单方式
curl -X POST https://<domain>/pay/query \
-d "merchant_id=M10086" \
-d "out_trade_no=T202605060001" \
-d "sign=3b5d7c9e8f1a2b4c6d0e8f1a2b4c6d0e"
<?php $params = [ 'merchant_id' => 'M10086', 'out_trade_no' => 'T202605060001', ]; // 计算签名 ksort($params); $signStr = urldecode(http_build_query($params)) . '&key={your_api_key}'; $params['sign'] = md5($signStr); $ch = curl_init('https://<domain>/pay/query'); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $params); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $result = curl_exec($ch); curl_close($ch); $data = json_decode($result, true); print_r($data);
响应示例
✓ 查询成功(已支付)
{
"code": 0,
"msg": "查询成功",
"data": {
"system_no": "SYS2026050600010001",
"out_trade_no": "T202605060001",
"trade_no": "2026050622001481234567",
"amount": 1.00,
"status": 2,
"subject": "测试商品",
"pay_time": 1746528000
}
}
ℹ 查询成功(未支付)
{
"code": 0,
"msg": "查询成功",
"data": {
"system_no": "SYS2026050600010001",
"out_trade_no": "T202605060001",
"trade_no": "",
"amount": 1.00,
"status": 0,
"subject": "测试商品",
"pay_time": 0
}
}
✗ 订单不存在
{
"code": 1,
"msg": "订单不存在"
}
↻ 异步通知
支付成功后,系统将通过 POST 方式向 notify_url 发送异步通知
通知参数
| 参数名 | 类型 | 说明 |
|---|---|---|
system_no | string | 系统订单号 |
out_trade_no | string | 商户订单号(原样回传) |
trade_no | string | 支付平台流水号 |
amount | float | 订单金额(元) |
settle_amount | float | 结算金额(元,扣减手续费后) |
status | int | 订单状态:2 = 支付成功 |
pay_time | int | 支付时间戳 |
sign | string | MD5 签名,签名方式与请求签名一致 |
商户回调处理要求
| 项 | 说明 |
|---|---|
| 必做 | 验签:使用 api_key 对通知参数计算签名,与 sign 参数比对 |
| 必做 | 去重:记录已处理的 system_no,防止重复通知导致重复发货/入账 |
| 必做 | 响应 success:处理成功后响应内容为 success(纯文本),系统识别后标记通知成功 |
| 建议 | 验证 amount 与商户系统中的订单金额一致 |
| 注意 | 响应其他内容(包括 SUCCESS、ok、JSON)均视为失败,触发重试 |
通知示例
# 系统 POST 到商户 notify_url 的请求参数
system_no=SYS2026050600010001
out_trade_no=T202605060001
trade_no=2026050622001481234567
amount=1.00
settle_amount=0.99
status=2
pay_time=1746528000
sign=8f1a2b4c6d0e3b5d7c9e8f1a2b4c6d0e
<?php // 接收异步通知 $params = $_POST; // 1. 验证签名 if (!verifySign($params, 'your_api_key')) { echo 'fail'; exit; } // 2. 去重处理(system_no 唯一) $systemNo = $params['system_no']; if (alreadyProcessed($systemNo)) { echo 'success'; // 已处理过,直接返回成功 exit; } // 3. 验证订单状态 if ($params['status'] == 2) { // 处理业务逻辑:更新订单、发货等 // ... } // 4. 返回 success 告知系统停止通知 echo 'success'; exit; // 签名验证函数 function verifySign($params, $apiKey) { $sign = $params['sign'] ?? ''; unset($params['sign']); ksort($params); $signStr = urldecode(http_build_query($params)) . '&key=' . $apiKey; return md5($signStr) === $sign; }
重试策略说明
| 次数 | 间隔 | 说明 |
|---|---|---|
| 首次 | 即时 | 支付成功后立即发起通知 |
| 第 1 次重试 | 1 分钟 | 首次通知失败或未返回 success,1 分钟后重试 |
| 第 2 次重试 | 3 分钟 | 再次失败,3 分钟后重试 |
| 第 3 次重试 | 5 分钟 | 继续失败,5 分钟后重试 |
| 第 4 次重试 | 10 分钟 | 最后一次重试,仍失败则放弃,需在后台手动补发 |
若商户地址返回 success,整个通知流程结束,不再重试。
# 签名算法
所有 API 请求必须携带 sign 参数,用于验证请求合法性
签名步骤
1
收集所有请求参数(不包含
sign 本身),过滤掉值为空的参数
2
按照参数名的 ASCII 码升序排序(
ksort)
3
拼接成
key1=value1&key2=value2&...&keyN=valueN 格式字符串
4
在字符串末尾追加
&key={api_key}(api_key 为商户密钥)
5
对完整字符串计算
md5,结果即为 sign 值(32 位小写)签名示例
假设请求参数如下:
merchant_id=M10086
out_trade_no=T202605060001
amount=1.00
type=alipay
notify_url=https://example.com/notify
▼ 排序后拼接
amount=1.00&merchant_id=M10086¬ify_url=https://example.com/notify&out_trade_no=T202605060001&type=alipay
▼ 追加 api_key
amount=1.00&merchant_id=M10086¬ify_url=https://example.com/notify&out_trade_no=T202605060001&type=alipay&key=abcdef1234567890abcdef1234567890
▼ 计算 MD5
sign = md5(above_string) = 3b5d7c9e8f1a2b4c6d0e8f1a2b4c6d0e
PHP 签名参考
<?php function generateSign(array $params, string $apiKey): string { // 1. 移除 sign 参数本身 unset($params['sign']); // 2. 过滤空值 $params = array_filter($params, function($v) { return $v !== '' && $v !== null; }); // 3. 按键名 ASCII 升序排序 ksort($params); // 4. 拼接待签字符串 $signStr = urldecode(http_build_query($params)) . '&key=' . $apiKey; // 5. 计算 MD5 签名 return strtolower(md5($signStr)); } // 使用示例 $params = [ 'merchant_id' => 'M10086', 'out_trade_no' => 'T202605060001', 'amount' => '1.00', 'type' => 'alipay', ]; $params['sign'] = generateSign($params, 'abcdef1234567890abcdef1234567890');
📌 状态码说明
全局状态码
- 0 成功 — 请求处理成功,业务数据在
data字段中 - 1 失败 — 请求处理失败,
msg字段描述具体错误原因
订单状态码
- 0 创建成功 — 订单已创建,等待用户支付
- 1 等待支付 — 用户正在支付流程中
- 2 支付成功 — 订单已支付完成
- 3 退款 — 订单已退款
- 4 关闭 — 订单已关闭/超时
常见错误提示
| 错误提示 | 原因 |
|---|---|
签名验证失败 | 签名计算方式有误,请检查拼接字符串和 api_key |
商户不存在 | merchant_id 不存在或已删除 |
商户已被禁用 | 商户状态异常,请联系管理员 |
外部订单号已存在 | out_trade_no 已被使用,请更换订单号 |
IP不在白名单内 | 请求 IP 不在商户配置的 IP 白名单中 |
系统维护中 | 系统正在维护,请稍后重试 |
💻 代码示例
Python 示例
import hashlib import requests def generate_sign(params, api_key): # 过滤空值、移除 sign params = {k: v for k, v in params.items() if v and k != 'sign'} # 按键名排序 sorted_keys = sorted(params.keys()) # 拼接字符串 sign_str = '&'.join(f"{k}={params[k]}" for k in sorted_keys) sign_str += f"&key={api_key}" return hashlib.md5(sign_str.encode('utf-8')).hexdigest() # 创建订单 params = { 'merchant_id': 'M10086', 'out_trade_no': 'T202605060001', 'amount': '1.00', 'type': 'alipay', 'notify_url': 'https://example.com/notify', } params['sign'] = generate_sign(params, 'your_api_key') resp = requests.post('https://<domain>/pay/create', data=params) print(resp.json())
Java 示例 (Hutool)
import cn.hutool.crypto.SecureUtil; import cn.hutool.http.HttpUtil; import java.util.HashMap; import java.util.Map; import java.util.TreeMap; public class PayApiDemo { public static String generateSign(Map<String, Object> params, String apiKey) { // TreeMap 自动按键名排序 Map<String, Object> sorted = new TreeMap<>(params); sorted.remove("sign"); StringBuilder sb = new StringBuilder(); for (Map.Entry<String, Object> entry : sorted.entrySet()) { if (entry.getValue() != null && !entry.getValue().toString().isEmpty()) { sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&"); } } sb.append("key=").append(apiKey); return SecureUtil.md5(sb.toString()); } public static void main(String[] args) { Map<String, Object> params = new HashMap<>(); params.put("merchant_id", "M10086"); params.put("out_trade_no", "T202605060001"); params.put("amount", "1.00"); params.put("type", "alipay"); params.put("sign", generateSign(params, "your_api_key")); String result = HttpUtil.post("https://<domain>/pay/create", params); System.out.println(result); } }