首页 > 代码库 > C# MVC 微信支付之微信模板消息推送

C# MVC 微信支付之微信模板消息推送

微信支付之微信模板消息推送
 
        
        今天我要跟大家分享的是“模板消息”的推送,这玩意呢,你说用途嘛,那还是真真的牛逼呐。原因在哪?就是因为它是依赖微信生存的呀,所以他能不牛逼吗?现在的社会,人多多少少都有或轻或重的“强迫症”。就是,看到有未读消息,都要去看一下。特别是现在的微信,大部分可以几个小时不看手机QQ有没有新消息来,但是这大部分人绝对做不到一个小时不看微信有没有消息来。现在的微信,真特么是神一样的存在,几乎人人手机上都会有微信。而且,如果你的公众号是服务号的话,这个推送消息会显示在首页列表中(如果有新的消息,会把公众号显示在最顶端,并提示有新消息),不像订阅好,会放在一个小盒子里面。成熟的例子也有许多,例如一些商业银行的刷卡消费提醒这些。好,这方面是他微信的优势,就是,you can‘t miss it.另外一个优势就是:只要你给他推送了,他的到达率是非常的高,几乎可以保证,100%送达(鉴于天朝的相关法规,这种100%的东西,是没人敢说的,所以,一般都和谐的说99.97%的样子)。所以,以这样的送达率,秒杀传统的短信通知。更有甚者,就是,他的推送,完全免费,完全免费,完全免费!而且,速度极快,速度极快,速度极快。。。好了,再说下去,估计等下你们以为我是腾讯的员工了。好了,这些我就不多说了,下面,我们进入主题吧。
 
        首先,新建一个MVC的项目,如下图(在第二个页面里面,身份认证,改为不需要身份验证):
 
技术分享
 
        然后我们去把官方demo(demo地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1)中的business和lib拷贝到我们的项目中,并新建一个文件夹WxLib,放到里面去,如下图:
 
技术分享
 
        然后我们再“重新生成”以下项目,或者快捷键:ctrl+shift+b,这时候,会提下如下错误:
 
技术分享
 
这时候,我们去添加引用,把WxLib/lib文件夹中的LitJson.dll 添加上即可,如下图:
 
技术分享
 
 
到这里,我们就基本把官方的demo的环境给搭建好了,接下来,我们就要开始编写代码了。
 
首先,我们来看看官方文档,怎么介绍这个模板消息的,查看步骤(这个是需要登录公众平台才能看到的),如下图:
 
技术分享
技术分享
 
这个是对外的API链接:http://mp.weixin.qq.com/wiki/17/304c1885ea66dbedf7dc170d84999a9d.html
 
        通过API文档,我们得知,我们推送模板消息,有三个必须要获取到的参数,他们分别是:ACCESS_TOKEN和OPENID,以及template_id,
ACCESS_TOKEN和OPENID,我们前面的额公众号支付已经演示过如何获取,如果忘了,可以回头看看(微信支付教程系列之公众号支付),
        
        下面,我来讲讲怎么获取这个template_id。template_id是模板消息的ID,我们推送消息,不像我们发短信一样,可以随意编辑内容,想说啥就说啥,必须要符合模板消息的规则。例如,我们这次选择一个“帐户资金变动提醒”,添加操作如下:
技术分享
技术分享
技术分享
点击”添加“之后,就会在”我的模板”里面出现,如下图:
技术分享
 
 
这个“lypG1jYyOEfYsr*********jOK7-LhEwpPeVNHHxemSI”就是我们的模板ID了,也就是:template_id。
 
        好,现在,我们知道ACCESS_TOKEN和OPENID以及template_id了,理论上,我们就有了可以发送推送消息的前提了。下面,我们来进入代码部分,如下:
 
 
        首先,新建一个HomeController.cs,代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace 微信支付之模板消息推送.Controllers
{
    public class HomeController : Controller
    {
        // GET: Home
        public ActionResult Index()
        {
            return View();
        }
    }
} 

 

 
 
        然后添加一个View,代码如下:
         技术分享
@{
    Layout = null;
}
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>
    <div> 
    </div>
</body>
</html>

 

 
 
 
 
 
        再说多几句,我们这次选的是一个“帐户资金变动提醒”,他的传参类型如下:
技术分享
 
在此之前:由于我们这里面的文字,还可以设定颜色,我们从这里(模板里面看不到,但是可以从官方的demo)可以看出来:
技术分享
但是,我们本次用的模板不是这个,是“帐户资金变动提醒”,但是道理相同,所以,我们也要新建一个类,文件名字就叫做:DataFontStyle.cs吧,代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace 微信支付之模板消息推送.Models
{
    public class DataFontStyle
    {
        public string value { get; set; }
        public string color { get; set; }
    }
}  

 

