C# 微信支付教程系列之扫码支付
微信支付教程系列之扫码支付
今天,我们来一起探讨一下这个微信扫码支付。何为扫码支付呢?这里面,扫的码就是二维码了,就是我们经常扫一扫的那种二维码图片,例如,我们自己添加好友的时候,可以通过输入对方的微信号,也可以扫一扫对方的二维码。扫码支付,作为,微信支付里面,不可或缺的一个功能,对商品的支付提供了极为方便的体验,用途也非常的多。例如我们在地铁、公交站常见的那些自动售货机(不错,就是那种投硬币,就可以自动出货的那种机器)中都用到。以前,那种机器,只能通过投硬币或者纸币,但是,这里面也有一定的风险,例如:假币,盗窃(真有人把机器砸了,把机器里面的钱偷走的),所以,微信(支付宝)的扫码支付的出现,大大的减少了这方面的风险,近些年来,二维码的应用越来越广,甚至有些地方,直接用来自动售票(就是把起始点设定好,票价设定好,直接把二维码贴出来,让乘客自动扫相关的二维码,完成购票,上车的时候,只需要提供自己的支付凭证给乘车员验证即可),这样,不仅绿色环保了,还大大的提高了售票的速度(去过大车站购票的人应该深有体验,排队买个票,好歹半个小时以上,心里也是万头草泥马在奔腾的)。
咱就不扯远了,说回咱么今天要做的微信支付之扫码支付。微信官方的文档,这个扫码支付(NativePay)分为两种,一种是“生成扫描支付模式”,另外一种是“生成直接支付url,支付url有效期为2小时”,至于这里面,两种扫码模式,怎么灵活利用呢,官方也没有一个明确的说明。个人理解为,第一种(生成扫描支付模式),适用于固定二维码的,就是永久使用的那种,例如一些商家的公众号的二维码,是永久的,什么时候扫,都是关注这个公众号的,但是,这种的话,我记得微信是有限量的,貌似是一个公众号,限量10w,个人观点,觉得这个限量,是足够我们使用的。第二种(生成直接支付url,支付url有效期为2小时),这种的话,因为有有效期这种时间限制,超过了2个小时,该二维码就失效,但是对生成的二维码数量没有限制,所以,这种个人观点觉得适用于那种临时根据实际情况生成的二维码,例如:公众平台登陆的时候二次验证的二维码,自定义生成,仅为一次性缴费使用的二维码,等等)。接下来,我们就开始讲讲实际例子,首先将的就是第一种模式。
扫码支付之模式一(生成扫描支付模式):
首先,我们新建一个“MVC”的项目(asp.net的官方的demo就是了,要asp.net的自己看demo吧,demo地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1),然后把系统自动生成的HomeControler和View中的Home都删了。
然后自己新建一个HomeControler,代码如下:
1 // GET: Home 2 public ActionResult Index() 3 { 4 return View(); 5 }
再添加一个View,代码如下:
1 @{ 2 Layout = null; 3 } 4 <!DOCTYPE html> 5 <html> 6 <head> 7 <meta name="viewport" content="width=device-width" /> 8 <title>首页</title> 9 </head> 10 <body> 11 <div> 12 </div> 13 </body> 14 </html>
接下来,我们先把官方的demo的一些我们会用到的东西拷贝过来,其中包括以下几个文件夹,如下图:
就这个lib和business两个,把这两个文件夹,支付复制到咱们的新项目中,并且包含在项目中,如下:
然后我们再“重新生成”以下项目,或者快捷键:ctrl+shift+b,这时候,会提下如下错误:
这时候,我们去添加引用,把lib文件夹中的LitJson.dll 添加上即可,如下图:
到这里,我们就基本把官方的demo的环境给搭建好了,接下来,我们就要开始编写代码了。
首先,我的逻辑是,从前到后,就是从前端到后端。前端是显示二维码的地方,那么我们就先给他一个div(本文使用到的是jquery的二维码生成插件,全名叫:jquery.qrcode.min.js,我会传到附件上),然后在页面加载完毕的时候,会请求后台,让他返回二维码字符串,然后再通过jquery的二维码生成插件,让他生成二维码并显示在前台,代码如下:
前端:
1 @{ 2 Layout = null; 3 } 4 <!DOCTYPE html> 5 <html> 6 <head> 7 <meta name="viewport" content="width=device-width" /> 8 <title>首页</title> 9 <link href="~/Scripts/jquery-easyui-1.4.5/themes/bootstrap/easyui.css" rel="stylesheet" /> 10 <link href="~/Scripts/jquery-easyui-1.4.5/themes/mobile.css" rel="stylesheet" /> 11 <link href="~/Scripts/jquery-easyui-1.4.5/themes/icon.css" rel="stylesheet" /> 12 </head> 13 <body> 14 <p> 15 模式一:生成扫描支付模式 16 <br /> 17 <div id="QRCode1"> 18 </div> 19 </p> 20 <p> 21 模式二:生成直接支付url,支付url有效期为2小时 22 <br /> 23 <div id="QRCode2"> 24 </div> 25 </p> 26 <script src="~/Scripts/jquery-1.10.2.js"></script> 27 <script src="~/Scripts/jquery-easyui-1.4.5/jquery.easyui.min.js"></script> 28 <script src="~/Scripts/jquery-easyui-1.4.5/jquery.easyui.mobile.js"></script> 29 <script src="~/Scripts/jquery-easyui-1.4.5/easyloader.js"></script> 30 <script src="~/Scripts/jquery.qrcode.min.js"></script> 31 <script type="text/javascript"> 32 $(function () { 33 fGetQRCode1(); 34 }) 35 function fGetQRCode1() { 36 $.messager.progress({ 37 title: "", 38 msg: "正在生成二维码:模式一,请稍后..." 39 }); 40 $.ajax({ 41 type: "post", 42 url: "/Home/GetQRCode1", 43 data: { 44 time: new Date(), 45 productId:7788 46 }, 47 success: function (json) { 48 $.messager.progress('close');//记得关闭 49 if (json.result) { 50 $('#QRCode1').qrcode(json.str); //生成二维码 51 } 52 else { 53 $('#QRCode1').html("二维码生成失败"); 54 } 55 } 56 }) 57 } 58 </script> 59 </body> 60 </html>
后端:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 using System.Web.Mvc; 6 using WxPayAPI; 7 namespace WxPay.Controllers 8 { 9 public class HomeController : Controller 10 { 11 // GET: Home 12 public ActionResult Index() 13 { 14 return View(); 15 } 16 /// <summary> 17 /// 模式一 18 /// </summary> 19 /// <returns></returns> 20 [HttpPost] 21 public ActionResult GetQRCode1() 22 { 23 object objResult = ""; 24 string strProductID = Request.Form["productId"]; 25 string strQRCodeStr = GetPrePayUrl(strProductID); 26 if (!string.IsNullOrWhiteSpace(strProductID)) 27 { 28 objResult = new { result = true, str = strQRCodeStr }; 29 } 30 else 31 { 32 objResult = new { result = false }; 33 } 34 return Json(objResult); 35 } 36 /** 37 * 生成扫描支付模式一URL 38 * @param productId 商品ID 39 * @return 模式一URL 40 */ 41 public string GetPrePayUrl(string productId) 42 { 43 WxPayData data = new WxPayData(); 44 data.SetValue("appid", WxPayConfig.APPID);//公众帐号id 45 data.SetValue("mch_id", WxPayConfig.MCHID);//商户号 46 data.SetValue("time_stamp", WxPayApi.GenerateTimeStamp());//时间戳 47 data.SetValue("nonce_str", WxPayApi.GenerateNonceStr());//随机字符串 48 data.SetValue("product_id", productId);//商品ID 49 data.SetValue("sign", data.MakeSign());//签名 50 string str = ToUrlParams(data.GetValues());//转换为URL串 51 string url = "weixin://wxpay/bizpayurl?" + str; 52 return url; 53 } 54 /** 55 * 参数数组转换为url格式 56 * @param map 参数名与参数值的映射表 57 * @return URL字符串 58 */ 59 private string ToUrlParams(SortedDictionary<string, object> map) 60 { 61 string buff = ""; 62 foreach (KeyValuePair<string, object> pair in map) 63 { 64 buff += pair.Key + "=" + pair.Value + "&"; 65 } 66 buff = buff.Trim('&'); 67 return buff; 68 } 69 } 70 }
这时候,模式一是不是感觉就完成了?那么我们现在试试,我们浏览该页面,如下:
然后用微信扫一扫功能扫一下,发现提示如下:
这是什么鬼,是不是,你心里面是不是想知道为啥,那我来告诉你,这是为啥,这是因为,你还没有设置回调页面或者回调页面有问题,这个时候,我们再新建一个Control,命名为:NativeNotifyController.cs,代码如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Web; 6 using System.Web.Mvc; 7 using WxPayAPI; 8 namespace WxPay.Controllers 9 { 10 public class NativeNotifyController : Controller 11 { 12 // GET: NativeNotify 13 public ActionResult Index() 14 { 15 string strData = ProcessNotify(); 16 Response.Write(strData); 17 return View(); 18 } 19 public string ProcessNotify() 20 { 21 WxPayData notifyData = GetNotifyData(); 22 //检查openid和product_id是否返回 23 if (!notifyData.IsSet("openid") || !notifyData.IsSet("product_id")) 24 { 25 WxPayData res = new WxPayData(); 26 res.SetValue("return_code", "FAIL"); 27 res.SetValue("return_msg", "回调数据异常"); 28 return res.ToXml(); 29 } 30 //调统一下单接口,获得下单结果 31 string openid = notifyData.GetValue("openid").ToString(); 32 string product_id = notifyData.GetValue("product_id").ToString(); 33 WxPayData unifiedOrderResult = new WxPayData(); 34 try 35 { 36 unifiedOrderResult = UnifiedOrder(openid, product_id); 37 } 38 catch (Exception ex)//若在调统一下单接口时抛异常,立即返回结果给微信支付后台 39 { 40 WxPayData res = new WxPayData(); 41 res.SetValue("return_code", "FAIL"); 42 res.SetValue("return_msg", "统一下单失败"); 43 return res.ToXml(); 44 } 45 //若下单失败,则立即返回结果给微信支付后台 46 if (!unifiedOrderResult.IsSet("appid") || !unifiedOrderResult.IsSet("mch_id") || !unifiedOrderResult.IsSet("prepay_id")) 47 { 48 WxPayData res = new WxPayData(); 49 res.SetValue("return_code", "FAIL"); 50 res.SetValue("return_msg", "统一下单失败"); 51 return res.ToXml(); 52 } 53 //统一下单成功,则返回成功结果给微信支付后台 54 WxPayData data = new WxPayData(); 55 data.SetValue("return_code", "SUCCESS"); 56 data.SetValue("return_msg", "OK"); 57 data.SetValue("appid", WxPayConfig.APPID); 58 data.SetValue("mch_id", WxPayConfig.MCHID); 59 data.SetValue("nonce_str", WxPayApi.GenerateNonceStr()); 60 data.SetValue("prepay_id", unifiedOrderResult.GetValue("prepay_id")); 61 data.SetValue("result_code", "SUCCESS"); 62 data.SetValue("err_code_des", "OK"); 63 data.SetValue("sign", data.MakeSign()); 64 return data.ToXml(); 65 } 66 /// <summary> 67 /// 接收从微信支付后台发送过来的数据并验证签名 68 /// </summary> 69 /// <returns>微信支付后台返回的数据</returns> 70 public WxPayData GetNotifyData() 71 { 72 //接收从微信后台POST过来的数据 73 System.IO.Stream s = Request.InputStream; 74 int count = 0; 75 byte[] buffer = new byte[1024]; 76 StringBuilder builder = new StringBuilder(); 77 while ((count = s.Read(buffer, 0, 1024)) > 0) 78 { 79 builder.Append(Encoding.UTF8.GetString(buffer, 0, count)); 80 } 81 s.Flush(); 82 s.Close(); 83 s.Dispose(); 84 //转换数据格式并验证签名 85 WxPayData data = new WxPayData(); 86 try 87 { 88 data.FromXml(builder.ToString()); 89 } 90 catch (WxPayException ex) 91 { 92 //若签名错误,则立即返回结果给微信支付后台 93 WxPayData res = new WxPayData(); 94 res.SetValue("return_code", "FAIL"); 95 res.SetValue("return_msg", ex.Message); 96 } 97 return data; 98 } 99 private WxPayData UnifiedOrder(string openId, string productId) 100 { 101 //统一下单 102 WxPayData req = new WxPayData(); 103 req.SetValue("body", "广东XXXX股份有限公司"); 104 req.SetValue("attach", "附加信息,用于后台或者存入数据库,做自己的判断"); 105 req.SetValue("out_trade_no", WxPayApi.GenerateOutTradeNo()); 106 req.SetValue("total_fee", 1); 107 req.SetValue("time_start", DateTime.Now.ToString("yyyyMMddHHmmss")); 108 req.SetValue("time_expire", DateTime.Now.AddMinutes(10).ToString("yyyyMMddHHmmss")); 109 req.SetValue("goods_tag", "商品的备忘,可以自定义"); 110 req.SetValue("trade_type", "NATIVE"); 111 req.SetValue("openid", openId); 112 req.SetValue("product_id", productId); 113 WxPayData result = WxPayApi.UnifiedOrder(req); 114 return result; 115 } 116 } 117 }
记得,也要新建一个View,就是在Index那里,右键添加一个View,View的代码如下(你没眼花,就是空的,不管他):
1 @{ 2 Layout = null; 3 } 4 <!DOCTYPE html> 5 <html> 6 <head> 7 <meta name="viewport" content="width=device-width" /> 8 <title>Index</title> 9 </head> 10 <body> 11 <div> 12 </div> 13 </body> 14 </html>
接着,把这个项目,发布出来,放到服务器的iis上,这里面,我把他发布在http://sm.lmx.ren/上面(必须要发布到网上哈,如果不懂发布的,你可以自己去学习基础知识先了),这还没完,还需要把到公众平台上,设置回调页面,操作如下:
这样,就大功告成了。这时候,我们再试试扫码,发现已经得到以下提示了,这样子,就代表,我们的模式一,已经成功完成了。如下图:
这时候,细心的朋友就会提问了,我这都支付成功了,怎么页面没啥提示呀,这页面不交互很不友好啊。嗯,没错,童鞋,你有前途,现在我就告诉你,怎么做交互,但是,为了你日后更加有前途,我只告诉你逻辑,具体怎么实现,自己来想,多动脑。
那么逻辑是怎么的呢?常规逻辑下,我们微信扫页面上的这个二维码的时候,这个时候,他已经把我们二维码里面的参数,传到微信服务器,然后有他们开始统一下单(如果对逻辑不清晰,可以看看官方的文档:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_3):他们在统一下单的时候,就会生成一个product_id,这个家伙的作用呢 ,就是告诉你现在微信服务器,已经生成了一个单号,劳资已经收到你的支付请求了,赶紧给老子付款,O(∩_∩)O哈哈~。。。停,停,停。这时候,思路不能继续往下走了。记得,前面有个叫做“统一下单“,那既然有这个步骤,那我们可以利用一下,就是当他统一下单成功的时候,我们可以在页面更新一下状态,告诉客户:您已成功扫描,并下单成功,请支付。是不是,我们可以提示他们这个。然后等用户在手机上,支付成功的时候,这个时候,页面是不是也要反馈给用户,告诉他,小子,你的钱已经到我的口袋了,你可以走了(你走,我没有你这样的宝宝)。O(∩_∩)O哈哈~,但是,你还要停,停住,停下来。我们服务公司怎么知道这个微信用户已经付款成功了呢?来,我们把视线回到代码上,找到lib/Config.cs,如下图:
然后打开config.cs,找到以下代码:
对了,你很聪明。微信的处理逻辑就是,等用户支付成功之后,他会给这个链接发送支付结果,默认是以前那个aspx的页面,现在我换成mvc,所以,我们得手动新建一个control了,命名为:ResultNotifyController,然后代码如下:
1 using LmxPublic.Log; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Web; 7 using System.Web.Mvc; 8 using WxPayAPI; 9 namespace WxPay.Controllers 10 { 11 public class ResultNotifyController : Controller 12 { 13 // GET: ResultNotify 14 public ActionResult Index() 15 { 16 string strData = ProcessNotify(); 17 Response.Write(strData); 18 return View(); 19 } 20 public string ProcessNotify() 21 { 22 WxPayData notifyData = GetNotifyData(); 23 //检查支付结果中transaction_id是否存在 24 if (!notifyData.IsSet("transaction_id")) 25 { 26 //若transaction_id不存在,则立即返回结果给微信支付后台 27 WxPayData res = new WxPayData(); 28 res.SetValue("return_code", "FAIL"); 29 res.SetValue("return_msg", "支付结果中微信订单号不存在"); 30 return res.ToXml(); 31 } 32 string transaction_id = notifyData.GetValue("transaction_id").ToString(); 33 //查询订单,判断订单真实性 34 if (!QueryOrder(transaction_id)) 35 { 36 //若订单查询失败,则立即返回结果给微信支付后台 37 WxPayData res = new WxPayData(); 38 res.SetValue("return_code", "FAIL"); 39 res.SetValue("return_msg", "订单查询失败"); 40 return res.ToXml(); 41 } 42 //查询订单成功 43 else 44 { 45 WxPayData res = new WxPayData(); 46 res.SetValue("return_code", "SUCCESS"); 47 res.SetValue("return_msg", "OK"); 48 Log.Info(this.GetType().ToString(), "order query success : " + res.ToXml()); 49 string strXml = res.ToXml(); 50 FileLog.WriteLog(strXml); 51 return res.ToXml();//如果我们走到这一步了,那就代表,用户已经支付成功了,所以,该干嘛干嘛了。 52 } 53 } 54 /// <summary> 55 /// 接收从微信支付后台发送过来的数据并验证签名 56 /// </summary> 57 /// <returns>微信支付后台返回的数据</returns> 58 public WxPayData GetNotifyData() 59 { 60 //接收从微信后台POST过来的数据 61 System.IO.Stream s = Request.InputStream; 62 int count = 0; 63 byte[] buffer = new byte[1024]; 64 StringBuilder builder = new StringBuilder(); 65 while ((count = s.Read(buffer, 0, 1024)) > 0) 66 { 67 builder.Append(Encoding.UTF8.GetString(buffer, 0, count)); 68 } 69 s.Flush(); 70 s.Close(); 71 s.Dispose(); 72 Log.Info(this.GetType().ToString(), "Receive data from WeChat : " + builder.ToString()); 73 //转换数据格式并验证签名 74 WxPayData data = new WxPayData(); 75 try 76 { 77 data.FromXml(builder.ToString()); 78 } 79 catch (WxPayException ex) 80 { 81 //若签名错误,则立即返回结果给微信支付后台 82 WxPayData res = new WxPayData(); 83 res.SetValue("return_code", "FAIL"); 84 res.SetValue("return_msg", ex.Message); 85 Log.Error(this.GetType().ToString(), "Sign check error : " + res.ToXml()); 86 return res; 87 } 88 89 return data; 90 } 91 //查询订单 92 private bool QueryOrder(string transaction_id) 93 { 94 WxPayData req = new WxPayData(); 95 req.SetValue("transaction_id", transaction_id); 96 WxPayData res = WxPayApi.OrderQuery(req); 97 if (res.GetValue("return_code").ToString() == "SUCCESS" && 98 res.GetValue("result_code").ToString() == "SUCCESS") 99 { 100 return true; 101 } 102 else 103 { 104 return false; 105 } 106 } 107 } 108 }
前台,对,也是要新建一个View,代码如下(没错,也是空的)
1 @{ 2 Layout = null; 3 } 4 <!DOCTYPE html> 5 <html> 6 <head> 7 <meta name="viewport" content="width=device-width" /> 8 <title>Index</title> 9 </head> 10 <body> 11 <div> 12 </div> 13 </body> 14 </html>
好,模式一就到这里了,呼呼。。。没想到啊,一个模式一,让我从上午写到下午,真心累。。。还有一个模式二呢。。。喝口水先,咱,接着来。
好,喝完水,接着干,下面是模式二:
模式二(生成直接支付url,支付url有效期为2小时)
由于有了上面模式一的详细说明,模式二,我就简单一点的来说了,如果又不懂的,到群里来问我吧。
模式二,前端,增加一些代码,如下(完整的,包括模式一的代码了):
1 @{ 2 Layout = null; 3 } 4 <!DOCTYPE html> 5 <html> 6 <head> 7 <meta name="viewport" content="width=device-width" /> 8 <title>首页</title> 9 <link href="~/Scripts/jquery-easyui-1.4.5/themes/bootstrap/easyui.css" rel="stylesheet" /> 10 <link href="~/Scripts/jquery-easyui-1.4.5/themes/mobile.css" rel="stylesheet" /> 11 <link href="~/Scripts/jquery-easyui-1.4.5/themes/icon.css" rel="stylesheet" /> 12 </head> 13 <body> 14 <p> 15 模式一:生成扫描支付模式 16 <br /> 17 <div id="QRCode1"> 18 </div> 19 </p> 20 <p> 21 模式二:生成直接支付url,支付url有效期为2小时 22 <br /> 23 <div id="QRCode2"> 24 </div> 25 </p> 26 <script src="~/Scripts/jquery-1.10.2.js"></script> 27 <script src="~/Scripts/jquery-easyui-1.4.5/jquery.easyui.min.js"></script> 28 <script src="~/Scripts/jquery-easyui-1.4.5/jquery.easyui.mobile.js"></script> 29 <script src="~/Scripts/jquery-easyui-1.4.5/easyloader.js"></script> 30 <script src="~/Scripts/jquery.qrcode.min.js"></script> 31 <script type="text/javascript"> 32 $(function () { 33 fGetQRCode1(); 34 }) 35 function fGetQRCode1() { 36 $.messager.progress({ 37 title: "", 38 msg: "正在生成二维码:模式一,请稍后..." 39 }); 40 $.ajax({ 41 type: "post", 42 url: "/Home/GetQRCode1", 43 data: { 44 time: new Date(), 45 productId:7788 46 }, 47 success: function (json) { 48 $.messager.progress('close');//记得关闭 49 if (json.result) { 50 $('#QRCode1').qrcode(json.str); //生成二维码 51 } 52 else { 53 $('#QRCode1').html("二维码生成失败"); 54 } 55 fGetQRCode2(); 56 }, 57 error: function (json) { 58 $('#QRCode1').html("二维码生成失败"); 59 fGetQRCode2(); 60 } 61 }) 62 } 63 function fGetQRCode2() { 64 $.messager.progress({ 65 title: "", 66 msg: "正在生成二维码:模式二,请稍后..." 67 }); 68 $.ajax({ 69 type: "post", 70 url: "/Home/GetQRCode2", 71 data: { 72 time: new Date(), 73 productId: 7788 74 }, 75 success: function (json) { 76 $.messager.progress('close');//记得关闭 77 if (json.result) { 78 $('#QRCode2').qrcode(json.str); //生成二维码 79 } 80 else { 81 $('#QRCode2').html("二维码生成失败"); 82 } 83 }, 84 error: function (json) { 85 $('#QRCode2').html("二维码生成失败"); 86 } 87 }) 88 } 89 </script> 90 </body> 91 </html>
后端:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 using System.Web.Mvc; 6 using WxPayAPI; 7 namespace WxPay.Controllers 8 { 9 public class HomeController : Controller 10 { 11 // GET: Home 12 public ActionResult Index() 13 { 14 return View(); 15 } 16 /// <summary> 17 /// 模式一 18 /// </summary> 19 /// <returns></returns> 20 [HttpPost] 21 public ActionResult GetQRCode1() 22 { 23 object objResult = ""; 24 string strProductID = Request.Form["productId"]; 25 string strQRCodeStr = GetPrePayUrl(strProductID); 26 if (!string.IsNullOrWhiteSpace(strProductID)) 27 { 28 objResult = new { result = true, str = strQRCodeStr }; 29 } 30 else 31 { 32 objResult = new { result = false }; 33 } 34 return Json(objResult); 35 } 36 /// <summary> 37 /// 模式二 38 /// </summary> 39 /// <returns></returns> 40 [HttpPost] 41 public ActionResult GetQRCode2() 42 { 43 object objResult = ""; 44 string strProductID = Request.Form["productId"]; 45 string strQRCodeStr = GetPayUrl(strProductID); 46 if (!string.IsNullOrWhiteSpace(strProductID)) 47 { 48 objResult = new { result = true, str = strQRCodeStr }; 49 } 50 else 51 { 52 objResult = new { result = false }; 53 } 54 return Json(objResult); 55 } 56 /** 57 * 生成扫描支付模式一URL 58 * @param productId 商品ID 59 * @return 模式一URL 60 */ 61 public string GetPrePayUrl(string productId) 62 { 63 WxPayData data = new WxPayData(); 64 data.SetValue("appid", WxPayConfig.APPID);//公众帐号id 65 data.SetValue("mch_id", WxPayConfig.MCHID);//商户号 66 data.SetValue("time_stamp", WxPayApi.GenerateTimeStamp());//时间戳 67 data.SetValue("nonce_str", WxPayApi.GenerateNonceStr());//随机字符串 68 data.SetValue("product_id", productId);//商品ID 69 data.SetValue("sign", data.MakeSign());//签名 70 string str = ToUrlParams(data.GetValues());//转换为URL串 71 string url = "weixin://wxpay/bizpayurl?" + str; 72 return url; 73 } 74 /** 75 * 参数数组转换为url格式 76 * @param map 参数名与参数值的映射表 77 * @return URL字符串 78 */ 79 private string ToUrlParams(SortedDictionary<string, object> map) 80 { 81 string buff = ""; 82 foreach (KeyValuePair<string, object> pair in map) 83 { 84 buff += pair.Key + "=" + pair.Value + "&"; 85 } 86 buff = buff.Trim('&'); 87 return buff; 88 } 89 /** 90 * 生成直接支付url,支付url有效期为2小时,模式二 91 * @param productId 商品ID 92 * @return 模式二URL 93 */ 94 public string GetPayUrl(string productId) 95 { 96 WxPayData data = new WxPayData(); 97 data.SetValue("body", "广东XXXX股份有限公司");//商品描述 98 data.SetValue("attach", "附加信息,用于后台或者存入数据库,做自己的判断");//附加数据 99 data.SetValue("out_trade_no", WxPayApi.GenerateOutTradeNo());//随机字符串 100 data.SetValue("total_fee", 1);//总金额 101 data.SetValue("time_start", DateTime.Now.ToString("yyyyMMddHHmmss"));//交易起始时间 102 data.SetValue("time_expire", DateTime.Now.AddMinutes(10).ToString("yyyyMMddHHmmss"));//交易结束时间 103 data.SetValue("goods_tag", "商品的备忘,可以自定义");//商品标记 104 data.SetValue("trade_type", "NATIVE");//交易类型 105 data.SetValue("product_id", productId);//商品ID 106 WxPayData result = WxPayApi.UnifiedOrder(data);//调用统一下单接口 107 string url = result.GetValue("code_url").ToString();//获得统一下单接口返回的二维码链接 108 109 return url; 110 } 111 } 112 }
由于模式二是没有支付结果回调的,所以,我们要查询支付成功与否,需要自己写方法来查询,官方提供的查询支付成功与否的方法有以下,
1 /*** 2 * 订单查询完整业务流程逻辑 3 * @param transaction_id 微信订单号(优先使用) 4 * @param out_trade_no 商户订单号 5 * @return 订单查询结果(xml格式) 6 */ 7 public static string Run(string transaction_id, string out_trade_no) 8 { 9 Log.Info("OrderQuery", "OrderQuery is processing..."); 10 WxPayData data = new WxPayData(); 11 if(!string.IsNullOrEmpty(transaction_id))//如果微信订单号存在,则以微信订单号为准 12 { 13 data.SetValue("transaction_id", transaction_id); 14 } 15 else//微信订单号不存在,才根据商户订单号去查单 16 { 17 data.SetValue("out_trade_no", out_trade_no); 18 } 19 WxPayData result = WxPayApi.OrderQuery(data);//提交订单查询请求给API,接收返回数据 20 Log.Info("OrderQuery", "OrderQuery process complete, result : " + result.ToXml()); 21 return result.ToPrintStr(); 22 }
可以通过这个微信订单号(transaction_id)来查询,也可以通过商户订单号(out_trade_no),所以,我们要合理利用这里面的技巧,上述模式二,我用的
out_trade_no 是一个随机字符串,我们可以把这个字符串记录好,放数据库还是放哪里,你自己喜欢,然后写一个ajsx长轮询来,定时查询这个商户订单号,看看有没有支付成功,来做支付确认。
o0孤独夜莺0o