微信小程序web-view公众号与小程序支付间的切换使用
最近小程序开放了新功能,支持内嵌网页。html写的网页,官网,网站,运行在浏览器上的,有域名的那种,可以内嵌到小程序里了!
- 1
- 2
那么这意味着什么呢?你还需要开发独立开发官网小程序吗?之前的微信公众号功能大部分也可以直接通过小程序webview实现了。
这几天刚好公司也有这方面的需求,怀着激动心情的我开始了踩坑之旅。
开发之前我们需要把需要的环境配置好,这里就引用官方的介绍了————
- 1
- 2
1. 开发者登录小程序后台,选择设置-开发设置-业务域名,新增配置域名模块。目前小程序内嵌网页能力暂不开放给个人类型帐号和海外类型帐号。
2.配置的业务域名必须时https
那么配置好后,马上可以进入开发啦!
页面中引入公众号的首页,小程序端的代码不是很多,主要的是后台进行结合。
1.在后端添加一个小程序的入口方法,
- 1
- 2
- 3
- 4
/**
* 小程序第一次入口
* @return
*/
public String wxIndex(){//返回首页
sessions=request.getSession().getId();
return "home";
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
这里我没有做任何的处理,这个项目首页是不用登陆的,直接返回首页;来到这一步时我们发现之前公众号后台保存在session
中的信息获取不到,使用后发现小程序每次请求sessionId
都会发生改变,这样后台无法使用session
储存数据。
解决方案:
- 1
- 2
小程序第一次请求后台返回一个sessionId
,之后小程序在参数或header
中带入这个sessionId
,后台使用这个session
来处理。注意session
销毁以及过期设置。
wx.login({
success: function (res) { wx.getSetting({
success(setRes) {
// 判断是否已授权
if (!setRes.authSetting['scope.userInfo']) {
// 授权访问
wx.authorize({
scope: 'scope.userInfo',
success() {
//获取用户信息
wx.getUserInfo({
lang: "zh_CN",
success: function (userRes) {
// var session_id= wx.getStorageSync('JSESSIONID');
//发起网络请求
wx.request({
url: 'https://www.ulin5.com/vip/buser-bind-mobile_wxXiaoChengXuLogin.html',
data: {
code: res.code,
encryptedData: userRes.encryptedData,
iv: userRes.iv
},
header: {
"Content-Type": "application/x-www-form-urlencoded"
},
method: 'POST',
//服务端的回掉
success: function (result) {
console.log(result)
// var session_id = wx.getStorageSync('JSESSIONID');//本地取存储的sessionID
// if (session_id == null || session_id == "") {
wx.setStorageSync('JSESSIONID', result.data.sessionId) //如果本地没有就说明第一次请求 把返回的session id 存入本地
// }
console.log(result.data.openId)
getApp().openid = result.data.openId
// var data = result.data.result;
// data.expireTime = nowDate + EXPIRETIME;
// wx.setStorageSync("userInfo", data);
// userInfo = data;
}
})
}
})
}
})
})
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
把小程序所需要的openid和unionid拿到,由于公众号的openid 和小程序的openid不同,所以这需要用到unionid。
换句话说,同一用户,对同一个微信开放平台帐号下的不同应用,UnionID是相同的。
此前的OpenID机制,每个微信号对应每个公众号只有唯一的OpenID,所以不同微信公众号之间是不能共享用户的,现在有了UnionID就可以了。
前面说到小程序每次请求的sessionId不同,所以通过sessionId可以获取到我们存储的数据信息。
注意Servlet2.1之后不支持SessionContext里面getSession(String id)方法。
我这里通过HttpSessionListener监听器和全局静态map自己实现一个SessionContext。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
public class MySessionContext {
private static HashMap mymap = new HashMap();
public static synchronized void AddSession(HttpSession session) {
if (session != null) {
mymap.put(session.getId(), session);
}
}
public static synchronized void DelSession(HttpSession session) {
if (session != null) {
mymap.remove(session.getId());
}
}
public static synchronized HttpSession getSession(String session_id) {
if (session_id == null)
return null;
return (HttpSession) mymap.get(session_id);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
public class SessionListener implements HttpSessionListener {
/*public static Map userMap = new HashMap(); */
/*private MySessionContext myc=MySessionContext.getInstance(); */
@Override
public void sessionCreated(HttpSessionEvent arg0) {
/*myc.AddSession(arg0.getSession()); */
MySessionContext.AddSession(arg0.getSession());
}
@Override
public void sessionDestroyed(HttpSessionEvent arg0) {
HttpSession session = arg0.getSession();
/*myc.DelSession(session);*/
MySessionContext.DelSession(session);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
web.xml添加一个监听器:
<listener>
<listenerclass>com.common.util.SessionListener</istener-class>
</listener>
- 1
- 2
- 3
- 4
根据sessionId
获取Session
对象:
String sessionId = request.getParameter("sessionId");
HttpSession session = MySessionContext.getSession(sessionId);
写到这里已经算是完成一半了,只要在后台加一个处理小程序进来的方法,把所需要的用户信息保存到数据库中。
我们不需要改变以前写好处理公众号的任何代码(根据项目进行整合),
- 1
- 2
- 3
public String wxLogin(){//java后端
String Code = request.getParameter("code");// 登陆状态码
String typeDate = request.getParameter("encryptedData");// 用户加密信息,用于解密获取unionid
String iv = request.getParameter("iv");// 解密密钥
String OPENID_URL = "https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret"
+ "=SECRET&js_code=JSCODE&grant_type=authorization_code";// 获取openid的地址
UserInfo userInfo = null;
String url = OPENID_URL.replace("APPID", Pkcs7Encoder.APPID)
.replace("SECRET", Pkcs7Encoder.APPSECRET)
.replace("JSCODE", Code);
JSONObject jsonObject = null;
jsonObject = Pkcs7Encoder.doGetStr(url);// 调取Get提交方法
String OpenId = jsonObject.getString("openid"); // 获取openid
/*
* ResourceBundle resource = ResourceBundle.getBundle("weixin");
* //读取属性文件
*/String requestUrl = "https://api.weixin.qq.com/sns/jscode2session"; // 请求地址
/* String wxUserInfo=Pkcs7Encoder.sendPost(requestUrl, requestUrlParam); */
// 获取会话密钥(session_key)
String session_key = jsonObject.getString("session_key");
.... //通过逻辑代码保存我们需要的信息
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
接下来到最重要的环节了,到了我们最期待的支付环节了。由于web-view目前还不支持支付接口。
我们需要判断是否运行在小程序环境中进行支付的互换,从而实现支付功能。
- 1
- 2
- 3
<script type="text/javascript"src="https://res.wx.qq.com/open/js/jweixin-1.3.0.js"></script>
在网页内可通过window.__wxjs_environment变量判断是否在小程序环境。
- 1
- 2
// web-view下的页面内
wx.ready(function() {
console.log(window.__wxjs_environment === 'miniprogram') // true
})
例如:
var isWxMini = window.__wxjs_environment === 'miniprogram';
if (isWxMini) {//判断是否在微信小程序环境中调用支付接口
$("#wxloading").hide();
var jumpUrl = encodeURIComponent(window.location)
//把要用到的参数传到小程序中进行支付
var path = '/pages/pay/pay?jhNum='+jhNum+'&cproType='+cproType+'&zcProId='+zcProId+'&url='+url+'&cproId='+cproId+'&esOrderc='+esOrderc;
wx.miniProgram.navigateTo({
url: path
})
}else{//公众号支付
window.location.href="/vip/buy-group_saveJoinPt.html?cproId="+cproId+"&jhNum="+jhNum+"&cproType="+cproType+"&zcProId="+zcProId+"&esOrderc="+esOrderc;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
小程序pay文件夹下新建pay.js和pay.wxml
pay.js
Page({
onLoad: function (options) {
console.log(options)
// 获取网页传过来的值
// TODO 用es6解构来获取值TODO
var jhNum = options.jhNum
var cproType = options.cproType
var zcProId = options.zcProId
var cproId = options.cproId
var esOrderc = options.esOrderc
// var jumpUrl = decodeURIComponent(options.jumpUrl)
var tourl=options.url
// console.log(tourl)
var openid = getApp().openid
console.log(openid)
var session_id = wx.getStorageSync('JSESSIONID')//本地取存储的sessionID
// console.log('----------sessionid' + session_id+'-----------------')
wx.request({
url: tourl,
data: {
openid: openid,
jhNum: jhNum,
cproType: cproType,
zcProId: zcProId,
cproId: cproId,
esOrderc: esOrderc
},
header: {
"Content-Type": "application/x-www-form-urlencoded",
'Cookie': 'JSESSIONID=' + session_id
},
method: 'POST',
success: function (res){
console.log(res)
wx.requestPayment({
'timeStamp': res.data.timeStamp,
'nonceStr': res.data.nonceStr,
'package': res.data.package,
'signType': 'MD5',
'paySign': res.data.sign,
'success': function (res) {
console.log('支付成功')
wx.navigateBack() //返回会上个页面
},
'fail': function (res) {
console.log('支付失败')
wx.navigateBack() //返回会上个页面
}
})
}
})
}
})
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
到这基本上已经完成,个人感觉这种实现方式在用户体验度上并不是很友好,但是基本的功能都可以实现。
相信官方也会对webview更进一步的优化。能在webview中直接调用支付的接口。
如果大家有更好的实现思路,千万别忘了告诉我哦。
由于作者还是个工作不到一年的小菜。在代码编写上还有很多的不足,欢迎大家指正。
这也是我的第一篇博客,嗯嗯嗯还是有点小激动。^_^