C# MVC 微信支付教程系列之扫码支付

来源:互联网 发布:ios9.3.3数据精灵 编辑:程序博客网 时间:2024/05/19 05:30
微信官方的文档,这个扫码支付(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;        }    }}
复制代码

 

 特此更正,感谢“ abc54288”的指出,模式二也是有回调的,回调的接口设置在Config.cs,中,如下图:
 

原文链接:http://www.cnblogs.com/nangong/p/9fa625bfb92a986125f0ba72704c46ec.html
作者:南宫萧尘  
E-mail:314791147@qq.com
QQ:314791147
日期:2016-05-28

支付宝接口开发:http://www.cnblogs.com/webapi/p/6020393.html

原创粉丝点击