再分析他的参数,分别有:first,date,adCharge,type,cashBalance以及remark,所以,我们给他新建一个Model,到时候会用到的,新建的Model的文件名字就叫做:CashModel.cs,代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
 
namespace 微信支付之模板消息推送.Models
{
    /// <summary>
    /// 帐户资金变动提醒Model
    /// </summary>
    public class CashModel
    {
        public DataFontStyle first { get; set; }
        public DataFontStyle date { get; set; }
        public DataFontStyle adCharge { get; set; }
        public DataFontStyle type { get; set; }
        public DataFontStyle cashBalance { get; set; }
        public DataFontStyle remark { get; set; }
    }
}  
 

 

 
 
 
 
此外,还有最后一个,那就是这个推送的Model,也要新建一个类,文件名,我们就叫做PushMess.cs吧,代码如下:
 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace 微信支付之模板消息推送.Models
{
    public class PushMessage
    {
        public string touser { get; set; }
        public string template_id { get; set; }
        public string url { get; set; }
        public string topcolor { get; set; }
        /// <summary>
        /// 注意,这里是CashModel,你如果要通用,有多个推送模板要用,那你就用object
        /// </summary>
        public CashModel data { get; set; }
   
    }
}  

 

漏了一个类,现在补回来,我们命名为:WxResult.cs,代码如下:
 
   public class WxResult
    {
        public int? errcode { get; set; }
        public string errmsg { get; set; }
        public int? msgid { get; set; }
    }  

 

 
 
文件物理路径逻辑如下,检查一下,看看有无偏差,如下图:
技术分享
 
        说明一下:一些推送必须的代码,我就直接贴出(例如后去Access_token和推送代码),不再说明了,因为在前面几篇都有说明,如果看不懂,请翻阅前面的教程。
 
        现在,我们来编辑一下前后端的代码,前端的代码的逻辑,还是跟往常一样,我代码尽量精简,本次,那个推送,我只传一个参数到后台,那就是first,其他的,都是在后台固定,实际项目中应用,到时候,你们再行修改即可,前台代码:
 
 
 
@{
    Layout = null;
}
 
<!DOCTYPE html>
 
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>身份认证</title>
    <link href="http://www.mamicode.com/~/Scripts/jquery-easyui-1.4.5/themes/bootstrap/easyui.css" rel="stylesheet" />
    <link href="http://www.mamicode.com/~/Scripts/jquery-easyui-1.4.5/themes/mobile.css" rel="stylesheet" />
    <link href="http://www.mamicode.com/~/Scripts/jquery-easyui-1.4.5/themes/icon.css" rel="stylesheet" />
    <style type="text/css">
 
        .line {
            width: 100%;
            float: left;
            height: auto;
            text-align: center;
            margin-top: 15pt;
            font-size: x-large;
        }
 
        .lineText {
            width: 100%;
            float: left;
            height: auto;
            text-indent: 5%;
            text-align: left;
            font-size: x-large;
            margin: 0;
        }
 
        a {
            text-decoration: none;
            color: white;
        }
 
        .input {
            height: 30pt;
            width: 90%;
            font-size: x-large;
            border-radius: 10px;
            margin: 0;
            padding: 0;
        }
 
        .btn {
            width: 90%;
            height: 35pt;
            font-size: x-large;
            background-color: green;
            color: white;
            border: none;
            border-radius: 10px;
        }
    </style>
