一.介绍
允许通过特定方式在小程序内通过 webview 内嵌 h5 页面,跳转以及支付等流程。
大致流程如下:
小程序创建 webview 加载 h5 url(https://3400.retail.../shop/index),后续跳转通过 url 后拼接的参数判断是否要跳转新的 webview 页面(参数符合规则并且此跳转点击使用了 CommonRouter(见在H5页面中集成的小程序跳转路由),否则只是普通的页面跳转),支付环节需要小程序创建一个原生页面用于支付中转;
二.集成
1.创建 webview 容器用于承载 h5 页面(webview 组件用法参考官方组件)
创建一个 webview page,用于加载 h5 url,路径没有要求,只需在 步骤2>参数拼接 时带上路径即可。
2.参数拼接
在 webview 加载 url 之前,需要在 url 后拼接上必要的参数,否则无法在新的 webview 打开下一次页面和在H5生成支付参数,在H5页面生成支付参数,请求后端生成支付参数接口要用小程序的appid,openid。webview 完整示例如下:
Page({
data: {
weburl:'' //加载的 url
},
onLoad: function (options) {
let url = decodeURIComponent(options.url)
// url 拼接参数
this.dealUrl(url)
},
dealUrl(url) {
/**
* miniProgramWebviewPath webview 的路径, 必传 解决采用小程序路由跳转H5嵌在多个小程序webview,跳回小程序webview
* miniProgramPayPath 支付中转页路径,必传
* pageStackLength 页面路由栈个数,必传 解决小程序跳转10级问题,改用redirectTo跳转,
* ec_appid、ec_openid 两个参数为支付所需参数,必传
*/
let miniProgramWebviewPath = encodeURIComponent(`${this.route}?weburl=`)
let miniProgramPayPath = "pages/h5payment/payMiddle/index"
let pageStackLength = getCurrentPages().length
let ec_appid = "wxb956c83845b9c0"
let ec_openid = "oOVgY4xSj-LyuFifcaF_YDcRLU"
const urlParams = {}
urlParams.ec_openid = ec_openid
urlParams.ec_appid = ec_appid
urlParams.miniProgramWebviewPath = miniProgramWebviewPath
urlParams.pageStackLength = pageStackLength
urlParams.miniProgramPayPath = miniProgramPayPath
let weburl = this.onInsterParamsInUrl(url, urlParams)
this.setData({
weburl
})
},
/**
* 解析 url param
*/
onInsterParamsInUrl(url, params) {
let tempUrl = ''
let hasParams = url.indexOf('?') !== -1;
tempUrl = Object.keys(params).reduce(function (before, after) {
return before + after + '=' + params[after] + '&'
}, tempUrl)
tempUrl = tempUrl.substring(0, tempUrl.length - 1)
return hasParams ? (url + '&' + tempUrl) : (url + '?' + tempUrl)
}
})
3.支付中转页
小程序内嵌 h5 的支付无法直接使用微信环境 sdk 支付,需要借助小程序的支付能力,所以小程序需要提供一个支付页面,接收在h5 生成的小程序支付参数,支付成功失败的回调url,发起小程序支付。小程序支付完成,需要跳转到结果回调页面,此页面需要由 webview 打开,所以在下面代码 PayDoneCallback 方法里的跳转路径对应 webview 的路径。
Page({
onLoad: function(options) {
/**
* 解析 options, options为H5传过来的参数(支付参数,回跳H5的URL)
*/
this.successUrl = options.successUrl
this.failUrl = options.failUrl
options.package = decodeURIComponent(options.package);
options.paySign = decodeURIComponent(options.paySign);
delete options.successUrl
delete options.failUrl
const { success, fail, cancel } = this.PayDoneCallback(this.successUrl, this.failUrl)
this.payment({options, success, fail, cancel})
},
payment(opts) {
const data = opts.options
let hasComplete = false;
wx.requestPayment({
...data,
success: function(res){
typeof opts.success =='function'&&opts.success()
},
fail: function(res) {
if (res.errMsg == 'requestPayment:fail cancel') {
typeof opts.cancel =='function'&&opts.cancel();
hasComplete = true;
return;
}
typeof opts.fail =='function'&&opts.fail()
},
complete:function(res){
console.log(res)
if (hasComplete) {
hasComplete = false;
}else if(res.errMsg =='requestPayment:fail cancel'||res.errMsg=="requestPayment:cancel") {
typeof opts.cancel =='function'&&opts.cancel()
}
typeof opts.complete =='function' && opts.complete(res)
}
})
},
PayDoneCallback(successUrl, failUrl) {
return {
success(){
wx.redirectTo({
url: `/pages/h5payment/webview/webview?weburl=${successUrl}`
});
},
fail(){
wx.redirectTo({
url: `/pages/h5payment/webview/webview?weburl=${failUrl}`
});
},
cancel(){
wx.redirectTo({
url: `/pages/h5payment/webview/webview?weburl=${failUrl}`
});
}
}
}
})