微信小程序订阅消息

6,198 阅读10分钟

这是我参与更文挑战的第3天,活动详情查看: 更文挑战

1. 前言

    小程序消息订阅其实是个挺常见的功能。因项目已有封装好的第三方依赖包、故一直没去研究小程序的消息订阅是如何实现的, 最近踩了个坑: 消息订阅面板偶尔出现偶而不出现, 百思不得其解, 痛下决心要好好理一理, 故有了此文。

2. 小程序消息

小程序的消息能力分为以下四种:

  • 订阅消息: 需要用户主动订阅消息通知, 开发者才可向用户推送、不受时间限制.
  • 统一服务消息
  • 客服消息
  • 位置消息
    此文围绕小程序的订阅消息做讲解, 若对其它消息感兴趣, 请前往官网查看: developers.weixin.qq.com/miniprogram…

订阅消息: 为开发者提供了订阅消息能力, 以便实现服务的闭环和更优的体验. 先来看看官网提供的基础知识: 订阅消息.png 需要留意下的是微信开发者工具vs真机的消息面板存在差异.
先来看看微信开发者工具(居中出现): image.png 再来看看真机效果图(从底部弹起面板): image.png 坑: 微信开发者工具上无法显示订阅消息的所有列表且无法勾选选择、也无法显示'总是保持以上选择, 不再询问'的选项.

基础知识具备了、接下来我们看看如何实现小程序的订阅消息!

3. 使用说明

3.1 获取模板ID

A. 打开微信公众平台地址,扫码登录后台, 并找到订阅消息tab.
微信公众平台链接请戳此: mp.weixin.qq.com/
若您未开通消息订阅模板, 会先提示您去开通此功能. image.png 以下是开通后的截图:
image.png

B. 手动配置模板ID(若在公共模板库中没有找到合适的,可以申请添加新模板、审核通过后即可使用).
首先, 选定xxx模板, 点击选用(为演示方便、选中公共模板库中的) image.png 然后, 会跳转至订阅消息设置页:在这里,可去配置订阅消息模板并会实时预览(左侧) image.png
配置完成后提交, 你就会在我的模板看到该条订阅消息相关信息. image.png 至此、我们就可以复制这个模板到代码处。

3.2 获取下发权限

关于小程序端消息订阅接口, 请戳此链接: developers.weixin.qq.com/miniprogram…
在发送小程序的订阅消息前由用户来决定是否需要收到订阅消息(即订阅面板)。

wx.requestSubscribeMessage(Object object) API可获取下发权限 调起客户端小程序订阅消息界面, 返回用户订阅消息的操作结果。
当用户勾选了订阅面板中的'总是保持以上选择、不再询问'时, 模板消息会被添加到用户的小程序设置页, 通过wx.getSetting接口可获取用户对相关模板的订阅状态。
2.8.2版本开始, 用户发生点击行为或者发起支付回调后、才可以调起订阅消息界面.

调用成功的回调函数说明如下: image.png

调用失败的回调函数如下(官网有提供错误码及其含义): image.png

接下来我主要演示下如何使用.

// wxml
<text class="user-motto" bind:tap="subscribe">点我唤起订阅面板</text>

// js
const app = getApp()
Page({
  subscribe () {
    wx.requestSubscribeMessage({
      tmplIds: ['xxxxxxxxxx'],
      success (res) {
        console.log('接口调用成功的回调函数', res)
      },
      fail (err) {
        console.log('接口调用失败的回调函数', err)
      },
      complete () {
        console.log('接口调用结束的回调函数(调用成功、失败都会执行)')
      }
    })
  }
})

点击按钮会出现订阅消息面板、点击允许/取消、均会给到相关回调、具体场景如下演示.

3.2.1 成功订阅(单条)

tmplIds只配置了一条、未勾选'总是保持以上选择, 不再询问'选项,点击允许接口调用成功、会回调如下信息: image.png
用户再次点击仍可唤醒订阅面板.

3.2.2 取消订阅(单条)

tmplIds只配置了一条、未勾选'总是保持以上选择, 不再询问'选项,点击取消接口调用成功、会回调如下信息: image.png
到了这里你会发现、只要接口调用成功、触发的都是success回调、只是模板ID对应的值可能是accept/reject/ban/filter.

模板ID值意义说明
accept用户同意订阅该条id对应的模板消息
reject用户拒绝订阅该条id对应的模板消息
ban已被后台封禁
filter该模板因为模板标题同名被后台过滤
3.2.3 部分订阅(多条)

