項目需求:學校學生網上繳費項目,剛來公司實習網上百度了各種資料,感謝很多大神避免了很多大坑。
本次掃碼支付為:電腦生成二維碼,手機微信掃碼進行付款。建議開發前下載官方demo熟悉及后續有用到里面代碼:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1
1.微信公眾平台→支付配置→支付授權目錄(授權目錄就是指你要進行支付的目錄)添加你的項目發布文件網址,手上暫無公司微信公眾平台賬號,百度上下有什么詳細說明就不再說明了。
2.新建MVC項目→新建文件夾Resources將官方demo中的business和lib文件夾復制過來

3.打開lib文件夾里的config,在這里配置基礎信息和支付回調地址

4.新建WxPay頁面,此頁面展示付款二維碼,采用了jquery.qrcode.min.js生成二維碼,貼部分重要代碼
<script language="javascript"> $(function () { GetWXQRCode(); }); function GetWXQRCode() { //$('#QRCode').css('display', ''); //去除隱藏 //$('#paytitle').html('微信支付'); $('#tradeno').html(''); $('#paymoney').html(''); $.ajax({ type: "post", url: "/StudentIndex/GetWXQRCode", data: { time: new Date(), productId: "考試費用", idcard: '@ViewBag.idcard', }, success: function (json) { if (json.result) { $("#QRCode").qrcode(json.str); //生成二維碼 $("#tradeno").html(json.no); //訂單編號 $('#paymoney').html(json.money); } else { $("#QRCode").html("二維碼生成失敗"); } }, error: function (json) { $("#QRCode").html("二維碼生成錯誤"); } }) } <div class="i_ma"> <div class="i_name"> 微信支付 <p>WeChat Payment</p> </div> <div class="space_hx"> </div> <div>訂單編號:<p id="tradeno"></p></div> <div>考試費用:<p id="paymoney"></p></div> <div id="QRCode"> </div> </div> <div class="space_hx"> </div>
5.控制器內添加生成二維碼方法,這里面的idcard是學生身份證,添加到附加數據內便於后面查詢訂單時判斷是誰繳了費
//生成微信支付二維碼 [HttpPost] public ActionResult GetWXQRCode(string idcard) { object objResult = ""; string tradeno; string paymoney; string strProductID = Request.Form["productId"]; //商品ID string strQRCodeStr = GetWXPayUrl(strProductID, idcard, out tradeno, out paymoney); //Session["outtradeno"] = outtradeno; if (!string.IsNullOrWhiteSpace(strProductID)) { objResult = new { result = true, str = strQRCodeStr, no = tradeno, money = paymoney }; } else { objResult = new { result = false }; } return Json(objResult); } //生成直接微信支付url,支付url有效期為10分鍾,模式二 public string GetWXPayUrl(string productId, string idcard, out string out_trade_no, out string money) { WxPayData data = new WxPayData(); data.SetValue("body", "分類考試學費");//商品描述 data.SetValue("attach", idcard);//附加數據 out_trade_no = WxPayApi.GenerateOutTradeNo(); //Session["out_trade_no"] = out_trade_no; data.SetValue("out_trade_no", out_trade_no);//隨機字符串 string total = Convert.ToInt32((context.receiptInfoModel.OrderBy(x => x.ID).FirstOrDefault().PayMoney * 100)).ToString(); money = context.receiptInfoModel.OrderBy(x => x.ID).FirstOrDefault().PayMoney + "元"; data.SetValue("total_fee", total);//總金額 data.SetValue("time_start", DateTime.Now.ToString("yyyyMMddHHmmss"));//交易起始時間 data.SetValue("time_expire", DateTime.Now.AddMinutes(10).ToString("yyyyMMddHHmmss"));//交易結束時間 data.SetValue("goods_tag", "商品的備忘,可以自定義");//商品標記 data.SetValue("trade_type", "NATIVE");//交易類型 data.SetValue("product_id", productId);//商品ID WxPayData result = WxPayApi.UnifiedOrder(data);//調用統一下單接口 string url = result.GetValue("code_url").ToString();//獲得統一下單接口返回的二維碼鏈接 PreWxPayOrder(idcard, out_trade_no); return url; } //生成二維碼同時,生成預支付訂單 public void PreWxPayOrder(string idcard, string out_trade_no) { WxPayOrderModel model = new WxPayOrderModel(); model.StudentCard = idcard; model.OrderNo = out_trade_no; model.OrderTime = DateTime.Now; model.Status = 0; model.OrderType = "微信"; context.wxpayOrderModel.Add(model); context.SaveChanges(); //Log.Info("生成預支付訂單","訂單號:"+out_trade_no); }
6.新建wxpayok頁面(此頁面是config里配置的支付回調url),視圖不需要改動,在控制器里添加接收微信返回的數據
//接收微信返回信息 public ActionResult WxPayOK() { //接收從微信后台POST過來的數據 System.IO.Stream s = Request.InputStream; int count = 0; byte[] buffer = new byte[1024]; StringBuilder builder = new StringBuilder(); while ((count = s.Read(buffer, 0, 1024)) > 0) { builder.Append(Encoding.UTF8.GetString(buffer, 0, count)); } s.Flush(); s.Close(); s.Dispose(); //轉換數據格式並驗證簽名 WxPayData data = new WxPayData(); try { data.FromXml(builder.ToString()); } catch (WxPayException ex) { //若簽名錯誤,則立即返回結果給微信支付后台 WxPayData res = new WxPayData(); res.SetValue("return_code", "FAIL"); res.SetValue("return_msg", ex.Message); Log.Error("簽名錯誤", "Sign check error : " + res.ToXml()); Response.Write(res.ToXml()); Response.End(); } ProcessNotify(data); return View(); } //微信支付后台返回的數據 public void ProcessNotify(WxPayData data) { WxPayData notifyData = data; //檢查支付結果中transaction_id是否存在 if (!notifyData.IsSet("transaction_id")) { WxPayData res = new WxPayData(); res.SetValue("return_code", "FAIL"); res.SetValue("return_msg", "支付結果中微信訂單號不存在"); Log.Error("ERROR", "訂單號不存在"); Response.Write(res.ToXml()); Response.End(); } else { string transaction_id = notifyData.GetValue("transaction_id").ToString(); if (!QueryOrder(transaction_id)) { WxPayData res = new WxPayData(); res.SetValue("return_code", "FAIL"); res.SetValue("return_msg", "訂單查詢失敗"); Log.Error("ERROR", "訂單查詢失敗"); Response.Write(res.ToXml()); Response.End(); } else { WxPayData res = new WxPayData(); res.SetValue("return_code", "SUCCESS"); res.SetValue("return_msg", "OK"); //Log.Info("success", "支付成功"); AddWayPayRecord(data.GetValue("out_trade_no").ToString(), data.GetValue("transaction_id").ToString(), data.GetValue("attach").ToString()); Response.Write(res.ToXml()); Response.End(); } } } //查詢訂單 public bool QueryOrder(string transaction_id) { WxPayData req = new WxPayData(); req.SetValue("transaction_id", transaction_id); WxPayData res = WxPayApi.OrderQuery(req); if (res.GetValue("return_code").ToString() == "SUCCESS" && res.GetValue("result_code").ToString() == "SUCCESS") { return true; } else { return false; } } //微信支付完成添加記錄 tradeno:訂單號 public void AddWayPayRecord(string tradeno, string tranid, string idcard) { lock (wxlock)//線程同步 { WxPayOrderModel wxpayOrderModel = context.wxpayOrderModel.FirstOrDefault(x => x.OrderNo == tradeno && x.OrderType == "微信" && x.StudentCard == idcard && x.Status == 0); if (wxpayOrderModel != null) { wxpayOrderModel.Status = 1; context.Entry(wxpayOrderModel).State = System.Data.EntityState.Modified; context.SaveChanges(); StudentModel studentModel = context.studentModel.Where(a => a.identityCard == wxpayOrderModel.StudentCard).FirstOrDefault(); PayModel payModel = context.payModel.FirstOrDefault(x => x.studentid == studentModel.candidateNum); Random rd = new Random(); if (payModel == null) { PayModel paymodel = new PayModel(); paymodel.paycount = context.receiptInfoModel.OrderBy(x => x.ID).FirstOrDefault().PayMoney; paymodel.ordernumber = tradeno; paymodel.paytime = DateTime.Now; paymodel.PayOddNum = tranid; context.payModel.Add(paymodel); context.SaveChanges(); } } } }
7.這時候頁面也接收到微信返回的數據同時也添加到數據庫中,頁面上得給用戶一個友好提示,告訴他支付成功了。在wxpay頁面里加個ajax實時輪詢數據庫
$(function () { setInterval(GetWxPayResult, 2000); function GetWxPayResult() { var no = $("#tradeno").text(); $.ajax({ url: "/StudentIndex/WxPayResult", type: "post", data: { idcard: '@ViewBag.idcard', tradeno:no, }, success: function (json) { if (json.result) { document.location.href = "/StudentIndex/WxPayisOK?idcard=@ViewBag.idcard"+"&&tradeno="+no; } else { } }, error: function (json) { alert("錯誤"); } }) } })
同時控制器里需要加上對應的方法,判斷支付成功后跳轉到一個新的頁面wxpayisok
//ajax輪詢支付結果 public ActionResult WxPayResult(string idcard, string tradeno) { object data = ""; StudentModel studentModel = context.studentModel.FirstOrDefault(x => x.identityCard == idcard); PayModel model = context.payModel.FirstOrDefault(x => x.studentid == studentModel.candidateNum); if (model != null) { data = new { result = true }; } return Json(data); } //支付完成 public ActionResult WxPayisOK(string idcard, string tradeno) { StudentModel studentModel = context.studentModel.FirstOrDefault(x => x.identityCard == idcard); PayModel model = context.payModel.FirstOrDefault(x => x.studentid == studentModel.candidateNum); if (model != null) { return Content("<Script>alert('繳費成功!');window.location.href='/StudentIndex/StudentIndex';</Script>"); } else { return Content("<Script>alert('繳費失敗!請保留好支付憑證前往繳費處申請退款');window.location.href='/StudentIndex/StudentIndex';</Script>"); } }
結尾:第一次寫博客有些亂,微信支付安全性還需要提高,項目部署也沒經過大批量測試,就直接上線。
到現在也經歷過兩次幾千人的繳費,一分鍾繳費好幾次,也碰到過網絡延遲問題繳費成功后沒及時更新數據庫和一個瀏覽器打開兩次微信支付界面導致兩次繳費只算一次的各種問題。
