用于记录 uniapp 编译成H5后,完成微信授权和微信支付,微信浏览器中主要看场景二
业务需要在H5中完成支付功能(微信支付)
1、H5运行在普通浏览器中;
2、H5运行在微信浏览器中。
H5普通浏览器支付过程并不复杂,大致流程如下:
a、前端发起支付请求给后端,后端收到后,调用微信支付接口下单;
b、微信支付接口会返回一个支付链接给到后端;
c、后端把微信返回的支付链接通过支付请求(a)接口返回给前端;
d、前端拿到链接之后,跳转到此链接,开启轮询调用后端接口查询是否支付成功;
e、在跳转之后的链接完成支付动作的操作,用户自己点击返回到发起支付的页面。
微信H5支付开发文档
// 页面卸载时清除计时器,停止轮询,用于页面计时器重复启用
onUnload() {
clearTimeout(this.payTime);
}
// 如下为methods:{}中代码的一部分
// 同意支付方法
agreePayFn(price) {
let params = {};
params.price = price; //价格
params.id = this.estimateSchemeId; //支付的产品ID
this.getWxPayCb(); //获取支付是否成功的回调方法 ,用于轮询后端接口
h5WxPay(params).then(res => { // 发起微信支付请求给后端,res是后端返回的支付链接
params = null; //释放内存
location.href = res; //跳转到支付链接
});
},
// 获取微信支付结果
getWxPayCb() {
const self = this;
let params = {};
params.id = this.estimateSchemeId; //产品ID
clearTimeout(this.payTime); //清除计时器
function next() { //用于轮询的函数
getWxPayReuslt(params) //获取微信支付结果接口
.then(res => {
if (res == true) { //后台返回 true 成功
// doSomething
return;
}
if (res == false) { //后台返回 false 失败
// doSomething
showToast('支付失败,请重新支付', 'none', 2000); //此方法是自己封装的uni.showToast方法
return;
}
// 无结果时每隔一秒轮询接口
self.payTime = setTimeout(() => {
clearTimeout(self.payTime);
next();
}, 1000);
})
.catch(e => { //接口出现异常时处理
self.payTime = setTimeout(() => {
clearTimeout(self.payTime);
next();
}, 1000);
});
}
next(); //进入就进行调用
}
H5支付是指商户在微信客户端外的移动端网页展示商品或服务
因此在微信浏览器支付时需要,使用另外一套支付过程----微信jsapi传送门
根据微信【JSAPI】支付开发的要求,则需要完成2步操作
1、有支付功能的页面,需要通过微信网页授权获取code,并把code传给后台获得当前用户的openid,openid将用于后台统一下单时使用;
2、发起支付(有两种方式,a、直接调用;b、通过jssdkapi调用)
由于uniapp开发的H5采用的hash路由模式,在网页授权时,微信会扰乱带#号的参数,以至于回调时不能回到发起页面。
1、需要授权的页面,在授权之前,把带#号的url转换成不带#号的url;
2、微信授权回来之后,在项目入口,把不带#号的url转换成带#号的url。
封装WX_JS_SDK 文件
// 引入qs解析包,用于将URL解析成对象
import qs from 'qs'
// 引入微信jsapi包
import wx from 'weixin-jsapi';
// 引入自己定义的微信配置和请求
import { weChatConfig, callSever } from '../api/requestConfig.js';
// 需要用到的微信api接口
const jsApiList = ['updateAppMessageShareData', 'updateTimelineShareData', 'onMenuShareTimeline',
'onMenuShareAppMessage', 'onMenuShareQQ', 'onMenuShareQZone', 'onMenuShareWeibo', 'chooseWXPay'
];
// 注入微信权限验证配置
let injectionWxConfig = function(params) {
const { timestamp, nonceStr, signature } = params
wx.config({
debug: false,
appId: weChatConfig.appId, // 必填,公众号的唯一标识
timestamp: parseInt(timestamp), // 必填,生成签名的时间戳
nonceStr: nonceStr, // 必填,生成签名的随机串
signature: signature, // 必填,签名
jsApiList: jsApiList // 必填,需要使用的JS接口列表
})
}
/**
* 调起微信支付
* params 支付参数
timestamp: '', 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
nonceStr: '', 支付签名随机串,不长于 32 位
package: '', 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=\*\*\*)
signType: '', 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
paySign: '', 支付签名
signature: '' //签名 用于权限注入使用
* successCallBack 成功回调
* failCallBack 失败回调
* cancelBack 取消回调
*/
const wxPay = (params, successCallBack, failCallBack, cancelBack) => {
injectionWxConfig(params)
wx.ready(() => {
wx.chooseWXPay({
timestamp: params.timestamp,
nonceStr: params.nonceStr,
package: params.package,
signType: params.signType,
paySign: params.paySign,
success: successCallBack,
fail: failCallBack,
cancel: cancelBack
})
})
}
//-------------------------------------------------------------------------------------//
/**
* 如下配置是为了在微信浏览器环境中,网页拿去授权
* 由于采用的hash模式路由,会导致授权后的链接被扰乱(微信会处理掉#号) redirect_uri
* 解决方案:
* 1、授权前重置 redirect_uri 改为没有#号的url,把当前页面的原有参数信息,额外信息,codePath参数(用来指定跳转页面的,此处为授权发起页)
* 2、项目入口处,将没有#号的url改回到 带#号的url,跳转到指定页面
*/
/**
* 微信授权-需要授权的页面: [页面用到的参数key]
* 参数key适用于获取微信回调跳传回来时的参数(授权时传了此参数,如未传则不填,也不用写页面)
*/
const plagForm = {
'/pages/interestModule/interestLoan/interestLoan': ['']
}
/**
* 重置redirect_uri 需要用到的页面使用
* 把有#号的url改为没有#号的url,
* scope 授权作用域 snsapi_base(静默授权) snsapi_userinfo(弹框授权)
* path 授权所在的页面路径
*/
const getWxCode = function(scope = 'snsapi_base', path) {
let homeUrl = '',
searchObj = {},
searchStr = '';
let oriUrls = location.href.split('?');
let baseShareUrl = location.origin + location.pathname
homeUrl = baseShareUrl
// 如果有携带参数,把参数进行拼接,参数不能带code
if (oriUrls.length > 1) {
let searchUrls = oriUrls[1].split('#')
let searchUrlReal = searchUrls[0]
searchObj = qs.parse(searchUrlReal)
let { code } = searchObj
if (code) {
delete searchObj.code
}
}
// 路径存入到codePath中,用于微信回调时入口处处理
searchObj.codePath = path
searchStr = `?${qs.stringify(searchObj)}`
homeUrl += searchStr
let redirect_uri = encodeURIComponent(homeUrl)
let response_type = 'code'
let state = ''
let url =
`https://open.weixin.qq.com/connect/oauth2/authorize?appid=${weChatConfig.appId}&redirect_uri=${redirect_uri}&response_type=${response_type}&scope=${scope}&state=${state}#wechat_redirect`
location.href = url
}
/**
* 重置redirect_uri 项目入口处使用
* 将没有#号的url改回到 带#号的url
* 如果地址有页面需要传递的参数,需要在 plagForm 进行设置
* 返回之后的例子:
* https://xxxx.com/?codePath=%2Fpages%2FinterestModule%2FinterestLoan%2FinterestLoan&code=021cZo0002D87L1BSH200bPoNK3cZo0Z&state=#/pages/indexTabBar/index
*/
const urlResetForWxCode = function() {
let needRedirect = false,
homeUrl = '',
searchObj = {},
searchStr = '';
let oriUrls = location.href.split('?');
let baseShareUrl = location.origin + location.pathname
homeUrl = baseShareUrl
if (oriUrls.length > 1) {
let searchUrls = oriUrls[1].split("#")
let searchUrlReal = searchUrls[0]
let searchObjReal = {}
searchObj = qs.parse(searchUrlReal)
let { codePath } = searchObj
needRedirect = codePath
if (codePath) {
plagForm[codePath].forEach(item => searchObjReal[item] = searchObj[item])
if (plagForm[codePath].length) {
searchStr = `?${qs.stringify(searchObjReal)}`
}
homeUrl += `#${codePath}` + (searchstr.length > 1 ? searchstr : '')
}
}
return {
needRedirect, //需要重定向
homeUrl, //跳转链接
search: searchObj //存着code信息和codePath信息等
}
}
const WX_JS_SDK = {
urlResetForWxCode,
getWxCode,
wxPay
}
export default WX_JS_SDK
在main.js中引用刚刚封装好的WX_JS_SDK文件
import WX_JS_SDK from './utils/WX_JS_SDK.js';
//添加到原型中
Vue.prototype.$wxSdk = WX_JS_SDK
第一步:需要授权的页面,把带#号的地址更换成不带#号的地址,用于获取微信code
onLoad(options) {
// 如果是微信环境,且没有wxCode,则需要进行网页授权
const wxCode = uni.getStorageSync('wxCode');
console.log('是否浏览器环境','isWeChatH5--自定义的判断是否是浏览器环境')
if (isWeChatH5) {
if (isEmpty(wxCode)) {
this.$wxSdk.getWxCode('snsapi_base', '/pages/interestModule/interestLoan/interestLoan');
return;
} else {
// 通过code调取接口获取openID,用于 统一下单接口 预支付
}
}
},
第二步:项目入口处APP.VUE,处理微信返回的地址,把不带#号的地址更换成带#号的地址,跳转到指定页面
import { getSystemInfo, isWeChatH5 } from '@/utils/index.js';
export default {
onLaunch: function(options) {
getSystemInfo();
// 如果是在微信环境,有通过授权页面回来时
if (isWeChatH5) {
let { needRedirect, homeUrl, search } = this.$wxSdk.urlResetForWxCode();
if (needRedirect) {
uni.setStorageSync('wxCode', search.code); //本地存入code
location.href = homeUrl; //跳转到发起授权的页面
return
}
}
//其它业务逻辑 。。。。。
}
};
第三步,需要支付的页面调用 WX_JS_SDK 封装的方法
// 同意支付
agreePayFn(price) {
let params = {};
params.price = price;
params.id = this.estimateSchemeId;
if (isWeChatH5) {
//微信浏览器环境,调用微信api支付方法,如需直接支付可以看场景二的直接调用支付
this.$wxSdk.wxPay(
params,
sucess => {
// 成功回调
console.log('成功回调了')
},
fail => {
// 失败回调
console.log('失败回调了')
},
cancel => {
// 取消回调
console.log('取消回调了')
}
);
} else {
// 非微信浏览器环境 支付,可看场景一
this.getWxPayCb();
h5WxPay(params).then(res => {
params = null;
uni.removeStorageSync('from');
location.href = res;
});
}
},