微信小程序登录、支付(小程序、微信H5、非微信H5)即请求加密方法

6,597 阅读4分钟

本文以uni-app为例,介绍下: 微信小程序登录、支付(小程序、微信H5、非微信H5)即请求加密方法

微信登录

微信调整了获取用户信息的 API ,故不推荐使用button的 @getuserinfo 方法,使用DOM的点击事调用 getUserProfile API 获取用户信息。

tips: 解密userInfo的信息 调用getUserProfile可能会刷新session_key,故新的 code会解密失败,需要使用之前的code,这里使用了简单粗暴的方式,在页面show的钩子中调用wx.login,每次授权是再调用一次,确保解密信息的解密code和加密时的一致。

<template>
    <button @tap="getWxUserinfo">微 信 登 录</button>
</template>
export default {
    data() {
        return {
            wxCode: null
        }
    },
    onShow() {
        this.getCode()
    },
    methods: {
        // 获取 wxCode
        getCode(callback) {
            wx.login({
                success: res => {
                    this.wxCode = res.code
                    callback && callback(res.code)
                }
            });
        },
        // 获取用户信息
        getWxUserinfo() {
            wx.getUserProfile({
                desc: '用于完善用户资料',
                success: res => {
                    console.log(res)
                    wx.showLoading({
                            title: '正在登录',
                            mask: true
                    })
                    this.getCode(code => {
                        const data = {
                            code: this.wxCode,
                            iv: res.iv,
                            encryptedData: res.encryptedData
                        }
                        //发起请求登录
                        wx.request({
                            url: '/api/login',
                            method: 'POST',
                            success: res => {
                                console.log(res);
                                // 登录成功

                                wx.hideLoading()
                            }
                        })
                    })
                },
                fail: err => {
                    // 获取信息失败
                    console.log(err)
                }
            })
        }
    }
}

微信小程序支付

首先获取code即使用uni.login() 然后调后台接口 把code和订单号传给后台,后台返回的数据就是小程序调支付的参数,然后调用uni.requestPayment() 就可以了

wx.login({
    success: res => {
        wx.request({
            url: '/api/wxpay',
            method: 'POST',
            data: {
                code: res.code,
                order_id: '订单id', // 后台接口参数 示例
                pay_type: 'wxpay',  // 后台接口参数 示例
            },
            success: res => {
                // 返回数据是json字符串,转为对象 - 具体根据后台返回数据操作
                const result = JSON.parse(res.data.pay_data)
                wx.requestPayment({
                    provider: 'wxpay',
                    timeStamp: result.timeStamp,
                    nonceStr: result.nonceStr,
                    package: result.package,
                    signType: result.signType,
                    paySign: result.paySign,
                    success: res => {
                        // 支付成功
                        console.log('success:' + JSON.stringify(res))
                    },
                    fail: err => {
                        // 支付失败
                        console.log('fail:' + JSON.stringify(err))
                    }
                })
            }
        })
    }
})

H5支付(微信内浏览器/JSPAI)

获取code

在登录时就获取code,后台得到openId将其存起来,之后后台在用到openId的地方就不用再获取了(不会过期)

getCode(){
    let appid = 'xxxxxxxxxxxx'
    let url = 'https://xxx.xxxx.xxx'
    let redirect_uri = encodeURIComponent(url)//回调页面地址
    window.location.href = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + appid + "&redirect_uri=" + redirect_uri + "&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect"
}

封装H5支付代码

**
 * 支付
 * url 后台下单接口地址
 * data 下单参数
 * callback 回调函数
 */
const payH5 = {
    onBridgeReady: function(url, data, callback) {
        wx.request({
            url,
            method: 'POST',
            data,
            success(res) {
                // 后台返回的数据因为是字符串,转换对象
                const result_H5 = JSON.parse(res.data.pay_data)
                WeixinJSBridge.invoke(
                    'getBrandWCPayRequest', {
                        "appId": result_H5.appId, //公众号名称,由商户传入
                        "timeStamp": result_H5.timeStamp, //时间戳,自1970年以来的秒数
                        "nonceStr": result_H5.nonceStr, //随机串     
                        "package": result_H5.package,
                        "signType": result_H5.signType, //微信签名方式:     
                        "paySign": result_H5.paySign //微信签名 
                    },
                    function(res) {
                        if (res.err_msg == "get_brand_wcpay_request:ok") {
                            callback && callback('支付成功')
                        } else {
                            callback && callback('支付失败')
                        }
                    }
                );
            },
            fail(err) {
                console.log('网络错误:', err)
            }
        })
    },
    doPay(url, $params, callback) {
        if (typeof WeixinJSBridge == "undefined") {
            if (document.addEventListener) {
                document.addEventListener('WeixinJSBridgeReady', this.onBridgeReady, false);
            } else if (document.attachEvent) {
                document.attachEvent('WeixinJSBridgeReady', this.onBridgeReady);
                document.attachEvent('onWeixinJSBridgeReady', this.onBridgeReady);
            }
        } else {
            this.onBridgeReady(url, $params, callback)
        }
    }
}

