返回技术资讯

十堰小程序开发实战:微信订阅消息推送完整落地指南(授权率从28%提到71%)

十堰小程序开发订阅消息微信推送小程序运营

一、小程序没有推送,等于没有回头客

做过小程序的人都有一个共同感受:获客难,留存更难。用户扫一次码、领一张券、下一单,然后就再也没有打开过。原因很简单——小程序不像APP那样能常驻桌面,也不像公众号那样能群发图文,它没有任何"主动把用户叫回来"的常规手段。

微信给小程序留的唯一一个合规触达通道,就是订阅消息。用户主动授权后,商家可以在订单状态变化、预约即将开始、优惠即将过期、商品到货这些关键节点,向用户下发一条服务通知。这条通知会出现在微信的"服务通知"会话里,打开率远高于短信和邮件。

但我们在十堰小程序开发项目里看到的真实情况是:超过七成的企业小程序根本没有接入订阅消息;接了的那三成里,授权率普遍在25%到35%之间,还有大量推送调用直接返回 43101。这篇文章不讲概念,只讲怎么把它做成一套能稳定跑、授权率高、不超发的工程方案。

二、先搞清楚三种订阅类型,选错了代码写了也白写

微信的订阅消息分为三类,能力边界完全不同,很多团队第一步就选错:

  • 一次性订阅:用户授权一次,商家只能下发一条。下次再想推,必须再次引导用户点击授权。所有普通企业小程序(餐饮、零售、汽修、教培、美容)能用的就是这一类。
  • 长期订阅:用户授权一次,可长期下发。但只对特定行业类目开放——政务、医疗、交通、金融、教育、民生等,需要在微信公众平台后台提交类目资质申请,普通商业类目基本申请不下来。不要在这上面浪费时间。
  • 设备订阅:面向智能硬件(如共享设备、门禁、健康监测设备),与绝大多数企业小程序无关。

结论很明确:十堰的中小企业小程序,老老实实做一次性订阅,把"每次交互顺手拿一次授权"做到极致,比幻想长期订阅现实得多。一个用户一个月在你这里下单3次、预约2次,理论上你就有5次合法触达机会,关键在于你有没有在正确的时机把授权框弹出来。

2.1 一次性订阅的硬性规则

  1. 一次 wx.requestSubscribeMessage 调用最多传入 3 个模板ID;
  2. 用户可以选择"总是保持以上选择,不再询问",此后同一模板静默返回 accept,不再弹窗;
  3. 授权次数可以累积:用户点了3次授权,你就有3次下发额度;
  4. 额度不会过期,但也不能转让,且与 openid + 模板ID 绑定。

三、90%的授权率损失,来自弹窗时机不对

这是全文最值钱的一段。订阅消息的授权弹窗有一个无法绕过的限制:必须在用户点击(tap)事件的同步调用栈中触发。如果你把它放在接口请求的 success 回调里、放在 setTimeout 里、放在页面 onLoad 里,微信会直接报错:

requestSubscribeMessage:fail can only be invoked by user TAP gesture.

所以正确的做法不是"想推的时候再申请",而是把授权动作前置绑在一个用户本来就要点的按钮上。对比一下两种时机的实测数据(来自我们服务的十堰本地项目):

  • 错误时机:小程序启动后第一屏自动弹窗,或者下单成功后弹窗。用户此时没有心理预期,只觉得被打扰,授权率 28% 左右;
  • 正确时机:用户点击"提交预约""确认下单""领取优惠券"这些主动表达意图的按钮时,把订阅授权和业务请求合并成一次点击。用户知道"我要收到结果通知",授权率可以做到 65%~75%。

我们给十堰一家连锁汽车美容门店改造后,同一批用户的授权率从 31% 提升到 71%,改造内容只有一件事:把弹窗从"进入页面时"挪到了"点击预约按钮时"。

3.1 前端实现:一次点击,两件事

关键点:requestSubscribeMessage 必须在 tap 事件里第一个被调用,业务请求放在它的回调里。不要把顺序写反。

