微信支付有两种模式:微信用户主动发起的支付、签约委托支付协议后自动支付。
自动支付又分为两种:首次支付时签约、纯签约。
首次支付时签约和纯签约在后续周期若需要发起自动扣款时,需要在应用服务中发起申请扣款,申请扣款不会立即到账,有处理延时,可通过通知url接收到账提醒;
首次支付时签约和纯签约均需要指定在商户平台中配置的模版id(plan_id),但普通商户是没有那个‘委托代扣模版’的菜单,若需要就要找串串开通,鄙视?
本次实现的是微信用户主动发起的支付行为扣费。
1、小程序端通过接口wx.requestPayment({})实现支付发起,拉取支付框。本请求接口有两个很关键的参数package、paySign 。请看以下代码:
wx.requestPayment({ 'timeStamp': result.timeStamp,//时间戳 'nonceStr': result.nonceStr,//随机数 'package': result.package,//prepay_id=xxx 'signType': result.signType,//签名类型,一般为MD5 'paySign': result.paySign,//已签名的参数串 'success': function (res) { app.sysShowToast('充值成功!正在进行后台续费操作,请稍后...', 'success',2000); //支付成功后,后台续费保存 that.buyCombo(comboId, outTradeNo); }, 'fail': function (res) { app.sysShowToast('充值失败:' + JSON.stringify(res), 'fail', 2000); } })
这里支付请求的参数我全部都是从后台服务中处理好后返回到小程序前端的。我的后端请求代码如下(仅参考):
wx.request({ url: app.globalData.serverUrl + '/Weixin/WXPay', method: 'POST', data: { comboId: comboId, /*套餐id*/ openid: app.globalData.openId, }, header: { 'content-type': 'application/json' }, success: function (res) { if (res.statusCode === 200) { if (res.data.IsSuccess)//成功 { var result = JSON.parse(res.data.Result); var outTradeNo = result.outTradeNo; //拉取支付框 wx.requestPayment({ 'timeStamp': result.timeStamp,//时间戳 'nonceStr': result.nonceStr,//随机数 'package': result.package,//prepay_id=xxx 'signType': result.signType,//签名类型,一般为MD5 'paySign': result.paySign,//已签名的参数串 'success': function (res) { app.sysShowToast('充值成功!正在进行后台续费操作,请稍后...', 'success',2000); //支付成功后,后台续费保存 that.buyCombo(comboId, outTradeNo); }, 'fail': function (res) { app.sysShowToast('充值失败:' + JSON.stringify(res), 'fail', 2000); } }) } } else{ app.sysShowModal("充值失败", "链接地址无效!") } }, fail: function (err) { app.sysShowModal("连接错误", "无法访问到服务端或请求超时!") } })
补充一句:代码中多次出现的全局方法app.sysShowToast()、app.sysShowModal()
是我封装好的,放置在app.js中。以下代码中包含了对模态框、提示框的封装,感兴趣的朋友可以直接拿去用
//模态框 sysShowModal: function (title, content, showCancel = false, pages = "", isNavigation = false, isTab = false) { var that=this; wx.showModal({ title: title==null?'':title, content: content==null?'':content, showCancel: showCancel, success: function (res) { if (res.confirm) { if (isNavigation && isTab && !that.isEmpty(pages)) { //跳转到tab页 wx.switchTab({ url: pages }); } else if (isNavigation && !isTab && !that.isEmpty(pages)){ //跳转到指定页 wx.navigateTo({ url: pages }) } } else{ app.sysShowModal('您已取消操作'); } } }); }, //模态框(确定后自动返回上一页) sysShowModalBackLastPage: function (title, content,showCancel = false) { var that=this; wx.showModal({ title: title == null ? '' : title, content: content == null ? '' : content, showCancel: showCancel, success: function (res) { if (res.confirm) { that.backLastPage(); } else { wx.showToast({ title: '您已取消操作', icon: 'none', //icon: 'warn', duration: 1000 }); } } }); }, //提示框 sysShowToast: function (title, icon="none", duration = 1000, pages = "", isNavigation = false, isTab = false) { var that = this; wx.showToast({ title: title, icon: icon, duration: duration, //弹出提示框时长 mask: true, success(data) { setTimeout(function () { if (isNavigation && isTab && !that.isEmpty(pages)) { //跳转到tab页 wx.switchTab({ url: pages }); } else if (isNavigation && !isTab && !that.isEmpty(pages)) { //跳转到指定页 wx.navigateTo({ url: pages }) } }, duration) //延迟时间 } }); }, //提示框(自动返回上一页) sysShowToastBackLastPage: function (title, icon = "none", duration = 1000){ var that = this; wx.showToast({ title: title, icon: icon, duration: duration, //弹出提示框时长 mask: true, success(data) { setTimeout(function () { //自动回到上一页 that.backLastPage(); }, duration) //延迟时间 } }); }, //判断字符串是否为空的方法 isEmpty:function (obj) { if(typeof obj == "undefined" || obj == null || obj == "" || obj == 'null') { return true; } else { return false; } }, IsEmpty:function (obj) { if (typeof obj == "undefined" || obj == null || obj == "" || obj == 'null') { return true; } else { return false; } }, //返回上一页(并刷新返回的页面) backLastPage: function () { var pages = getCurrentPages();//当前页面栈 if (pages.length > 1) { var beforePage = pages[pages.length - 2];//获取上一个页面实例对象 beforePage.onLoad();//触发父页面中的方法 } wx.navigateBack({ delta: 1 }); },