上个月有个做独立站的哥们,把易支付的接口文档打印出来贴墙上,研究了两天,最后跑来问我一句话:"这文档每个字我都认识,连在一起我就不知道从哪下手了。"
我太理解这种感受了。支付接口的文档,从来不是写给第一次对接的人看的。它默认你已经知道"签名是干嘛的""回调是干嘛的",只管把参数往那一列,剩下的你自己悟。
所以这篇我不照搬文档。我把对接易支付 API 接口这件事,拆成"一张参数表 + 一套签名算法 + 一段能直接跑的 PHP 代码",你照着顺序走,半小时能把第一笔订单跑通。文末还有三个我亲眼见过的翻车点,每个都能卡你半天。
先别写代码,把这张参数表看明白
对接接口最忌讳的,是上来就抄代码。代码抄得再对,参数含义没搞懂,出了错你连排查方向都没有。
下面是支付下单接口(/api/submit.php)的核心参数。我把"必填 / 选填 / 谁最容易填错"都标出来了:
| 参数 | 含义 | 必填 | 最容易错在哪 |
|---|---|---|---|
pid | 商户 ID,后台开通商户后分配 | 是 | 不是你的登录账号,是独立的一串数字 |
type | 支付方式:alipay / wxpay / qqpay | 是 | 写错大小写、写成中文"支付宝" |
out_trade_no | 商户订单号,你自己生成的 | 是 | 同一商户号下不能重复,否则会被判重复下单 |
notify_url | 异步通知地址,支付成功后平台回调这里 | 是 | 必须是公网能访问的 HTTPS 地址 |
return_url | 同步跳转地址,用户付完跳回这里 | 是 | 别和 notify_url 写反了,功能完全不同 |
name | 商品名称,展示给用户看 | 是 | 别带特殊符号,有些通道会拦截 |
money | 支付金额,单位是元 | 是 | 一定要用字符串传,别用浮点数(下面细说) |
sign | MD5 签名,防篡改 | 是 | 拼接顺序错了,签名必错 |
sign_type | 签名类型,固定 MD5 | 是 | 漏传这个字段,直接报签名错误 |
这张表你截图存一下。后面每一步出错,回来对着这张表查,十有八九能找到原因。
签名算法:接口对接真正的分水岭
参数表是固定的,签名算法是灵活的,也是绝大多数人最容易出错的地方。
签名的本质,是防止有人在你下单的过程中把金额改了。比如你本来要收 10 块,中间被人截获改成 0.01,如果没有签名,平台根本发现不了。
易支付的签名规则是这样的:把所有请求参数(sign 除外)按参数名的 ASCII 码从小到大排序,拼成 key1=value1&key2=value2 的字符串,末尾再拼上商户密钥,最后对整串做 MD5。
这里有两个细节,文档里写了但很多人没当回事:
第一,排序是按参数名的 ASCII 码,不是按你写代码的顺序。你代码里先写 pid 再写 money,排序后可能 money 排在 pid 前面。这就是为什么你不能手动拼字符串,必须用程序排序。
第二,密钥是拼在最后,格式是 &key=你的密钥,不是拼接在某个参数值里。这个 & 和 key= 都不能少。
写成代码,核心就三行:
// 1. 按参数名排序(sign、sign_type 都不参与签名)
ksort($params);
// 2. 拼成 key1=value1&key2=value2 的字符串
$paramStr = urldecode(http_build_query($params));
// 3. 末尾拼密钥,算 MD5
$sign = md5($paramStr . $merchantKey);
注意第二行的 urldecode。很多人漏掉这一步,http_build_query 会把中文和特殊字符转成百分号编码,直接拿去算 MD5,得出来的签名和平台对不上。这个坑我在 支付回调sign签名:MD5和RSA到底有什么区别、该用哪个 里也提过,签名这事,细节决定成败。
一段能直接跑的 PHP 代码
别急着自己从头写。先把下面这段完整代码跑通,看到支付页面跳出来了,再改成你自己的业务逻辑。
<?php
// 你的商户信息(后台开通商户后能拿到)
$pid = '1001'; // 商户 ID
$merchantKey = '你的商户密钥'; // 商户密钥,后台查看
// 下单参数
$params = [
'pid' => $pid,
'type' => 'alipay', // 支付方式
'out_trade_no' => 'ORDER' . time(), // 商户订单号
'notify_url' => 'https://你的域名/notify.php', // 异步通知
'return_url' => 'https://你的域名/return.php', // 同步跳转
'name' => '测试商品',
'money' => '0.01', // 金额用字符串
];
// 生成签名
ksort($params); // 按参数名排序
$paramStr = urldecode(http_build_query($params)); // 拼字符串
$params['sign'] = md5($paramStr . $merchantKey); // 算 MD5
$params['sign_type'] = 'MD5';
// 发起请求
$apiUrl = 'https://xxkkfz.cn/api/submit.php';
$ch = curl_init($apiUrl);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
// 响应是 JSON,里面有支付跳转链接
$result = json_decode($response, true);
if (isset($result['url'])) {
header('Location: ' . $result['url']);
exit;
}
echo $response;
把 你的商户密钥 和 你的域名 换成自己的,直接跑。正常情况下,浏览器会跳到一个支付页面,那就是对接成功了。
如果没成功,看返回的 JSON 里 msg 字段写了什么。九成的情况是"签名错误",那问题就出在上面的排序和拼接上,回去对着签名算法一节逐行检查。
回调验签:别让订单状态成了摆设
支付页面跳出来了,只是走完了一半。更关键的是 notify_url 这个异步通知——它是平台告诉你"钱真的到账了"的唯一可靠信号。
这里有个原则必须记住:永远不要相信同步跳转页(return_url)来判断支付成功。用户付完钱,可能直接关掉浏览器,根本不会跳回来。只有异步通知到了,才是真正的支付成功。
收到通知后,你要做两件事:
第一,验签。平台发过来的通知里也带一个 sign,你要用同样的算法,把通知里的参数(sign 除外)排序拼接,算一遍 MD5,和传来的 sign 比对。对得上,说明这条通知真的是平台发的,不是有人伪造。
第二,处理完后返回 success。平台收到这个字符串,才认为通知送达,否则会反复重试。
<?php
// notify.php —— 异步通知处理
$data = $_POST;
$sign = $data['sign'];
unset($data['sign']);
unset($data['sign_type']);
ksort($data);
$paramStr = urldecode(http_build_query($data));
$checkSign = md5($paramStr . $merchantKey);
if ($checkSign !== $sign) {
exit('fail'); // 验签失败,可能是伪造
}
// 验签通过,处理你的业务
$outTradeNo = $data['out_trade_no'];
$tradeStatus = $data['trade_status'];
if ($tradeStatus === 'TRADE_SUCCESS') {
// 更新订单为已支付
}
echo 'success'; // 必须返回这个
这段代码是整个对接里最不能省的一步。我见过有人图省事,回调过来直接改订单状态不验签,结果被人伪造回调刷了单。
三个能卡你半天的翻车点
最后说三个真实的坑。每个都是我或者身边的人真实遇到过的,写出来帮你省时间。
金额一定要用字符串传
PHP 里 '0.01' 和 0.01 看起来一样,但如果是 19.90 这种带小数的金额,用浮点数传,底层二进制表示是有误差的,签名算出来可能对不上。所以 money 这个字段,务必用字符串。
密钥里的换行符
有些商户后台复制密钥的时候,会多带一个看不见的换行符。密钥末尾多了个 \n,签出来的 MD5 永远对不上,你还查不出原因。这个坑我在 易支付支付失败排查实录:藏在密钥里的换行符 里写过完整排查过程,建议一并看一眼。
notify_url 必须公网能访问
很多人本地测试时,把 notify_url 填成 localhost 或者内网 IP。平台回调的时候,是从公网主动访问你这个地址的,它根本摸不到你的 localhost。本地测试回调,得用内网穿透工具,或者直接部署到线上服务器再测。
对接易支付 API 接口,说穿了就三件事:参数填对、签名算对、回调验签。这三件事都不难,难的是没人把它们串起来讲清楚。希望这篇能让你少走一点弯路。