開發微信應用,微信支付是永遠要面對的。如今的微信支付相對以往已經很穩定,不多出現詭異狀況。再加上無數人開發的經驗分享,如今開發微信支付已經沒什麼難度了。javascript
我此次主要是想基於沐雪微信平臺的微商城業務來分析微信支付到底該怎麼作。主要講思路,代碼也同時會獻上。java
1、思考:一、支付前系統該作什麼?二、支付的時候該怎麼處理業務?三、同步和異步回調該怎麼處理業務邏輯?四、安全性,穩定性,數據一致性如何保證?web
分析:第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,其餘的都不須要放。json
<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>
有人確定會問,爲啥不把微信支付功能與訂單確認頁放在同一個頁面?或者是微信支付頁面是否要顯示支付的金額而後再加個確認支付按鈕?api
沐雪微信平臺之因此這麼作,緣由有幾點:一、訂單確認頁面邏輯至關龐雜,二、而微信支付跟訂單實際上是兩塊業務邏輯,三、支付業務能夠抽出來做爲公共的方法頁面;四、隨着業務模塊增多,支付頁面的邏輯也會愈來愈複雜;五、代碼隔離帶來無限的好處;六、我的不喜歡一個頁面承擔太多東西,寧願犧牲點用戶體驗,也不喜歡吧代碼融合在一塊兒;七、根據以往經驗,代碼融合在一個頁面,一個頁面代碼越多,出錯的機率越是成倍的增加,排查問題的難度也越大,很是不利於代碼迭代更新與的長期維護;安全
支付完成後,就跳轉到用戶中心的訂單詳情頁面查看信息;通常是間隔2,3秒才跳,等待異步回調處理;微信
異步回調函數裏,必定要驗證簽名:app