假设现在有2条订阅消息、一条勾选、一条不勾选, 然后点击允许 image.png 成功回调里面会显示有条同意、有条拒绝了, 如下图: image.png 第二次点击会再重新弹窗授权。 这里有几个地方需要注意下:

一次授权调用里面,每个tmpId对应的模板标题不能存在相同的, 若出现相同的、则保留一个.
一次调用最多可订阅3条消息(需要留意客户端版本)
iOS客户端7.0.6版本、Android客户端7.0.7版本之后的一次性订阅/长期订阅才支持多个模板消息;
iOS客户端7.0.5版本、Android客户端7.0.6版本之前的一次订阅只支持一个模板消息;

若真超过了3条、微信小程序会直接抛出异常: requestSubscribeMessage:fail Templates count out of max bounds

// 获取应用实例
const app = getApp()
Page({
  subscribe () {
    wx.requestSubscribeMessage({
      tmplIds: ['xxxx1','xxxx2','xxxx3','xxx4'], // 4个模板ID不一样
      success (res) {
        console.log('接口调用成功的回调函数', res)
      },
      fail (err) {
        console.log('接口调用失败的回调函数', err)
      },
      complete () {
        console.log('接口调用结束的回调函数(调用成功、失败都会执行)')
      }
    })
  }
})

详细报错截图如下: image.png

3.2.4 全部订阅(多条)

假设现在有2条订阅消息、全部勾选, 然后点击允许 image.png 成功回调里面会显示2条都同意了, 如下图: image.png 第二次点击会再重新弹窗授权.

3.2.5 手动关闭订阅按钮(设置页)

跑去小程序原生设置页-通知管理-关闭接收通知 image.png 第二次点击时不会唤醒订阅面板, 请看如下提示. image.png
从提示里面得知: 'The main switch is switched off'.

3.3.6 勾选'总是保持以上选择, 不再询问'

假设现在有2条订阅消息、全部勾选, 并勾选'总是保持以上选择, 不再询问', 然后再点击允许. image.png 成功回调如下图: image.png 第二次点击时不会出现重新弹窗授权, 但代码是继续执行的. image.png

坑: 若勾选了'总是保持以上选择, 不再询问'选项, 代码层面仍会调用wx.requestSubscribeMessage()这个函数、只是不会出现弹窗而已, 若需要修改设置、要去到小程序原生设置页.

3.3.7 获取用户订阅通知权限

wx.getSetting可获取用户订阅通知权限.

// wx.getSetting()默认情况下是没法获取用户是否授权订阅消息的, 需要传参withSubscriptions
wx.getSetting({
  withSubscriptions: true,
  success (res) {
    console.log(res.subscriptionsSetting)
    // res.subscriptionsSetting = {
    //   "4xijn4T0leWUq6-aQnwPm2TCmmyttczwEktotiAuVhY": "reject",
    //   "F3GZ_x07Hc-tM7r2zOX7yRNgROoNIKx70C48mrHaTrs": "accept",
    //   "itemSettings": {
    //     "4xijn4T0leWUq6-aQnwPm2TCmmyttczwEktotiAuVhY": "reject",
    //     "F3GZ_x07Hc-tM7r2zOX7yRNgROoNIKx70C48mrHaTrs": "accept"
    //   },
    //   "mainSwitch": false
    // }
  }
})

image.png

用户在设置页开启或关闭订阅消息授权(wx.openSetting)、小程序目前没有提供相应的回调方法, 需要开发者调用wx.getSetting()去查看授权信息。

3.3 调用接口下发订阅消息

若用户在3.2中同意接收订阅消息、实现3.3后用户就可以接收到订阅消息了;
若用户在3.2中拒绝接收, 那么执行3.3用户也不会收到订阅消息.
先附赠官网详细说明: developers.weixin.qq.com/miniprogram…
调用方式:

  • HTTPS 调用
  • 云调用 本接口应该在服务器端调用, 作为个前端开发儿, 这里仅演示下云调用.

1.在云函数目录下的config.json中的permissions.openapi字段增加要调用的接口名字.

{
  "permissions": {
    "openapi": [
      "subscribeMessage.send"
    ]
  }
}

2.在云函数中使用云调用
云函数需要使用的版本号至少是0.4.0的wx-server-sdk, 建议始终保持最新, 保证云函数目录下的 package.json 的 wx-server-sdk 字段为 latest,如本地安装依赖,请执行 npm install --save wx-server-sdk@latest
若没安装过wx-server-sdk, 必须先安装.

// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init({
  env: cloud.DYNAMIC_CURRENT_ENV,
})
exports.main = async (event, context) => {
  try {
    const result = await cloud.openapi.subscribeMessage.send({
        "touser": cloud.getWXContext().OPENID, // 通过 getWXContext 获取 OPENID
        "page": '/pages/logs/logs?title=云开发传参',
        "lang": 'zh_CN',
        "data": {
          "date01": {
            "value": '2015年01月05日'
          }
        },
        "templateId": '0bzzt22P68jQtzFfWJHUEVKqVSOHDj48be3WAvuVJvw',
        "miniprogramState": 'developer'
      })
    return result
  } catch (err) {
    return err
  }
}

这一步要你先够好云开发环境、然后才能真正实现.

3.4 小程序页面接收订阅消息传进来的参数

点击服务通知卡片、会直接跳转至小程序卡片配置的页面路径, 开发者可以在onLoad生命周期函数中去接受传参数。

onLoad(options) {
  console.log(options,'跳转传参数')
}

4. 踩坑记录

4.1 订阅面板没法出现

报错代码如下:

  subscribe () {
    this.setData({
      status: 1
    },()=>{
      wx.requestSubscribeMessage({
        tmplIds: ['0bzzt22P68jQtzFfWJHUEVKqVSOHDj48be3WAvuVJvw'],
        success (res) {
          console.log('接口调用成功的回调函数', res)
        },
        fail (err) {
          console.log('接口调用失败的回调函数', err)
        },
        complete () {
          console.log('接口调用结束的回调函数(调用成功、失败都会执行)')
        }
      })
    })
  }

报错提示如下: image.png 报错原因: wx.requestSubscribeMessage()必须是同步的、不允许放在回调函数里面去实现(除了支付回调)

4.2 实现UI最后的坚持

设计师总是会有些花里胡哨的需求, 比如: 用户未订阅前、按钮文案显示: 提醒我订阅; 用户订阅后、按钮文案显示: 已设置订阅.
But若用户退出去、再从其它入口进来该页面、仍显示的是提醒我订阅文案(即使在退出页面前已订阅)

开发者无法得知该用户是否订阅过(初始状态下)

// wxml
<view>{{ isSubscribed ? '已设置订阅': '提醒我订阅'}}</view>
<text class="user-motto" bind:tap="subscribe">点我唤起订阅面板</text>

// js
const app = getApp()
Page({
  data:{
    isSubscribed: false
  },
  subscribe () {
    const tmpId = 'F3GZ_x07Hc-tM7r2zOX7yRNgROoNIKx70C48mrHaTrs'
    // 戳按钮前获取消息订阅权限
    wx.getSetting({
      withSubscriptions: true,
      success: (res) => {
        console.log('订阅消息设置', res)
        const { subscriptionsSetting } = res
        // 判定该订阅消息是否订阅过
        if (subscriptionsSetting && subscriptionsSetting.itemSettings && subscriptionsSetting.itemSettings[tmpId] && subscriptionsSetting.itemSettings[tmpId] === 'accept') {
          console.log('已订阅')
          return
        }
        // 未订阅后、允许出现订阅面板
        this.doSubscribe(tmpId)
      },
      fail: () => {
        // 未订阅后、允许出现订阅面板
        this.doSubscribe(tmpId)
      }
    })

  },
  doSubscribe (tmpId) {
    const _this = this
    if(tmpId){
      wx.requestSubscribeMessage({
        tmplIds: [tmpId],
        success (res) {
          console.log('接口调用成功的回调函数', res)
          const { errMsg } = res
          // 若用户点击允许、将文案改成: 已设置订阅
          if(errMsg === 'requestSubscribeMessage:ok' && res[tmpId] === 'accept'){
            _this.setData({
              isSubscribed: true
            })
          }
          // 若用户点击取消、文案不变, 点击再次唤醒订阅面板
        },
        fail (err) {
          console.log('接口调用失败的回调函数', err)
        },
        complete () {
          console.log('接口调用结束的回调函数(调用成功、失败都会执行)')
        }
      })
    }
  }
})

5. 总结

  1. wx.requestSubscribeMessage()必须是同步的、若放在异步操作后请求, 则会提示如下报错: 'can only be invoked by user tap gesture'(只能由用户点击手势调用)
  2. 若用户勾选了'总是保持以上选择, 不再询问'选项, 那么第二次请求不会再出现订阅面板;
  3. 用户点击过订阅消息的允许或拒绝, 还不知道如何去清除用户订阅消息授权记录.

6. 写在最后

若有错误之处, 恳请留言, 定会及时更正!
若觉着对您有帮助的话恳请点个赞或着收藏吧!