// pages/booking/booking.js
Page({
  data: { submitting: false },

  onTapSubmit() {
    if (this.data.submitting) return;
    this.setData({ submitting: true });

    // 1) 先在 tap 同步栈里请求订阅授权(最多3个模板)
    wx.requestSubscribeMessage({
      tmplIds: [
        'TPL_BOOKING_SUCCESS',   // 预约成功通知
        'TPL_BOOKING_REMIND',    // 到店前提醒
        'TPL_COUPON_EXPIRE'      // 优惠券到期提醒
      ],
      success: (res) => {
        // accept / reject / ban,逐个模板判断,不要整体判断
        const granted = Object.keys(res).filter(k => res[k] === 'accept');
        this.doSubmit(granted);
      },
      fail: () => {
        // 用户拒绝或环境不支持,业务必须继续,不能卡住下单
        this.doSubmit([]);
      }
    });
  },

  doSubmit(grantedTmplIds) {
    wx.request({
      url: 'https://api.example.com/api/booking',
      method: 'POST',
      data: { shopId: 12, slot: '2026-10-30 10:00', grantedTmplIds },
      success: () => {
        wx.showToast({ title: '预约成功', icon: 'success' });
      },
      complete: () => this.setData({ submitting: false })
    });
  }
});

两个细节容易被忽略:一是 success 回调里的返回值是每个模板一个键,必须逐项判断 accept,不能只看有没有报错;二是授权失败绝不能阻断业务,用户拒收通知不该导致下不了单,这是很多项目上线后被投诉的原因。

3.2 别忘了记录授权结果

把 grantedTmplIds 一起提交给后端,后端为每个模板ID给该用户加一次可用额度。这是后面"防超发"的基础,没有这张表,你的推送迟早会大面积返回 43101。

四、后端发送:access_token 和次数扣减是两个坑

4.1 access_token 必须中控缓存

发送订阅消息的接口是:

POST https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=ACCESS_TOKEN

access_token 有效期 7200 秒,且全局唯一。如果多个进程各自去刷新,会把彼此刚拿到的 token 顶掉,表现为"上午还正常,下午突然全部 40001"。正确做法只有两个:

  • 用 /cgi-bin/stable_token 接口获取稳定版 token(推荐,同一 appid 重复调用不会互相失效);
  • 或者自己搭一个中控服务,token 存 Redis,设置 7000 秒过期,全站只有一个进程负责刷新,其余进程只读。

如果你用的是微信云开发,直接调云函数里的 cloud.openapi.subscribeMessage.send,token 由平台托管,可以跳过这一节——这也是我们给十堰中小企业做小程序的默认选择,省掉一整类线上故障。

4.2 订阅次数扣减表:防止 43101 的核心设计

43101 的含义是"用户拒绝接收或没有订阅次数"。绝大多数团队把它当成偶发错误忽略,实际上是自己的账没算清。建议建一张这样的表:

CREATE TABLE user_subscribe (
  id            BIGINT PRIMARY KEY AUTO_INCREMENT,
  openid        VARCHAR(64) NOT NULL,
  template_id   VARCHAR(64) NOT NULL,
  granted_count INT NOT NULL DEFAULT 0,   -- 累计授权次数
  used_count    INT NOT NULL DEFAULT 0,   -- 已下发次数
  last_grant_at DATETIME NULL,
  UNIQUE KEY uk_openid_tpl (openid, template_id)
);

发送前的判断逻辑必须是原子的,用一条带条件的 UPDATE 抢占额度,抢到才发:

UPDATE user_subscribe
   SET used_count = used_count + 1
 WHERE openid = ? AND template_id = ?
   AND granted_count > used_count;

影响行数为 0 就说明额度用完了,此时不要硬发,直接降级:要么跳过,要么改走短信/公众号模板消息。这样既不会产生一堆失败日志,也能保证用户不会收到超出授权次数的骚扰。

4.3 一次完整的发送请求

{
  "touser": "oXXXXXXXXXXXXXXXXXXX",
  "template_id": "TPL_BOOKING_SUCCESS",
  "page": "pages/booking/detail?id=8821",
  "miniprogram_state": "formal",
  "lang": "zh_CN",
  "data": {
    "thing1":  { "value": "十堰北京路店·精洗套餐" },
    "time2":   { "value": "2026-10-30 10:00" },
    "thing3":  { "value": "请提前10分钟到店" }
  }
}

