微信支付教程系列之扫码支付

 目录

(一)微信公众号开发之VS远程调试
(二)微信公众号开发之基础梳理
(三)微信公众号开发之自动消息回复和自定义菜单
(四)微信公众号开发之网页受权获取用户基本信息
(五)微信公众号开发之网页中及时获取当前用户Openid及注意事项
(六)微信公众号开发之扫码支付
(七)微信公众号开发之公众号支付
(八)微信公众号开发之现金红包
(九)微信公众号开发之回复图文消息(被动)

微信支付教程系列之扫码支付

         今天,咱们来一块儿探讨一下这个微信扫码支付。何为扫码支付呢?这里面,扫的码就是二维码了,就是咱们常常扫一扫的那种二维码图片,例如,咱们本身添加好友的时候,能够经过输入对方的微信号,也能够扫一扫对方的二维码。扫码支付,做为,微信支付里面,不可或缺的一个功能,对商品的支付提供了极为方便的体验,用途也很是的多。例如咱们在地铁、公交站常见的那些自动售货机(不错,就是那种投硬币,就能够自动出货的那种机器)中都用到。之前,那种机器,只能经过投硬币或者纸币,可是,这里面也有必定的风险,例如:假币,盗窃(真有人把机器砸了,把机器里面的钱偷走的),因此,微信(支付宝)的扫码支付的出现,大大的减小了这方面的风险,近些年来,二维码的应用愈来愈广,甚至有些地方,直接用来自动售票(就是把起始点设定好,票价设定好,直接把二维码贴出来,让乘客自动扫相关的二维码,完成购票,上车的时候,只须要提供本身的支付凭证给乘车员验证便可),这样,不只绿色环保了,还大大的提升了售票的速度(去过大车站购票的人应该深有体验,排队买个票,好歹半个小时以上,内心也是万头草泥马在奔腾的)。
         
         咱就不扯远了,说回咱么今天要作的微信支付之扫码支付。微信官方的文档,这个扫码支付(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,代码以下:
      // GET: Home public ActionResult Index() { return View(); } 

 

 
            再添加一个View,代码以下:
复制代码
@{
    Layout = null;
}
<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>首页</title> </head> <body> <div> </div> </body> </html> 
复制代码

 

 
接下来,咱们先把官方的demo的一些咱们会用到的东西拷贝过来,其中包括如下几个文件夹,以下图:
 
 
就这个lib和business两个,把这两个文件夹,支付复制到我们的新项目中,而且包含在项目中,以下:
 
 
而后咱们再“从新生成”如下项目,或者快捷键:ctrl+shift+b,这时候,会提下以下错误:
 
 
这时候,咱们去添加引用,把 lib文件夹中的 LitJson.dll 添加上便可,以下图:
 
 
到这里,咱们就基本把官方的demo的环境给搭建好了,接下来,咱们就要开始编写代码了。
 
        首先,个人逻辑是,从前到后,就是从前端到后端。前端是显示二维码的地方,那么咱们就先给他一个div(本文使用到的是jquery的二维码生成插件,全名叫:jquery.qrcode.min.js,我会传到附件上),而后在页面加载完毕的时候,会请求后台,让他返回二维码字符串,而后再经过jquery的二维码生成插件,让他生成二维码并显示在前台,代码以下:
 
前端:
复制代码
@{
    Layout = null;
}
<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>首页</title> <link href="~/Scripts/jquery-easyui-1.4.5/themes/bootstrap/easyui.css" rel="stylesheet" /> <link href="~/Scripts/jquery-easyui-1.4.5/themes/mobile.css" rel="stylesheet" /> <link href="~/Scripts/jquery-easyui-1.4.5/themes/icon.css" rel="stylesheet" /> </head> <body> <p> 模式一:生成扫描支付模式 <br /> <div id="QRCode1"> </div> </p> <p> 模式二:生成直接支付url,支付url有效期为2小时 <br /> <div id="QRCode2"> </div> </p> <script src="~/Scripts/jquery-1.10.2.js"></script> <script src="~/Scripts/jquery-easyui-1.4.5/jquery.easyui.min.js"></script> <script src="~/Scripts/jquery-easyui-1.4.5/jquery.easyui.mobile.js"></script> <script src="~/Scripts/jquery-easyui-1.4.5/easyloader.js"></script> <script src="~/Scripts/jquery.qrcode.min.js"></script> <script type="text/javascript"> $(function () { fGetQRCode1(); }) function fGetQRCode1() { $.messager.progress({ title: "", msg: "正在生成二维码:模式一,请稍后..." }); $.ajax({ type: "post", url: "/Home/GetQRCode1", data: { time: new Date(), productId:7788 }, success: function (json) { $.messager.progress('close');//记得关闭 if (json.result) { $('#QRCode1').qrcode(json.str); //生成二维码  } else { $('#QRCode1').html("二维码生成失败"); } } }) } </script> </body> </html>
复制代码

 

后端:
复制代码
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using WxPayAPI; namespace WxPay.Controllers { public class HomeController : Controller { // GET: Home public ActionResult Index() { return View(); } /// <summary> /// 模式一 /// </summary> /// <returns></returns>  [HttpPost] public ActionResult GetQRCode1() { object objResult = ""; string strProductID = Request.Form["productId"]; string strQRCodeStr = GetPrePayUrl(strProductID); if (!string.IsNullOrWhiteSpace(strProductID)) { objResult = new { result = true, str = strQRCodeStr }; } else { objResult = new { result = false }; } return Json(objResult); } /** * 生成扫描支付模式一URL * @param productId 商品ID * @return 模式一URL */ public string GetPrePayUrl(string productId) { WxPayData data = new WxPayData(); data.SetValue("appid", WxPayConfig.APPID);//公众账号id data.SetValue("mch_id", WxPayConfig.MCHID);//商户号 data.SetValue("time_stamp", WxPayApi.GenerateTimeStamp());//时间戳 data.SetValue("nonce_str", WxPayApi.GenerateNonceStr());//随机字符串 data.SetValue("product_id", productId);//商品ID data.SetValue("sign", data.MakeSign());//签名 string str = ToUrlParams(data.GetValues());//转换为URL串 string url = "weixin://wxpay/bizpayurl?" + str; return url; } /** * 参数数组转换为url格式 * @param map 参数名与参数值的映射表 * @return URL字符串 */ private string ToUrlParams(SortedDictionary<string, object> map) { string buff = ""; foreach (KeyValuePair<string, object> pair in map) { buff += pair.Key + "=" + pair.Value + "&"; } buff = buff.Trim('&'); return buff; } } }
复制代码

 

这时候,模式一是否是感受就完成了?那么咱们如今试试,咱们浏览该页面,以下:
 
 
而后用微信扫一扫功能扫一下,发现提示以下:
 
 
这是什么鬼,是否是,你内心面是否是想知道为啥,那我来告诉你,这是为啥,这是由于,你尚未设置回调页面或者回调页面有问题,这个时候,咱们再新建一个Control,命名为:NativeNotifyController.cs,代码以下:
复制代码
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Web; using System.Web.Mvc; using WxPayAPI; namespace WxPay.Controllers { public class NativeNotifyController : Controller { // GET: NativeNotify public ActionResult Index() { string strData = ProcessNotify(); Response.Write(strData); return View(); } public string ProcessNotify() { WxPayData notifyData = GetNotifyData(); //检查openid和product_id是否返回 if (!notifyData.IsSet("openid") || !notifyData.IsSet("product_id")) { WxPayData res = new WxPayData(); res.SetValue("return_code", "FAIL"); res.SetValue("return_msg", "回调数据异常"); return res.ToXml(); } //调统一下单接口,得到下单结果 string openid = notifyData.GetValue("openid").ToString(); string product_id = notifyData.GetValue("product_id").ToString(); WxPayData unifiedOrderResult = new WxPayData(); try { unifiedOrderResult = UnifiedOrder(openid, product_id); } catch (Exception ex)//若在调统一下单接口时抛异常,当即返回结果给微信支付后台  { WxPayData res = new WxPayData(); res.SetValue("return_code", "FAIL"); res.SetValue("return_msg", "统一下单失败"); return res.ToXml(); } //若下单失败,则当即返回结果给微信支付后台 if (!unifiedOrderResult.IsSet("appid") || !unifiedOrderResult.IsSet("mch_id") || !unifiedOrderResult.IsSet("prepay_id")) { WxPayData res = new WxPayData(); res.SetValue("return_code", "FAIL"); res.SetValue("return_msg", "统一下单失败"); return res.ToXml(); } //统一下单成功,则返回成功结果给微信支付后台 WxPayData data = new WxPayData(); data.SetValue("return_code", "SUCCESS"); data.SetValue("return_msg", "OK"); data.SetValue("appid", WxPayConfig.APPID); data.SetValue("mch_id", WxPayConfig.MCHID); data.SetValue("nonce_str", WxPayApi.GenerateNonceStr()); data.SetValue("prepay_id", unifiedOrderResult.GetValue("prepay_id")); data.SetValue("result_code", "SUCCESS"); data.SetValue("err_code_des", "OK"); data.SetValue("sign", data.MakeSign()); return data.ToXml(); } /// <summary> /// 接收从微信支付后台发送过来的数据并验证签名 /// </summary> /// <returns>微信支付后台返回的数据</returns> public WxPayData GetNotifyData() { //接收从微信后台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); } return data; } private WxPayData UnifiedOrder(string openId, string productId) { //统一下单 WxPayData req = new WxPayData(); req.SetValue("body", "广东XXXX股份有限公司"); req.SetValue("attach", "附加信息,用于后台或者存入数据库,作本身的判断"); req.SetValue("out_trade_no", WxPayApi.GenerateOutTradeNo()); req.SetValue("total_fee", 1); req.SetValue("time_start", DateTime.Now.ToString("yyyyMMddHHmmss")); req.SetValue("time_expire", DateTime.Now.AddMinutes(10).ToString("yyyyMMddHHmmss")); req.SetValue("goods_tag", "商品的备忘,能够自定义"); req.SetValue("trade_type", "NATIVE"); req.SetValue("openid", openId); req.SetValue("product_id", productId); WxPayData result = WxPayApi.UnifiedOrder(req); return result; } } }
复制代码

 

 
 
记得,也要新建一个View,就是在Index那里,右键添加一个View,View的代码以下(你没眼花,就是空的,无论他):
复制代码
@{
    Layout = null;
}
<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>Index</title> </head> <body> <div> </div> </body> </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 ,而后代码以下:
复制代码
using LmxPublic.Log; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Web; using System.Web.Mvc; using WxPayAPI; namespace WxPay.Controllers { public class ResultNotifyController : Controller { // GET: ResultNotify public ActionResult Index() { string strData = ProcessNotify(); Response.Write(strData); return View(); } public string ProcessNotify() { WxPayData notifyData = GetNotifyData(); //检查支付结果中transaction_id是否存在 if (!notifyData.IsSet("transaction_id")) { //若transaction_id不存在,则当即返回结果给微信支付后台 WxPayData res = new WxPayData(); res.SetValue("return_code", "FAIL"); res.SetValue("return_msg", "支付结果中微信订单号不存在"); return res.ToXml(); } 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", "订单查询失败"); return res.ToXml(); } //查询订单成功 else { WxPayData res = new WxPayData(); res.SetValue("return_code", "SUCCESS"); res.SetValue("return_msg", "OK"); Log.Info(this.GetType().ToString(), "order query success : " + res.ToXml()); string strXml = res.ToXml(); FileLog.WriteLog(strXml); return res.ToXml();//若是咱们走到这一步了,那就表明,用户已经支付成功了,因此,该干吗干吗了。  } } /// <summary> /// 接收从微信支付后台发送过来的数据并验证签名 /// </summary> /// <returns>微信支付后台返回的数据</returns> public WxPayData GetNotifyData() { //接收从微信后台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(); Log.Info(this.GetType().ToString(), "Receive data from WeChat : " + builder.ToString()); //转换数据格式并验证签名 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(this.GetType().ToString(), "Sign check error : " + res.ToXml()); return res; } return data; } //查询订单 private 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; } } } }
复制代码

 

前台,对,也是要新建一个View,代码以下(没错,也是空的)
 
复制代码
@{
    Layout = null;
}
<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>Index</title> </head> <body> <div> </div> </body> </html>
复制代码

 

 
好,模式一就到这里了,呼呼。。。没想到啊,一个模式一,让我从上午写到下午,真心累。。。还有一个模式二呢。。。喝口水先,咱,接着来。
 
 
好,喝完水,接着干,下面是模式二:
 

模式二生成直接支付url,支付url有效期为2小时

 
因为有了上面模式一的详细说明,模式二,我就简单一点的来讲了,若是又不懂的,到群里来问我吧。
 
模式二,前端,增长一些代码,以下(完整的,包括模式一的代码了):
 
复制代码
@{
    Layout = null;
}
<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>首页</title> <link href="~/Scripts/jquery-easyui-1.4.5/themes/bootstrap/easyui.css" rel="stylesheet" /> <link href="~/Scripts/jquery-easyui-1.4.5/themes/mobile.css" rel="stylesheet" /> <link href="~/Scripts/jquery-easyui-1.4.5/themes/icon.css" rel="stylesheet" /> </head> <body> <p> 模式一:生成扫描支付模式 <br /> <div id="QRCode1"> </div> </p> <p> 模式二:生成直接支付url,支付url有效期为2小时 <br /> <div id="QRCode2"> </div> </p> <script src="~/Scripts/jquery-1.10.2.js"></script> <script src="~/Scripts/jquery-easyui-1.4.5/jquery.easyui.min.js"></script> <script src="~/Scripts/jquery-easyui-1.4.5/jquery.easyui.mobile.js"></script> <script src="~/Scripts/jquery-easyui-1.4.5/easyloader.js"></script> <script src="~/Scripts/jquery.qrcode.min.js"></script> <script type="text/javascript"> $(function () { fGetQRCode1(); }) function fGetQRCode1() { $.messager.progress({ title: "", msg: "正在生成二维码:模式一,请稍后..." }); $.ajax({ type: "post", url: "/Home/GetQRCode1", data: { time: new Date(), productId:7788 }, success: function (json) { $.messager.progress('close');//记得关闭 if (json.result) { $('#QRCode1').qrcode(json.str); //生成二维码  } else { $('#QRCode1').html("二维码生成失败"); } fGetQRCode2(); }, error: function (json) { $('#QRCode1').html("二维码生成失败"); fGetQRCode2(); } }) } function fGetQRCode2() { $.messager.progress({ title: "", msg: "正在生成二维码:模式二,请稍后..." }); $.ajax({ type: "post", url: "/Home/GetQRCode2", data: { time: new Date(), productId: 7788 }, success: function (json) { $.messager.progress('close');//记得关闭 if (json.result) { $('#QRCode2').qrcode(json.str); //生成二维码 } else { $('#QRCode2').html("二维码生成失败"); } }, error: function (json) { $('#QRCode2').html("二维码生成失败"); } }) } </script> </body> </html>
复制代码

 

 
后端:
 
复制代码
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using WxPayAPI; namespace WxPay.Controllers { public class HomeController : Controller { // GET: Home public ActionResult Index() { return View(); } /// <summary> /// 模式一 /// </summary> /// <returns></returns>  [HttpPost] public ActionResult GetQRCode1() { object objResult = ""; string strProductID = Request.Form["productId"]; string strQRCodeStr = GetPrePayUrl(strProductID); if (!string.IsNullOrWhiteSpace(strProductID)) { objResult = new { result = true, str = strQRCodeStr }; } else { objResult = new { result = false }; } return Json(objResult); } /// <summary> /// 模式二 /// </summary> /// <returns></returns>  [HttpPost] public ActionResult GetQRCode2() { object objResult = ""; string strProductID = Request.Form["productId"]; string strQRCodeStr = GetPayUrl(strProductID); if (!string.IsNullOrWhiteSpace(strProductID)) { objResult = new { result = true, str = strQRCodeStr }; } else { objResult = new { result = false }; } return Json(objResult); } /** * 生成扫描支付模式一URL * @param productId 商品ID * @return 模式一URL */ public string GetPrePayUrl(string productId) { WxPayData data = new WxPayData(); data.SetValue("appid", WxPayConfig.APPID);//公众账号id data.SetValue("mch_id", WxPayConfig.MCHID);//商户号 data.SetValue("time_stamp", WxPayApi.GenerateTimeStamp());//时间戳 data.SetValue("nonce_str", WxPayApi.GenerateNonceStr());//随机字符串 data.SetValue("product_id", productId);//商品ID data.SetValue("sign", data.MakeSign());//签名 string str = ToUrlParams(data.GetValues());//转换为URL串 string url = "weixin://wxpay/bizpayurl?" + str; return url; } /** * 参数数组转换为url格式 * @param map 参数名与参数值的映射表 * @return URL字符串 */ private string ToUrlParams(SortedDictionary<string, object> map) { string buff = ""; foreach (KeyValuePair<string, object> pair in map) { buff += pair.Key + "=" + pair.Value + "&"; } buff = buff.Trim('&'); return buff; } /** * 生成直接支付url,支付url有效期为2小时,模式二 * @param productId 商品ID * @return 模式二URL */ public string GetPayUrl(string productId) { WxPayData data = new WxPayData(); data.SetValue("body", "广东XXXX股份有限公司");//商品描述 data.SetValue("attach", "附加信息,用于后台或者存入数据库,作本身的判断");//附加数据 data.SetValue("out_trade_no", WxPayApi.GenerateOutTradeNo());//随机字符串 data.SetValue("total_fee", 1);//总金额 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();//得到统一下单接口返回的二维码连接 return url; } } }
复制代码

 

 特此更正,模式二也是有回调的,回调的接口设置在Config.cs,中,以下图:
 
 
原文地址:http://www.cnblogs.com/nangong/p/9fa625bfb92a986125f0ba72704c46ec.html
相关文章
相关标签/搜索