快速接入微信小程式的訂閱訊息
快速接入微信小程式的訂閱訊息
官方文件: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) } } }) } })
2、後臺介面核心程式碼
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、執行結果
注意未釋出體驗版無法接收到訊息