这里有几个必须遵守的格式约束,写错了就是 47003(参数错误):

  • data 的键必须与后台模板的关键词编号完全一致(thing1、time2、amount3……),不能自己起名;
  • thing 类型限 20 个字符以内,超出直接报错,中文按字符算;
  • time 类型必须是 2026-10-30 10:00:00 或 2026年10月30日 10:00 这类格式,不能传时间戳;
  • page 不能带前导斜杠,且页面必须已发布,否则用户点进去是白屏;
  • amount 类型单位是元,传"99.00"而不是"9900"。

五、错误码速查:把线上问题定位在五分钟内

  • 40001:token 失效,多为多进程抢刷新,改中控缓存;
  • 40003:openid 无效,检查是否用了公众号 openid 或测试号数据;
  • 43101:无订阅次数或用户拒收,检查扣减表,属于业务问题不是接口问题;
  • 47003:参数格式错误,90% 是 data 键名或字符长度不符合模板;
  • 45009:接口调用超过限额,注意单日总量与单用户频次;
  • 41030:page 路径不存在或未发布,跳转页必须先上线。

建议在后端把这几个码做成告警:43101 占比超过 30% 说明授权引导有问题;47003 出现一次就够你查半天,直接打到企业微信机器人。

六、两个十堰本地项目的实测数据

案例一:十堰某连锁汽车美容门店

业务场景是预约洗车。改造前,用户在页面停留、点预约、到店核销,全程没有任何通知,爽约率约 18%。改造后做了三件事:预约按钮合并订阅授权;预约成功立即下发一条"预约成功"通知;到店前 2 小时下发提醒。结果:授权率 31% → 71%,爽约率 18% → 7%,门店工位空转明显减少。技术上只是两个模板、一张订阅表、一个定时任务。

案例二:十堰某母婴用品店

这家店的问题是复购周期长,用户买完奶粉就消失。我们给它设计的是"到货通知 + 积分到期提醒"两个模板:用户点"到货提醒我"按钮时顺手授权,商品入库后自动下发;积分到期前 7 天再推一次。上线六周,30 天复购率提升约 12 个百分点,而被用户投诉的次数是 0——因为每条通知都对应一次用户主动表达过的需求。

七、合规红线:这三件事千万别做

  1. 不要用订阅消息做营销群发。模板内容必须与申请时填写的业务场景一致,发"双十一大促"这类纯促销内容,轻则模板被封,重则小程序被限制能力;
  2. 不要诱导授权。"不授权就不让下单""授权送5元"都属于违规诱导,被举报会扣信用分;
  3. 不要在夜间推送。非紧急通知建议限制在 8:00–21:00,用户体验和投诉率差别非常明显。

八、上线前检查清单

  1. 模板已在公众平台"订阅消息"中申请通过,且关键词编号与代码完全对应;
  2. requestSubscribeMessage 位于 tap 事件的同步调用栈首位,无异步包裹;
  3. 逐个模板判断 accept,授权失败不阻断主业务;
  4. 后端有 user_subscribe 额度表,发送前用条件 UPDATE 原子扣减;
  5. access_token 走 stable_token 或中控缓存,无多进程抢刷新;
  6. thing 字段做了 20 字符截断,time 字段格式统一;
  7. page 路径已发布、无前导斜杠、参数做了 URL 编码;
  8. 43101 与 47003 已接入告警,日志里保留 openid 后四位便于排查;
  9. 推送时段限制在 8:00–21:00,每条内容与业务场景强相关。

九、结语

订阅消息看起来只是一个 API 调用,真正难的是把"什么时候要授权""额度还剩几次""失败了怎么降级"这三件事工程化。做对了,它是十堰中小企业小程序里性价比最高的留存工具——零成本、打开率高、完全合规;做错了,就是一堆 43101 报错和用户投诉。

作为扎根十堰本地的技术服务商,十堰易度网络传媒有限公司在小程序开发中把订阅消息、支付、登录鉴权这些"看起来简单、上线就出事"的模块都做成了标准组件,服务过的十堰企业覆盖餐饮、汽修、零售、教培、母婴等多个行业。如果你手上的十堰小程序做完之后用户来了就走,不妨先按上面的清单自查一遍,多数问题并不需要重写代码,只需要把授权时机和额度账算清楚。欢迎有需求的十堰企业与我们交流。

准备好开始您的项目了吗?

无论是软件开发、网站建设还是APP定制,我们都能为您提供专业解决方案