首先打開開發文檔:
微信公眾號開發者文檔:http://mp.weixin.qq.com/wiki/home/index.html
一、創建測試賬號
可以先申請一個開發者測試賬號
用自己微信掃描后即可獲得測試賬號:
就有了appId 和 appsecret了,微信號在右上角。
二、獲取access_token (這個access_token是通過appID 和 appsecret來生成的,只要是向微信服務器發送請求都需要帶上這個access_token。)
打開 微信公眾平台接口調試工具
程序生成access_token,解決2小時失效的問題,2小時候會重新生成一個。
access_token幫助類:

using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Xml.Linq; namespace YangYiEcormerce.WeChat.Web.Common { /// <summary> /// AccessToken幫助類 /// </summary> public class AccessTokenHelp { //填寫自己微信的秘鑰 private static string appId = System.Configuration.ConfigurationManager.AppSettings["WeChatAppId"]; private static string appSecret = System.Configuration.ConfigurationManager.AppSettings["WeChatAppSecret"]; private static DateTime GetAccessToken_Time; /// <summary> /// 過期時間為7200秒 /// </summary> private static int Expires_Period = 7200; /// <summary> /// /// </summary> private static string mAccessToken; /// <summary> /// /// </summary> public static string AccessToken { get { //如果為空,或者過期,需要重新獲取 if (string.IsNullOrEmpty(mAccessToken) || HasExpired()) { //獲取 mAccessToken = GetAccessToken(appId, appSecret); } return mAccessToken; } } /// <summary> /// /// </summary> /// <param name="appId"></param> /// <param name="appSecret"></param> /// <returns></returns> private static string GetAccessToken(string appId, string appSecret) { string url = string.Format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}", appId, appSecret); string result = HttpUtility.GetData(url); XDocument doc = CommonHelp.ParseJsonToXML(result, "root"); XElement root = doc.Root; if (root != null) { XElement access_token = root.Element("access_token"); if (access_token != null) { GetAccessToken_Time = DateTime.Now; if (root.Element("expires_in") != null) { Expires_Period = int.Parse(root.Element("expires_in").Value); } return access_token.Value; } else { GetAccessToken_Time = DateTime.MinValue; } } return null; } /// <summary> /// 判斷Access_token是否過期 /// </summary> /// <returns>bool</returns> private static bool HasExpired() { if (GetAccessToken_Time != null) { //過期時間,允許有一定的誤差,一分鍾。獲取時間消耗 if (DateTime.Now > GetAccessToken_Time.AddSeconds(Expires_Period).AddSeconds(-60)) { return true; } } return false; } } }
HttpUtility類:

/// <summary> ///Http幫助類 /// </summary> public class HttpUtility { /// <summary> /// 發送請求 /// </summary> /// <param name="url">Url地址</param> /// <param name="data">數據</param> public static string SendHttpRequest(string url, string data) { return SendPostHttpRequest(url, "application/x-www-form-urlencoded", data); } /// <summary> /// /// </summary> /// <param name="url"></param> /// <returns></returns> public static string GetData(string url) { return SendGetHttpRequest(url, "application/x-www-form-urlencoded"); } /// <summary> /// 發送請求 /// </summary> /// <param name="url">Url地址</param> /// <param name="method">方法(post或get)</param> /// <param name="method">數據類型</param> /// <param name="requestData">數據</param> public static string SendPostHttpRequest(string url, string contentType, string requestData) { WebRequest request = (WebRequest)HttpWebRequest.Create(url); request.Method = "POST"; byte[] postBytes = null; request.ContentType = contentType; postBytes = Encoding.UTF8.GetBytes(requestData); request.ContentLength = postBytes.Length; using (Stream outstream = request.GetRequestStream()) { outstream.Write(postBytes, 0, postBytes.Length); } string result = string.Empty; using (WebResponse response = request.GetResponse()) { if (response != null) { using (Stream stream = response.GetResponseStream()) { using (StreamReader reader = new StreamReader(stream, Encoding.UTF8)) { result = reader.ReadToEnd(); } } } } return result; } /// <summary> /// 發送請求 /// </summary> /// <param name="url">Url地址</param> /// <param name="method">方法(post或get)</param> /// <param name="method">數據類型</param> /// <param name="requestData">數據</param> public static string SendGetHttpRequest(string url, string contentType) { WebRequest request = (WebRequest)HttpWebRequest.Create(url); request.Method = "GET"; request.ContentType = contentType; string result = string.Empty; using (WebResponse response = request.GetResponse()) { if (response != null) { using (Stream stream = response.GetResponseStream()) { using (StreamReader reader = new StreamReader(stream, Encoding.UTF8)) { result = reader.ReadToEnd(); } } } } return result; } }
ParseJsonToXML方法: (需要引入Newtonsoft.Json.dll)

/// <summary> /// 將Json轉化為XML /// </summary> /// <param name="json"></param> /// <param name="rootName"></param> /// <returns></returns> public static XDocument ParseJsonToXML(string json, string rootName) { return JsonConvert.DeserializeXNode(json, rootName); }
程序調用獲取access_token:
string access_token = Common.AccessTokenHelp.AccessToken;
三、創建菜單:
還是使用調試工具來創建:
JSON格式菜單內容:下面創建的都是一級菜單,更多詳細的菜單創建,參考說明文檔>>
{ "button": [ { "name": "商城", "type": "view", "url": "http://shop.com" //點擊菜單訪問網址 }, { "name": "防偽掃描", "type": "scancode_push", "key": "FangweiScan" //點擊調用微信二維碼掃描,是網址直接訪問,是文本則顯示文本內容 }, { "name": "訂單查詢", "type": "click", "key": "OrderQuery" //點擊出發click事件,向我們配置的API地址進行請求 } ] }
請求成功后,取消微信號關注並退出微信,重新進入關注,應該就可以看到添加好的文檔了。
想刪除重新創建菜單,調用菜單刪除就可以了。
效果:
四、開發接口、處理文本和事件 (當用戶使用微信發送消息或者單擊菜單出發事件,就會想配置的API發送請求,API進行處理響應) 消息回復參考文檔>>
接口是一個一般處理程序:
using System; using System.Collections.Generic; using System.Web; using System.IO; using System.Text; using System.Web.Security; using System.Xml; namespace weixin_api { /// <summary> /// interfaceTest 的摘要說明 /// </summary> public class interfaceTest : IHttpHandler { public void ProcessRequest(HttpContext param_context) { string postString = string.Empty; //用戶發送消息或點擊等事件一般都是POST過來,微信服務器向接口發送POST請求,根據請求我們進行處理反饋 if (HttpContext.Current.Request.HttpMethod.ToUpper() == "POST") { using (Stream stream = HttpContext.Current.Request.InputStream) { Byte[] postBytes = new Byte[stream.Length]; stream.Read(postBytes, 0, (Int32)stream.Length); postString = Encoding.UTF8.GetString(postBytes); Handle(postString); } } else { //第一次配置接口地址的時候,微信服務器會向接口發送一個GET請求來驗證你的接口地址 InterfaceTest(); } } /// <summary> /// 處理信息並應答 /// </summary> private void Handle(string postStr) { messageHelp help = new messageHelp(); string responseContent = help.ReturnMessage(postStr); HttpContext.Current.Response.ContentEncoding = Encoding.UTF8; HttpContext.Current.Response.Write(responseContent); } //成為開發者url測試,返回echoStr public void InterfaceTest() { string token = "token"; if (string.IsNullOrEmpty(token)) { return; } //微信服務器會將下面幾個參數發送到接口,接口這邊返回接收到的echoStr就說明驗證通過, //主要為了防止別人盜用你的接口,我這邊沒做邏輯判斷直接返回接收到的echoStr來通過驗證 string echoString = HttpContext.Current.Request.QueryString["echoStr"]; string signature = HttpContext.Current.Request.QueryString["signature"]; string timestamp = HttpContext.Current.Request.QueryString["timestamp"]; string nonce = HttpContext.Current.Request.QueryString["nonce"]; if (!string.IsNullOrEmpty(echoString)) { HttpContext.Current.Response.Write(echoString); HttpContext.Current.Response.End(); } } public bool IsReusable { get { return false; } } } }
接受/發送消息幫助類

using System; using System.Collections.Generic; using System.Web; using System.IO; using System.Text; using System.Web.Security; using System.Xml; namespace weixin_api { /// <summary> /// 接受/發送消息幫助類 /// </summary> public class messageHelp { //返回消息 public string ReturnMessage(string postStr) { string responseContent = ""; XmlDocument xmldoc = new XmlDocument(); xmldoc.Load(new System.IO.MemoryStream(System.Text.Encoding.GetEncoding("GB2312").GetBytes(postStr))); XmlNode MsgType = xmldoc.SelectSingleNode("/xml/MsgType"); if (MsgType != null) { switch (MsgType.InnerText) { case "event": responseContent = EventHandle(xmldoc);//事件處理 break; case "text": responseContent = TextHandle(xmldoc);//接受文本消息處理 break; default: break; } } return responseContent; } //事件 public string EventHandle(XmlDocument xmldoc) { string responseContent = ""; XmlNode Event = xmldoc.SelectSingleNode("/xml/Event"); XmlNode EventKey = xmldoc.SelectSingleNode("/xml/EventKey"); XmlNode ToUserName = xmldoc.SelectSingleNode("/xml/ToUserName"); XmlNode FromUserName = xmldoc.SelectSingleNode("/xml/FromUserName"); XmlNode ScanResult = xmldoc.SelectSingleNode("/xml/ScanCodeInfo/ScanResult"); if (Event != null) { //菜單單擊事件 if (Event.InnerText.Equals("CLICK")) { if (EventKey.InnerText.Equals("OrderQuery"))//點擊訂單查詢 這個OrderQuery就是菜單里面的key { responseContent = string.Format(ReplyType.Message_Text, FromUserName.InnerText, ToUserName.InnerText, DateTime.Now.Ticks, "正在開發中,敬請期待!"); } } else if (Event.InnerText.Equals("scancode_waitmsg")) //掃碼推事件且彈出“消息接收中”提示框的事件推送 { if (EventKey.InnerText.Equals("FangweiScan")) //點擊防偽掃描 { //....處理返回邏輯 } } } return responseContent; } //接受文本消息 public string TextHandle(XmlDocument xmldoc) { string responseContent = ""; XmlNode ToUserName = xmldoc.SelectSingleNode("/xml/ToUserName"); XmlNode FromUserName = xmldoc.SelectSingleNode("/xml/FromUserName"); XmlNode Content = xmldoc.SelectSingleNode("/xml/Content"); if (Content != null) { //回復文本信息 responseContent = string.Format(ReplyType.Message_Text, FromUserName.InnerText, ToUserName.InnerText, DateTime.Now.Ticks, "歡迎使用微信公共賬號,您輸入的內容為:" + Content.InnerText); } return responseContent; } //寫入日志 public void WriteLog(string text) { StreamWriter sw = new StreamWriter(HttpContext.Current.Server.MapPath(".") + "\\log.txt", true); sw.WriteLine(text); sw.Close(); } } //回復類型 public class ReplyType { /// <summary> /// 普通文本消息 /// </summary> public static string Message_Text { get { return @"<xml> <ToUserName><![CDATA[{0}]]></ToUserName> <FromUserName><![CDATA[{1}]]></FromUserName> <CreateTime>{2}</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[{3}]]></Content> </xml>"; } } /// <summary> /// 圖文消息主體 /// </summary> public static string Message_News_Main { get { return @"<xml> <ToUserName><![CDATA[{0}]]></ToUserName> <FromUserName><![CDATA[{1}]]></FromUserName> <CreateTime>{2}</CreateTime> <MsgType><![CDATA[news]]></MsgType> <ArticleCount>{3}</ArticleCount> <Articles> {4} </Articles> </xml> "; } } /// <summary> /// 圖文消息項 /// </summary> public static string Message_News_Item { get { return @"<item> <Title><![CDATA[{0}]]></Title> <Description><![CDATA[{1}]]></Description> <PicUrl><![CDATA[{2}]]></PicUrl> <Url><![CDATA[{3}]]></Url> </item>"; } } } }
五、發送模板消息 (官方說明文檔>>)
在公眾平台創建模板:
發送模板消息幫助類:

public class TemplateMessage { static JavaScriptSerializer Jss = new JavaScriptSerializer(); /// <summary> /// 給指定的用戶發送模板消息 /// </summary> /// <param name="openId">用戶標識openid</param> /// <param name="templateId">對應的模板id</param> /// <param name="data">對應模板的參數</param> /// <param name="url">點擊對應消息彈出的地址</param> /// <param name="topcolor">顏色</param> /// <returns>返回json數據包</returns> public static string SendTemplate(string openId, string templateId, object data, string url, string topcolor = "#173177") { string access_token = AccessTokenHelp.AccessToken; var msgData = new { touser = openId, template_id = templateId, topcolor = topcolor, url = url, data = data }; string postData = Jss.Serialize(msgData); return HttpUtility.SendHttpRequest("https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + access_token, postData); } /// <summary> /// 給指定的用戶發送模板消息 /// </summary> /// <param name="openId">用戶標識openid</param> /// <param name="templateId">對應的模板id</param> /// <param name="data">對應模板的參數</param> /// <param name="topcolor">顏色</param> /// <returns>返回json數據包</returns> public static string SendTemplate(string openId, string templateId, object data, string topcolor = "#173177") { string access_token = AccessTokenHelp.AccessToken; var msgData = new { touser = openId, template_id = templateId, topcolor = topcolor, data = data }; string postData = Jss.Serialize(msgData); return HttpUtility.SendHttpRequest("https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + access_token, postData); } }
發送模板消息調用:

var data = new { first = new { value = "恭喜你購買成功", color = "#173177" } }; string templateId = "ibrQIRAaFkbeRNKpj9SbuJ0Rgs6q1ZTpsNkdf31lZwM"; TemplateMessage.SendTemplate(FromUserName.InnerText, templateId, data);
效果:
六、接口開發完成,配置接口信息
配置驗證通過后,用戶發消息或事件,接口拿到信息就可以做出處理反饋了。
接口URL驗證:

public void ProcessRequest(HttpContext context) { string postString = string.Empty; if (HttpContext.Current.Request.HttpMethod.ToUpper() == "POST") { using (Stream stream = HttpContext.Current.Request.InputStream) { Byte[] postBytes = new Byte[stream.Length]; stream.Read(postBytes, 0, (Int32)stream.Length); postString = Encoding.UTF8.GetString(postBytes); Handle(postString); } } else { //驗證簽名 if (CheckSignature()) { HttpContext.Current.Response.Write(HttpContext.Current.Request.QueryString["echoStr"]); } else { HttpContext.Current.Response.Write("error"); } } }
驗證簽名方法:

/// <summary> /// 檢查簽名 /// </summary> /// <param name="request"></param> /// <returns></returns> private bool CheckSignature() { string token = System.Configuration.ConfigurationManager.AppSettings["WeChatToken"]; string signature = HttpContext.Current.Request.QueryString["signature"]; string timestamp = HttpContext.Current.Request.QueryString["timestamp"]; string nonce = HttpContext.Current.Request.QueryString["nonce"]; List<string> list = new List<string>(); list.Add(token); list.Add(timestamp); list.Add(nonce); //排序 list.Sort(); //拼串 string input = string.Empty; foreach (var item in list) { input += item; } //加密 string new_signature = CommonHelp.SHA1Encrypt(input); //驗證 if (new_signature == signature) { return true; } else { return false; } }
簽名中的SHA1Encrypt加密算法:

/// <summary> /// SHA1加密 /// </summary> /// <param name="intput">輸入字符串</param> /// <returns>加密后的字符串</returns> public static string SHA1Encrypt(string intput) { byte[] StrRes = Encoding.Default.GetBytes(intput); HashAlgorithm mySHA = new SHA1CryptoServiceProvider(); StrRes = mySHA.ComputeHash(StrRes); StringBuilder EnText = new StringBuilder(); foreach (byte Byte in StrRes) { EnText.AppendFormat("{0:x2}", Byte); } return EnText.ToString(); }
此時,如果填寫的Token和接口中的Token不一致就會驗證失敗,就可以防止其他人盜用你的接口了。
七、源碼下載
八、相關學習資源推薦:
2.C#開發微信門戶及應用 (比較全,winform實現,但沒完整源碼)
時間匆忙,寫的不是很詳細,有時間再慢慢完善。