快速接入微信小程序的订阅消息
快速接入微信小程序的订阅消息
官方文档:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/subscribe-message.html
2020年11月17日15:59:14 以下内容如有变更,请以官方文档为准。
小程序订阅消息
消息能力是小程序能力中的重要组成,我们为开发者提供了订阅消息能力,以便实现服务的闭环和更优的体验。
订阅消息推送位置:服务通知
订阅消息下发条件:用户自主订阅
订阅消息卡片跳转能力:点击查看详情可跳转至该小程序的页面

订阅消息包括两种:
一次性订阅消息
一次性订阅消息用于解决用户使用小程序后,后续服务环节的通知问题。用户自主订阅后,开发者可不限时间地下发一条对应的服务消息;每条消息可单独订阅或退订。
长期订阅消息
一次性订阅消息可满足小程序的大部分服务场景需求,但线下公共服务领域存在一次性订阅无法满足的场景,如航班延误,需根据航班实时动态来多次发送消息提醒。为便于服务,我们提供了长期性订阅消息,用户订阅一次后,开发者可长期下发多条消息。
目前长期性订阅消息仅向政务民生、医疗、交通、金融、教育等线下公共服务开放,后期将逐步支持到其他线下公共服务业务。
使用说明
步骤一:获取模板 ID
在微信公众平台手动配置获取模板 ID:
登录 https://mp.weixin.qq.com 获取模板,如果没有合适的模板,可以申请添加新模板,审核通过后可使用。
步骤二:获取下发权限
详见小程序端消息订阅接口 wx.requestSubscribeMessage
步骤三:调用接口下发订阅消息
详见服务端消息发送接口 subscribeMessage.send
注意事项
用户勾选 “总是保持以上选择,不再询问” 之后,下次订阅调用 wx.requestSubscribeMessage 不会弹窗,保持之前的选择,修改选择需要打开小程序设置进行修改。
快速接入
请先按照官文文档写的步骤开通好订阅消息模板。
1、小程序代码
<!--index.wxml-->
<view class="container">
<text class="user-motto">{{openId}}</text>
<button class="btn green" bindtap="onSubscribe" hover-class="btn-hover">
订阅
</button>
</view>
//index.js
//获取应用实例
const app = getApp()
Page({
data: {
openId: '',
userInfo: {},
hasUserInfo: false,
canIUse: wx.canIUse('button.open-type.getUserInfo')
},
//事件处理函数
bindViewTap: function() {
wx.navigateTo({
url: '../logs/logs'
})
},
//订阅按钮事件
onSubscribe: function(e) {
let that = this
//申请订阅消息 模板IDXXX
wx.requestSubscribeMessage({
tmplIds: ['模板IDXXX'],
success(res) {
let status = res['模板IDXXX']
if(status === "reject"){
console.log("拒绝")
//提示: 您拒绝了消息提示,后续将接收不到消息通知,打开小程序设置进行修改。
}
console.log(that.openId)
//发送测试消息
wx.request({
url: 'http://localhost:8080/api/wxmini/sendSubscribeMessage',
data: {
openId: that.data.openId
},
success(res){
console.log(res.data)
}
})
}});
},
onLoad: function () {
let that =this
//获取微信登录openId
wx.login({
success (res) {
//console.log(res.code)
if (res.code) {
//发起网络请求
wx.request({
url: 'http://localhost:8080/api/wxmini/jscode2session',
data: {
code: res.code
},
success(res){
console.log(res.data.openId)
that.setData({
openId: res.data.openId
})
}
})
} else {
console.log('登录失败!' + res.errMsg)
}
}
})
}
})
package com.zhaojie.wechat.demo.controller;
import com.zhaojie.wechat.demo.service.IWxService;
import com.zhaojie.wechat.demo.vo.Jscode2sessionVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@Api(tags = {"微信小程序"})
@Slf4j
@Validated
@RestController
@RequestMapping("/api/wxmini")
public class WxMiniController {
@Autowired
private IWxService wxService;
@ApiOperation(value = "获取小程序用户的openid")
@GetMapping("/jscode2session")
@ApiImplicitParams({
@ApiImplicitParam(name = "code", value = "wx.login返回的code", required = true, dataType = "String")
})
public Object jscode2session(@RequestParam String code) {
Jscode2sessionVo jscode2sessionVo = wxService.jscode2session(code);
return jscode2sessionVo;
}
@ApiOperation(value = "小程序订阅消息发送")
@ApiImplicitParams({
@ApiImplicitParam(name = "openId", value = "接收者(用户)的 openid", required = true, dataType = "String")
})
@GetMapping("/sendSubscribeMessage")
public Object sendSubscribeMessage(@RequestParam String openId) {
return "小程序订阅消息" + wxService.sendSubscribeMessage(openId);
}
}
package com.zhaojie.wechat.demo.service.impl;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.zhaojie.wechat.demo.common.Constant;
import com.zhaojie.wechat.demo.service.IWxService;
import com.zhaojie.wechat.demo.vo.Jscode2sessionVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Slf4j
@Service
public class WxServiceImpl implements IWxService {
@Autowired
private RestTemplate restTemplate;
@Override
public Jscode2sessionVo jscode2session(String code) {
//登录凭证校验。通过 wx.login 接口获得临时登录凭证 code 后传到开发者服务器调用此接口完成登录流程。
String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + Constant.WX_APP_ID +
"&secret=" + Constant.WX_SECRET + "&js_code=" + code + "&grant_type=authorization_code";
String forObject = restTemplate.getForObject(url, String.class);
//返回的 JSON 数据包
//属性 类型 说明
//openid string 用户唯一标识
//session_key string 会话密钥
//unionid string 用户在开放平台的唯一标识符,在满足 UnionID 下发条件的情况下会返回,详见 UnionID 机制说明。
//errcode number 错误码
//errmsg string 错误信息
JSONObject result = JSONUtil.parseObj(forObject);
Jscode2sessionVo jscode2sessionVo = new Jscode2sessionVo();
jscode2sessionVo.setOpenId(result.getStr("openid"));
jscode2sessionVo.setErrmsg(result.getStr("errmsg"));
jscode2sessionVo.setErrcode(result.getStr("errcode"));
return jscode2sessionVo;
}
@Override
public String getAccessToken() {
try {
// 线上不需要每次都去获取access_token,需要把它缓存起来。我这里仅测试就不缓存了!!!
/*String redisAccessToken = redisSeConstantrvice.get(Constant.WX_ACCESS_TOKEN);
if (StrUtil.isNotEmpty(redisAccessToken)) {
return redisAccessToken;
}*/
//获取小程序全局唯一后台接口调用凭据(access_token)。调用绝大多数后台接口时都需使用 access_token,开发者需要进行妥善保存。
String url = "https://api.weixin.qq.com/cgi-bin/token?appid=" + Constant.WX_APP_ID +
"&secret=" + Constant.WX_SECRET + "&grant_type=client_credential";
String forObject = restTemplate.getForObject(url, String.class);
//返回的 JSON 数据包
//属性 类型 说明
//access_token string 获取到的凭证
//expires_in number 凭证有效时间,单位:秒。目前是7200秒之内的值。
//errcode number 错误码
//errmsg string 错误信息
log.error("获取小程序全局唯一后台接口调用凭据,msg{}", forObject);
JSONObject result = JSONUtil.parseObj(forObject);
String accessToken = result.getStr("access_token");
/*if (StrUtil.isNotEmpty(accessToken)) {
//存入Redis
redisService.set(Constant.WX_ACCESS_TOKEN, accessToken, 7200 * 1000L);
}*/
return accessToken;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public String sendSubscribeMessage(String openId) {
String accessToken = this.getAccessToken();
log.info("accessToken {}", accessToken);
HttpHeaders httpHeaders = new HttpHeaders();
//注意这里要是json格式的提交
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
//我这里测试直接拼接的,请按照你自己模板的配置去改下面的json字符串
//官方文档:https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.send.html
StringBuffer jsonParam = new StringBuffer();
jsonParam.append("{");
jsonParam.append("\"touser\": \"" + openId + "\",");
jsonParam.append("\"template_id\": \"" + Constant.WX_TMPL_ID + "\",");
jsonParam.append("\"page\": \"" + Constant.WX_TMPL_PAGE + "\",");
jsonParam.append("\"data\": {");
jsonParam.append("\"name1\": { \"value\": \"zhaojie\"},");
jsonParam.append("\"thing13\": { \"value\": \"预约产品\"},");
jsonParam.append("\"date3\": { \"value\": \"2020年11月17日 16:59\"},");
jsonParam.append("\"phrase9\": { \"value\": \"预约中\"},");
jsonParam.append("\"thing7\": { \"value\": \"如有疑问请联系客服人员\"}");
jsonParam.append("}");
jsonParam.append("}");
HttpEntity<String> httpEntity = new HttpEntity<>(jsonParam.toString(), httpHeaders);
ResponseEntity<String> entity = restTemplate.exchange("https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=" + accessToken,
HttpMethod.POST, httpEntity, String.class);
String result = entity.getBody();
log.info("result {}", result);
return result;
}
}
package com.zhaojie.wechat.demo.common;
/**
* 常量
*/
public class Constant {
// 系统前缀
public static final String SYS_PREFIX = "WECHAT-DEMO:";
// 小程序全局唯一后台接口调用凭据(access_token)。调用绝大多数后台接口时都需使用 access_token,开发者需要进行妥善保存。
public static final String WX_ACCESS_TOKEN = SYS_PREFIX + "WX_ACCESS_TOKEN";
// 小程序订阅消息模板 改成自己的参数
public static final String WX_TMPL_ID = "XXXXX;
public static final String WX_TMPL_PAGE = "pages/index/index";
// 小程序配置信息 改成自己的
public static final String WX_APP_ID = "XXXX";
public static final String WX_SECRET = "XXXX";
}
3、运行结果
注意未发布体验版无法接收到消息
浙公网安备 33010602011771号