用户
 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,登录网站

小程序社区 首页 教程 实战教程 查看内容

微信小程序订阅消息:我踩过的坑和解决办法都在这里!

Rolan 2021-2-22 09:46

搜集分享使用订阅消息的流程方法以及各种奇葩问题,欢迎讨论~

不同点
  • 与「模板消息」不同的是,其是在用户点击触发或者支付成功之后,开发者可在7天内推送1-3条服务通知。而「订阅消息」则需要用户主动订阅消息通知,开发者才可向用户推送,但不受时间限制,具体发送信息条数根据该能力的不同类型有不同标准。

  • 此外,值得注意的是,使用「订阅消息」后,原小程序模板消息接口于2020年1月10日下线,也就无法再使用原接口推送模板消息。但是,微信服务号模板消息暂不受影响。

特点
  • 选择权回到用户手中。在小程序中,「订阅消息」像是一个开关,需要用户主动点击授权之后,小程序才能向其推送服务通知,当然,用户也可以随时拒收该小程序的服务通知。而此前,用户只能被动地接收消息。

  • 时长不受限制。「订阅消息」取消了7天内推送消息的时间限制,只要用户没有主动拒收消息推送,开发者就可以随时推送服务通知。对于服务周期超过7天的小程序而言,这就完美解决了此前的疑虑。

好了那我们来看看如何使用这个订阅消息吧~


1、简述大概流程

  • 小程序端发起的授权弹窗过程

顺便说一下如何获取用户授权信息wx.getting,及当用户关闭授权如何跳到设置授权页一条龙服务,附上其他诸多问题

  • 授权后服务端下发订阅消息流程

整理一下订阅发送各参数的具体意思,虽然文档有,但是有例子的话对着看更好理解

小程序端发起的授权弹窗过程

调起客户端小程序订阅消息界面,返回用户订阅消息的操作结果。当用户勾选了订阅面板中的“总是保持以上选择,不再询问”时,模板消息会被添加到用户的小程序设置页,通过 wx.getSetting 接口可获取用户对相关模板消息的订阅状态。

留意文档中的这句话 → 如果用户之前设置过不在询问的话,其实是调不起授权弹窗的,那我们怎么获取用户的授权信息呢?

wx.getSetting可以获取这些信息:对应的wx.getting文档
文档后面有示例代码这边就不在贴出来了

如果要获取 订阅消息的订阅状态 需要设置 withSubscriptions 为true即可(默认为false,不开不获取),成功获取后 authSetting 是有关于用户的一些权限,而 subscriptionsSetting 则是我们需要的订阅权限信息,里面有一个总开关 mainSwitch 就是你是否接受当前小程序的订阅信息,如果这个为false的话其实你根本调不起弹窗,那小伙伴就会问如果我要让他知道关闭了权限并且怎么引导他去开呢?

先贴出代码:

发送订阅消息逻辑处理

const SUBSCRIBE_ID = 'RHPuVfEyGe0q0n7lZyzz4r-zyGe07lZyzz4r3' // 模板IDgoCollectSet() {    let that = this;    if (wx.requestSubscribeMessage) {      wx.requestSubscribeMessage({        tmplIds: [SUBSCRIBE_ID],        success(res) {          if (res[SUBSCRIBE_ID] === 'accept') {            // 用户主动点击同意...do something          } else if (res[SUBSCRIBE_ID] === 'reject') {            // 用户主动点击拒绝...do something          } else {            wx.showToast({              title: '授权订阅消息有误',              icon: 'none'            })          }        },        fail(res) {          // 20004:用户关闭了主开关,无法进行订阅,引导开启          if (res.errCode == 20004) {            // 显示引导设置弹窗            that.setData({              isShowSetModel: true            })          }else{            // 其他错误信息码,对应文档找出原因            wx.showModal({              title: '提示',              content: res.errMsg,              showCancel: false            })          }        }      });    } else {      wx.showModal({        title: '提示',        content: '请更新您微信版本,来获取订阅消息功能',        showCancel: false      })    }  }

【wx.requestSubscribeMessage文档】

简述一下上面的代码,goCollectSet 方法则是授权订阅消息权限执行的方法,由于 wx.requestSubscribeMessage 需要基础库 2.4.4 后才支持,因此我们要做个判断,如果发现用户当前微信没有此方法,则提示更新微信版本;然后 tmplIds Array字段里面是填对应的订阅模板ID

