项目需求:学校学生网上缴费项目,刚来公司实习网上百度了各种资料,感谢很多大神避免了很多大坑。
本次扫码支付为:电脑生成二维码,手机微信扫码进行付款。建议开发前下载官方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>"); } }
结尾:第一次写博客有些乱,微信支付安全性还需要提高,项目部署也没经过大批量测试,就直接上线。
到现在也经历过两次几千人的缴费,一分钟缴费好几次,也碰到过网络延迟问题缴费成功后没及时更新数据库和一个浏览器打开两次微信支付界面导致两次缴费只算一次的各种问题。