订阅消息替代了旧模板消息

早期小程序用「模板消息」主动给用户推送,由于发送过于随意,后来被微信收紧。现在统一替换为「订阅消息」:用户必须先明确订阅一次,开发者才有一次(或持续的)下发资格。这一变化的核心是把「主动权」交还给用户,也要求开发者把推送用在真正有价值的服务节点上。

订阅消息不是营销工具,而是服务的延续——订单状态、审批结果、预约提醒,才是它的正确用法。
上手前要准备
  • 模板在公众平台「订阅消息」里选用公共模板或自建模板,拿到模板 id。
  • 字段确认模板内每个字段的类型(名称、时间、数字等)与长度限制。
  • 令牌后端能稳定换取 access_token,这是下发的前置。
订阅消息实战配图:抽象手机上方漂浮对话气泡与铃铛通知图标
一次订阅授权,换一次服务提醒;授权与下发必须一一对应。

一次性订阅与长期订阅的区别

一次性订阅是默认形态:用户点一次「允许」,你只能下发一条对应模板的消息,发完即失效,想再发需要用户再次订阅。长期订阅则面向政务、医疗、交通等特定类目,需要提交资质审核通过后才开放,通过后可在一定周期内多次下发。绝大多数普通电商、工具类小程序,能用好一次性订阅就已经足够。

产品设计上的关键判断是:把订阅授权「用在刀刃上」。不要在用户刚打开小程序就弹窗索要订阅,而应在他即将产生需要通知的行为(下单、预约、提交申请)时,顺势弹出「是否在结果出来时通知你」,此时同意率最高,也最符合场景。

前端授权与后端下发的完整链路

前端通过 wx.requestSubscribeMessage 唤起授权弹窗。用户同意后,前端拿到这次订阅资格,把需要通知的业务事件上报给后端;后端在条件满足时(例如订单发货)调用下发接口。下面这段用普通字符串拼接组装数据,避免在界面逻辑里使用大括号模板语法。

// 前端:在用户下单成功页唤起订阅授权
wx.requestSubscribeMessage({
  tmplIds: ['TEMPLATE_ID_DELIVERY'],
  success(res) {
    if (res['TEMPLATE_ID_DELIVERY'] === 'accept') {
      wx.request({ url: '/api/subscribe/grant', method: 'POST' });
    }
  }
});
// 后端(Node 示例):订单发货时下发服务通知
const payload = {
  touser: openid,
  template_id: 'TEMPLATE_ID_DELIVERY',
  page: 'pages/order/detail?id=' + orderId,
  data: {
    thing1: { value: '包裹已发出' },
    time2: { value: new Date().toLocaleString('zh-CN') }
  }
};
await sendSubscribeMessage(payload, accessToken);

注意下发接口要求的 access_token 有效期约两小时,后端要集中缓存与刷新,不要每次下发都重新获取,否则容易触发微信的频率限制。

下发配额与次数限制

一次性订阅的本质决定了它的限制:用户授权几次,你最多就能发几次,不能超发。超发会被微信直接拒绝。因此后端要为每个 openid、每个模板维护一个「剩余可发次数」的账本,授权时加一,成功下发时减一。

配额管理要点
  • 记账订阅授权入库 +1,成功下发 -1,失败不扣。
  • 幂等同一业务事件只发一次,防重复触发。
  • 兜底次数不足时,改为在小程序内的「消息中心」留痕。
  • 节流下发失败要降级重试,而不是无限轰炸接口。

合规要点与用户体验

订阅消息有明确的内容红线:只能用于服务状态通知,不能发营销促销、不能诱导分享、不能把通知当广告位。模板字段的内容要与模板标题语义一致,不能把「备注」字段塞满推广话术。违规会被封模板、限制下发能力,严重的下架小程序。

体验上还有两个细节:一是通知里的 page 要带正确参数,让用户点进来直接落到对应订单页,而不是首页;二是控制频率,即使用户多次订阅,也不要把同一事件重复推送,否则只会换来关闭通知和卸载。