从微信外我自己的页面拉起微信 H5 支付
背景说明
"H5 支付是指商户在微信客户端外的移动端网页展示商品或服务", 也就是说微信H5支付是一种可以在移动端微信客户端外拉起微信客户端支付的方法.
大概的流程是这样的:
- 请求微信统一下单接口, 获得
mweb_url
. 调用接口需要传递spbill_create_ip
参数, 值为用户的 IP. - 在
mweb_url
上追加redirect_url
, 将用户重定向到mweb_url
- 在
mweb_url
上, 通过top.location.href = weixin://wap/pay?xxxx
拉起微信客户端进行支付, 支付完成后, 用户返回到微信客户端外的浏览器, 浏览器会跳转到redirect_url
.
想干嘛 ?
整个支付流程增加了两步跳转, 对于特别是 SPA 的项目, 体验比较差 (我瞎说的), 那么... 搞事 !
既然是在 mweb_url
这个页面上通过 wexin://wap/pay
拉起微信客户端的, 那么就抓它页面! 自己来拉起.
首先明确下微信的限制:
- 访问
mweb_url
的用户 IP, 必须同调用统一下单时传的spbill_create_ip
一致. - 访问
mweb_url
的请求头中Referer
的域名必须与网页授权域名
一致. mweb_url
上追加的redirect_url
必须与网页授权域名
一致.
所以我们在调用统一下单 到 抓 mweb_url
的时候需要做一些工作:
spbill_create_ip
传递程序运行所在的服务器的 IP.- 抓取
mweb_url
的时候伪造Referer
为你申请微信 H5 支付时填写的网页授权域名 - 获取到
mweb_url
后, 不追加redirect_url
. 因为我们不需要微信帮我们自动跳转了.
一段简单的实现:
<?php
$parameters = [
'appid' => $appId,
'mch_id' => $mchId,
'nonce_str' => uniqid(),
'body' => $order['subject'],
'out_trade_no' => $order['id'],
'fee_type' => 'CNY',
'total_fee' => (int)($order['amount'] * 1000 / 10),
'spbill_create_ip' => $_SERVER['SERVER_ADDR'],
'trade_type' => 'MWEB',
'notify_url' => $notifyUrl,
'detail' => $description,
'sign_type' => 'MD5',
'scene_info' => json_encode([
'h5_info' => [
'type' => 'Wap',
'wap_url' => $authorizedHost,
'wap_name' => $siteName,
],
]),
];
// 请求微信统一下单, 获取到响应解析后赋值到 $charge
$charge = pay($parameters);
$curl = curl_init();
curl_setopt_array(
$curl,
[
CURLOPT_URL => $charge['mweb_url'],
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_HTTPHEADER => [
'Host: wx.tenpay.com',
'Accept-Language: en,zh-CN;q=0.8,zh;q=0.6,en-US;q=0.4',
'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Upgrade-Insecure-Requests: 1',
"Referer: {$authorizedHost}",
],
]
);
$result = curl_exec($curl);
$match = [];
preg_match('/var\surl="(.*?)"/', $result, $match);
echo $match[1]; // 输出 `wexin://wap/pay?xxxxx`
然后把获取到的 url 渲染到模板中:
<script>
location.href = '{!! $charge_url !!}';
// 拉起后, 延迟 20 秒自动跳转到付款结果页面. 或是通过轮询服务端查询支付结果后再跳转
set_timeout(
function () {
location.href = '{!! $return_url !!}';
},
20000
);
</script>
最后, 做一个漂亮的页面, 一个漂亮的交互, 然后愉快地让你的用户爸爸们在你的页面拉起微信客户端付款吧.
一个注意事项
iOS 在拉起微信付款后, 是不会自动回到浏览器的, 需要在拉起支付的时候提醒用户记得回来.
又一个支付事项
我们的服务器是阿里云华东节点的ECS, 在上线一段时间后, 发现抓取 mweb_url
非常慢, 频繁超时. 最后没办法, 我们在腾讯云深圳节点买了个服务器当代理, 通过这个代理去抓 mweb_url
, 响应速度非常快.
有没有开箱即用的 SDK ?
没有哈哈哈哈
请查收您开箱即用的 SDK
https://github.com/yansongda/pay
@yansongda 哈哈哈这个 sdk 很赞, 但是应该还没做这个抓取的功能哈
我来了: https://github.com/RunnerLee/nezha-cashier
大佬,spbill_create_ip 这个使用服务器ip 会不会造成微信风控啊。
全部都是服务器的ip,不是用户真实ip,是可以正常支付的,但是不知道官方是否允许这样操作。
@qq5000521 示例代码里面没注意,自己做的时候传用户 ip 就好了,但的确会有风控的风险