本文以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支付(非微信内置浏览器)
相对来说前端简单很多,没什么麻烦要求 后台会返回一个url ,window.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;
},