对外支付 API 接口文档
适用于无开发能力的网站方快速接入支付 · 南阳市儒谦商贸有限公司提供
快速开始(3 步完成对接)
- 复制下方「完整 PHP 示例」代码,替换 3 个配置项(商户ID、密钥、通知地址)
- 上传到你的服务器,访问该文件即可发起支付
- 看到跳转支付宝即对接成功
支付链路架构(双层验签)
本系统存在两套独立的验签流程,商户只需关注第一套:
第一层:商户 ↔ 平台(MD5)
- 商户下单时用商户密钥对参数做 MD5 签名
- 平台验证签名后受理订单
- 支付成功后,平台用同一密钥签名并 GET 通知商户的 notify_url
- 商户收到通知后用同一密钥验签确认
第二层:平台 ↔ 支付宝(RSA2)
- 平台用应用私钥对支付宝请求做 RSA2 签名
- 支付宝支付成功后回调平台的 alipay-notify 接口
- 平台用支付宝公钥验证回调签名(RSA2)
- 验签通过后平台更新订单状态,再转发通知给商户
商户无需对接支付宝、无需配置支付宝密钥,只需与平台完成 MD5 验签即可。支付宝的 notify_url 由平台内部管理,商户传入的 notify_url 仅用于接收平台转发的支付结果通知。
1. 接口地址与请求头
Content-Type: application/x-www-form-urlencoded 或 application/json(两种均支持)
每个请求必须携带以下 3 个请求头(直接复制,所有商户通用):
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlIjoiYW5vbiIsImlzcyI6InBvbGFyZGIiLCJpYXQiOjE3ODQ2MTQ2MTgsImV4cCI6MjA5OTk3NDYxOH0.StbzHSCkim8How6H3eZWH-RKMiIXqNc1QSm0LKZi_Vg
apikey: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlIjoiYW5vbiIsImlzcyI6InBvbGFyZGIiLCJpYXQiOjE3ODQ2MTQ2MTgsImV4cCI6MjA5OTk3NDYxOH0.StbzHSCkim8How6H3eZWH-RKMiIXqNc1QSm0LKZi_Vg
Referer: https://www.ruqianshop.cn
这是平台网关的固定认证信息,无需理解其含义,直接复制即可。
2. 请求参数
| 字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
|---|---|---|---|---|---|
| 商户ID | pid | 是 | Int | 1001 | 由平台分配的商户ID |
| 支付方式 | type | 是 | String | alipay | 固定为 alipay |
| 商户订单号 | out_trade_no | 是 | String | 20160806151343349 | 商户系统内部唯一订单号 |
| 异步通知地址 | notify_url | 是 | String | https://yourdomain.com/notify | 支付成功后接收 GET 通知 |
| 同步跳转地址 | return_url | 否 | String | https://yourdomain.com/return | 支付完成后用户跳转地址 |
| 商品名称 | name | 是 | String | VIP会员 | 不超过 127 字节 |
| 商品金额 | money | 是 | String | 1.00 | 单位元,最多两位小数 |
| 用户 IP | clientip | 是 | String | 192.168.1.100 | 用户发起支付的 IP |
| 设备/场景类型 | device | 否 | String | pc | 可选 qr(或 scan)、pc、mobile。qr 为扫码场景,会把后台标记为「仅扫码」的通道(如当面付)纳入可选池;pc 为电脑网页场景;mobile 为手机网页场景。不传时平台按请求 User-Agent 自动识别 pc/mobile;UA 也认不出终端(服务端 curl/PHP/Java 等)则归入「未识别兜底」(web)场景。服务端发起的请求强烈建议显式传参,详见 2.2 常见问答 |
| 业务扩展参数 | param | 否 | String | abc123 | 支付后原样返回 |
| 签名字符串 | sign | 是 | String | md5 计算结果 | 见下方签名算法 |
| 签名类型 | sign_type | 是 | String | MD5 | 固定为 MD5 |
2.1 设备/场景类型与通道选择
平台按以下优先级确定本次下单的场景,并据此筛选支付通道:
| 优先级 | 判定依据 | 说明 |
|---|---|---|
| 1 | 显式传入 device=qr / scan / pc / mobile | 最高优先级,大小写不敏感;qr 与 scan 等价,均表示扫码场景;其他值视为未传 |
| 2 | 请求头 User-Agent 自动识别 | 含 Android/iPhone/iPad/MicroMessenger 等判为 mobile,其余浏览器 UA 判为 pc;订单备注标记 device_src:ua |
| 3 | 无法判定(UA 为空或为服务端客户端) | curl / PHP / Java / Python / okhttp / Go-http-client 等归为「未识别兜底」(web)场景,只在勾选了该标签或未限定场景的通道中按权重随机 |
device=qr 扫码场景
- 返回 qrcode + urlscheme,payurl 为平台收银台中间页
- 后台被设为「不进大轮询 + 仅扫码」的通道(如当面付)在此场景被优先独占使用
- 存在这类专属通道时,其他不限场景的通用轮询通道不再参与本场景分配;没有专属通道时才按权重随机
device=pc 可选通道
- 订单码(order_code):返回 qrcode + urlscheme,收银台页展示二维码
- 当面付(precreate / face):返回 qrcode + urlscheme,同上
- 电脑网站支付(page):返回 payurl,跳转支付宝原生收银台
device=mobile 可选通道
- 手机网站支付(wap):返回 payurl,手机浏览器内唤起支付宝 App
- 订单码 / 当面付:返回 qrcode + urlscheme,可直接用 urlscheme 唤起支付宝
- 不会分配电脑网站支付(page),避免手机端跳到 PC 收银台无法付款
已对接商户升级说明(接口完全向下兼容,不改也能正常收款)
- 签名算法、请求地址、请求头、返回结构、异步通知格式均无变化,无需重新对接
- 服务端发起下单(PHP/curl/Java 等)请务必显式传 device:PC 站点传 pc,手机站或 H5 传 mobile,需要走扫码收银台传 qr;不传时因 UA 为服务端客户端,会归为「未识别兜底」场景,可能分配不到你期望的通道
- 本次升级仅新增 device=qr / scan 可选值与平台内部的通道场景筛选规则,签名算法、请求地址、请求头、返回结构、异步通知格式均无变化;已按 pc / mobile 对接的商户无需修改代码
- 平台侧可为通道配置「是否参与通用大轮询」与「适用场景」(扫码/电脑网页/手机网页/未识别兜底):不参与大轮询但显式勾选了当前场景的通道属于场景专属通道,该场景的订单会优先只分配给它,因此商户传对 device 直接决定走哪个通道
- 预下单遇到通道致命错误(如未签约产品、无权限 ACCESS_FORBIDDEN)时,平台会自动切换到其他可用通道重试(最多 3 次)并回写实际通道,商户侧无感知;全部失败才返回 code=-101,订单标记为 failed
- 由浏览器直接调用下单接口的场景可不传 device,平台按 UA 自动识别
- 新增可选参数 device 参与签名:传了就按普通参数一起排序签名,不传则不参与(空值不签名规则不变)
- 返回字段新增 pay_method(实际使用的通道支付方式),可用于前端判断展示二维码还是跳转 payurl;旧代码忽略该字段不受影响
- 扫码类通道(订单码/当面付)返回 qrcode 与 urlscheme,payurl 为平台收银台中间页;网页类通道(wap/page)返回 payurl 与 ready=true,qrcode 为空。建议按「qrcode 是否有值」分支处理,而不是按固定模式写死
- 异步通知时机为资金确认到账后发送(小额自动确认,大额等待结算通知或人工确认),商户侧仍需保持幂等处理与返回 success
2.2 常见问答
Qdevice=qr 适合什么场景?和 pc / mobile 有什么区别?▶
你的页面需要展示二维码、让用户拿手机扫码付款时传 qr(scan 与 qr 完全等价)。
此时平台会把后台标记为「仅扫码」的通道(如当面付)纳入可选池,同时保留不限场景的通用通道,一起按权重随机分配。
返回 qrcode 与 urlscheme,payurl 为平台收银台中间页;pc / mobile 场景则不会分配「仅扫码」通道。
Q不传 device 时平台怎么判断?▶
读取请求头 User-Agent:含 Android / iPhone / iPad / MicroMessenger 等判为 mobile,其余浏览器 UA 判为 pc,订单备注会标记 device_src:ua。
浏览器直接调用下单接口时可以不传,由平台自动识别。
QUA 识别失败(未识别兜底)是什么情况,会有什么影响?▶
服务端发起的请求(curl / PHP / Java / Python / okhttp / Go-http-client 等)UA 认不出终端,平台归入 web 兜底场景,订单 device 字段为空。
兜底场景只在「未限定场景」或显式勾选了「未识别兜底」的通道中按权重随机;标记为「仅扫码」的通道不会被选中。
若某场景下没有任何通道命中,平台会回退到全部可用通道,保证订单不会因场景筛选而失败。
因此服务端对接请务必显式传 device,否则可能分配不到你期望的通道。
Q通道被设为「不参与通用大轮询」后还能被选中吗?▶
能,而且会被优先使用:当下单场景显式命中它勾选的适用场景标签时,该场景的订单只分配给这类专属通道,其他通用通道不再参与随机。
例如某当面付通道关闭大轮询且只勾「扫码」,则 device=qr 的订单全部走它,pc / mobile / 兜底场景都不会命中它。
若同一场景勾选了多个专属通道,则在这些专属通道之间按权重随机。
Q传了 device 之后签名要怎么算?▶
device 是普通可选参数:传了就和其他参数一起按参数名 a-z 排序参与 MD5 签名,不传则不参与(空值不签名规则不变)。
注意大小写不敏感(PC / pc 均可),但签名请使用你实际发送的原始值。
Q预下单遇到通道错误会自动换通道吗?▶
会。遇到未签约产品、无权限(ACCESS_FORBIDDEN)等致命错误时,平台自动切换到同场景下的其他可用通道重试,最多 3 个通道,并回写实际使用的通道,商户侧无感知。
全部失败才返回 code=-101,订单标记为 failed,请勿高频重试。
3. 签名算法
签名规则(务必严格遵守):
- 去掉 sign 和 sign_type 两个参数
- 去掉值为空的参数(空字符串不参与签名)
- 剩余参数按参数名 a-z 排序
- 拼接为 key1=value1&key2=value2 格式(值不做 URL 编码)
- 末尾直接拼接商户密钥(不加 & 符号)
- 对整个字符串计算 MD5(32位小写)
验签常见踩坑(务必注意):
- 不要对值做 URL 编码/解码:签名使用原始值拼接,不要 urlencode 也不要 urldecode
- 不要引入多余空格:key=value 之间、& 前后、密钥前后都不能有空格
- 末尾无 &:最后一个参数值后面直接拼密钥,不加 & 分隔符
- MD5 必须 32 位小写:不要大写,不要 16 位截取
- 空值参数不参与:值为空字符串的参数直接跳过,不是拼 key=
4. 返回结果(JSON)
| 字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
|---|---|---|---|---|---|
| 返回状态码 | code | 否 | Int | 1 | 1 成功,其它为失败 |
| 返回信息 | msg | 否 | String | success | 失败原因 |
| 订单号 | trade_no | 否 | String | 20160806151343349 | 商户订单号(原样返回) |
| 支付跳转链接 | payurl | 否 | String | https://... | wap/page 通道为支付宝收银台地址,直接跳转;扫码类通道为平台收银台中间页(页内展示二维码,移动端可自动唤起支付宝) |
| 二维码链接 | qrcode | 否 | String | https://qr.alipay.com/xxx | 扫码支付模式返回,可用于生成二维码 |
| 支付宝 scheme | urlscheme | 否 | String | alipays://platformapi/... | 扫码支付模式返回,APP 内唤起支付宝用 |
| 就绪标记 | ready | 否 | Boolean | true | 网页/手机支付模式返回 true,表示可直接跳转 payurl |
| 通道支付方式 | pay_method | 否 | String | order_code | 本次实际使用的通道类型:order_code 订单码、precreate/face 当面付、wap 手机网站支付、page 电脑网站支付。可用于前端判断展示二维码还是跳转 payurl |
code=1 表示成功,此时 payurl 必有值,将用户跳转至 payurl 即可完成支付。扫码模式额外返回 qrcode 和 urlscheme;网页/手机模式额外返回 ready=true。
5. 异步通知(GET)
支付宝确认收款后,平台先通过 RSA2 验签确认支付宝回调,待资金实际到账确认后(即时到账自动确认,延迟到账需等待结算通知),再用商户密钥(MD5)签名并向你的 notify_url 发送 GET 请求:
| 字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
|---|---|---|---|---|---|
| 商户ID | pid | 否 | Int | 1001 | 固定值 |
| 系统订单号 | trade_no | 否 | String | 2026081234567890 | 支付宝交易号 |
| 商户订单号 | out_trade_no | 否 | String | 20160806151343349 | 商户订单号 |
| 支付方式 | type | 否 | String | alipay | 固定值 |
| 商品名称 | name | 否 | String | VIP会员 | 下单时传入的商品名 |
| 商品金额 | money | 否 | String | 1.00 | 订单金额 |
| 支付状态 | trade_status | 否 | String | TRADE_SUCCESS | 仅此值表示成功 |
| 业务扩展参数 | param | 否 | String | abc123 | 下单时传入则原样返回;未传入则通知中不包含此字段 |
| 签名字符串 | sign | 否 | String | md5 值 | 按签名算法验证 |
| 签名类型 | sign_type | 否 | String | MD5 | 固定值 |
收到通知后:1. 按相同签名算法验证 sign;2. 确认 trade_status=TRADE_SUCCESS;3. 更新订单状态;4. 返回 HTTP 200 且响应体包含 success(不区分大小写)。若未满足条件,系统最多重试 3 次。
<?php
// notify.php - 异步通知接收
$md5Key = '你的商户密钥';
$params = $_GET;
$sign = $params['sign'];
unset($params['sign']);
unset($params['sign_type']);
ksort($params);
$str = '';
foreach ($params as $k => $v) {
if ($v === '' || $v === null) continue;
$str .= $k . '=' . $v . '&';
}
$expected = md5(rtrim($str, '&') . $md5Key);
if ($expected === $sign && $_GET['trade_status'] === 'TRADE_SUCCESS') {
// 验签通过,更新你的订单状态
// $outTradeNo = $_GET['out_trade_no'];
// $money = $_GET['money'];
http_response_code(200);
echo 'success'; // 必须返回 success,否则系统认为通知失败会重试
} else {
http_response_code(400);
echo 'fail';
}6. 错误码
| code | 说明 |
|---|---|
1 | 成功 |
-1 | 缺少必要参数 |
-2 | 仅支持 MD5 签名 |
-3 | 签名验证失败 |
-4 | 金额无效 |
-5 | 无效的 notify_url |
-6 | 订单号已存在(重复) |
-7 | 当前IP付款次数已达上限,请稍后再试 |
-100 | 数据库错误 |
-101 | 业务错误(如支付通道异常)。HTTP 状态码仍为 200,请以 body 中的 code 为准;平台已内置通道致命错误自动切换(最多重试 3 个通道),仍返回 -101 说明当前无可用通道,请勿高频重试 |
7. 查询单个订单接口
用于查询支付订单状态,签名算法与支付接口完全一致。
支持 GET 和 POST 请求,需携带与下单接口相同的 Authorization、apikey、Referer 请求头(见第1节)。
注意:GET 请求参数放在 URL query string 中;POST 请求参数以 JSON 格式放在 body 中(Content-Type: application/json)。
7.1 请求参数
| 字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
|---|---|---|---|---|---|
| 操作类型 | act | 是 | String | order | 固定值 order |
| 商户ID | pid | 是 | Int | 1001 | 由平台分配的商户ID;必传,且只能查到归属本商户ID的订单 |
| 商户订单号 | out_trade_no | 二选一 | String | 20160806151343349 | 与 trade_no 二选一 |
| 系统订单号 | trade_no | 二选一 | String | 20160806151343312 | 优先使用此参数 |
| 签名字符串 | sign | 是 | String | MD5计算结果 | 见支付接口签名算法 |
| 签名类型 | sign_type | 是 | String | MD5 | 固定为 MD5 |
7.2 返回结果(JSON)
| 字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
|---|---|---|---|---|---|
| 返回状态码 | code | - | Int | 1 | 1 成功,其它为失败 |
| 返回信息 | msg | - | String | 查询成功 | 失败原因 |
| 系统订单号 | trade_no | - | String | 2026081234567890 | 支付宝交易号(未支付时回退为商户订单号) |
| 商户订单号 | out_trade_no | - | String | 20160806151343349 | 商户订单号 |
| 第三方订单号 | api_trade_no | - | String | 2026081234567890 | 支付宝交易号(未支付为空字符串) |
| 支付方式 | type | - | String | alipay | 固定值 |
| 商户ID | pid | - | Int | 1001 | 商户ID |
| 创建订单时间 | addtime | - | String | 2026-08-06 22:55:52 | 订单创建时间 |
| 完成交易时间 | endtime | - | String | 2026-08-06 22:55:52 | 支付成功时间(未支付为空字符串) |
| 商品名称 | name | - | String | VIP会员 | 下单时商品名 |
| 商品金额 | money | - | String | 1.00 | 订单金额 |
| 支付状态 | status | - | Int | 1 | 1 已支付,0 未支付 |
| 业务扩展参数 | param | - | String | abc123 | 下单时传入的 param 原样返回(未传则为空字符串) |
| 支付者账号 | buyer | - | String | 当前留空 |
7.3 错误码
| code | 说明 |
|---|---|
1 | 成功 |
-1 | 无效操作 |
-2 | 缺少商户ID |
-3 | 缺少订单号 |
-4 | 缺少签名 |
-5 | 签名验证失败 |
-6 | 订单不存在(订单号不存在,或该订单不归属当前 pid) |
-100 | 数据库错误 |
-101 | 系统错误 |
7.4 示例请求
https://baas-api.wanwang.xin/supabase/proxy/WS20260801233620000001/functions/v1/query-order?act=order&pid=1001&out_trade_no=order_1786533221877324&sign=xxx&sign_type=MD5
说明:pid 为必传参数,服务端按 pid 精确匹配订单归属,商户仅可查询自己名下订单,查询他人订单号统一返回 -6 订单不存在;sign 按第 3 节签名算法,使用参与请求的参数(不含 sign、sign_type)加商户密钥计算。
8. 完整 PHP 示例(复制即用)
将以下代码保存为 pay.php,替换前 3 行配置后上传到你的服务器即可使用:
<?php
// ===== 只需修改这 3 项 =====
$pid = 1001; // 你的商户ID
$md5Key = '你的商户密钥'; // 平台分配的密钥
$notifyUrl = 'https://你的域名/notify.php'; // 你的异步通知地址
// ===== 以下无需修改 =====
$apiUrl = 'https://baas-api.wanwang.xin/supabase/proxy/WS20260801233620000001/functions/v1/external-api';
$anonKey = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlIjoiYW5vbiIsImlzcyI6InBvbGFyZGIiLCJpYXQiOjE3ODQ2MTQ2MTgsImV4cCI6MjA5OTk3NDYxOH0.StbzHSCkim8How6H3eZWH-RKMiIXqNc1QSm0LKZi_Vg';
function isMobileUA($ua) {
return (bool) preg_match('/android|iphone|ipod|ipad|windows phone|harmonyos|micromessenger|mobile/i', $ua);
}
function buildSign($params, $key) {
unset($params['sign']);
unset($params['sign_type']);
ksort($params);
$str = '';
foreach ($params as $k => $v) {
if ($v === '' || $v === null) continue;
$str .= $k . '=' . $v . '&';
}
return md5(rtrim($str, '&') . $key);
}
$params = [
'pid' => $pid,
'type' => 'alipay',
'out_trade_no' => 'order_' . time(),
'notify_url' => $notifyUrl,
'return_url' => 'https://你的域名/return.php',
'name' => '测试商品',
'money' => '0.01',
'clientip' => $_SERVER['REMOTE_ADDR'],
// 服务端发起请求必须显式传 device:PC 站点传 pc,手机站/H5 传 mobile
// 若希望走扫码收银台(含仅扫码场景可用的通道,如当面付),传 qr
// 不传时平台只能读到 curl 的 UA,无法识别终端类型,会归为网页端场景
'device' => isMobileUA($_SERVER['HTTP_USER_AGENT'] ?? '') ? 'mobile' : 'pc',
'sign_type' => 'MD5',
];
$params['sign'] = buildSign($params, $md5Key);
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $apiUrl,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($params),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_TIMEOUT => 15,
CURLOPT_REFERER => 'https://www.ruqianshop.cn',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $anonKey,
'apikey: ' . $anonKey,
],
]);
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
if ($data['code'] == 1) {
header('Location: ' . $data['payurl']);
} else {
echo '错误:' . $data['msg'];
}接入流程
- 联系平台运营获取商户ID(pid)与商户密钥。
- 复制上方完整 PHP 示例,替换配置项。
- 将用户跳转至返回的 payurl 完成支付。
- 在 notify_url 接收异步通知并验签,更新订单状态。
商户信息与本站经营资质见 关于我们。