开发前准备工作
- 登录微信公众平台-小程序后台,在模板消息里面设置需要发送的模板,拿到模板id。
地址: https://mp.weixin.qq.com
- 找到微信官方文档-小程序开发-下发小程序和公众号统一的服务消息接口。
地址: https://developers.weixin.qq.com/miniprogram/dev/api-backend/
3.下发小程序和公众号统一的服务消息接口需要接口调用凭证。
4.接口调用凭证需要小程序的appid 和 secret,在小程序后台获取。
代码编写
调用接口凭证,获取access_token
注意:access_token 的有效期目前为 2 个小时,需定时刷新,重复获取将导致上次获取的 access_token 失效;
建议开发者使用中控服务器统一获取和刷新 access_token;
/**
* 获取access_token
* @param appid 凭证
* @param appsecret 密钥
* @return
*/
public static AccessToken getAccessToken() {
AccessToken accessToken = null;
String requestUrl = WeChatConstants.WECHAT_ACCESS_TOKEN_URL.replace("APPID", WeChatConstants.WECHAT_APPID).replace("APPSECRET", WeChatConstants.WECHAT_APPSECRET);
JSONObject jsonObject = WeChatUtil.httpRequest(requestUrl, "GET", null); //调用通用的https请求方法
// 如果请求成功
if (null != jsonObject) {
try {
accessToken = new AccessToken();
accessToken.setToken(jsonObject.getString("access_token"));
accessToken.setExpiresIn(jsonObject.getInteger("expires_in"));
accessToken.setCreatedate(new Date());
weixincache.put("accessToken", accessToken);
} catch (JSONException e) {
accessToken = null;
// 获取token失败
log.error("获取token失败 errcode:{} errmsg:{}", jsonObject.getInteger("errcode"), jsonObject.getString("errmsg"));
}
}
return accessToken;
}
配置类
public class WeChatConstants {
public static final String WECHAT_TICKET_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";
public static final String WECHAT_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
// 待支付课程提醒
public static final String UNPAID_PAY_TEMPLATE_ID = "需要发送的小程序后台模板id";
// 支付成功课程提醒
public static final String PAY_SUCCEED_TEMPLATE_ID = "需要发送的小程序后台模板id";
public static final String WECHAT_APPID = "小程序appid";
public static final String WECHAT_APPSECRET = "小程序secret密钥";
}
公众平台通用接口工具类
public class WeChatUtil {
private static Logger log = LoggerFactory.getLogger(WeChatUtil.class);
/**
* 发起https请求并获取结果
*
* @param requestUrl 请求地址
* @param requestMethod 请求方式(GET、POST)
* @param outputStr 提交的数据
* @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值)
*/
public static JSONObject httpRequest(String requestUrl, String requestMethod, String outputStr) {
JSONObject jsonObject = null;
StringBuffer buffer = new StringBuffer();
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 httpUrlConn = (HttpsURLConnection) url.openConnection();
httpUrlConn.setSSLSocketFactory(ssf);
httpUrlConn.setDoOutput(true);
httpUrlConn.setDoInput(true);
httpUrlConn.setUseCaches(false);
// 设置请求方式(GET/POST)
httpUrlConn.setRequestMethod(requestMethod);
if ("GET".equalsIgnoreCase(requestMethod))
httpUrlConn.connect();
// 当有数据需要提交时
if (null != outputStr) {
OutputStream outputStream = httpUrlConn.getOutputStream();
// 注意编码格式,防止中文乱码
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
// 将返回的输入流转换成字符串
InputStream inputStream = httpUrlConn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
bufferedReader.close();
inputStreamReader.close();
// 释放资源
inputStream.close();
inputStream = null;
httpUrlConn.disconnect();
jsonObject = JSONObject.parseObject(buffer.toString());
} catch (ConnectException ce) {
log.error("Weixin server connection timed out.");
} catch (Exception e) {
log.error("https request error:{}", e);
}
return jsonObject;
}
}
发送模板消息接口
/**
* @param orderId 订单id
* @param openid 用户openid
* @param formId 表单ID
* @param templateId 模板ID
* @param keywords {与模板字段一一对应}
* @return
*/
public static Map<String,String> pushOneUser(Long orderId, String openid, String formId,String templateId,String[] keywords) {
Map<String,String> map = new HashMap<>();
String msg = null;
try {
//如果access_token为空则从新获取
String access_token = RedisUtil.getString("access_token");
if (access_token == null) {
AccessToken accessToken = getAccessToken();
access_token = accessToken.getToken();
RedisUtil.setString("access_token", access_token, (60 * 120));
}
if (RedisUtil.exists(access_token)){
AccessToken accessToken = getAccessToken();
access_token = accessToken.getToken();
RedisUtil.setString("access_token", access_token, (60 * 120));
}
String url = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send" +
"?access_token=" + access_token;
//拼接推送的模版
WxMssVo wxMssVo = new WxMssVo();
wxMssVo.setTouser(openid);//用户openid
wxMssVo.setForm_id(formId);//formId
wxMssVo.setTemplate_id(templateId);//模版id
wxMssVo.setPage(wxMssVo.getPage()+"?orderId="+orderId);
Map<String, TemplateDataVo> m = new HashMap<>();
//封装数据
if(keywords.length>0){
for(int i=1;i<=keywords.length;i++){
TemplateDataVo keyword = new TemplateDataVo();
keyword.setValue(keywords[i-1]);
m.put("keyword"+i, keyword);
}
wxMssVo.setData(m);
}else{
log.error("keywords长度为空");
return null;
}
log.info("发送模板消息请求参数"+JSONObject.toJSONString(JSONObject.toJSONString(wxMssVo)));
JSONObject jsonObject = WeChatUtil.httpRequest(url, "POST", JSONObject.toJSONString(wxMssVo)); //调用通用的https请求方法
log.info("发送模板消息返回"+JSONObject.toJSONString(jsonObject));
map.put("content",JSONObject.toJSONString(m));
map.put("returnMsg",JSONObject.toJSONString(JSONObject.toJSONString(jsonObject)));
map.put("errcode",jsonObject.getString("errcode"));
// 如果请求成功
if (null != jsonObject) {
msg = jsonObject.getString("errmsg");
}
}catch (Exception e){
e.printStackTrace();
}
return map;
}
封装模板类
public class WxMssVo {
private String touser;//用户openid
private String template_id;//模版id
private String page = "pages/MyOrder/MyOrderDetail";// 订单详情页
private String form_id;//收集到的用户formid
// private String emphasis_keyword = "keyword1.DATA";//放大那个推送字段
private Map<String, TemplateDataVo> 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 template_id) {
this.template_id = template_id;
}
public String getPage() {
return page;
}
public void setPage(String page) {
this.page = page;
}
public String getForm_id() {
return form_id;
}
public void setForm_id(String form_id) {
this.form_id = form_id;
}
public Map<String, TemplateDataVo> getData() {
return data;
}
public void setData(Map<String, TemplateDataVo> data) {
this.data = data;
}
}
public class TemplateDataVo {
//字段值例如:keyword1:content,keyword2:课程名称,keyword3:创建时间,keyword4:订单金额
private String value;//依次排下去
// private String color;//字段颜色(微信官方已废弃,设置没有效果)
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
测试发送模板消息
关于获取formid
https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/template-message.html
/**
* @param orderEntity
* @param openid 用户openid,可以是小程序的openid,也可以是mp_template_msg.appid对应的公众号的openid
* @param formId 小程序模板消息formid
*/
public void pushOneUser(OrderEntity orderEntity,String openid,String formId){
try {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String template = WeChatConstants.PAY_SUCCEED_TEMPLATE_ID; // 需要发送的模板id
String price = "12.30"; // 订单金额
Date orderDate = orderEntity.getCreateTime(); // 创建时间
String dateString = formatter.format(orderDate);
String courseName = "测试发送模板消息"; // 课程名称
String content = "您的课程已支付,我们会在1-3个工作日内联系您,安排开课~";
String[] keywords = {content, courseName, dateString, price};
WeChat.pushOneUser(orderEntity.getId(),openid,formId,template,keywords);
}catch (Exception e){
e.printStackTrace();
}
}
官方返回值
程序调用成功返回值
程序调用失败返回值
发送模板消息记录表