微信内置浏览器里的 H5 支付,是站长和开发者最容易卡壳的环节。用户点一个按钮,能不能直接拉起微信收银台、输完密码就付完款,靠的是一套前后端配合的完整链路。这篇教程把易支付JSAPI支付对接的授权目录、后端下单、前端唤起、异步验签、高频报错串成一条可落地的流程,代码都能直接跑,报错按根因讲清楚。
易支付作为支付接口技术服务商,仅提供标准化 API 对接技术服务,资金清算由持牌支付机构完成,开发者对接时应遵守微信支付及相关监管合规要求。
一、前期对接必备准备工作
动手写代码前,先把三件事准备好,能少走一半弯路。「URL 未注册」「无法获取 openid」等报错,根源多在这一步没配齐。
1.1 账号与权限准备
你需要一个已开通 JSAPI 能力的商户账号,以及一个已完成微信认证的公众号。易支付JSAPI接口开发依赖公众号网页授权能力,公众号必须通过认证才能拿到用户身份信息。同时确认商户后台「支付方式」已勾选微信 JSAPI 通道,漏了会返回「通道未开通」。
1.2 域名备案、微信商户平台基础配置
公众号H5 JSAPI对接易支付,域名是绕不过去的一道坎。支付页面必须部署在已备案域名下,并完成两处配置:
- 公众号网页授权域名:在公众号后台「功能设置」里,把支付页面所在域名填进「网页授权域名」,这一步决定能不能拿到 openid。
- JSAPI 支付授权目录:在微信商户平台「产品中心 - 开发配置」里,填发起支付页面的上一级目录,以斜杠结尾,这是易支付JSAPI授权目录配置的关键。
这是易支付JSAPI 当前页面URL未注册报错的底层成因:微信唤起收银台前会校验当前页面 URL 是否落在授权目录内,不匹配就拒绝。
1.3 获取易支付对接基础参数
登录商户后台,在「接口信息」里拿到两个关键值:商户 ID(pid)和通信密钥(key)。pid 标识商户身份,key 用于对请求参数做签名。密钥等同于账户「第二密码」,泄露后别人就能伪造下单请求,务必妥善保管,绝不写进前端页面。
二、易支付JSAPI整体业务时序流程
易支付JSAPI支付完整对接步骤如下:
用户访问支付页面 → 页面引导用户完成公众号网页授权,拿到 openid → 后端用它向易支付发起「创建预支付订单」请求 → 易支付返回一组支付参数 → 前端调用 wx.chooseWXPay 唤起微信收银台 → 用户完成支付 → 易支付向商户配置的 notify_url 发起异步通知 → 商户服务端验签、更新本地订单状态。
贯穿全文的关键原则:前端展示的支付结果只做交互提示,订单最终状态永远以异步通知验签结果为准。
三、后端接口对接实操
后端是易支付JSAPI支付对接的核心,订单创建、签名、回调处理都在这一层。下面按「参数 → 签名 → 代码」顺序拆解。
3.1 创建JSAPI订单请求参数详解
创建预支付订单的核心必填参数如下:
pid(必填):商户 ID。type(必填):支付方式,JSAPI 场景固定为jsapi。out_trade_no(必填):商户订单号,需保证唯一。notify_url(必填):异步通知地址,须公网可访问。name(必填):商品名称,展示在收银台。money(必填):金额,单位元,保留两位小数,如 1.00 元。openid(JSAPI 必填):用户在该公众号下的唯一标识,28 位字符。sign(必填):签名,按规则排序后计算。
选填参数如 return_url、param 按需填写,金额不要用科学计数法。单笔订单金额上限一般由平台风控规则决定,常见为 50000 元以内。
3.2 签名生成、签名校验规范
签名是防篡改的核心。通用规则:把除 sign 外的参数按键名 ASCII 升序排序,拼成 key1=value1&key2=value2,再拼接通信密钥 key,最后做 MD5。三个易踩点:拼接时排除空值参数、编码统一 UTF-8、密钥接在末尾不加分隔符。
3.3 PHP 易支付JSAPI支付示例代码
下面是一段可直接运行的 PHP 示例:
<?php
// 易支付JSAPI支付对接:创建预支付订单示例
$config = array(
'pid' => '1000', // 商户ID,替换成你自己的
'key' => 'your_secret_key', // 通信密钥,务必保密
'api_url' => 'https://api.xxx.com/', // 接口地址,替换成平台提供值
);
$params = array(
'pid' => $config['pid'],
'type' => 'jsapi',
'out_trade_no' => 'ORDER' . date('YmdHis') . mt_rand(1000, 9999),
'notify_url' => 'https://yourdomain.com/notify.php',
'return_url' => 'https://yourdomain.com/return.php',
'name' => '测试商品',
'money' => '1.00',
'openid' => $openid, // 由网页授权获得
);
// 生成签名:排除空值,键名升序排序
ksort($params);
$sign_str = '';
foreach ($params as $k => $v) {
if ($v !== '' && $v !== null) {
$sign_str .= $k . '=' . $v . '&';
}
}
$sign_str = rtrim($sign_str, '&') . $config['key'];
$params['sign'] = md5($sign_str);
// 发起请求
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $config['api_url'] . 'submit.php');
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);
$result = json_decode($response, true);
if ($result['code'] == 1) {
echo json_encode($result); // 返回前端,用于 wx.chooseWXPay
} else {
echo $result['msg'];
}
?>
实际接入替换 pid、key、api_url 三个值即可。
四、前端页面唤起JSAPI支付配置
后端下单成功后,前端拿到的参数要交给微信 JSSDK 唤起收银台。这一步是易支付微信网页支付集成里最容易出错的地方,涉及 JSSDK 签名和支付参数签名两层,两者不要混淆。
4.1 引入微信JSSDK,完成wx.config权限注入
先在页面引入微信 JSSDK,再调用 wx.config 注入权限,声明要使用的 chooseWXPay 能力:
<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
<script>
wx.config({
debug: false,
appId: '<?= $jsApiConfig["appId"] ?>',
timestamp: '<?= $jsApiConfig["timestamp"] ?>',
nonceStr: '<?= $jsApiConfig["nonceStr"] ?>',
signature: '<?= $jsApiConfig["signature"] ?>',
jsApiList: ['chooseWXPay']
});
</script>
4.2 chooseWXPay唤起支付完整前端代码
权限注入成功后,把后端返回的支付参数传给 wx.chooseWXPay:
wx.ready(function () {
var payParams = {
appId: 'wx8888888888888888',
timeStamp: '1710000000',
nonceStr: 'abcdefghijklmn',
package: 'prepay_id=wx20260828000000abcd',
signType: 'MD5',
paySign: '后端返回的支付签名'
};
wx.chooseWXPay({
timestamp: payParams.timeStamp,
nonceStr: payParams.nonceStr,
package: payParams.package,
signType: payParams.signType,
paySign: payParams.paySign,
success: function () { /* 仅代表用户确认,结果看异步回调 */ },
cancel: function () { /* 用户主动取消 */ },
fail: function () { /* 常见原因是授权目录没配对 */ }
});
});
4.3 前端支付结果处理规范
再次强调:前端 success 回调只是「用户完成密码输入」的交互确认,绝不能作为订单已支付的依据。正确做法是前端等待后端异步通知确认订单状态后再展示「支付成功」,避免用户付了钱、前端却因网络抖动显示失败引发客诉。
五、异步通知notify_url配置与安全验签
异步通知是易支付JSAPI回调通知配置的核心,也是安全防线所在。notify_url 公网可访问,任何人都能向它发请求,不验签就会被伪造的「支付成功」通知骗过。
5.1 回调通知接收基础逻辑
易支付以 POST 方式向 notify_url 发送通知,包含订单号、金额、支付状态等参数。服务端收到后第一步不是急着改订单状态,而是先验签,通过后再处理业务。
5.2 签名校验代码示例,拦截伪造恶意回调
<?php
// 易支付JSAPI支付异步回调验签
$key = 'your_secret_key';
$params = $_POST;
$sign = isset($params['sign']) ? $params['sign'] : '';
unset($params['sign']);
ksort($params);
$sign_str = '';
foreach ($params as $k => $v) {
if ($v !== '' && $v !== null) {
$sign_str .= $k . '=' . $v . '&';
}
}
$sign_str = rtrim($sign_str, '&') . $key;
if (md5($sign_str) !== $sign) {
exit('fail'); // 验签失败,拒绝
}
$out_trade_no = $params['out_trade_no'];
if ($params['trade_status'] === 'TRADE_SUCCESS') {
updateOrderStatus($out_trade_no, 'paid');
}
exit('success');
?>
5.3 幂等性处理方案,避免重复回调多次变更订单状态
同一笔订单,易支付可能因网络重试多次推送通知。每次收到都直接改状态,可能触发重复的积分发放、库存扣减等副作用,所以必须做幂等处理:
<?php
function updateOrderStatus($out_trade_no, $status) {
$order = findOrderByNo($out_trade_no);
if ($order['status'] === 'paid') {
return; // 已处理过,直接返回
}
$updated = updateOrderIfStatus($out_trade_no, 'unpaid', $status);
if ($updated) {
grantUserBenefits($order['user_id']); // 仅状态真正变化时执行一次
}
}
?>
核心是「先查状态、再原子更新、状态真正变化时才触发业务」,平台重推多少次,业务逻辑都只执行一次。
5.4 return_url同步跳转页面说明
return_url 是用户支付完成后浏览器跳转的页面,用于展示结果。它与 notify_url 的区别:return_url 是「给人看的」,由浏览器跳转,不可靠;notify_url 是「给机器验的」,由服务器发起,才是订单状态的权威来源。
六、易支付JSAPI高频报错问题汇总
下面四个报错,占据易支付JSAPI支付对接排错工作的九成,逐个讲根因和正确解法。
6.1 当前页面的URL未注册:根因+正确配置方案
报错「当前页面的URL未注册」几乎只指向一个原因:发起支付页面的 URL 不在微信商户平台配置的「JSAPI 支付授权目录」范围内。例如支付页面是 https://yourdomain.com/pay/index.html,授权目录就填 https://yourdomain.com/pay/,必须以斜杠结尾。
高频细节:授权目录修改后有缓存延迟,微信侧一般需几分钟到半小时才生效。刚改完就测试仍报「URL 未注册」,先等一会儿再试。
6.2 异步回调接收不到通知:防火墙、内网地址、公网访问校验排查
回调收不到,按三层排查:一、notify_url 必须是公网可访问地址,内网 IP、localhost 一律不行;二、检查防火墙或安全组是否放行回调来源 IP;三、用 curl 从外部模拟 POST 验证接口能通。结合平台「回调日志」判断:已推送但收不到是网络层问题,推送失败多半是 notify_url 配错。
6.3 签名校验失败:参数排序、编码格式、密钥填写错误排查
签名对不上,按顺序查三处:参数是否严格按键名 ASCII 升序排序;编码是否统一 UTF-8(尤其是中文商品名);密钥是否与后台一致、是否带进首尾空格。自检法:把拼好的签名字符串打印出来,和平台文档示例逐字符比对。
6.4 无法获取OpenID:公众号授权回调域名配置错误修复
易支付JSAPI支付如何获取openid,最常见问题是公众号「网页授权域名」没配或配错,须与实际发起授权域名一致。另一个原因是 scope 用错,应按微信 OAuth2.0 流程选用 snsapi_base 或 snsapi_userinfo 换取 openid。
七、上线前安全自查清单
正式上线前,逐条过一遍这份清单,能挡住大部分安全事故:
- 密钥禁止硬编码写入前端页面:key 只存在于服务端。
- 回调接口做好鉴权、幂等处理:验签 + 幂等,缺一不可。
- JSAPI授权目录精细化配置:不推荐直接填根目录,精确到发起支付页面的目录。
- 测试环境完整联调后切换正式通道:先用测试参数跑通全流程,再切生产密钥。
FAQ 常见问答
下面是开发者高频咨询的几个问题,统一解答。
Q1:易支付JSAPI和Native支付区别是什么?如何选择适用场景?
核心区别在「支付入口」:JSAPI 面向微信内置浏览器,用户在公众号或 H5 页面点按钮直接拉起收银台;Native 则生成二维码,用户用「扫一扫」完成支付。公众号菜单、H5 商城选 JSAPI;PC 网站、线下收银台选 Native。
Q2:配置JSAPI授权目录后,多久正式生效?
微信侧一般几分钟到半小时内生效,存在缓存延迟。建议配置后等 10 到 30 分钟再测试,若仍报「URL 未注册」,先核对目录格式。微信授权目录最多可配置 5 个,超出需先清理旧的。
Q3:JSAPI支付对接是否必须公众号AppID?
是的。JSAPI 依赖公众号网页授权获取 openid,而 openid 是 JSAPI 下单必填参数,所以必须有已认证的公众号及其 AppID,否则无法走 JSAPI 通道。
Q4:支付成功,异步回调没有触发该如何排查?
按「地址可达性 → 网络放行 → 验签结果」三层排查:确认 notify_url 公网可访问、防火墙放行、回调日志里平台是否推送成功。已推送但收不到,查服务器日志;收到但验签失败,逐项核对参数排序。