微信公众号被动回复消息

背景:微信扫描二维码,点击关注或取消关注公众号,同时公众号会给用户发消息。

如图:

微信公众号开发文档介绍了消息的多种类型,微信开发文档–>公众号–>基础消息能力–>「被动回复用户消息」 如下

当用户发送消息给公众号时(或某些特定的用户操作引发的事件推送时),会产生一个POST请求,开发者可以在响应包(Get)中返回特定XML结构,来对该消息进行响应(现支持回复文本、图片、图文、语音、视频、音乐)。严格来说,发送被动响应消息其实并不是一种接口,而是对微信服务器发过来消息的一次回复。

微信服务器在将用户的消息发给公众号的开发者服务器地址(开发者中心处配置)后,微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次,如果在调试中,发现用户无法收到响应的消息,可以检查是否消息处理超时。关于重试的消息排重,有msgid的消息推荐使用msgid排重。事件类型消息推荐使用FromUserName + CreateTime 排重。

如果开发者希望增强安全性,可以在开发者中心处开启消息加密,这样,用户发给公众号的消息以及公众号被动回复用户消息都会继续加密,详见被动回复消息加解密说明。

假如服务器无法保证在五秒内处理并回复,必须做出下述回复,这样微信服务器才不会对此作任何处理,并且不会发起重试(这种情况下,可以使用客服消息接口进行异步回复),否则,将出现严重的错误提示。详见下面说明:
1、直接回复success(推荐方式) 2、直接回复空串(指字节长度为0的空字符串,而不是XML结构体中content字段的内容为空)
一旦遇到以下情况,微信都会在公众号会话中,向用户下发系统提示“该公众号暂时无法提供服务,请稍后再试”:
1、开发者在5秒内未回复任何内容 2、开发者回复了异常数据,比如JSON数据等
另外,请注意,回复图片(不支持gif动图)等多媒体消息时需要预先通过素材管理接口上传临时素材到微信服务器,可以使用素材管理中的临时素材,也可以使用永久素材。

各消息类型需要的XML数据包结构如下:
1 回复文本消息
2 回复图片消息
3 回复语音消息
4 回复视频消息
5 回复音乐消息
6 回复图文消息

Java 代码

