微信小程序订阅消息需前后端协同:前端用uni.requestSubscribeMessage(用户点击触发)+ withSubscriptions: true检查权限,后端用缓存access_token调用/send接口;模板ID、字段名须完全一致,真机测试。

微信小程序的“订阅消息”在 uni-app 里不能直接发,必须走「前端引导授权 + 后端调用微信接口」的链路。跳过服务端、只在前端 uni.requestSubscribeMessage 拿到 accept 就以为完事了?那消息永远发不出去。
uni.getSetting 检查权限时必须传 withSubscriptions: true
很多开发者用 uni.getSetting() 却没拿到订阅状态,根本原因是漏了这个参数。默认不带它,返回的 res 里压根没有 subscriptionsSetting 字段。
-
withSubscriptions: false或不传 → 返回结果不含订阅相关字段,等同于白查 -
withSubscriptions: true→ 才能拿到res.subscriptionsSetting.mainSwitch(总开关)和itemSettings(各模板授权状态) - 即使用户点过“允许”,
itemSettings[templateId]也可能为undefined:说明该模板未被请求过,不是“拒绝”,而是“未触达”
uni.requestSubscribeMessage 必须由用户主动点击触发
微信强制要求该 API 只能在用户手势(如 @click)中调用,否则直接报错 errCode: -1, errMsg: "requestSubscribeMessage:fail api scope is not declared"。
- 不能放在
onLoad、onShow或定时器里自动调;也不能在success回调里嵌套调用另一个模板 - 一次最多传 10 个
tmplIds,但建议单次只传 1 个——避免用户点“拒绝”后整批失效,影响后续其他场景 - 返回的
res是对象,键名为模板 ID,值为'accept'/'reject'/'ban',不是布尔值,别用=== true判断
后端发消息必须用 access_token,且需自行缓存
前端拿到 accept 后,要立刻把 openid 和模板 ID 传给后端;后端不能每次发消息都重新拉 access_token,否则大概率触发频率限制(2000 次/天,且每 2 小时仅限 1 次获取)。
-
access_token有效期 2 小时,必须存在服务端内存或 Redis 中,带过期时间自动刷新 - 调用微信发送接口的 URL 是:
https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=xxx,不是模板消息旧地址 - 请求体里的
data字段必须严格匹配模板字段名,比如模板定义了thing1,就不能传keyword1;值必须是{ value: 'xxx' }结构,不能直接写字符串 - 如果返回
{"errcode":43101,"errmsg":"user refuse to accept the msg"},说明用户当时点了“拒绝”,且没再进设置打开——此时前端应提示“请前往设置开启”,并调用uni.openSetting({ withSubscriptions: true })
模板 ID 和字段名必须和微信公众平台完全一致
复制模板 ID 时多一个空格、少一个字母,或者字段名写成 thing1 却在后台看到的是 thing2,都会导致后端请求失败并返回 47001 错误。
- 模板 ID 在「功能 → 订阅消息 → 我的模板」里查看,长度固定为 32 位字母数字组合,注意区分大小写
- 字段名(如
thing1、time4)必须从模板详情页里逐字抄,不能靠记忆或“大概像” - 字段顺序无关,但每个字段的
value长度不能超限(通常中文 ≤ 15 字,英文 ≤ 30 字),超长会被微信截断且不报错 - 测试阶段务必用真机扫码,开发者工具里
requestSubscribeMessage始终返回accept,不具备真实授权逻辑
最易忽略的一点:用户首次拒绝后,再次调用 uni.requestSubscribeMessage 不会再弹窗,而是直接走 fail 回调。这时候你得先用 uni.getSetting 检查 itemSettings 状态,再决定是否跳转设置页——而不是无脑重试。











