微信公眾平臺開發之模板訊息(Java)
由於柳峰老師的新書還沒有出來,網上也沒有過多介紹基於Java語言的開發微信公眾平臺模板訊息的例子,因此有了本文。由於個人表達能力和程式設計能力有限,請多多包涵。本文僅介紹擁有模板訊息許可權的微信公眾賬號開發。
本文分為以下兩部分:
1.開發模板訊息SDK
2.構造模板訊息併發送
首先看一下模板訊息介面文件:
模版訊息
模板庫/ 模板訊息介面文件為了保證使用者不受到騷擾,在開發者出現需要主動提醒、通知使用者時,才允許開發者在公眾平臺網站中模板訊息庫中選擇模板,選擇後獲得模板ID,再根據模板ID向用戶主動推送提醒、通知訊息。
模板訊息呼叫時主要需要模板ID和模板中各引數的賦值內容。請注意:
1.模板中引數內容必須以".DATA"結尾,否則視為保留字;
2.模板保留符號"{{ }}"
具體呼叫方法
第一步:獲取模板ID
通過在模板訊息功能的模板庫中使用需要的模板,可以獲得模板ID。
第二步:請求介面
請注意,URL置空,則在傳送後,點選模板訊息會進入一個空白頁面(ios),或無法點選(android)。
POST請求
https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN請求包為一個json:
{ "touser":"OPENID", "template_id":"ngqIpbwh8bUfcSsECmogfXcV14J0tQlEpBO27izEYtY", "url":"http://weixin.qq.com/download", "topcolor":"#FF0000", "data":{ "User": { "value":"黃先生", "color":"#173177" }, "Date":{ "value":"06月07日 19時24分", "color":"#173177" }, "CardNumber":{ "value":"0426", "color":"#173177" }, "Type":{ "value":"消費", "color":"#173177" }, "Money":{ "value":"人民幣260.00元", "color":"#173177" }, "DeadTime":{ "value":"06月07日19時24分", "color":"#173177" }, "Left":{ "value":"6504.09", "color":"#173177" } } }
傳送效果圖:
事件推送
在模版訊息傳送任務完成後,微信伺服器會將是否送達成功作為通知,傳送到開發者中心中填寫的伺服器配置地址中。
1、送達成功時,推送的XML如下:
<xml> <ToUserName><![CDATA[gh_7f083739789a]]></ToUserName> <FromUserName><![CDATA[oia2TjuEGTNoeX76QEjQNrcURxG8]]&g;</FromUserName> <CreateTime>1395658920</CreateTime> <MsgType><![CDATA[event]]></MsgType> <Event><![CDATA[TEMPLATESENDJOBFINISH]]></Event> <MsgID>200163836</MsgID> <Status><![CDATA[success]]></Status> </xml>
2、送達由於使用者拒收(使用者設定拒絕接收公眾號訊息)而失敗時,推送的XML如下:
<xml> <ToUserName><![CDATA[gh_7f083739789a]]></ToUserName> <FromUserName><![CDATA[oia2TjuEGTNoeX76QEjQNrcURxG8]]></FromUserName> <CreateTime>1395658984</CreateTime> <MsgType><![CDATA[event]]></MsgType> <Event><![CDATA[TEMPLATESENDJOBFINISH]]></Event> <MsgID>200163840</MsgID> <Status><![CDATA[failed:user block]]></Status> </xml>
3、送達由於其他原因失敗時,推送的XML如下:
<xml> <ToUserName><![CDATA[gh_7f083739789a]]></ToUserName> <FromUserName><![CDATA[oia2TjuEGTNoeX76QEjQNrcURxG8]]></FromUserName> <CreateTime>1395658984</CreateTime> <MsgType><![CDATA[event]]></MsgType> <Event><![CDATA[TEMPLATESENDJOBFINISH]]></Event> <MsgID>200163840</MsgID> <Status><![CDATA[failed: system failed]]></Status> </xml>
返回碼說明
在呼叫模板訊息介面後,會返回JSON資料包。正常時的返回JSON資料包示例:
{ "errcode":0, "errmsg":"ok", "msgid":200228332 }
錯誤時的返回JSON資料,形式類似,錯誤碼請見本頁下方返回碼說明。
返回碼 | 說明 |
---|---|
-1 | 系統繁忙 |
0 | 請求成功 |
40001 | 驗證失敗 |
40002 | 不合法的憑證型別 |
40003 | 不合法的OpenID |
40004 | 不合法的媒體檔案型別 |
40005 | 不合法的檔案型別 |
40006 | 不合法的檔案大小 |
40007 | 不合法的媒體檔案id |
40008 | 不合法的訊息型別 |
40009 | 不合法的圖片檔案大小 |
40010 | 不合法的語音檔案大小 |
40011 | 不合法的視訊檔案大小 |
40012 | 不合法的縮圖檔案大小 |
40013 | 不合法的APPID |
41001 | 缺少access_token引數 |
41002 | 缺少appid引數 |
41003 | 缺少refresh_token引數 |
41004 | 缺少secret引數 |
41005 | 缺少多媒體檔案資料 |
41006 | access_token超時 |
42001 | 需要GET請求 |
43002 | 需要POST請求 |
43003 | 需要HTTPS請求 |
44001 | 多媒體檔案為空 |
44002 | POST的資料包為空 |
44003 | 圖文訊息內容為空 |
45001 | 多媒體檔案大小超過限制 |
45002 | 訊息內容超過限制 |
45003 | 標題欄位超過限制 |
45004 | 描述欄位超過限制 |
45005 | 連結欄位超過限制 |
45006 | 圖片連結欄位超過限制 |
45007 | 語音播放時間超過限制 |
45008 | 圖文訊息超過限制 |
45009 | 介面呼叫超過限制 |
46001 | 不存在媒體資料 |
47001 | 解析JSON/XML內容錯誤 |
一、開發模板訊息SDK
模板訊息的定義如下:
模板訊息也是使用access_token作為授權來發送。但是請大家注意:這裡的access_token與網頁授權的access_token完全不是一回事。可不要拿網頁授權的access_token當作引數傳遞。
獲取模板訊息access_token:
String appId = "xxxxxxxxxxxxx"; //公眾賬號的唯一標識
String appSecret="xxxxxxxxxxx"; //公眾賬號的金鑰
Token token = CommonUtil.getToken(appId, appSecret);
String access_token = token.getAccessToken();
#CommonUtil工具類具體程式碼:
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.ConnectException;
import java.net.URL;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import net.sf.json.JSONException;
import net.sf.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 通用工具類
*
* @author liufeng
*/
public class CommonUtil {
private static Logger log = LoggerFactory.getLogger(CommonUtil.class);
// 憑證獲取(GET)
public final static String token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
/**
* 傳送 https 請求
*
* @param requestUrl 請求地址
* @param requestMethod 請求方式(GET、POST)
* @param outputStr 提交的資料
* @return JSONObject(通過 JSONObject.get(key) 的方式獲取 JSON 物件的屬性值)
*/
public static JSONObject httpsRequest(String requestUrl, String requestMethod, String outputStr) {
JSONObject jsonObject = null;
try {
// 建立 SSLContext 物件,並使用我們指定的信任管理器初始化
TrustManager[] tm = { new MyX509TrustManager() };
SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
sslContext.init(null, tm, new java.security.SecureRandom());
// 從上述 SSLContext 物件中得到 SSLSocketFactory 物件
SSLSocketFactory ssf = sslContext.getSocketFactory();
URL url = new URL(requestUrl);
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(ssf);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);
// 設定請求方式(GET/POST)
conn.setRequestMethod(requestMethod);
// 當 outputStr 不為 null 時,向輸出流寫資料
if (null != outputStr) {
OutputStream outputStream = conn.getOutputStream();
// 注意編碼格式
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
// 從輸入流讀取返回內容
InputStream inputStream = conn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
StringBuffer buffer = new StringBuffer();
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
// 釋放資源
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
inputStream = null;
conn.disconnect();
jsonObject = JSONObject.fromObject(buffer.toString());
} catch (ConnectException ce) {
log.error(" 連線超時:{}", ce);
} catch (Exception e) {
log.error("https 請求異常:{}", e);
}
return jsonObject;
}
/**
* 獲取介面訪問憑證
*
* @param appid 憑證
* @param appsecret 金鑰
* @return
*/
public static Token getToken(String appid, String appsecret) {
Token token = null;
String requestUrl = token_url.replace("APPID", appid).replace("APPSECRET", appsecret);
// 發起GET請求獲取憑證
JSONObject jsonObject = httpsRequest(requestUrl, "GET", null);
if (null != jsonObject) {
try {
token = new Token();
token.setAccessToken(jsonObject.getString("access_token"));
token.setExpiresIn(jsonObject.getInt("expires_in"));
} catch (JSONException e) {
token = null;
// 獲取token失敗
log.error("獲取token失敗 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg"));
}
}
return token;
}
/**
* URL編碼(utf-8)
*
* @param source
* @return
*/
public static String urlEncodeUTF8(String source) {
String result = source;
try {
result = java.net.URLEncoder.encode(source, "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return result;
}
/**
* 根據內容型別判斷副檔名
*
* @param contentType 內容型別
* @return
*/
public static String getFileExt(String contentType) {
String fileExt = "";
if ("image/jpeg".equals(contentType))
fileExt = ".jpg";
else if ("audio/mpeg".equals(contentType))
fileExt = ".mp3";
else if ("audio/amr".equals(contentType))
fileExt = ".amr";
else if ("video/mp4".equals(contentType))
fileExt = ".mp4";
else if ("video/mpeg4".equals(contentType))
fileExt = ".mp4";
return fileExt;
}
}
#Token實體類具體程式碼:
/**
* 憑證實體類
*
* @author liufeng
*/
public class Token {
// 介面訪問憑證
private String accessToken;
// 憑證有效期,單位:秒
private int expiresIn;
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public int getExpiresIn() {
return expiresIn;
}
public void setExpiresIn(int expiresIn) {
this.expiresIn = expiresIn;
}
}
至此,我們獲得了模板訊息的access_token。將它作為引數傳遞到模板訊息介面文件中POST請求的ACCESS_TOKEN。
//得到構造好的模板訊息請求地址
二、構造模板訊息併發送
我們以一個餐飲行業的收到新訂單通知的模板為例,模板詳情如下:
模版訊息
我的模板/ 模板詳情-
模板ID
YtO2XATY0VtRbgE4jWWNl-Zdc992FDdguhMUbomNkA0
開發者呼叫模板訊息介面時需提供模板ID - 標題收到新訂單通知
- 行業餐飲 - 餐飲
-
詳細內容
{{first.DATA}} 日期:{{Day.DATA}} 訂單編號:{{orderId.DATA}} 訂單型別:{{orderType.DATA}} 聯絡人:{{customerName.DATA}} 聯絡電話:{{customerPhone.DATA}} {{remark.DATA}}
在傳送時,需要將內容中的引數({{.DATA}}內為引數)賦值替換為需要的資訊 -
內容示例
收到一個新的訂單,確認接受請回復0,拒絕請回復1。 日期:2014-10-10 訂單編號:1002 訂單型別:訂位 聯絡人:陳醜醜 聯絡電話:13222222222 訂單金額:100.00元 付款狀態:已微信支付 請及時處理您的訂單。
- 按照上述條件,我們的訊息體構造如下:
-
{"data":{"customerName":{"color":"#173177","value":"陳醜醜"},"customerPhone":{"color":"#173177","value":"13222222222"},"Day":{"color":"#173177","value":"15時06分"},"first":{"color":"#173177","value":"收到一個新的訂單"},"orderId":{"color":"#173177","value":"1002"},"orderType":{"color":"#173177","value":"訂位"},"remark":{"color":"#173177","value":"請及時處理您的訂單"}},"template_id":"YtO2XATY0VtRbgE4jWWNl-Zdc992FDdguhMUbomNkA0","topcolor":"#173177","touser":"orsrOt9qBgO6dC-F3IL_MF52eplI","url":"http://weixin.qq.com/download"}
- 該訊息體為一個json。具體封裝如下:
-
/**
* 餐飲行業收到新訂單通知模板訊息實體類
* @author xjw
*
*/
public class NewOrdersTemplate {
private String touser; //使用者OpenID
private String template_id; //模板訊息ID
private String url; //URL置空,則在傳送後,點選模板訊息會進入一個空白頁面(ios),或無法點選(android)。
private String topcolor; //標題顏色
private Data data; //詳細內容
public String getTouser() {
return touser;
}
public void setTouser(String touser) {
this.touser = touser;
}
public String getTemplate_id() {
return template_id;
}
public void setTemplate_id(String templateId) {
template_id = templateId;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getTopcolor() {
return topcolor;
}
public void setTopcolor(String topcolor) {
this.topcolor = topcolor;
}
public Data getData() {
return data;
}
public void setData(Data data) {
this.data = data;
}
} -
/**
* "餐飲行業收到新訂單通知"模板訊息詳細內容實體類
* @author xjw
*
*/
public class Data {
private Data_first first;
private Data_Day Day; //日期
private Data_orderId orderId; //訂單編號
private Data_orderType orderType; //訂單型別
private Data_customerName customerName; //聯絡人
private Data_customerPhone customerPhone; //聯絡電話
private Data_remark remark;
public Data_first getFirst() {
return first;
}
public void setFirst(Data_first first) {
this.first = first;
}
public Data_Day getDay() {
return Day;
}
public void setDay(Data_Day day) {
Day = day;
}
public Data_orderId getOrderId() {
return orderId;
}
public void setOrderId(Data_orderId orderId) {
this.orderId = orderId;
}
public Data_orderType getOrderType() {
return orderType;
}
public void setOrderType(Data_orderType orderType) {
this.orderType = orderType;
}
public Data_customerName getCustomerName() {
return customerName;
}
public void setCustomerName(Data_customerName customerName) {
this.customerName = customerName;
}
public Data_customerPhone getCustomerPhone() {
return customerPhone;
}
public void setCustomerPhone(Data_customerPhone customerPhone) {
this.customerPhone = customerPhone;
}
public Data_remark getRemark() {
return remark;
}
public void setRemark(Data_remark remark) {
this.remark = remark;
}
}
/**
* first
* @author xjw
*
*/
public class Data_first {
private String value;
private String color;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
/**
* 日期
* @author xjw
*
*/
public class Data_Day {
private String value;
private String color;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
/**
* 訂單標號
* @author xjw
*
*/
public class Data_orderId {
private String value;
private String color;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
/**
* 訂單型別
* @author xjw
*
*/
public class Data_orderType {
private String value;
private String color;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
/**
* 聯絡人
* @author xjw
*
*/
public class Data_customerName {
private String value;
private String color;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
/**
* 聯絡電話
* @author xjw
*
*/
public class Data_customerPhone {
private String value;
private String color;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
/**
* remark
* @author xjw
*
*/
public class Data_remark {
private String value;
private String color;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
/**
* 傳送模板訊息
* appId 公眾賬號的唯一標識
* appSecret 公眾賬號的金鑰
* openId 使用者標識
*/
public void send_template_message(String appId, String appSecret, String openId) {
Token token = CommonUtil.getToken(appId, appSecret);
String access_token = token.getAccessToken();
String url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token="+access_token;
NewOrdersTemplate temp = new NewOrdersTemplate();
Data data = new Data();
Data_first first = new Data_first();
Data_Day Day = new Data_Day();
Data_orderId orderId = new Data_orderId();
Data_orderType orderType = new Data_orderType();
Data_customerName customerName = new Data_customerName();
Data_customerPhone customerPhone = new Data_customerPhone();
Data_remark remark = new Data_remark();
first.setValue("收到一個新的訂單");
first.setColor("#173177");
Day.setValue("14時56分");
Day.setColor("#173177");
orderId.setValue("1002");
orderId.setColor("#173177");
orderType.setValue("訂位");
orderType.setColor("#173177");
customerName.setValue("陳醜醜");
customerName.setColor("#173177");
customerPhone.setValue("13222222222");
customerPhone.setColor("#173177");
remark.setValue("請及時處理您的訂單");
remark.setColor("#173177");
data.setFirst(first);
data.setDay(Day);
data.setOrderId(orderId);
data.setOrderType(orderType);
data.setCustomerName(customerName);
data.setCustomerPhone(customerPhone);
data.setRemark(remark);
temp.setTouser(openId);
temp.setTemplate_id("YtO2XATY0VtRbgE4jWWNl-Zdc992FDdguhMUbomNkA0");
temp.setUrl("http://weixin.qq.com/download");
temp.setTopcolor("#173177");
temp.setData(data);
String jsonString = JSONObject.fromObject(temp).toString().replace("day", "Day");
JSONObject jsonObject = WeixinUtil.httpRequest(url, "POST", jsonString);
System.out.println(jsonObject);
int result = 0;
if (null != jsonObject) {
if (0 != jsonObject.getInt("errcode")) {
result = jsonObject.getInt("errcode");
log.error("錯誤 errcode:{} errmsg:{}", jsonObject.getInt("errcode"), jsonObject.getString("errmsg"));
}
}
log.info("模板訊息傳送結果:"+result);
}
WeixinUtil工具類在柳峰老師的部落格裡有原始碼。。。。。
如果小夥伴們覺得有什麼問題可以提出,大家共同學習。