第一次接触微信小程序的订阅消息,之前见过的是公众号内部推送的那种模板消息。

先来看一下官方文档的介绍:传送门

功能介绍

  • 订阅消息推送位置:服务通知
  • 订阅消息下发条件:用户自主订阅
  • 订阅消息卡片跳转能力:点击查看详情可跳转至该小程序的页面

操作流程

步骤一:获取模板 ID

在微信公众平台手动配置获取模板 ID,模板 ID 用于步骤三中接口下发订阅消息。

步骤二:获取下发权限

详见小程序端消息订阅接口 wx.requestSubscribeMessage

有点像获取用户授权,但使用 getSetting 获取需要另加字段。需要用户点击 “总是允许”,才有具体的已订阅模板记录返回。

步骤三:调用接口下发订阅消息

详见服务端消息发送接口 subscribeMessage.send

一次性订阅的坑

微信小程序这个一次性订阅需要理解透才能不掉到坑里,我对接的时候是掉过好几次才勉强算是成功,一步一坑。

步骤一获取模板的坑

相对于一次性模板,那应该有对应的长久或者永久模板吧,但就是没有。公共模板库下方有一个 一次性订阅 的按钮,猜测之前应该是做筛选用的,现在无反应。没有尝试所有模板,但我通过 申请模板 创建的订单相关的几个模板成功后类型都是一次性的。

这一步要获取的信息有两个部分,一个是模板 ID,另一个是详细内容。模板 ID 确定是哪一个模板,详细内容决定展示的字段和顺序。

注意:详细内容中定义的字段名很重要,在步骤三中接口需要用到,少一个都不行。

步骤二获取下发权限的坑

wx.requestSubscribeMessage(Object object)

小程序前端调用这个 API 获取订阅权限是存在很多限制条件的:

  • 首先,基础库 2.4.4 开始支持这个 API,这个限制主要针对开发工具,线上基础库用户大多用的都是 2.10.4 了。
  • 只有在真机上调用 wx.requestSubscribeMessage(Object object) 才能选择 “总是允许” 的勾选项;只有选择 “总是允许” 后,wx.getSetting 接口才能获取到具体的已订阅模板记录。
  • 订阅模板 ID 对应的 tmplIds 参数在低版本基础库 2.4.4~2.8.3 只支持一个,也只能放一个,多了报错;在基础库 2.10.0 之后到目前 2.11.0 为止也只支持三个,也就是说你一次授权订阅消息,只能传三个,多了一样会报错。
  • 2.8.2 版本开始到现在 2.11.0 为止,用户发生点击行为或者发起支付回调后,才可以调起订阅消息界面。什么意思呢,就是只能在 bindTap 或者 wx.requestPaymentsuccess 回调中调用 wx.requestSubscribeMessage(Object object) 小程序才会真的执行获取权限这个动作。其他的任何情况调用 wx.requestSubscribeMessage(Object object) 是无效的,内部代码并不执行。

还有一个比较重要的坑点,因为订阅消息的一次性,决定了你需要在每次调用接口下发订阅消息前,都必须确保已经再次获取到了订阅权限。获取一次订阅权限,只能发送一次订阅消息。这么坑爹的设定,让我不禁想要口吐芬芳。这种 SB 一次性订阅消息意味着,它自己提供的公共模板库里 99.99% 的模板消息都触发不了。难道我要给小程序前端加一个按钮,让用户没事点两下?

步骤三调用接口下发订阅消息的坑

前面提到了,微信小程序接口不稳定,改动比较大且频繁。订阅消息服务端接口除了请求地址变化以外,作为模板内容的 data 参数也要做适配修改。

之前的 data 形式:

'data' => [
    'keyword1' => [
        'value' => $goods_names,
        'color' => '#333333',
    ],
    'keyword2' => [
        'value' => $this->order->express,
        'color' => '#333333',
    ],
    'keyword3' => [
        'value' => $this->order->express_no,
        'color' => '#333333',
    ],
    'keyword4' => [
        'value' => '订单已发货',
        'color' => '#333333',
    ],

现在的 data

'data' => [
    'thing1' => [
        'value' => $goods_names,
        'color' => '#333333',
    ],
    'character_string2' => [
        'value' => $this->order->order_no,
        'color' => '#333333',
    ],
    'amount3' => [
        'value' => $this->order->total_price,
        'color' => '#333333',
    ],
    'thing4' => [
        'value' => $remark,
        'color' => '#333333',
    ],
],

这里面的字段名要完全与(微信公众号后台申请的)模板消息详情里字段名一致,一个都不能少,字段值不能为空。颜色 color 字段看官方文档上已经没有了,但并没有报错。请求参数中的 form_id 在新版本里也不再需要。

测试下发订阅消息的过程中,频繁报错:43101 user refuse to accept message. 官方说明:“用户拒绝接受消息,如果用户之前曾经订阅过,则表示用户取消了订阅关系。” 但是我反复查看小程序前端设置里的订阅权限都没发现问题,无果。

在微信小程序文档的帖子里看到很多人提到这个问题,知道原因的比较少还讲不清楚。其实这就是上面提到的订阅消息一次性造成的。每次后台想要发送订阅消息,都得要小程序前端向用户发出申请。一次订阅,一次发送,点了 “总是允许” 也没有 “软” 用。前端根据 wx.getSetting 判断是否已获取订阅权限不仅没有 “软” 用,还会在后续造成逻辑上不能再获取订阅权限的 bug,所以必须去掉这样的判断逻辑。

总结

我的需求是,在用户支付成功,客户 O2O 配送之后给下单的用户发送一条到货信息(派送成功通知)。如果在用户支付成功后给用户发送订单支付成功的信息,那么后续再想发送到货信息就不太现实了。所以解决方案就是,去掉支付成功后发送订阅消息的代码。用户支付成功订阅一次,客户到货后台发送一次。

讲了这么多,总结一句话就是,微信小程序一次性订阅消息很坑,对接需谨慎。如果有其他消息通知的选择,比如短信之类的,还是不要趟这趟浑水了。

扩展阅读:

微信小程序的坑 (一) request 请求方法为POST,服务器端拿不到数据