Webview支付问题的处理
微信小程序里面的webview可以做很多事情,但还是有很多限制,比如webview里面是无法吊起小程序常规的支付接口的。但是我们可以通过webview带参跳转到小程序内部页面,在小程序内部页面获取到这些参数,调起支付就可以了。
这里整合网上的资料说一说这个流程。
前端步骤:
①:点击支付按钮后请求java后台,获取到吊起小程序的必要参数'timeStamp','package','paySign','signType','nonceStr'
②:带参跳转到小程序中的某个页面
③:小程序页面load时直接解析参数,并吊起支付接口: wx.requestPayment
④:支付结果处理,跳转回webview或者其他页面
前端代码展示:
1. webview(html里面)
<button id="buttom8xx">购买一年会员</button>
<script type="text/javascript">
$("#buttom8xx").click(function() {
$.ajax({
type : 'Get',
url : "http://192.168.0.15:8080/carplatform/User/BecomeVip",
dataType : 'json',
contentType : "application/json;charset=UTF-8",
success : function(data) {
if(data.state)
{
alert(data.payinform.package);
var pid=(data.payinform.package).split('=')[1]; //跳转到小程序时这个参数有等号,小程序只能获取到‘=’前面的字符,这里我们发送=后面字符,小程序里面自行拼凑
//注意这个地方是有坑的,小程序在跳转传参时如果某一个参数key对应的内容形如“xxx=yyy”这中格式,
//我们在小程序页面解析参数时实际是只能获取到‘=’前面一部分,也就是‘xxx’
var params = '?timeStamp='+data.payinform.timeStamp
+'&package='+pid
+'&paySign='+data.payinform.paySign
+'&signType='+data.payinform.signType
+'&nonceStr='+data.payinform.nonceStr;
var payurl='/pages/wxpay/wxpay'+params;
//小程序跳转到指定的小程序页面.跳转不需要加载jssdk配置
alert(payurl);
wx.miniProgram.navigateTo({url: payurl}); //带参跳转到小程序内部页面
console.log(data);
}
else
{
alert(data.message);
}
}
});
});
</script>
小程序的支付页面中 wxpay.js:
/**
* 页面加载,获取支付参数直接吊起支付
*/
onLoad: function (options) {
var that = this;
//页面加载调取微信支付(原则上应该对options的携带的参数进行签名计算,校验)
var param = {
"timeStamp": options.timeStamp,
"package": 'prepay_id=' +options.package,//组装下参数
"paySign": options.paySign,
"signType": "MD5",
"nonceStr": options.nonceStr
}
console.log(param);
that.pay(param)//吊起支付
},
/**
* 调起支付函数
*/
pay: function (param) {
wx.requestPayment({
timeStamp: param.timeStamp,//时间搓
nonceStr: param.nonceStr,//随机码
package: param.package,//统一下单接口返回的 prepay_id
signType: param.signType,//签名算法MD5
paySign: param.paySign,//签名字符串
success: function (res) {
// success
console.log(res)
wx.navigateBack({
delta: 1, // 回退前 delta(默认为1) 页面
success: function (res) {
wx.showToast({
title: '支付成功',
icon: 'success',
duration: 2000
})
},
fail: function (res) {
// fail
console.log(res)
},
complete: function (res) {
// complete
console.log(res)
}
})
},
fail: function (res) {
// fail
console.log("支付失败");
console.log(res)
},
complete: function (res) {
// complete
console.log("pay complete");
console.log(res)
}
})
},
后台逻辑:
①:计算支付参数,后台调用微信小程序的统一下单接口,获得支付参数prepay_id=xxx,然后计算吊起小程序支付接口所需要的参数,这个没什么好说的,根据wx接口写就行了
②:回调处理。
我是这么理解的:涉及到金钱的东西我是比较敏感的,我们在收到微信回调后不应该是直接根据它的状态就进行数据库改变,或者账户上的改变。这里需要我们多次校验,多次比对方可认为对方真的已经付款。确保资金安全,不妨牺牲掉一点后台性能!!!所以在收到回调我进行以下步骤
1°:防止回调太集中,导致数据混乱,我用了一个锁来判断一次回调是否处理完,没处理完的话就不要其它的回调进来(当然这个一看就有缺陷,具体的话应该是需要建立队列进行处理的)。
2°:回掉参数签名验证
3°:回调参数中判断各种是否成功。
4°:参数中所有状态、支付状态都成功后,查询我们数据库中这个订单状态是否已经支付过了(主要是防止我们返回给微信‘SUCCESS’他没有收到或者什么的,导致微信继续重复通知)。
5°:我们在参数中拿到微信的订单号,调用微信订单查询接口。看看这个订单返回是不是真的已经支付。
6°:修改数据库,修改session储存。(这里的session修改是通过用户下单时,将用户的sessionid传递的商家数据包‘attach’字段,回调时获取到sessionid,在我们的监听类里面找到对应的用户session修改内容。)
java后台支付回调代码:
// 成为会员,用户支付回调!
@ResponseBody
@RequestMapping(value = "User/wx/bvipback")
public String Userwxbvipback(@RequestBody String request, HttpSession session) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, Exception {
Map<String,String> result = new HashMap<>();
if(!vip_paybackend)//一次还未处理完,收到其它回调,直接返回
{
result.put("return_code", "FAIL");
return user_wxAPI.GetInstance().mapToXml(result);
}
else
{
vip_paybackend=false; //设置为正在处理,不予许其它回调进入
}
if(!user_wxAPI.GetInstance().CheckReturn(request))//验证签名是否一致,签名不一致直接返回
{
result.put("return_code", "FAIL");
vip_paybackend=true;//可以接收下一次数据回调
return user_wxAPI.GetInstance().mapToXml(result);
}
else
{
Map<String, String> data = user_wxAPI.GetInstance().xmlToMap(request);//转成map
if (data.get("return_code").equals("FAIL"))//通讯为失败,直接返回
{
result.put("return_code", "FAIL");
vip_paybackend=true;//可以接收下一次数据回调
return user_wxAPI.GetInstance().mapToXml(result);
}
if (data.get("result_code").equals("FAIL"))//业务结果为失败,支付失败直接返回
{
result.put("return_code", "FAIL");
vip_paybackend=true;//可以接收下一次数据回调
return user_wxAPI.GetInstance().mapToXml(result);
}
if(data.get("result_code").equals("SUCCESS"))//业务结果成功,我们再次查询订单状态,在决定是否写入数据库
{
String transaction_id=data.get("transaction_id"); //得到这个微信订单号
String out_trade_no=data.get("out_trade_no"); //得到这个商户订单号
wxorder onewxorder=ordersdao.GetWXOrder(out_trade_no);//查询微信我们数据库订单状态
if(!data.get("total_fee").equals(String.valueOf(onewxorder.wxorder_money)))//如果订单金额不匹配
{
result.put("return_code", "FAIL");
vip_paybackend=true;//可以接收下一次数据回调
return user_wxAPI.GetInstance().mapToXml(result);
}
if(!onewxorder.wxorder_state.equals(1)) //如果不是未支付状态我们就返回
{
result.put("return_code", "SUCCESS");
vip_paybackend=true;//可以接收下一次数据回调
return user_wxAPI.GetInstance().mapToXml(result);//告诉微信服务器,我已经处理过数据
}
Map<String, String> wxorder_infom=user_wxAPI.GetInstance().OrderQuery(transaction_id);//用微信订单号,订单查询
if(wxorder_infom.get("return_code").equals("FAIL"))//查询订单失败了
{
result.put("return_code", "FAIL");
vip_paybackend=true;//可以接收下一次数据回调
return user_wxAPI.GetInstance().mapToXml(result);
}
if(wxorder_infom.get("return_code").equals("SUCCESS")
&&wxorder_infom.get("result_code").equals("SUCCESS")
&&wxorder_infom.get("trade_state").equals("SUCCESS"))//查询订单返回三个success作为,支付成功的判断
{
String session_id=wxorder_infom.get("attach");//商家数据包中获取用户sessionid
HttpSession usersession=MyHttpsessions.GetSession(session_id);
//修改微信订单状态,添加或者修改用户的会员时间,包括user自身
if(onewxorder.wxorder_state.equals(1))//如果是未支付状态,我们才进行数据库处理
{
onewxorder.wxorder_state=2;//状态改为已支付
onewxorder.wxorder_wxnum=transaction_id;//添加微信订单号
ordersdao.UpdateOneWxOrder_State(onewxorder);//跟新为已支付
Date user_endtime=ordersdao.saveviporder(onewxorder.wxorder_userid);//插入、跟新vip订单表
Userdao.UpdateUserEndtime(user_endtime, onewxorder.wxorder_userid);//跟新用户endtime
//修改用户ession状态
User user=new User();
if(usersession!=null) user=(User) usersession.getAttribute("user");
user.setUser_bvip(1);//是vip
user.setUser_endtime(df.format(user_endtime));
usersession.setAttribute("user", user);
result.put("return_code", "SUCCESS");
vip_paybackend=true;//可以接收下一次数据回调
return user_wxAPI.GetInstance().mapToXml(result);//告诉微信服务器,已经处理好数据
}
}
else
{
result.put("return_code", "FAIL");
vip_paybackend=true;//可以接收下一次数据回调
return user_wxAPI.GetInstance().mapToXml(result);
}
}
}
result.put("return_code", "FAIL");
vip_paybackend=true;//可以接收下一次数据回调
return user_wxAPI.GetInstance().mapToXml(result);
}