</head>
<body>
    <div style="width: 100%; text-align: center;">
        <form id="form1">
            <div class="line">
                <div class="lineText">*接收者openid:</div>
            </div>
            <div class="line">
                <input type="text" id="openid" name="openid" class="input" />
            </div>
            <div class="line">
                <div class="lineText">*first.DATA:</div>
            </div>
            <div class="line">
                <input type="text" id="first" name="first" class="input" />
            </div>
        </form>
 
    </div>
    <div class="line">
        <input type="button" id="btnSave" class="btn" value="http://www.mamicode.com/推送" onclick="fPush()" />
    </div>
    <script src="http://www.mamicode.com/~/Scripts/jquery-easyui-1.4.5/jquery.min.js"></script>
    <script src="http://www.mamicode.com/~/Scripts/jquery-easyui-1.4.5/jquery.easyui.min.js"></script>
    <script src="http://www.mamicode.com/~/Scripts/jquery-easyui-1.4.5/jquery.easyui.mobile.js"></script>
    <script src="http://www.mamicode.com/~/Scripts/jquery-easyui-1.4.5/easyloader.js"></script>
    <script type="text/javascript">
        $(function () {
            var vCode = getQueryString("code");
            if (vCode != "" && vCode != null) {
                $.ajax({
                    type: ‘post‘,
                    data: {
                        code: vCode
                    },
                    url: ‘/Home/getWxInfo‘,
                    success: function (sjson) {
                        $.messager.show({
                            title: ‘提示‘,
                            msg: ‘欢迎您的到来(看到这个提示,代表已经成功获取openid和access_token了)。‘
                        });
                        $("#openid").val(sjson.openid);
                    }
                })
            }
            else {
                $.ajax({
                    type: ‘post‘,
                    url: ‘/Home/getCode‘,
                    success: function (sjson) {
                        location.href = http://www.mamicode.com/sjson;"(^|&)" + name + "=([^&]*)(&|$)", "i");
            var r = window.location.search.substr(1).match(reg);
            if (r != null) return unescape(r[2]); return null;
        }
 
        //推送
        function fPush() {
            var vTtile = $("#first").val();
            $.ajax({
                type: ‘post‘,
                data: {
                    first: vTtile
                },
                url: ‘/Home/Push‘,
                success: function (sjson) {
                    if (sjson.result) {
                        $.messager.alert("提示", sjson.msg, ‘info‘);
                    }
                    else {
                        $.messager.alert("提示", sjson.msg, ‘warning‘);
                    }
                }
            })
        }
    </script>
</body>
</html>

 

 
 
后台代码:
 
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Web;
using System.Web.Mvc;
using System.Web.Script.Serialization;
using WxPayAPI;
using 微信支付之模板消息推送.Models;
 
namespace 微信支付之模板消息推送.Controllers
{
    public class HomeController : Controller
    {
        JsApiPay jsApiPay = new JsApiPay();
 
        JavaScriptSerializer JsonHelper = new JavaScriptSerializer();
        /// <summary>
        /// 最后更新Access_token的时间
        /// </summary>
        public static DateTime dtAccess_token;
        /// <summary>
        /// Access_token的值
        /// </summary>
        public static string strAccess_token;
        // GET: Home
        public ActionResult Index()
        {
            if (Session["openid"] == null)
            {
                try
                {
                    //调用【网页授权获取用户信息】接口获取用户的openid和access_token
                    GetOpenidAndAccessToken();
 
                }
                catch (Exception ex)
                {
                    //Response.Write(ex.ToString());
                    //throw;
                }
            }
            return View();
        }
 
        /**
        * 
        * 网页授权获取用户基本信息的全部过程
        * 详情请参看网页授权获取用户基本信息:http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html
        * 第一步:利用url跳转获取code
        * 第二步:利用code去获取openid和access_token
        * 
        */
        public void GetOpenidAndAccessToken()
        {
            if (Session["code"] != null)
            {
                //获取code码,以获取openid和access_token
                string code = Session["code"].ToString();
                Log.Debug(this.GetType().ToString(), "Get code : " + code);
                jsApiPay.GetOpenidAndAccessTokenFromCode(code);
            }
            else
            {
                //构造网页授权获取code的URL
                string host = Request.Url.Host;
                string path = Request.Path;
                string redirect_uri = HttpUtility.UrlEncode("http://" + host + path);
                WxPayData data = http://www.mamicode.com/new WxPayData();"appid", WxPayConfig.APPID);
                data.SetValue("redirect_uri", redirect_uri);
                data.SetValue("response_type", "code");
                data.SetValue("scope", "snsapi_base");
                data.SetValue("state", "STATE" + "#wechat_redirect");
                string url = "https://open.weixin.qq.com/connect/oauth2/authorize?" + data.ToUrl();
                Log.Debug(this.GetType().ToString(), "Will Redirect to URL : " + url);
                Session["url"] = url;
            }
        }
 
 
        /// <summary>
        /// 获取code
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        public ActionResult getCode()
        {
            object objResult = "";
            if (Session["url"] != null)
            {
                objResult = Session["url"].ToString();
            }
            else
            {
                objResult = "url为空。";
            }
            return Json(objResult);
        }
 
        /// <summary>
        /// 通过code换取网页授权access_token和openid的返回数据
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        public ActionResult getWxInfo()
        {
            object objResult = "";
            string strCode = Request.Form["code"];
            string strAccess_Token = "";
            string strOpenid = "";
            if (Session["access_token"] == null || Session["openid"] == null)
            {
                jsApiPay.GetOpenidAndAccessTokenFromCode(strCode);
                strAccess_Token = Session["access_token"].ToString();
                strOpenid = Session["openid"].ToString();
            }
            else
            {
                strAccess_Token = Session["access_token"].ToString();
                strOpenid = Session["openid"].ToString();
            }
            objResult = new { openid = strOpenid, access_token = strAccess_Token };
            return Json(objResult);
        }
 
        /// <summary>
        /// 推送
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        public ActionResult Push()
        {
            object objResult = "";
            string strFirst = Request.Form["first"];
            string strMsg = "";
            bool bResult = false;
            //这个是推送消息的类
            PushMessage aPushMessage = new PushMessage()
            {
                template_id = "lypG1jYyOEfY********OK7-LhEwpPeVNHHxemSI",//模板ID
                //data=http://www.mamicode.com/,//暂时不赋值"#FF0000",//头部颜色
                touser = Session["openid"].ToString(),//用户的Openid
                url = "http://www.baidu.com"//用途是当用户点击推送消息的时候,会进入这个页面,具体用途,自己拓展
            };
 
            //构造要推送的内容
            CashModel aCachData = http://www.mamicode.com/new CashModel()"#589E63",
                    value = "http://www.mamicode.com/对应变动金额"
                },
                cashBalance = new DataFontStyle()
                {
                    color = "#589E63",
                    value = "http://www.mamicode.com/对应帐户余额"
                },
                date = new DataFontStyle()
                {
                    color = "#589E63",
                    value = http://www.mamicode.com/DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")
                },
                first = new DataFontStyle()
                {
                    color = "#589E63",
                    value = http://www.mamicode.com/strFirst"#589E63",
                    value = "http://www.mamicode.com/对应:点击“查看详情“立即查阅您的帐户财务记录。"
                },
                type = new DataFontStyle()
                {
                    color = "#589E63",
                    value = "http://www.mamicode.com/对应“现金”"
                }
            };
            //这时候,把要推送的内容,赋值给push,这样,我们要推送的内容就完成了。
            aPushMessage.data = http://www.mamicode.com/aCachData;"https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + GetLatestAccess_token();
            
            string strJsonData = http://www.mamicode.com/JsonHelper.Serialize(aPushMessage);"通讯失败,请重试。";
            }
 
            objResult = new { result = bResult, msg = strMsg };
            return Json(objResult);
        }
 
 
 
        /// <summary>
        /// HttpPost
        /// </summary>
        /// <param name="Url"></param>
        /// <param name="postDataStr"></param>
        /// <returns></returns>
        public static string HttpPost(string Url, string postDataStr)
        {
            byte[] postData = http://www.mamicode.com/Encoding.UTF8.GetBytes(postDataStr);//编码,尤其是汉字,事先要看下抓取网页的编码方式   "Content-Type", "application/x-www-form-urlencoded");//采取POST方式必须加的header,如果改为GET方式的话就去掉这句话即可  
            byte[] responseData = http://www.mamicode.com/webClient.UploadData(Url,"POST", postData);//得到返回字符流             
            string srcString = Encoding.UTF8.GetString(responseData);//解码 
            return srcString;
           
        }
 
 
        /// <summary>
        /// 返回最新的Access_token
        /// </summary>
        /// <returns></returns>
        public string GetLatestAccess_token()
        {
            if (dtAccess_token == null || dtAccess_token <= DateTime.Now.AddHours(-1) || string.IsNullOrWhiteSpace(strAccess_token))
            {
                string strUrl = string.Format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}", WxPayConfig.APPID, WxPayConfig.APPSECRET);
                string strAccess_tokenData = http://www.mamicode.com/HttpGet(strUrl,"");
                ModelForAccess_token aToken = JsonHelper.Deserialize<ModelForAccess_token>(strAccess_tokenData);
                dtAccess_token = DateTime.Now;
                strAccess_token = aToken.access_token;
                return strAccess_token;
            }
            else
            {
                return strAccess_token;
            }
        }
 
        /// <summary>
        /// WebGet
        /// </summary>
        /// <param name="Url"></param>
        /// <param name="postDataStr"></param>
        /// <returns></returns>
        public static string HttpGet(string Url, string postDataStr)
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url + (postDataStr == "" ? "" : "?") + postDataStr);
            request.Method = "GET";
            request.ContentType = "text/html;charset=UTF-8";
 
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            Stream myResponseStream = response.GetResponseStream();
            StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.GetEncoding("utf-8"));
            string retString = myStreamReader.ReadToEnd();
            myStreamReader.Close();
            myResponseStream.Close();
 
            return retString;
        }
 
        public class ModelForAccess_token
        {
            public string access_token { get; set; }
            public int? expires_in { get; set; }
        }
    }
} 

 

 
 
代码已完整提供,如果有缺漏的,请在评论中指出或者进群来告诉我。如果代码提供正常,应该就能推送到的了,目前这个代码,只能给自己推送。实际应用中,可以通过这个页面,然后绑定用户,这样,我们就可以通过用户名字,给他们的openid推送消息。下面贴出推送的截图
 
技术分享
 
 
对了,忘记了一点,这个要发布到IIS上,然后你发布的域名,要在公众平台里面,那个获取用户权限(前面博文提及到)的网址里面,改成你发布的网址,例如:push.lmx.ren ,否则是获取不到用户信息的。
 

C# MVC 微信支付之微信模板消息推送