export default payH5

支付调用

将这个文件引入需要的页面或者挂载到vue原型上(这里以uni-app中介绍)

// 引入: 
import H5Pay from './api/h5Pay.js' // H5Pay.doPay(url,params,callback)

export default {
    methods: {
        H5pay(){
            H5Pay.doPay(
            'url',
            {
                order_id: order_id,//订单号
                pay_type: '',//支付方式
            }, 
            function(msg) {
                // 支付结果
                console.log(msg)
            }
        )
    }
}

H5支付(非微信内置浏览器)

相对来说前端简单很多,没什么麻烦要求 后台会返回一个urlwindow.location.href = mwev_url + redirect_uri (这里的redirect_uri 是重定向的页面地址,域名要和支付域名一致 !!! url 需要encodeURIComponent编码

dopayWeb() { //非微信浏览器支付
    wx.request({
        url: '/api/h5pay',
        method: 'POST',
        data: {
            order_id: '订单id', // 后台接口参数 示例
            pay_type: 'wxpay',  // 后台接口参数 示例
        },
        success(res) {
            console.log(res);
            const url = res.pay_url
            const redirect_uri = encodeURIComponent(url)
            window.location.href = url + "&redirect_uri=" + redirect_uri 
        },
        fail(err) {
            console.log('网络错误')
        }
    })
},

区分是否是微信内置浏览器

function isMicroMessenger() {
    let userAgent = window.navigator.userAgent;    
    if(userAgent.indexOf('MicroMessenger') > -1) result = true;
    return false;
}

js生成接口请求参数签名加密

定义规则:

对所有发起请求的参数(包括公共参数),按key进行升序排序 ,然后组合成key1=value1&key2=value2的形式

如:parames = {b:value-b, c:value-c,a:value-a} 排序后应为:string = "a=value-a&b=value-b&c=value-c" 排序后对其进行md5: string = md5(string)注意:这里要过滤掉所有为空的参数,空参数,不传 将md5的串与密钥再做一次md5: sign = md5( string + secret ) ,sceret 为密钥,后端分配

/** 
 * 定义对象排序方法
 */
objectSort(parames) 
{ 
    var newkeys = Object.keys(parames).sort();
    var newObj = {};
    for(var i = 0; i < newkeys.length; i++) 
    {
        newObj[newkeys[i]] = parames[newkeys[i]]; 
    }
    return newObj;
}
/** 
 * 计算签名方法
 */
makeSign(params)
{
    var sign = '';
    var parm = '';

    params = objectSort(params);
    for (let i in params)
    {
        var j = params[i];
        parm += i + '=' + j + '&';
    }
    parm = parm.slice(0,-1);
    parm.trim();

    sign = Md5.md5(Md5.md5(parm)+$secret);//$secret为密钥,后端分配 
    return sign;
},
/** 
 * 过滤data参数中的空字符
 */	
filterData(data){
    var result = {};
    for (let i in data) 
    {
        var value = data[i];
        if (typeof (value) == 'undefined') {
                value = '';
        }
        if(typeof(value) == 'string'){
                value = value.trim();
        }
        if(value)
        {
                result[i] = value;
        }
    }
    return result; 
},
/** 
 * 构建data参数
 */
bulidData(data) {
    if(typeof(data) != 'object'){
            data = {};
    }
    //构建参数
    data.timestamp = (new Date()).valueOf();
    data.app_id = Config.app_info.app_id;
    //过滤
    data = this.filterData(data);
    //检测用户是否登录
    var token = Helper.user.getToken();
    if(token)
    {
            data.token = token;
    }
    data.sign = this.makeSign(data);
    return data;
},