【微信小程序】订阅消息_微信小程序订阅消息
概述
微信小程序订阅消息允许开发者在用户主动触发订阅后,向用户发送服务通知。这有助于提升用户体验和业务转化率。订阅消息分为一次性订阅和长期订阅两种类型,开发者需根据业务需求合理选择。
微信小程序的订阅消息分“一次性订阅”和“长期订阅”两种方式:
1、一次性订阅就是要用户点击同意一次消息订阅,服务端才能发送一次信息。点击多少次就能发送多少次。
2、长期订阅。服务端可以无限,但是教育、交通、医疗等行业才有长期订阅。
流程实现
1、申请消息模版
- 登录微信公众平台,选择小程序登录,进入“设置”-“基础功能”-“订阅消息”-“订阅消息模板管理”。
- 点击“添加模板”,选择合适的模板并添加,获取模板ID。
2、获取用户订阅权限
- 在小程序端,引导用户订阅消息。可以通过
wx.requestSubscribeMessage
API请求用户授权订阅特定模板消息。
3、后台订阅微信消息(可选)
配置服务器地址:
找到“开发与服务”-“开发管理”-“消息推送”,开启消息推送,配置业务服务器地址。
配置的地址需要支持外网访问,并同时需要GET和POST两种方式的API
GET方式API被微信服务器回调,验证上面配置的签名,Token等信息:
public boolean checkSignature(String signature, String timestamp, String nonce) { String[] array = new String[]{ token, timestamp, nonce}; //先对这三个字符串字典排序,然后拼接 Arrays.sort(array); StringBuilder sb = new StringBuilder(); for (String s : array) { sb.append(s); } //使用SHA-1算法对拼接字符串加密 MessageDigest messageDigest; String hexStr = null; try { messageDigest = MessageDigest.getInstance(\"SHA-1\"); byte[] digest = messageDigest.digest(sb.toString().getBytes()); //将加密后的字符串转换成16进制字符串 hexStr = CommonUtils.bytesToHexStr(digest); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } //返回校验结果 return signature.equalsIgnoreCase(hexStr); }
POST方法被微信回调,可以处理以下相关事件(微信文档):
- 当用户触发订阅消息弹框后,用户的相关行为事件结果会推送至开发者所配置的服务器地址
- 当用户在手机端服务通知里消息卡片右上角“...”管理消息时,相应的行为事件会推送至开发者所配置的服务器地址
- 调用订阅消息接口发送消息给用户的最终结果,会推送下发结果事件至开发者所配置的服务器地址
业务服务器可以将用户订阅消息的状态做记录,后期发送消息时,可以过滤掉没有接受订阅的用户,或通过其他手段对该部分用户单独处理。
@PostMapping(\"/subscribeMessageCallback\") public R handleSubscribeMessageCallback(HttpServletRequest request, HttpServletRequest httpRequest) { BufferedReader reader; StringBuilder requestContent = new StringBuilder(); //消息类型 try { reader = new BufferedReader(new InputStreamReader(request.getInputStream())); String line; while ((line = reader.readLine()) != null) { requestContent.append(line); } reader.close(); String jsonStr = requestContent.toString(); log.info(\"wechat callback message=[{}]\", jsonStr); JSONObject rootObject = JSONUtil.parseObj(jsonStr); // 在这里处理你的订阅消息事件,例如获取OpenID、模板ID和事件数据等 String openId = rootObject.getStr(\"FromUserName\"); // 注意:这里的MsgType可能需要根据实际情况调整,因为订阅消息的回调中可能没有直接的MsgType字段,而是其他表示事件类型的字段 String eventType = rootObject.getStr(\"MsgType\"); if(\"event\".equals(eventType)){ String event = rootObject.getStr(\"Event\"); String list = rootObject.getStr(\"List\"); // 小程序用户接受,或改变订阅状态事件 if(\"subscribe_msg_popup_event\".equals(event) || \"subscribe_msg_change_event\".equals(event)){ //小程序用户是否确定接受订阅消息事件 if(StrUtil.isNotBlank(list)){ JSONArray listArray = JSONUtil.parseArray(list); listArray.forEach(item -> { JSONObject jsonObject = JSONUtil.parseObj(item); String subscribeStatusString = jsonObject.getStr(\"SubscribeStatusString\"); String templateId = jsonObject.getStr(\"TemplateId\"); // 判断用户是否接受订阅消消息 boolean accept = \"accept\".equals(subscribeStatusString); MiniSubscribeUser plaMiniSubscribeUser = new MiniSubscribeUser(); plaMiniSubscribeUser.setOpenId(openId); plaMiniSubscribeUser.setTemplateId(templateId); plaMiniSubscribeUser.setAcceptState(accept ? \"1\" : \"2\"); plaMiniSubscribeUserService.saveOrUpdateSubscribeUser(plaMiniSubscribeUser); }); } } else if (\"subscribe_msg_sent_event\".equals(event)) { //小程序订阅消息是否发送成功事件 /** * \"List\": { * \"TemplateId\": \"BEwX0BO-T3MqK3Uc5oTU3CGBqzjpndk2jzUf7VfExd8\", * \"MsgID\": \"1864323726461255680\", * \"ErrorCode\": \"0\", * \"ErrorStatus\": \"success\" * } */ JSONArray listArray = JSONUtil.parseArray(list); listArray.forEach(item -> { JSONObject jsonObject = JSONUtil.parseObj(item); String msgId = jsonObject.getStr(\"MsgID\"); String templateId = jsonObject.getStr(\"TemplateId\"); String errorCode = jsonObject.getStr(\"ErrorCode\"); String errorStatus = jsonObject.getStr(\"ErrorStatus\"); log.info(\"小程序用户{},发送{}消息结果{}\", openId, templateId, errorStatus); }); } } // 返回给微信服务器的响应(通常是一个简单的成功响应) return R.ok(\"success\"); } catch (Exception e) { e.printStackTrace(); return R.fail(HttpStatus.BAD_REQUEST, e.getMessage()); } }
4、实现发送订阅消息的接口
在需要发送订阅消息的地方,调用微信提供的发送订阅消息接口。
POST https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=ACCESS_TOKEN
这通常涉及到调用微信的API,传递必要的参数如模板ID、用户OpenID、消息内容等。封装一个消息发送的对象:
public class WeChatSubscribeMsg { /** * GQ7x0v7gelxp7C8AmT2EcJMmop8evA8P2qSiIr7QEyw * { * \"touser\": \"OPENID\", * \"template_id\": \"TEMPLATE_ID\", * \"page\": \"index\", * \"data\": { * \"name01\": { * \"value\": \"某某\" * }, * \"amount01\": { * \"value\": \"¥100\" * }, * \"thing01\": { * \"value\": \"广州至北京\" * } , * \"date01\": { * \"value\": \"2018-01-01\" * } * } * } */ private String touser; private String userId; private String template_id; private String page; private TreeMap data; @Data public static class DataValue{ public String value; }
根据小程序appid和secret 获取小程序token 发送消息:
public Boolean sendSubscribeMessage(WeChatSubscribeMsg weChatSubscribeMsg) { String touser = weChatSubscribeMsg.getTouser(); String templateId = weChatSubscribeMsg.getTemplate_id(); MiniSubscribeUser miniSubscribeUser = getMiniSubscribeUser(templateId, touser); if (miniSubscribeUser == null ) { log.info(\"用户未关注小程序\"); return false; } if(miniSubscribeUser.getAcceptState().equals(\"2\")){ log.info(\"用户{}已拒绝接收订阅消息\", touser); return false; } String postData = JSONUtil.toJsonStr(weChatSubscribeMsg); val accessToken = wechatService.getMiniAccessToken(); String uniformSend = \"https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=ACCESS_TOKEN\"; uniformSend = uniformSend.replace(\"ACCESS_TOKEN\", accessToken); log.info(\"send Wechat subscribe msg Url :{} postData:{} \", uniformSend, postData); if (null != postData) { //http 调用第三方接口 String result = HttpUtil.post(uniformSend, postData); log.info(\"send wechat subscribe msg result:\" + result); JSONObject jsonObject = JSONUtil.parseObj(result); if (jsonObject.getInt(\"errcode\") == 0) { return true; } } return false; }