asp.net mvc 微信支付程式碼分析
開發微信應用,微信支付是永遠要面對的。現在的微信支付相對以往已經很穩定,很少出現詭異情況。再加上無數人開發的經驗分享,現在開發微信支付已經沒什麼難度了。
我這次主要是想基於沐雪微信平臺的微商城業務來分析微信支付到底該怎麼做。主要講思路,程式碼也同時會獻上。
一、思考: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秒才跳,等待非同步回撥處理;
非同步回撥函式裡,一定要驗證簽名。
下期我們將繼續講解沐雪微信平臺原始碼的設計思路。