点击关注或者取消关注,微信那边会自动触发关注/取消关注事件,并请求事先配置好的地址

   /*** 接收微信推送事件** @param request* @param response*/@RequestMapping(value = "/check/signature", method = RequestMethod.POST, produces = {"application/xml; charset=UTF-8"})@ResponseBodypublic void wechatEvent(HttpServletRequest request, HttpServletResponse response) {log.error("--------------------接收微信推送事件-----------------------");try {// 为了防止消息乱码request.setCharacterEncoding("UTF-8");response.setCharacterEncoding("UTF-8");} catch (IOException e) {e.printStackTrace();}wxService.handleEvent(request, response);}

下面是处理被动回复消息逻辑代码,需要根据具体的需求选择具体的消息类型,我这里以文本消息为例。
触发事件的时候会返回微信openId等其他信息。

   public void handleEvent(HttpServletRequest request, HttpServletResponse response) {InputStream inputStream = null;try {inputStream = request.getInputStream();Map<String, Object> map = XmlUtil.parseXML(inputStream);// openIdString userOpenId = (String) map.get("FromUserName");// 微信账号String userName = (String) map.get("ToUserName");// 事件String event = (String) map.get("Event");// 区分消息类型String msgType = (String) map.get("MsgType");// 普通消息if ("text".equals(msgType)) {// todo 处理文本消息}
//            else if ("image".equals(msgType)) {//            } else if ("voice".equals(msgType)) {//            } else if ("video".equals(msgType)) {//            } // 事件推送消息else if ("event".equals(msgType)) {if ("subscribe".equals(event)) {logger.info("用户扫码|关注|openId:{},userName:{}", userOpenId, userName);String ticket = (String) map.get("Ticket");if (StringUtils.isNotBlank(ticket)) {redisCacheManager.set(ConstantsRedisKey.ADV_WX_LOGIN_TICKET.replace("ticketId", ticket), userOpenId, 10 * 60);}String mapToXml = handleEventSubscribe(map, userOpenId);response.getWriter().print(mapToXml);return;} else if ("SCAN".equals(event)) {logger.info("用户扫码|登录|openId:{},userName:{}", userOpenId, userName);String ticket = (String) map.get("Ticket");if (StringUtils.isNotBlank(ticket)) {redisCacheManager.set(ConstantsRedisKey.ADV_WX_LOGIN_TICKET.replace("ticketId", ticket), userOpenId, 10 * 60);}// todo 业务处理} else if ("unsubscribe".equals(event)) {logger.info("用户取消关注,拜拜~,openId:{}", userOpenId);// todo 取消关注 业务处理}}logger.info("接收参数:{}", map);} catch (IOException e) {logger.error("处理微信公众号请求异常:", e);} finally {if (inputStream != null) {try {inputStream.close();} catch (IOException ioe) {logger.error("关闭inputStream异常:", ioe);}}}}/*** 处理 subscribe 类型的event** @param map* @param userOpenId* @return*/private String handleEventSubscribe(Map<String, Object> map, String userOpenId) {String resXmlStr = getReturnMsgSubscribe(map);logger.info("用户扫码关注返回的xml:{}", resXmlStr);return resXmlStr;}public String getReturnMsgSubscribe(Map<String, Object> decryptMap) {logger.info("---开始封装xml---decryptMap:" + decryptMap.toString());TextMessage textMessage = new TextMessage();textMessage.setToUserName(decryptMap.get("FromUserName").toString());textMessage.setFromUserName(decryptMap.get("ToUserName").toString());textMessage.setCreateTime(System.currentTimeMillis());textMessage.setMsgType("text");textMessage.setContent("你好,欢迎关注XXX!\n" +"\n" +"关注XXX。立即登录PC端网址 \n" + domainname +" 即可完成注册!\n" +"\n" +"或," +"<a href='" + domainname + "'>点击这里立即完成注册</a>");return getXmlString(textMessage);}public String getXmlString(TextMessage textMessage) {String xml = "";if (textMessage != null) {xml = "<xml>";xml += "<ToUserName><![CDATA[";xml += textMessage.getToUserName();xml += "]]></ToUserName>";xml += "<FromUserName><![CDATA[";xml += textMessage.getFromUserName();xml += "]]></FromUserName>";xml += "<CreateTime>";xml += textMessage.getCreateTime();xml += "</CreateTime>";xml += "<MsgType><![CDATA[";xml += textMessage.getMsgType();xml += "]]></MsgType>";xml += "<Content><![CDATA[";xml += textMessage.getContent();xml += "]]></Content>";xml += "</xml>";}return xml;}

微信平台上配置触发事件的回调地址

登录微信公众号平台–>设置开发–>「基础配置」–>服务器配置

注意⚠️:
服务器地址 必须是外网可以访问的,否则微信调不通。

令牌 需要和代码里的token一致,因为提交服务器配置的时候,会触发配置好的那个外网地址,发送一个Get请求,进行验证签名。

消息加密密钥 自动生成即可。

消息加密方式 为了方便开发调试,可以选择兼容模式。

填好信息别忘记 提交。

服务器配置以后还可以修改的,每次修改也都会发Get请求验证签名。


签名验证代码:

 /**** 填写服务器URL点击提交时,微信服务器触发get请求用于检测签名* @return echoStr*/@GetMapping("/check/signature")@ResponseBodypublic String wechatCheckSignature(HttpServletRequest request) {log.error("-------------------服务器配置|进行签名检测------------------------");String signature = request.getParameter("signature");String timestamp = request.getParameter("timestamp");String nonce = request.getParameter("nonce");String echoStr = request.getParameter("echostr");boolean checkSignature = WechatPublicUtils.checkSignature(signature, timestamp, nonce, Constants.WX_SERVER_CONFIG_TOKEN);if (checkSignature) {return echoStr;}return null;}

校验签名工具类

public class WechatPublicUtils {/*** 校验签名** @param signature 微信签名* @param timestamp 时间戳* @param nonce     随机字符串* @param token     我们在公众号平台「基本配置」里定义的token* @return 验证结果*/public static boolean checkSignature(String signature, String timestamp, String nonce, String token) {// 将token、timestamp、nonce 进行字典序排序String[] arr = new String[]{token, timestamp, nonce};Arrays.sort(arr);// 字符串拼接StringBuilder content = new StringBuilder();for (int i = 0; i < arr.length; i++) {content.append(arr[i]);}MessageDigest md = null;String tmpStr = null;try {md = MessageDigest.getInstance("SHA-1");// sha1加密byte[] digest = md.digest(content.toString().getBytes());tmpStr = byteToStr(digest);} catch (NoSuchAlgorithmException e) {e.printStackTrace();}content = null;// sha1加密后的字符串与signature对比return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;}private static String byteToStr(byte[] byteArray) {StringBuilder strDigest = new StringBuilder();for (byte b : byteArray) {strDigest.append(byteToHexStr(b));}return strDigest.toString();}private static String byteToHexStr(byte mByte) {char[] digit = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};char[] tempArr = new char[2];tempArr[0] = digit[(mByte >>> 4) & 0X0F];tempArr[1] = digit[mByte & 0X0F];return new String(tempArr);}
}

微信公众号被动回复消息 Java实现相关推荐

  1. java springboot微信公众号接收回复消息和超过5秒被动回复消息

    本次就是记录一下我的开发过程,不是教程,纯属自己做个笔记. 现在项目有个需求,需要用户在公众号发送图片消息的时候,我后台程序能接收到这个图片,并用ai处理图片并返回信息. 1.首先第一步要接收微信消息 ...

  2. 微信公众号被动回复方案梳理

    微信公众号机器人客服回复方案预梳理 一.微信公众平台开发接入指南 接入微信公众平台开发,需要按照如下步骤完成: 1.填写服务器配置 登录微信公众平台官网后,在公众平台后台管理页面 - 开发者中心页,点 ...

  3. 微信公众号对话框回复消息链接跳转小程序

    在项目过程之中,可能会遇到这样的需求,点击微信公众平台开发的界面里的某个图标或者文字,跳转到小程序,这样更加有利于用户去访问小程序,如果是把小程序的卡片直接插入文章,这个功能在文章编辑一栏已经有现成的 ...

  4. 微信公众号 java发送消息_微信公众号发送模板消息 Java实现。

    本博文是测试公众号调用模板接口测试.请不要完全复制我的代码.里面的测试代码中有本人测试号的微信模板id.麻烦替换成自己的可以吗? 第一步:创建模板信息 第二步:准备模板代码实体类用到的属性自行加入就行 ...

  5. 关于微信公众号被动回复带有表情的文字

    表情的消息类型实质是文本消息.每个表情都有与之对应的表情代码.

  6. php公众号被动回复,微信公众号被动消息回复原理解析

    背景:某分厂需要实时查询工件堆放的位置,要求快速便捷,因此设计了采用微信公众号被动回复信息的方案. 技术实现:开发者服务器--基于Angular2框架的已发布网站,编程语言为Python,后台存储数据 ...

  7. 微信公众号被动消息回复实现

    引言 公众号经常会有一些自动回复,最近顺便研究了一下公众号被动回复的代码实现,并不是特别难:下面我用代码演示一下: 注:公众号的一些准备工作已经在<获取公众号二维码>一文中说过了,这里就不 ...

  8. Java微信公众号推送消息

    Java微信公众号推送消息 1.导入pom文件 2.编写yml文件 3.配置文件 4.配置bean 5.service 模板消息主要代码 6.外放接口测试 ==注:== 1.控制台报40164,解决方 ...

  9. 微信公众平台——被动回复用户消息

    微信公众平台--被动回复用户消息 开发模式下的回复信息基础接口,可用来向用户回复文本消息.图片消息.语音消息.视频消息.小视频消息.地理位置消息.链接消息. 1.回复文本消息 function Rep ...

  10. java 模板接口开发_微信公众平台 发送模板消息(Java接口开发)

    前言:最近一直再弄微信扫码推送图文消息和模板消息发送,感觉学习到了不少东西.今天先总结一下微信公众平台模板消息的发送.因为这个自己弄了很久,开始很多地方不明白,所以今天好好总结一下. 微信公众平台技术 ...

最新文章

  1. CISCO设备部分型号IOS下载
  2. 编程之美 3.10 分层遍历二叉树
  3. spring 配置文件无法加载,junit找不到xml配置文件java.lang.IllegalStateException: Failed to load ApplicationContext...
  4. selenium如何解决IE自动填充表单问题
  5. element vue 获取select 的label_Vue动态组件component的深度使用
  6. 从Java连接到Cassandra
  7. typora 分割线_实战 | 五分钟,使用Typora+PicGo提升百倍写作效率
  8. 95-32-010-ChannelPipeline-ChannelPipeline简介
  9. sublime3快捷键
  10. 4083. 最大公约数
  11. 淘淘商城系列——VMware添加已配置好的虚拟机
  12. 一起来看流星雨在线观看/在线播放/全集下载/在线直播全集
  13. latex参考文献bib基本格式_LaTeX 中的参考文献(.bib) BibTex 用法
  14. 2022最新谷歌商店上架流程_google play 上架流程
  15. Android Q 上的Biometric生物识别
  16. 用python计算邮费考虑是否加急,用python计算residuals
  17. SQL16号统计1~15号数据,1号统计上月15~月底数据
  18. 人行征信2.0对接服务:全业务种类数据,精细您的征信业务管理!
  19. 校验输入的拼音是否符合声母韵母搭配规则(不是根据汉字生成拼音)
  20. 2019java8u201环境变量_CentOS7.4安装jdk1.8.0_201、Tomcat-8.5.38环境

热门文章

  1. Windows明文密码获取
  2. matlab求一维热传导方程数值解代码,一维热传导方程的数值解
  3. Matlab实现分离变量法求解一维热传导方程的初边值问题
  4. 硬盘扩容linux重新检查,Linux 无损扩容磁盘
  5. 高性能RPC框架BRPC核心机制分析
  6. Revit二次开发——一个简单的插件
  7. 计算机等级考试如何评改试题,全国计算机考试上机考试是如何改卷的
  8. wps for linux字体缺失,wps for linux 字体库缺失问题的解决办法
  9. 24点递归实现(c语言)
  10. 模拟医院信息管理系统