【问】订阅模板ID哪里获取、哪里新建呢?
·
【答】
微信公众号平台 登录后,如果有多个小程序,选择当前开发的小程序,成功进入界面后,在 功能 板块 -> 订阅消息,里面可以新建模板也可以使用公共订阅模板,有模板的话直接复制对应的ID即可

然后如果此时用户开启了订阅消息通知权限的话,则会出现授权订阅消息的窗口啦,如图:

如果此时用户点【允许】此时就会返回 accept 状态,反之点【取消】则返回 reject,具体的状态字段如图

我们可以发现这个返回值,是用你的模板id作为键,所以获取的时候要根据模板ID获取,这边只说允许和拒绝的返回,对于 filter 的返回就是因为模板标题同名导致过滤的,所以新建的时候记得区分开已有的标题的模板禁止同名,success成功一般就这些,主要是用户自行的操作,【允许】后就会订阅一次,注意这里是说一次性订阅的订阅消息哦,关于区别可以看下面:

【问】一次性订阅模板和永久性订阅有什么区别?如果我需要用永久性订阅怎么操作?

【答】1、一次性订阅消息:用户订阅一次后,开发者可下发一条消息,不限时间。若用户勾选了“总是保持以上选择,不再询问”且点击了允许,那么以后都默认同意订阅这条消息。用户不再做多次选择,开发者也避免了更繁琐的提醒。

2、长期性订阅消息:用户订阅一次后,可长期下发多条消息。目前长期性订阅消息向 政务、医疗、交通、金融、教育 等线下公共服务开放,后续将综合评估行业需求和用户体验持续完善。(长期订阅消息只针对特定行业开放,所以普通开发者并无法使用)

好了回到正题,此时用户点击一次就相当于订阅了一次,相当于你拥有了一次获取系统通知的门票,理论上用户多次点击就相当于订阅了多次,此时是会将这些订阅次数储存起来,如果服务器下发通知时就会消耗一次订阅次数(理论上是这样的,文档也没有说,只要确保每次下发前触发一次订阅交互,所以想想还是长期性订阅香呀,一次授权终身使用~)

其次说说弹窗下面的【总是保持以上选择,不再询问】如果没有勾选,每次订阅时都是会弹窗的,而如果用户勾选了,此时执行的逻辑是没有变的,你勾选后点【允许】其实就直接执行了 'accept' 里的逻辑

反之如果你勾选了然后点了【取消】就是一直执行 reject 里的逻辑,所以有些小伙伴就会说,为什么每次授权时都没有弹窗还自动拒绝,其实【取消】就是拒绝的意思再加上勾选了不再询问就尴尬了…

这个误操作成本就很高了,以后不再弹窗还直接拒绝,如果用户某天想订阅了就不知道哪里订阅了,知道哪里设置(设置页)开启还好,如果不知道就尴尬了,有小伙伴就说那我们再拒绝的逻辑上加上引导弹窗的话不就行了?

但是从执行逻辑来看,有无勾选不再询问下点拒绝都是走 reject 的逻辑,如果这边就引导用户去设置页是不是有点奇怪,所以这边其实很蛋疼,只能祈求微信小程序开发大哥加个不再询问的拒绝返回标识多好,这样我们开发也可以根据对应标识返回来判断用户是普通的拒绝,还是不再询问的拒绝

当然上面的问题并不是没有解决方法,只是需要自定义一个弹窗,这个下面细讲,我们慢慢来…我们先说 fail 失败的逻辑。

能走到 fail 失败的逻辑直接查文档的错误代码表,这边就说几个常见的吧

【问】返回错误代码 20001、20002、20003、20004什么意思,怎么解决?

【答】20001 :没有模板数据,一般是模板 ID 不存在 或者和模板类型不对应 导致的

20001解决 :一般要注意代码写入的模板id有没有在微信公众号中,如果没有就新建;还有注意 tmplIds: [‘订阅模板id1’, ‘订阅模板id2’] 这种格式不要写错

20002 :模板消息类型 既有一次性的又有永久的

20002解决 :文档已经说明不能混用,所以检查写入tmplIds中的id是否同一种类型

