开发前准备工作
- 登录微信公众平台-小程序后台,在模板消息里面设置需要发送的模板,拿到模板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);//用户openidwxMssVo.setForm_id(formId);//formIdwxMssVo.setTemplate_id(templateId);//模版idwxMssVo.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;//用户openidprivate String template_id;//模版idprivate 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; // 需要发送的模板idString 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();}}
官方返回值
程序调用成功返回值
程序调用失败返回值
发送模板消息记录表