一.前言 为了保证用户不受到骚扰,在开发者出现需要主动提醒、通知用户时,才允许开发者在公众平台网站中模板消息库中选择模板,选择后获得模板ID,再根据模板ID向用户主动推送提醒、通知消息。这个意思也就是,我们作为微信商户,不能主动的给用户推送消息,如果这个功能完全开放,那么用户有可能会受到大量的垃圾信息,为了做一个很好的控制,微信那边给我们开放了一个模板消息,通过模板消息我们可以友好的给用户发送一些相关的消息提醒。
二.开发前的准备 1.0模板消息官方文档地址
2.0查看我们的模板消息是否申请成功。申请成功后你可以看到如下图片:
三.开始编码 一般我们在客户支付成功订单以后,会有一个支付的跳转页面,在我们前面的代码中也有相应的页面,代码如下:
<script type="text/javascript">//调用微信JS api 支付function jsApiCall()
{
WeixinJSBridge.invoke('getBrandWCPayRequest',<%=wxJsApiParam%>,//josn串function (res)
{if(res.err_msg =="get_brand_wcpay_request:ok")
{varOrderId=$("#OrderId").val();varorderPRoductName=$("#orderProductName").val();varorderMoneySum=$("#orderMoneySum").val();
window.location.href="http://www.baidu.com/PaySkip.aspx?OrderId="+OrderId+"&orderMoneySum="+orderMoneySum+"&orderProductName="+orderProductName;//支付成功后的跳转页面}else{
WeixinJSBridge.call('closeWindow');
}
}
);
}
function callpay()
{if(typeofWeixinJSBridge =="undefined")
{if(document.addEventListener)
{
document.addEventListener('WeixinJSBridgeReady', jsApiCall,false);
}elseif(document.attachEvent)
{
document.attachEvent('WeixinJSBridgeReady', jsApiCall);
document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
}
}else{
jsApiCall();
}
}</script>
在上一篇,微信支付成功后,我们有这样一个支付跳转页面,在这个支付跳转页面里,我们可以写一些我们自己的业务逻辑,比如我们今天的主角,模板消息提醒:直接上代码
#region发送支付成功的消息ErrorMessage errorMessage;stringusername = System.Configuration.ConfigurationManager.AppSettings["weixinid"].ToString();stringtemplateId = System.Configuration.ConfigurationManager.AppSettings["templateId"].ToString();stringfirst ="您已成功付费,欢迎下次光临。";stringorderMoneySum = Request["orderMoneySum"] ??"0";stringorderProductName = Request["orderProductName"] ??"有位停车付费系统测试";stringRemark ="如有问题请致电400-6238-136或直接在微信留言,我们将第一时间为您服务!";
Color color=Color.Red;
Tuple<string,string, Color>[] data =newTuple<string,string, Color>[]{newTuple<string,string,Color>("first",first,Color.Blue),newTuple<string,string,Color>("orderMoneySum",orderMoneySum,Color.Red),newTuple<string,string,Color>("orderProductName",orderProductName,Color.Orange),newTuple<string,string,Color>("Remark",Remark,Color.Green)
};longmsgId =TemplateMessage.Send(username, openId, templateId,"", color, data,outerrorMessage);if(errorMessage.IsSuccess)
{
Response.Write("errorMessage"+ errorMessage.errmsg +"<br/>");
}else{
Response.Write("errorMessage.errmsg:"+ errorMessage.errmsg +"<br/>");
}#endregion
从上面的代码中我们可以看到TemplateMessage.Send()这个方法是我们发送消息的关键,我们来看看这个方法是怎样的:
#region发送模板消息///<summary>///发送模板消息///</summary>///<param name="userName">公众号</param>///<param name="touser">接收消息的账号</param>///<param name="templateId">模板id</param>///<param name="detailUrl">详情地址</param>///<param name="topColor">顶端颜色</param>///<param name="data">数据</param>///<param name="errorMessage">返回发送是否成功</param>///<returns>返回消息id;如果发送失败,返回-1。</returns>publicstaticlongSend(stringuserName,stringtouser,stringtemplateId,stringdetailUrl, Color topColor,
Tuple<string,string, Color>[] data,outErrorMessage errorMessage)
{
errorMessage=newErrorMessage(ErrorMessage.ExceptionCode,"");longid = -1;//校验参数if(string.IsNullOrWhiteSpace(touser))
{
errorMessage.errmsg="接收消息的账号不能为空。";returnid;
}if(string.IsNullOrWhiteSpace(templateId))
{
errorMessage.errmsg="模板id不能为空。";returnid;
}if(data ==null|| data.Length ==0)
{
errorMessage.errmsg="模板数据不能为空。";returnid;
}foreach(Tuple<string,string, Color> itemindata)
{if(string.IsNullOrWhiteSpace(item.Item1) ||string.IsNullOrWhiteSpace(item.Item2))
{
errorMessage.errmsg="模板数据不能为空。";returnid;
}
}//获取许可令牌accessToken token =AccessToken.Get(userName);if(token ==null)
{
errorMessage.errmsg="获取许可令牌失败。";returnid;
}stringurl =string.Format(urlForSending, token.access_token);//生成待发送的数据dynamic postData =newExpandoObject();
postData.touser=touser;
postData.template_id=templateId;
postData.url= detailUrl ??string.Empty;
postData.topcolor=Utility.GetColorString(topColor);
postData.data=newExpandoObject();
IDictionary<string,object> dataDict = (IDictionary<string,object>)postData.data;foreach(Tuple<string,string, Color> itemindata)
{
dataDict.Add(item.Item1,new{ value = item.Item2, color =Utility.GetColorString(item.Item3) });
}stringjson =JsonConvert.SerializeObject(postData);//发送数据stringresponseContent;if(!HttpHelper.Request(url,outresponseContent, httpMethod, json))
{
errorMessage.errmsg="提交数据到微信服务器失败。";returnid;
}//解析结果JObject jo =JObject.Parse(responseContent);
JToken jt;if(jo.TryGetValue("errcode",outjt) && jo.TryGetValue("errmsg",outjt))
{
errorMessage.errcode= (int)jo["errcode"];
errorMessage.errmsg= (string)jo["errmsg"];if(jo.TryGetValue("msgid",outjt))
id= (long)jt;
}elseerrorMessage.errmsg="解析返回结果失败。";returnid;
}#endregion
AccessToken token =AccessToken.Get(userName);获取许可令牌上一篇文章中我们已经说过这个类,这里就不多说了,HttpHelper帮助类的代码如下:
///<summary>///HttpHelper:http请求与响应辅助类///</summary>publicstaticclassHttpHelper
{///<summary>///向微信服务器发送请求时的编码///</summary>publicstaticreadonlyEncoding RequestEncoding =Encoding.UTF8;///<summary>///微信服务器响应的编码///</summary>publicstaticreadonlyEncoding ResponseEncoding =Encoding.UTF8;///<summary>///向微信服务器提交数据,并获取微信服务器响应的数据///</summary>///<param name="url">服务器地址</param>///<param name="responseData">返回响应数据</param>//////<param name="httpMethod">http方法</param>///<param name="data">数据</param>///<returns>返回是否提交成功</returns>publicstaticboolRequest(stringurl,outbyte[] responseData,stringhttpMethod = WebRequestMethods.Http.Get,byte[] data =null)
{boolsuccess =false;
responseData=null;
Stream requestStream=null;
HttpWebResponse response=null;
Stream responseStream=null;
MemoryStream ms=null;try{
HttpWebRequest request=(HttpWebRequest)WebRequest.Create(url);
request.Method=httpMethod;if(data !=null&& data.Length >0)
{
request.ContentLength=data.Length;
requestStream=request.GetRequestStream();
requestStream.Write(data,0, data.Length);
}
response=(HttpWebResponse)request.GetResponse();//由于微信服务器的响应有时没有正确设置ContentLength,这里不检查ContentLength//if (response.ContentLength > 0){
ms=newMemoryStream();
responseStream=response.GetResponseStream();intbufferLength =2048;byte[] buffer =newbyte[bufferLength];intsize = responseStream.Read(buffer,0, bufferLength);while(size >0)
{
ms.Write(buffer,0, size);
size= responseStream.Read(buffer,0, bufferLength);
}
responseData=ms.ToArray();
}
success=true;
}finally{if(requestStream !=null)
requestStream.Close();if(responseStream !=null)
responseStream.Close();if(ms !=null)
ms.Close();if(response !=null)
response.Close();
}returnsuccess;
}///<summary>///向微信服务器提交数据,并获取微信服务器响应的数据///</summary>///<param name="url">服务器地址</param>///<param name="responseData">返回响应数据</param>//////<param name="httpMethod">http方法</param>///<param name="data">数据</param>///<returns>返回是否提交成功</returns>publicstaticboolRequest(stringurl,outbyte[] responseData,stringhttpMethod = WebRequestMethods.Http.Get,stringdata =null)
{byte[] bytes =string.IsNullOrEmpty(data) ?null: RequestEncoding.GetBytes(data);returnRequest(url,outresponseData, httpMethod, (byte[])bytes);
}///<summary>///向微信服务器提交数据,并获取微信服务器响应的内容///</summary>///<param name="url">服务器地址</param>///<param name="responseContent">返回响应内容</param>//////<param name="httpMethod">http方法</param>///<param name="data">数据</param>///<returns>返回是否提交成功</returns>publicstaticboolRequest(stringurl,outstringresponseContent,stringhttpMethod = WebRequestMethods.Http.Get,byte[] data =null)
{byte[] responseData;
responseContent=string.Empty;boolsuccess = Request(url,outresponseData, httpMethod, data);if(success && responseData !=null&& responseData.Length >0)
responseContent=ResponseEncoding.GetString(responseData);returnsuccess;
}///<summary>///向微信服务器提交数据,并获取微信服务器响应的内容///</summary>///<param name="url">服务器地址</param>///<param name="responseContent">返回响应内容</param>//////<param name="httpMethod">http方法</param>///<param name="data">数据</param>///<returns>返回是否提交成功</returns>publicstaticboolRequest(stringurl,outstringresponseContent,stringhttpMethod = WebRequestMethods.Http.Get,stringdata =null)
{byte[] bytes =string.IsNullOrEmpty(data) ?null: RequestEncoding.GetBytes(data);returnRequest(url,outresponseContent, httpMethod, (byte[])bytes);
}///<summary>///向微信服务器提交数据///</summary>///<param name="url">服务器地址</param>//////<param name="httpMethod">http方法</param>///<param name="data">数据</param>///<returns>返回是否提交成功</returns>publicstaticboolRequest(stringurl,stringhttpMethod = WebRequestMethods.Http.Get,byte[] data =null)
{boolsuccess =false;
Stream requestStream=null;
HttpWebResponse response=null;try{
HttpWebRequest request=(HttpWebRequest)WebRequest.Create(url);
request.Method=httpMethod;if(data !=null&& data.Length >0)
{
request.ContentLength=data.Length;
requestStream=request.GetRequestStream();
requestStream.Write(data,0, data.Length);
}
response=(HttpWebResponse)request.GetResponse();
success=true;
}finally{if(requestStream !=null)
requestStream.Close();if(response !=null)
response.Close();
}returnsuccess;
}///<summary>///组合url,发送数据,然后返回响应字符串///</summary>///<param name="urlFormat">url格式字符串,第一个参数为userName获取到的许可令牌,然后依次为parameters中的参数</param>///<param name="userName">公众号</param>///<param name="urlParameters">参数</param>///<param name="httpMethod">执行请求的http方法</param>///<param name="data">请求的内容</param>///<returns>返回响应内容;如果请求失败,或者发生错误,返回空字符串</returns>publicstaticstringRequestResponseContent(stringurlFormat,stringuserName, IEnumerable<object> urlParameters =null,stringhttpMethod = WebRequestMethods.Http.Get,stringdata =null)
{stringresponseContent =string.Empty;
AccessToken token=AccessToken.Get(userName);if(token ==null)returnresponseContent;stringurl;if(urlParameters ==null)
url=string.Format(urlFormat, token.access_token);else{
List<object> paramList =newList<object>(urlParameters);
paramList.Insert(0, token.access_token);
url=string.Format(urlFormat, paramList.ToArray());
}
HttpHelper.Request(url,outresponseContent, httpMethod, (string)data);returnresponseContent;
}///<summary>///组合url,发送数据,然后返回响应的错误消息。///注:错误消息不一定代表失败或者错误。///</summary>///<param name="urlFormat">url格式字符串,第一个参数为userName获取到的许可令牌,然后依次为parameters中的参数</param>///<param name="userName">公众号</param>///<param name="urlParameters">参数</param>///<param name="httpMethod">执行请求的http方法</param>///<param name="data">请求的内容</param>///<returns>返回响应的错误消息</returns>publicstaticErrorMessage RequestErrorMessage(stringurlFormat,stringuserName, IEnumerable<object> urlParameters =null,stringhttpMethod = WebRequestMethods.Http.Get,stringdata =null)
{stringresponseContent =RequestResponseContent(urlFormat, userName, urlParameters, httpMethod, data);if(string.IsNullOrWhiteSpace(responseContent))returnnewErrorMessage(ErrorMessage.ExceptionCode,"请求失败。");elseif(ErrorMessage.IsErrorMessage(responseContent))returnErrorMessage.Parse(responseContent);elsereturnnewErrorMessage(ErrorMessage.ExceptionCode,"解析响应失败。");
}///<summary>///组合url,发送数据,然后返回结果。///注:结果为需要解析的类。///</summary>///<typeparam name="T">返回结果的类型</typeparam>///<param name="urlFormat">url格式字符串,第一个参数为userName获取到的许可令牌,然后依次为parameters中的参数</param>///<param name="userName">公众号</param>///<param name="errorMessage">返回请求是否成功</param>///<param name="urlParameters">参数</param>///<param name="httpMethod">执行请求的http方法</param>///<param name="data">请求的内容</param>///<returns>返回结果;如果请求失败,或者发生错误,返回null。</returns>publicstaticT RequestParsableResult<T>(stringurlFormat,stringuserName,outErrorMessage errorMessage, IEnumerable<object> urlParameters =null,stringhttpMethod = WebRequestMethods.Http.Get,stringdata =null)whereT : IParsable,new()
{
T result=default(T);
errorMessage=newErrorMessage(ErrorMessage.ExceptionCode,"请求失败。");stringresponseContent =RequestResponseContent(urlFormat, userName, urlParameters, httpMethod, data);if(string.IsNullOrWhiteSpace(responseContent))returnresult;if(ErrorMessage.IsErrorMessage(responseContent))
errorMessage=ErrorMessage.Parse(responseContent);else{try{
result= Utility.Parse<T>(responseContent);if(result !=null)
errorMessage=newErrorMessage(ErrorMessage.SuccessCode,"请求成功。");elseerrorMessage=newErrorMessage(ErrorMessage.ExceptionCode,"解析失败。");
}catch{
errorMessage=newErrorMessage(ErrorMessage.ExceptionCode,"解析失败。");
}
}returnresult;
}///<summary>///组合url,发送数据,然后返回结果。///注:结果为已知的简单值类型。///</summary>///<typeparam name="T">返回结果的类型</typeparam>///<param name="urlFormat">url格式字符串,第一个参数为userName获取到的许可令牌,然后依次为parameters中的参数</param>///<param name="userName">公众号</param>///<param name="propertyNameInJson">返回结果在json中的键名</param>///<param name="errorMessage">返回请求是否成功</param>///<param name="urlParameters">参数</param>///<param name="httpMethod">执行请求的http方法</param>///<param name="data">请求的内容</param>///<returns>返回结果;如果请求失败,或者发生错误,返回default(T)。</returns>publicstaticT RequestValueTypeResult<T>(stringurlFormat,stringuserName,stringpropertyNameInJson,outErrorMessage errorMessage, IEnumerable<object> urlParameters =null,stringhttpMethod = WebRequestMethods.Http.Get,stringdata =null)whereT :struct{
errorMessage=newErrorMessage(ErrorMessage.ExceptionCode,"请求失败。");stringresponseContent =RequestResponseContent(urlFormat, userName, urlParameters, httpMethod, data);returnConvertValueTypeResult<T>(responseContent, propertyNameInJson,outerrorMessage);
}///<summary>///获取值类型的结果///</summary>///<typeparam name="T">返回结果的类型</typeparam>///<param name="responseContent">响应内容</param>///<param name="propertyNameInJson">返回结果在json中的键名</param>///<param name="errorMessage">返回请求是否成功</param>///<returns>返回结果;如果请求失败,或者发生错误,返回default(T)。</returns>privatestaticT ConvertValueTypeResult<T>(stringresponseContent,stringpropertyNameInJson,outErrorMessage errorMessage)whereT :struct{if(string.IsNullOrWhiteSpace(responseContent))
{
errorMessage=newErrorMessage(ErrorMessage.ExceptionCode,"请求失败。");returndefault(T);
}if(ErrorMessage.IsErrorMessage(responseContent))
errorMessage=ErrorMessage.Parse(responseContent);elseerrorMessage=newErrorMessage(ErrorMessage.SuccessCode,"请求成功。");
JObject jo=JObject.Parse(responseContent);
JToken jt;if(jo.TryGetValue(propertyNameInJson,outjt))returnConvertValueTypeResult<T>((string)jt);elsereturndefault(T);
}///<summary>///获取值类型的结果///</summary>///<typeparam name="T">返回结果的类型</typeparam>///<param name="responseContent">响应内容</param>///<param name="propertyNameInJson">返回结果在json中的键名</param>///<param name="errorMessage">返回请求是否成功</param>///<returns>返回结果;如果请求失败,或者发生错误,返回default(T)。</returns>privatestaticT ConvertValueTypeResult<T>(stringvalue)whereT :struct{
Type type=typeof(T);if(type.IsEnum)return(T)Enum.Parse(type, value);elseif(type ==typeof(sbyte))return(T)(object)Convert.ToSByte(value);elseif(type ==typeof(byte))return(T)(object)Convert.ToByte(value);elseif(type ==typeof(char))return(T)(object)Convert.ToChar(value);elseif(type ==typeof(short))return(T)(object)Convert.ToInt16(value);elseif(type ==typeof(ushort))return(T)(object)Convert.ToUInt16(value);elseif(type ==typeof(int))return(T)(object)Convert.ToInt32(value);elseif(type ==typeof(uint))return(T)(object)Convert.ToUInt32(value);elseif(type ==typeof(long))return(T)(object)Convert.ToInt64(value);elseif(type ==typeof(ulong))return(T)(object)Convert.ToUInt64(value);elseif(type ==typeof(float))return(T)(object)Convert.ToSingle(value);elseif(type ==typeof(double))return(T)(object)Convert.ToDouble(value);elseif(type ==typeof(decimal))return(T)(object)Convert.ToDecimal(value);elseif(type ==typeof(bool))return(T)(object)Convert.ToBoolean(value);elsethrownewArgumentException("不支持的值类型。");
}///<summary>///向微信服务器提交数据///</summary>///<param name="url">服务器地址</param>//////<param name="httpMethod">http方法</param>///<param name="data">数据</param>///<returns>返回是否提交成功</returns>publicstaticboolRequest(stringurl,stringhttpMethod = WebRequestMethods.Http.Get,stringdata =null)
{byte[] bytes =string.IsNullOrEmpty(data) ?null: RequestEncoding.GetBytes(data);returnRequest(url, httpMethod, (byte[])bytes);
}///<summary>///上传文件///</summary>///<param name="url">服务器地址</param>///<param name="filename">文件名(不包含路径)</param>///<param name="fileData">文件数据</param>///<param name="formData">表单数据</param>///<returns>返回服务器的响应字符串</returns>publicstaticstringUpload(stringurl,stringfilename,byte[] fileData, NameValueCollection formData =null)
{stringresponseContent =string.Empty;if(string.IsNullOrWhiteSpace(url) ||string.IsNullOrWhiteSpace(filename) || fileData ==null|| fileData.Length ==0)returnresponseContent;//边界符stringboundary ="AaB03xAaB03x";//开始边界符byte[] beginBoundary = Encoding.ASCII.GetBytes("--"+ boundary +"\r\n");//结束符byte[] endBoundary = Encoding.ASCII.GetBytes("--"+ boundary +"--\r\n");//换行byte[] newLine = Encoding.ASCII.GetBytes("\r\n");
MemoryStream ms=null;
Stream stream=null;
HttpWebResponse response=null;
StreamReader sr=null;try{
HttpWebRequest request=(HttpWebRequest)WebRequest.Create(url);
request.Method=WebRequestMethods.Http.Post;
request.ContentType="multipart/form-data; boundary="+boundary;//写入文件stringheader =string.Format("Content-Disposition: form-data; filename=\"{0}\"\r\n"+"Content-Type:application/octet-stream\r\n\r\n",
filename);byte[] headerbytes =Encoding.UTF8.GetBytes(header);
ms=newMemoryStream();
ms.Write(beginBoundary,0, beginBoundary.Length);
ms.Write(headerbytes,0, headerbytes.Length);
ms.Write(fileData,0, fileData.Length);//写入表单数据if(formData !=null&& formData.Count >0)
{varformItem ="\r\n--"+ boundary +"\r\nContent-Disposition: form-data; name=\"{0}\""+"\r\n\r\n{1}\r\n";foreach(stringkeyinformData.Keys)
{stringvalue =formData[key];byte[] bytes = Encoding.UTF8.GetBytes(string.Format(formItem, key, value));
ms.Write(bytes,0, bytes.Length);
}
}//写入结束边界符ms.Write(newLine,0, newLine.Length);
ms.Write(endBoundary,0, endBoundary.Length);
request.ContentLength=ms.Length;
stream=request.GetRequestStream();
stream.Write(ms.ToArray(),0, (int)ms.Length);//获取响应response =(HttpWebResponse)request.GetResponse();
sr=newStreamReader(response.GetResponseStream(), HttpHelper.ResponseEncoding);
responseContent=sr.ReadToEnd();
}finally{if(ms !=null)
ms.Close();if(stream !=null)
stream.Close();if(sr !=null)
sr.Close();if(response !=null)
response.Close();
}returnresponseContent;
}///<summary>///上传文件///</summary>///<param name="url">服务器地址</param>///<param name="pathname">包含路径的文件名</param>///<param name="formData">表单数据</param>///<returns>返回服务器的响应字符串</returns>publicstaticstringUpload(stringurl,stringpathname, NameValueCollection formData =null)
{stringfilename =Path.GetFileName(pathname);byte[] data =null;
FileStream fs=null;
MemoryStream ms=null;try{
fs=newFileStream(pathname, FileMode.Open, FileAccess.Read);
ms=newMemoryStream();intbufferLength =2048;byte[] buffer =newbyte[bufferLength];intsize = fs.Read(buffer,0, bufferLength);while(size >0)
{
ms.Write(buffer,0, size);
size= fs.Read(buffer,0, bufferLength);
}
data=ms.ToArray();
}finally{if(fs !=null)
fs.Close();if(ms !=null)
ms.Close();
}returnUpload(url, filename, data, formData);
}
}
HttpHelper
四.最终效果如下:
五.微信公众号开发系列导航1.0初始微信公众号
2.0创建自定义菜单
3.0查询自定义菜单
4.0公众号消息处理
5.0微信支付
6.0模板消息