20003 :模板消息数量超过上限

20003解决 :需要订阅的消息模板的id的集合,一次调用最多可订阅3条消息(注意:iOS客户端7.0.6版本、Android客户端7.0.7版本之后的一次性订阅/长期订阅才支持多个模板消息,iOS客户端7.0.5版本、Android客户端7.0.6版本之前的一次订阅只支持一个模板消息)

这个骚限制如果遇到的话,首先找产品开干,为什么这边要一下子搞那么多订阅一下让用户选择,能分开就分开;其次对于低版本的微信用户只支持一个模板消息的话,如果此时有必要两个一起订阅的话就直接提醒用户更新版本就行,否则会影响功能使用就行了·

20004 :用户关闭了主开关,无法进行订阅

20004解决 :这个就是用户设置页将订阅通知消息的总开关关闭了,这边就可以引导用户去设置页开启了

好了,这边主要说 20004 的错误返回,也就是如何引导用户去设置页;不是网上那些用图案去引导操作,我们这边直接利用 微信中button标签 open-type="openSetting" 直接直达,首先我们看看这个功能的基础库 2.0.7 ,丝毫不慌能用订阅模板消息 2.4.4 那这个功能也是可以用的,那我们看看wxml,对应的样式不贴出来了自定DIY

引导跳授权设置wxml

    <view class="jumpSetModel" wx:if="{{isShowSetModel}}">    <view class="jumpSetBox">     <view class="m-title">提示view>      <view class="m-content">检测到您未开启订阅消息通知权限,是否去设置?view>      <view class="m-control">        <button class="m-cancel" catchtap="closeSetModel">取消button>        <button class="m-confirm" open-type="openSetting" bindopensetting="openSetCallback">去设置button>      view>    view>  view>

如上图所示,如果返回 20004 的话我们就显示该弹窗,点去设置就会跳到设置页,舒服呀~

有些小伙伴就会说,如果设置开启后,手势返回会不会监听到,这个你不用担心,自动刷新订阅通知总开关的状态的,不影响再次执行的逻辑

好了,认真阅读的小伙伴是不是记得上面留了一个问题:如果用户点了不再询问并且取消的话,我们这样弹就行了(与普通拒绝一起使用)

授权后服务端下发订阅消息流程

调用消息订阅接口 wx.requestSubscribeMessage,获取下发权限后,接下来就是下发订阅消息啦~。通过文档我们可以知道有两种下发方式,一种是 HTTPS调用,另一种是使用 云函数

小程序·云开发 – 云函数

云调用是小程序·云开发提供的在云函数中调用微信开放接口的能力,需要在云函数中通过 wx-server-sdk 使用,云调用还可以免去了 access_token 的获取

代码如下:

