阅读 242

真 · 手把手 教你实现微信公众号支付

这是我参与8月更文挑战的第11天,活动详情查看:8月更文挑战

前面在《微信公众号整合JSAPI支付方式入门基础步骤》中介绍了微信JSAPI支付方式的对接基础,那么这一篇就来再做一个具体点的实现。实现一个简单的页面,把功能串通起来。

前端

界面

  • 添加两个按钮,做触发授权支付函数使用
<html>
<body>
<div>
    <span>JSAPI支付</span>
    <div>
        <button type="button" onclick="getCode()">授权</button>
        <button type="button" onclick="sendPay()">支付</button>
    </div>
</div>
</body>
</html>
复制代码

index.png

功能

  • 获取code:打开首页之后会去访问微信的认证地址,然后重定向到我们指定的地址上,后面会携带codestate参数
// 微信客户端获取code
function getCode(){
    var local = window.location.href
    var code = getParam('code')
    if (code) {
        getOpenId(code);
    } else {
        // 跳转至授权地址,该地址只支持微信浏览器打开
        // APPID,SCOPE需要根据自己实际情况填写
        window.location.href = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=" + encodeURIComponent(local) + "&response_type=code&scope=SCOPE#wechat_redirect";
    }
}

// 获取url中指定参数的值
function getParam(paramName) {
    var query = window.location.search.substring(1);
    var vars = query.split("&");
    for (var i = 0; i < vars.length; i++) {
        var pair = vars[i].split("=");
        if (pair[0] === paramName) {
            return pair[1];
        }
    }
    return (false);
}
复制代码
  • 获取openid: 发送code到后端,拿到openid
// 像后端发起请求,获取openid
function getOpenId() {
    const request = new XMLHttpRequest();
    request.open("POST", "https://host/pay/accessToken")
    // 可以通过参数携带,不一定要通过请求体
    request.setRequestHeader("code", getParam("code"))
    request.send()
    request.onreadystatechange = function () {
        // 得到响应之后调用后端接口生成预支付订单
        if (request.readyState === 4 && request.status === 200) {
            // 生成预支付订单
            wxPayOrder(request)
        }
    }
}
复制代码
  • 获取prepayId: 调用后端接口生成预支付订单,得到prepayId

(具体参数参考链接:pay.weixin.qq.com/wiki/doc/ap… )

// 生成预支付订单
function wxPayOrder(request) {
    const openId = request.responseText
    request.open("POST", "https://host/wechatPay/wxPayOrder")
    request.send(openId)
    request.onreadystatechange = function () {
        if (request.readyState === 4 && request.status === 200) {
            // 唤醒微信支付
            cellBack(JSON.parse(request.responseText))
        }
    }
}
复制代码
  • 唤醒微信支付: 生成预支付订单之后,通过JSAPI唤起微信支付,将响应结果发送到微信服务端
// 微信官方提供案例,自己加参数进行赋值
function onBridgeReady(data){
    WeixinJSBridge.invoke(
        'getBrandWCPayRequest', {
            "appId": data.appId,               //公众号ID,由商户传入
            "timeStamp": data.timeStamp,       //时间戳,自1970年以来的秒数
            "nonceStr": data.nonceStr,         //随机串
            "package": data.prepayId,          //prepayId
            "signType": data.signType,         //微信签名方式:
            "paySign": data.paySign            //微信签名
        },
        function (data) {
            if (data.err_msg === 'get_brand_wcpay_request:ok') {
                console.log("支付成功")
            } else if (data.err_msg === 'get_brand_wcpay_request:cancel') {
                console.log("用户取消支付")
            } else if (data.err_msg === 'get_brand_wcpay_request:fail') {
                console.log("支付失败")
            }
        });
}

// 微信官方提供案例,自己加的 function 来做入口触发
function cellBack(data) {
    if (typeof WeixinJSBridge == "undefined"){
        if( document.addEventListener ){
            document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
        }else if (document.attachEvent){
            document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
            document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
        }
    }else{
        onBridgeReady(data);
    }
}
复制代码

后端

授权

  • 首页
// 定义一个入口,打开前端页面
@GetMapping("/payPage")
public String payPage(){
    return "index";
}
复制代码
  • 获取用户信息
@PostMapping("/accessToken")
@ResponseBody
public String accessToken(HttpServletRequest request){
    String code = request.getHeader("code");
    String uri = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + APPID + "&secret=" + SECRET + "&code=" + code + "&grant_type=authorization_code";
    String body = "";
    try{
        HttpRequest get = HttpUtil.createGet(uri);
        HttpResponse response = get.execute();
        body = response.body();
    }catch (Exception e){
        e.printStackTrace();
    }
    // 具体属性参考官网: https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html
    AccessTokenData accessTokenData = JSONUtil.toBean(body, AccessTokenData.class);
    
    // 将 accessToken 保存在服务端,不要发送到前端
    String openid = accessTokenData.getOpenid();
    accessTokens.put(openid, body);

    return openid;
}
复制代码

支付

  • 生成预付单
@PostMapping("/wxPayOrder")
public Object wxPayOrder(@RequestBody PayOrder payOrder) {
    // 这里我是根据自己的业务需求做了封装。实际JSAPI下单参考: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml
    String call = InterfaceCallUtil.call(PAYMENT_URL + DEV_PAY, payOrder, PRI_KEY, PUB_KEY);
    
    // 得将响应反序列化成对象,返回给前端(prepayId就在这里面)
    WechatPaymentResParam paymentResParam = JSON.parseObject(call, WechatPaymentResParam.class);
    WechatPayData payData = paymentResParam.getPayData();
    payData.setAppid(APP_ID);
    return payData;
}
复制代码

最后

整体的步骤还是比较简单的,先在前端这边得到用户授权之后,拿到专属的openid,再通过openid生成一个预支付订单,然后将结果返回给前端这边,前端通过调用微信的JSAPI来唤醒支付功能实现扣款操作。

这里有需要注意的就是支付页面需要在商户平台先设置好权限,才能正常唤醒,否则会提示没有设置权限等错误。

如果觉得文章对你有帮助,请 点赞、收藏、关注、评论 一键四连支持,你的支持就是我创作最大的动力!!!

文章分类
后端
文章标签