从微信外我自己的页面拉起微信 H5 支付

背景说明

微信H5支付文档

"H5 支付是指商户在微信客户端外的移动端网页展示商品或服务", 也就是说微信H5支付是一种可以在移动端微信客户端外拉起微信客户端支付的方法.

大概的流程是这样的:

  1. 请求微信统一下单接口, 获得 mweb_url. 调用接口需要传递 spbill_create_ip 参数, 值为用户的 IP.
  2. mweb_url 上追加 redirect_url, 将用户重定向到 mweb_url
  3. mweb_url 上, 通过 top.location.href = weixin://wap/pay?xxxx 拉起微信客户端进行支付, 支付完成后, 用户返回到微信客户端外的浏览器, 浏览器会跳转到 redirect_url.

想干嘛 ?

整个支付流程增加了两步跳转, 对于特别是 SPA 的项目, 体验比较差 (我瞎说的), 那么... 搞事 !

既然是在 mweb_url 这个页面上通过 wexin://wap/pay 拉起微信客户端的, 那么就抓它页面! 自己来拉起.

首先明确下微信的限制:

  1. 访问 mweb_url 的用户 IP, 必须同调用统一下单时传的 spbill_create_ip 一致.
  2. 访问 mweb_url 的请求头中 Referer 的域名必须与 网页授权域名 一致.
  3. mweb_url 上追加的 redirect_url 必须与 网页授权域名 一致.

所以我们在调用统一下单 到 抓 mweb_url 的时候需要做一些工作:

  1. spbill_create_ip 传递程序运行所在的服务器的 IP.
  2. 抓取 mweb_url 的时候伪造 Referer 为你申请微信 H5 支付时填写的网页授权域名
  3. 获取到 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 ?

没有哈哈哈哈

《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 5

@yansongda 哈哈哈这个 sdk 很赞, 但是应该还没做这个抓取的功能哈

6年前 评论

大佬,spbill_create_ip 这个使用服务器ip 会不会造成微信风控啊。

全部都是服务器的ip,不是用户真实ip,是可以正常支付的,但是不知道官方是否允许这样操作。

4年前 评论

@qq5000521 示例代码里面没注意,自己做的时候传用户 ip 就好了,但的确会有风控的风险

4年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!