开发微信应用,微信支付是永远要面对的。现在的微信支付相对以往已经很稳定,很少出现诡异情况。再加上无数人开发的经验分享,现在开发微信支付已经没什么难度了。
我这次主要是想基于沐雪微信平台的微商城业务来分析微信支付到底该怎么做。主要讲思路,代码也同时会献上。
一、思考:1、支付前系统该做什么?2、支付的时候该怎么处理业务?3、同步和异步回调该怎么处理业务逻辑?4、安全性,稳定性,数据一致性如何保证?
分析:第1个问题:支付前的一个页面一般来说是订单确认页面,在支付前,必须把该订单的所有数据(订单编号等基本数据,用户信息,订单商品信息,物流信息等)都保存到数据库,状态设置为“待支付”;再跳转到微信支付页面,微信支付页面接收到参数(订单编号,wid,业务模块编号)立即处理逻辑:
public class PayController : BaseController { string module = "支付页面"; /// <summary> /// 微信支付 /// </summary> /// <param name="wid"></param> /// <param name="moduletype">业务分类</param> /// <param name="orderNo"></param> /// <returns></returns> public ActionResult WXPaySubmit(int wid,string moduletype,string orderNo) { string parameters = string.Format("wid:{0},moduletype:{1},orderNo:{2}", wid,moduletype, orderNo); string operating = "微信支付页面"; bool isWeiXin = false; ViewBag.errorInfo = ""; ViewBag.pay_json = ""; string pay_uri = ""; string userAgent = base.Request.UserAgent; try { LogHelper.Info(parameters, 0, fromplat, module, operating); if (userAgent.ToLower().IndexOf("micromessenger") > -1) { isWeiXin = true; } ViewBag.isWeiXin = isWeiXin; ViewBag.pay_uri = pay_uri; if (string.IsNullOrWhiteSpace(orderNo)) { ViewBag.errorInfo = "订单号为空,不能进行支付!"; return View(); } ViewBag.orderNo = orderNo; BLL.orders otBll = new BLL.orders(); Model.orders orderEntity = otBll.GetModel(orderNo, wid); if (orderEntity == null) { ViewBag.errorInfo = "错误的订单号,不能进行支付!"; return View(); } if (orderEntity.payment_status != 1 || orderEntity.status != 1) { ViewBag.errorInfo = "订单状态错误,不能进行支付!"; return View(); } BLL.wx_crm_users userBll = new BLL.wx_crm_users(); Model.wx_crm_users user = userBll.GetModel(orderEntity.user_id, wid); if (orderEntity.payment_id == 1) { //线下付款 ViewBag.isOfflineOrder = true; } else { ViewBag.isOfflineOrder = false; } ViewBag.IsServiceOrder = false;//服务订单 ViewBag.isFightGroup = false;//团购的订单 ViewBag.Wid = wid; string packageValue = ""; if (orderEntity.payment_id == 3) {//微信支付 packageValue = WxPayDataV3(wid, orderEntity.order_amount, orderEntity.orderSubject, orderEntity.order_no, orderEntity.id, orderEntity.user_id, user.openid); } ViewBag.packageValue = packageValue; } catch (Exception ex) { LogHelper.Error(parameters + ",message:" + ex.Message, ex, wid , fromplat, module, operating); } return View(); } /// <summary> /// 微信支付最新接口调用 /// 2018-9-20 搬家 /// </summary> /// <param name="wid">微账号</param> /// <param name="ttFee">支付金额(单位元)</param> /// <param name="busiBody">订单内容</param> /// <param name="out_trade_no">订单号</param> /// <param name="code"></param> protected string WxPayDataV3(int wid, decimal ttFee, string busiBody, string out_trade_no, int order_id,int user_id,string openid) { string operating = "微信支付最新接口调用"; string parameters = string.Format("wid:{0},ttFee:{1},busiBody:{2},out_trade_no:{3},order_id:{4},user_id:{5},openid:{6}", wid, ttFee, busiBody, out_trade_no, order_id, user_id, openid); try { LogHelper.Info(parameters, 0, fromplat, module, operating); JsApiConfig jsapiConfig = new JsApiConfig(wid, SysModuleNameEnum.微商城); if (jsapiConfig == null) { LogHelper.Warn("微信支付没有配置正确(wid=" + wid + ")", user_id, fromplat, module, operating); return null; } string packageValue = ""; //先设置基本信息 string MchId = jsapiConfig.MchId; string partnerKey = jsapiConfig.Key;// 商户支付密钥Key。登录微信商户后台,进入栏目【账户设置】【密码安全】【API 安全】【API 密钥】 string notify_url = jsapiConfig.Notify_url;// MyCommFun.getAppSettingValue("webapi_url") + "/api/Pay/notify_url";//回调函数 string timeStamp = ""; string nonceStr = ""; string paySign = ""; string sp_billno = out_trade_no; //当前时间 yyyyMMdd string date = DateTime.Now.ToString("yyyyMMdd"); if (null == sp_billno) { //生成订单10位序列号,此处用时间和随机数生成,商户根据自己调整,保证唯一 sp_billno = DateTime.Now.ToString("HHmmss") + TenPayV3Util.BuildRandomStr(28); } timeStamp = TenPayV3Util.GetTimestamp(); nonceStr = TenPayV3Util.GetNoncestr(); string attach = wid + "|" + order_id +"|" + user_id; int price = (int)(ttFee * 100);//商品金额,以分为单位(money * 100).ToString() var xmlDataInfo = new TenPayV3UnifiedorderRequestData(jsapiConfig.AppId, MchId, busiBody, sp_billno, price, Request.UserHostAddress, notify_url, TenPayV3Type.JSAPI, openid, partnerKey, nonceStr, "vshop", null, null, "", attach); LogHelper.Info(parameters + ",xmlDataInfo=" + xmlDataInfo.ToString(), 0, fromplat, module, operating); var result = TenPayV3.Unifiedorder(xmlDataInfo);//调用统一订单接口 string prepayId = result.prepay_id; LogHelper.Info(parameters + ",预支付的prepayId=" + prepayId, 0, fromplat, module, operating); //设置支付参数 paySign = TenPayV3.GetJsPaySign(jsapiConfig.AppId, timeStamp, nonceStr, string.Format("prepay_id={0}", prepayId), partnerKey); packageValue = ""; packageValue += " \"appId\": \"" + jsapiConfig.AppId + "\", "; packageValue += " \"timeStamp\": \"" + timeStamp + "\", "; packageValue += " \"nonceStr\": \"" + nonceStr + "\", "; packageValue += " \"package\": \"" + string.Format("prepay_id={0}", prepayId) + "\", "; packageValue += " \"signType\": \"MD5\", "; packageValue += " \"paySign\": \"" + paySign + "\""; LogHelper.Info(parameters + ",packageValue=" + packageValue, 0, fromplat, module, operating); return packageValue; } catch (Exception ex) { LogHelper.Error(parameters + ",message:" + ex.Message, ex, wid, fromplat, module, operating); throw; } } }
将数据给试图里处理,jssdk调起微信支付。试图页里只有一段Js,其他的都不需要放。
<script type="text/javascript"> var orderNo = "@ViewBag.orderNo"; var IsServiceOrder = "@ViewBag.IsServiceOrder"; var isFightGroup = "@ViewBag.isFightGroup"; var isOfflineOrder = "@ViewBag.isOfflineOrder"; //调用微信JS api 支付 function jsApiCall() { WeixinJSBridge.invoke(‘getBrandWCPayRequest‘, { @Html.Raw(ViewBag.packageValue) }, function (res) { if (res.err_msg == "get_brand_wcpay_request:ok") { alert("订单支付成功!点击确认进入我的订单中心"); location.replace("/VShop/Order/OrderDetail/@ViewBag.Wid?orderno=" + orderNo); } else { //alert(res.err_code + res.err_desc + res.err_msg); if (res.err_msg.indexOf("cancel") != -1) { //主动取消支付 } else { alert("支付取消或者失败res.err_msg=" + res.err_msg); } location.replace("/VShop/Order/OrderDetail/@ViewBag.Wid?orderno=" + orderNo); } //alert("订单支付成功!点击确认进入我的订单中心"); }); } function callpay() { if (typeof WeixinJSBridge == "undefined") { if (document.addEventListener) { document.addEventListener(‘WeixinJSBridgeReady‘, jsApiCall, false); } else if (document.attachEvent) { document.attachEvent(‘WeixinJSBridgeReady‘, jsApiCall); document.attachEvent(‘onWeixinJSBridgeReady‘, jsApiCall); } } else { jsApiCall(); } } callpay(); </script>
有人肯定会问,为啥不把微信支付功能与订单确认页放在同一个页面?或者是微信支付页面是否要显示支付的金额然后再加个确认支付按钮?
沐雪微信平台之所以这么做,原因有几点:1、订单确认页面逻辑相当庞杂,2、而微信支付跟订单其实是两块业务逻辑,3、支付业务可以抽出来作为公共的方法页面;4、随着业务模块增多,支付页面的逻辑也会越来越复杂;5、代码隔离带来无限的好处;6、个人不喜欢一个页面承担太多东西,宁愿牺牲点用户体验,也不喜欢吧代码融合在一起;7、根据以往经验,代码融合在一个页面,一个页面代码越多,出错的概率越是成倍的增长,排查问题的难度也越大,非常不利于代码迭代更新与的长期维护;
支付完成后,就跳转到用户中心的订单详情页面查看信息;一般是间隔2,3秒才跳,等待异步回调处理;
异步回调函数里,一定要验证签名: