在微信公众号相关的功能开发中,时常涉及一些与用户的消息交互,微信为了公众号运行方不对用户进行骚扰操作,所以对公众号方发送给用户方的消息设置了很多限制。在公众号方涉及一些需要主动提醒、通知用户的提升用户体验的操作时,允许开发者在公众平台网站中模板消息库中选择模板,选择后获得模板ID,再根据模板ID向用户主动推送提醒、通知消息。本文主要讲述模板消息的模板创建,消息发送,通用回调消息接收等功能的使用方式。
本文还是以测试号为例,测试号页面:
我们可以看到测试号二维码模块,首先我们使用微信扫码关注这个测试号,关注成功后再用户列表会多出一条用户信息,昵称即为关注方的微信昵称,而微信号即为关注方的微信在本测试号的内唯一标识,我们可以通过该标识跟该关注用户实现交互,在正式公众号内这个标识叫做openId。
在二维码的下方可以看到模板消息接口模块,我们点击新增测试模板创建新的消息模板:
需要注意到相关消息提示:
- 1、测试模板的模板ID仅用于测试,不能用来给正式帐号发送模板消息
- 2、为方便测试,测试模板可任意指定内容,但实际上正式帐号的模板消息,只能从模板库中获得
- 3、需为正式帐号申请新增符合要求的模板,需使用正式号登录公众平台,按指引申请
- 4、模板内容可设置参数(模板标题不可),供接口调用时使用,参数需以{{开头,以.DATA}}结尾
本文模拟以核酸报告提醒为主题,设定完毕后,如图所示:
其中模板ID是我们调用这个消息模板的标识,可以看到我在模板内容中设定了三个变量参数,“one”、“two”、“three”,我们调用这个模板的时候必须携带这三个参数的值,否则就会报格式错误等提示。
我们点击下方模板消息的功能接口调用文档,文档内有详细的调用说明和示例:
具体调用方法
第一步:获取template_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
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
发送效果图:
事件推送
在模版消息发送任务完成后,微信服务器会将是否送达成功作为通知,发送到开发者在开发模式中填写的URL中。
1、送达成功时,推送的XML如下:
1395658920 200163836
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
2、送达由于用户拒收(用户设置拒绝接收公众号消息)而失败时,推送的XML如下:
1395658984 200163840
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
3、送达由于其他原因失败时,推送的XML如下:
1395658984 200163840
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
返回码说明
在调用模板消息接口后,会返回JSON数据包。正常时的返回JSON数据包示例:
{ "errcode":0, "errmsg":"ok", "msgid":200228332 }
- 1
- 2
- 3
- 4
- 5
错误时的返回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内容错误
可以看到我们只需要
"touser":"用户微信号/openId",
"template_id":"模板消息Id",
"url":"用户点击模板消息跳转到的页面或调用的接口",
"topcolor":"模板消息顶部背景色",
"data":{存放变量参数的值和其字体对应颜色}
这五个参数再加上access_token即可实现对模板消息的发送,其中变量的值需要以字段名对应一个value和color对象的方式统一包裹在“data”中。
另外,调用说明中:
在模版消息发送任务完成后,微信服务器会将是否送达成功作为通知,发送到开发者在开发模式中填写的URL中
所以,据此我们可以了解到模板消息的发送在微信内应该是一个异步操作,微信收到我们的调用请求后立即回复给我们能不能执行,但执行是否成功了它会通过一个额外的接口给我们提醒,这个接口就是我们最初绑定激活所用的配置信息内的URL,它承担了所有微信功能接口调用的回调消息接收操作。但需要注意的是,此时承担接收回调消息的接口类型是POST,所以接口地址不变的情况下我们还需要设定一个接收消息的POST接口,接收成功后统一回复字符串“success”即可,否则微信会重发三次左右,编写代码如下:
/**
* 恒定配置接口,post类型用于接收各种消息推送和回调
*
* @param request 请求体
* @return 请求合法的情况下恒定返回字符串 “success”
*/
@PostMapping("/auth")
public String sayHi(HttpServletRequest request) {
//获取param参数(可用以验签,确定请求方身份是否合法)
String reqStr = "";
try {
Map<String, String[]> parameterMap = request.getParameterMap();
if (ObjectUtil.isNotEmpty(parameterMap)) {
reqStr = JSONObject.toJSONString(parameterMap, SerializerFeature.WriteMapNullValue);
}
} catch (Exception e) {
log.error("接收param消息异常" + e.getMessage(), e);
}
log.info("恒定配置接口接收param参数:" + reqStr);
//获取xml参数(可用以具体业务处理)
Map<String, String> xmlMap = new HashMap<>(16);
try {
// 从request中取得输入流
InputStream inputStream = request.getInputStream();
// 读取输入流
SAXReader reader = new SAXReader();
Document document = reader.read(inputStream);
// 得到xml根元素
Element root = document.getRootElement();
// 得到根元素的所有子节点
List<Element> elementList = root.elements();
// 遍历所有子节点
for (Element e : elementList) {
xmlMap.put(e.getName(), e.getText());
}
} catch (Exception e) {
log.error("接收xml消息异常" + e.getMessage(), e);
}
log.info("恒定配置接口接收xml参数:" + JSONObject.toJSONString(xmlMap, SerializerFeature.WriteMapNullValue));
return "success";
}
因为回调业务消息基本为xml消息,所以我们需要从流中获取xml消息并转换为java对象,所需pom依赖:
<dependency>
<groupId>dom4jgroupId>
<artifactId>dom4jartifactId>
<version>1.6version>
dependency>
另外,回调消息内也会携带相关签名身份信息,可以用以确定身份是否合法,正式号还可以设置消息使用密钥加密传输,此处不涉及。
编写好了回调消息接收方法,我们再编写模板消息调用方法:
/**
* 发送模板消息
*
* @param reqMap 包含所需参数的集合
* @return 响应结果
*/
@PostMapping("/tem-msg")
public String sendTemplateMsg(@RequestBody Map<String, String> reqMap) {
String uri = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN";
String accessToken = reqMap.get("accessToken");
uri = uri.replace("ACCESS_TOKEN", accessToken);
String temId = reqMap.get("temId");
String redirectUri = reqMap.get("redirectUri");
String openId = reqMap.get("openId");
Map<String, Object> wxMap = new HashMap<>(16);
wxMap.put("touser", openId);
wxMap.put("template_id", temId);
wxMap.put("url", redirectUri);
wxMap.put("topcolor", "#FF0000");
Map<String, Map<String, String>> dataMap = new HashMap<>(16);
Map<String, String> map1 = new HashMap<>(2);
map1.put("value", "孙武");
map1.put("color", "#173177");
dataMap.put("one", map1);
Map<String, String> map2 = new HashMap<>(2);
map2.put("value", "核酸检测报告");
map2.put("color", "#173177");
dataMap.put("two", map2);
Map<String, String> map3 = new HashMap<>(2);
map3.put("value", "http://www.baidu.com");
map3.put("color", "#173177");
dataMap.put("three", map3);
wxMap.put("data", dataMap);
JSONObject jsonObject = JSONObject.parseObject(JSONObject.toJSONString(wxMap));
String res = RestClientUtils.sendPostString(restTemplate, uri, null, jsonObject, null);
return res;
}
postman调用参数:
{
"accessToken": "59_qzI8PAKyHTPtYbFU2VNOxYqoef2JRiOcfFESsCWJh8CAzGnFZALWH4-gtaU3H52KZgYcbN7T4v6FfkRkAlReDgtxZ02TC7i9EQvk7OEOtSBdWuMVNZ0voVnq6FYL1AANWkScYEYE_eQObTJJUBNjACAFMY",
"temId": "EWxi5NXmiZkJIHiYtfnA_WOTPosRqt6UR7li4RCRODw",
"redirectUri": "https://XXXXXXXX.wocp.fun/module/wx/v1/init/redirect-page",
"openId": "xxxxxwbxxxxxE-Whk"
}
PS: Hutool工具的post调用方法博主在使用时,微信一直提示参数格式异常,所以这里使用是自己的一个工具类,工具类在之前博文,具体链接。
因为博主没有页面部署,所以跳转路径url使用的是一个接口用以测试,接口编写如下:
/**
* 点击模板消息跳转的页面路径或接口(此处使用接口,一般情况下应为前端页面路径)
*
* @return 处理结果
*/
@GetMapping("/redirect-page")
public String senTemplateMsgCallback() {
log.info("进入页面跳转接口......");
return "success页面消息";
}
至此,代码编写完成,我们首先获取access_token,然后再postman调用接口进行模板消息的发送操作:
可以看到,响应消息提示成功,我们再查看springboot日志:
可以看到,在回调消息接收方法内接受到了模板消息发送的结果为success。xml参数内,ToUserName接收者即为我们的公众号微信号,FromUserName发送者即为用户的微信号(openId)。
打开我们的手机,可以看到订阅号内有一条提示消息:
我们点击这个消息:
可以看到,页面参数和颜色都是跟我们设定发送的一致,我们再点击这个消息:
可以看到页面消息与我们跳转接口内返回参数一致,查看后端日志:
至此,模板消息发送功能完成!