const cloud = require('wx-server-sdk')cloud.init()exports.main = async (event, context) => {  try {    const result = await cloud.openapi.subscribeMessage.send({        touser: 'wx12345645645zawqead',  // 接受当前模板消息的用户openid        templateId: 'TEMPLATE_ID',   // 当前下发的模板ID,不可写多个,目前只支持一个        page: 'index?id=666',   // 定义用户点击该模板消息跳转的小程序路径        // 模板所需要的键值        data: {          thing1: {            value: '情感咨询课程'          },          amount1: {            value: '9.99'          },          date1: {            value: '2020-03-10 15:24:08'          }        }      })    return result  } catch (err) {    return err  }}

这样就可以下发到对应的模板到制定的用户啦~

后端服务器下发订阅消息

 $data = json_decode(file_get_contents('php://input'), true); $post_data = array(  // 用户的 openID,可用过 wx.getUserInfo 获取  "touser"           => $data["touser"],  // 小程序后台申添加的订阅消息模板 ID  "template_id"      => $data["template_id"],  // 点击模板消息后跳转到的页面,可以传递参数  "page"             => $data["page"],  // 发送给用户的数据  "data"             => $data["data"] ); // 发送 POST 请求的函数function send_post($url, $post_data){  $options = array(    'http' => array(      'method'  => 'POST',      'header'  => 'Content-type:application/json',      'content' => $post_data,      'timeout' => 60    )  );   $context = stream_context_create($options);  $result = file_get_contents($url, false, $context);   return $result;} // 小程序 appID 和 appSecret 获取 tokenfunction getAccessToken($appid, $appsecret){  $url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' . $appid . '&secret=' . $appsecret;  $html = file_get_contents($url);  $output = json_decode($html, true);  $access_token = $output['access_token'];   return $access_token;} // 这里替换为你的 appID 和 appSecret$url = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=" . getAccessToken($appid, $appsecret); $data = json_encode($post_data, true); $return = send_post($url, $data);var_dump($return);

这里贴出的是php的demo代码下发小程序订阅消息,关于 appID 和 appSecret 的获取可以在对应的 小程序公众号 里的开发设置中获取

【问】小程序云函数如何创建?

【答】关于使用小程序云开发创建云步骤可以参考该文章:微信小程序云开发之云函数的创建与环境配置

【问】下发时报错了怎么回事,怎么解决?

【答】

40003 touser字段openid为空或者不正确

40003解决 :注意这里的openid是接受该订阅消息用户的

43101 用户拒绝接受消息,如果用户之前曾经订阅过,则表示用户取消了订阅关系

43101解决 :这个报错就要注意在 小程序端发起的授权弹窗过程 中获取用户订阅授权是否
成功,如果没有成功请在获取授权操作中查找问题

47003 模板参数不准确,可能为空或者不满足规则,errmsg会提示具体是哪个字段出错

47003解决:订阅了一次性消息,但是发送时也有可能会失败,这个可能就跟内容有关了,模板 data 参数务必参考对应的模板id开放出来的模板字段进行填写( 订阅消息的模板内容参数非常严格,多一个字符或者与参数规定值不符都导致发送失败,记得对字数进行判断。 填写时对应参数时根据后台的模板填写,比如说 thing03、name05,后面都是带数字的 )


41030 page路径不正确,需要保证在线上版本小程序中存在,与app.json保持一致

41030解决:必须要填对,路径要小程序有的

关于服务器下发订阅消息的流程大概说到这里~

2、搜集解疑各种疑难杂症

订阅消息算是个大功能了,免不了开发时出现各种各样的问题,上面讲述流程中已经贴出了一些问题及对应的解决方法,这里就不在重复上面写的,这里就补充一下没有提及到的

1、开发者工具无法调试

后来试了一下在真机上可以吊起授权弹窗,逛了下开发社区才发现这是一个普遍的问题

不过现在的提示已经很直白了,这锅我们不背,记得要在真机上调试!

2、如果我发起两个订阅模板消息,但用户只收到其中一个,怎么回事?

首先 要确保 tmplIds 字段中写入两个相同类型的模板id ['模板id1','模板id2'] ,代码上没写错就会正常吊起授权窗口(前提是用户没有勾选 总是保持以上选择的选项),如果用户要接收两个的话必选勾选两个模板的,如果用户取消了其中一个的话当然是接受不到对应的模板消息的,所以可以在 wx.requestSubscribeMessage 中 success 逻辑中对应处理返回值,如果请求授权多个模板id的话对应的返回也是根据模板id作为键来返回的,所以对应处理即可

好了上面是用户没有勾选 「总是保持以上选项,不在询问」的情况,如果勾选了那就不会提示出弹窗,只能在「设置」界面重新打开,因为此时是不会弹窗的,关于引导在上面已经提及过了,这里就不在多诉

还有用户还可以再发送订阅通知的微信服务号中,进行取消订阅通知,这个也是直接会把设置里对应的通知接收关闭,同理引导也是跟上面一样

success(res) {   // 模板id1的处理逻辑   if (res['模板id1'] === 'accept') {     // 用户主动点击同意...do something   } else if (res['模板id1'] === 'reject') {     // 用户主动点击拒绝...do something   }     // 模板id2的处理逻辑   if (res['模板id2'] === 'accept') {     // 用户主动点击同意...do something   } else if (res['模板id2'] === 'reject') {     // 用户主动点击拒绝...do something     wx.showModal({       title: '提示',       content: '你取消了xxx的通知',       showCancel: false     })  } }

3、为什么我使用 wx.requestSubscribeMessage 调试时执行成功了也没吊起授权框?

首先要确保能调用成功(基础库支持,代码无错误),这时一般就是之前勾选过「总是保持以上选项,不在询问」这个选项,其次就是你的模板id是属于长期性订阅类型,这种类型,只要你一确认后就不会再给你弹授权窗了,只能根据状态让用户去设置中自行打开

4、我同时填写多个模板id字段,报了错:Templates count out of max bounds

(注意:iOS客户端7.0.6版本、Android客户端7.0.7版本之后的一次性订阅/长期订阅才支持多个模板消息,iOS客户端7.0.5版本、Android客户端7.0.6版本之前的一次订阅只支持一个模板消息)

消息模板id在[微信公众平台-功能-订阅消息]中配置。每个tmplId对应的模板标题需要不相同,否则会被过滤,所以也有小伙伴说为什么我填了三个模板id最后只显示两个,那就要去公众号平台看看是不是出现了标题相同的问题~

5、对于一次性订阅授权弹窗,为什么我点允许后,在该小程序设置里面没有看到该订阅消息呢?

对应一次性订阅消息来说,如果不勾选"总是保持以上选择,不再提示"的话,是不会加入小程序设置页里的,反之勾选了,小程序才会存储该用户在该小程序里的默认授权操作;

同理对于长期性订阅消息来说,用户允许或拒绝后都会直接存入设置中(可在设置中看到该长期性订阅的订阅状态)

并且我们可以通过 wx.getSetting 接口可获取用户相关模板消息的订阅状态。具体可以看官方文档: wx.getSetting

6. 如何获取用户openid?

首先对应openid的获取,有三种方法:

  1. 1、前台小程序通过 wx.login() 获取到的 code 用户登录凭证(有效期五分钟)。然后需要在开发者服务器后台调用auth.code2Session,使用code 换取 openid 和 session_key 等信息;【该操作是不需要用户授权的】

  2. 2、如果你是用云开发的话,可以调用云函数来获取这些信息,具体代码如下:

    用这个云函数就可以啦 cloud.getWXContext() (对于云部署之类的就不细讲,有疑问的可以参考上面提到的云函数部署文章)

    在getopenid云函数的index.js中写好后记得上传部署该云函数

// 云函数入口文件const cloud = require('wx-server-sdk')
cloud.init()
// 云函数入口函数exports.main = async (event, context) => { const wxContext = cloud.getWXContext()
return { event, openid: wxContext.OPENID, appid: wxContext.APPID, unionid: wxContext.UNIONID, }}
  1. 然后在需要的获取的界面调用该云即可

page{  data{    openid:'',  },  onLuanch(){    this.getopenid()  }  // 定义调用云函数获取openid  getOpenid(){    let page = this;    wx.cloud.callFunction({      name:'getOpenid',      complete:res=>{        var openid = res.result.openid        page.setData({          openid:openid        })      }    })  },}
  1. 【该操作也是不需要用户授权的】

  2. 3、通过 wx.getUserInfo 接口获取用户信息

    调用这个方法是需要用户授权的(就是平时我们看到的抽屉式授权信息弹窗),而且如果要获取像openid及其他敏感信息的话还要该参数 withCredentials 为 true 时,并且要求此前有调用过 wx.login 且登录态尚未过期,此时返回的数据会包含 encryptedData, iv 等敏感信息,然而接口返回的 openid 并非明文传输,还需要进行通过 加密数据解密算法 才能拿到明文值;

  3. 所以如果你只是单单获取 openid 的话,就不需要如此大费周章,用第一二中方法即可;此外该操作还需要用户授权同意才行,如果用户拒绝直接走 fail 逻辑;

用户openid会变吗还是唯一值?这个问题要分情况诉说:

  1. 同一个用户访问同一个小程序,他的openid是不会变的,第一次进来的是什么,之后进来该小程序都是同一个openid(唯一性)

  2. 同一个用户访问不同小程序,他的openid在各个小程序中都是不一样的

  3. 不同用户访问同一个小程序,各自用户的openid是不一样的
    总结:每个小程序都有一个身份值且唯一,他就是appid(就像人民币一样不可能有同一个码,出现就是假钱),而openid是跟appid关联管理的,所以openid其实也是唯一值


暂且列出这些问题,如果有其他问题可以一起讨论解决~

鲜花
鲜花
鸡蛋
鸡蛋
分享至 : QQ空间
收藏