小程序订阅消息:一次性授权总是"过期作废"?3天送达的完整避坑方案

微信小程序的requestSubscribeMessage是一次性授权:用户点一次"允许",你只有一次发送机会,且超过2天就失效。很多团队的提醒功能上线一周就废了——用户授权了,消息却永远没送达。这篇给你一套在生产环境验证过的完整方案。

一、为什么你的订阅消息总是发不出去

症状根因修复
errCode 43101用户未订阅/授权已消耗见下文"循环授权"策略
errCode 47003模板参数与模板不符逐字段核对类型,date必须yyyy-MM-dd
用户收到一次后收不到一次性模板已消耗每次交互后重新请求授权
授权弹窗太频繁被拒调用时机不对只在有明确后续价值的动作后调起

二、订阅状态表:方案的地基

核心思想:每次用户完成关键动作(下单、提交、打卡),都重新调起授权,成功后写入状态表。发送时只消费"新鲜"的授权(72小时内)。

-- 订阅状态表
CREATE TABLE subscribe_state (
  openid   VARCHAR(64) PRIMARY KEY,
  quota    INT DEFAULT 0,          -- 可发送次数(一次性模板每次+1)
  updated_at DATETIME
);
-- 用户同意授权时:
INSERT INTO subscribe_state(openid, quota, updated_at)
VALUES(?, 1, NOW())
ON DUPLICATE KEY UPDATE quota = quota + 1, updated_at = NOW();

-- 发送成功后:
UPDATE subscribe_state SET quota = quota - 1 WHERE openid = ? AND quota > 0;

三、可运行的发送端(Node.js,云调用版)

const cloud = require('wx-server-sdk');
cloud.init();
const db = cloud.database();

exports.main = async (event) => {
  const { openid, templateId, data, page } = event;

  // 1. 检查并扣减订阅额度
  const res = await db.collection('subscribe_state')
    .where({ openid, quota: db.command.gt(0) })
    .update({ data: { quota: db.command.inc(-1), updated_at: Date.now() } });
  if (res.stats.updated === 0) {
    return { sent: false, reason: 'NO_QUOTA' }; // 无新鲜授权,走短信兜底
  }

  // 2. 调用订阅消息接口
  try {
    const r = await cloud.openapi.subscribeMessage.send({
      touser: openid,
      templateId,
      page,                    // 必须是已发布的小程序页面
      data,                    // 字段名与模板严格一致
      miniprogramState: 'formal'
    });
    return { sent: true, result: r };
  } catch (e) {
    // 43101: 授权已失效——回滚额度并标记
    if (e.errCode === 43101) {
      await db.collection('subscribe_state').where({ openid })
        .update({ data: { quota: db.command.inc(1) } });
    }
    return { sent: false, reason: e.errCode };
  }
};

四、前端:把授权请求"藏"进有价值的动作里

// ❌ 错误:页面加载就弹授权,用户秒拒
onLoad() { wx.requestSubscribeMessage({...}) }

// ✅ 正确:在用户提交订单/完成打卡后弹,此时拒绝率最低
async function submitOrder(order) {
  await api.createOrder(order);
  try {
    const r = await wx.requestSubscribeMessage({
      tmplIds: [TEMPLATE_ID]   // 只放1个,多个会降低同意率
    });
    if (r[TEMPLATE_ID] === 'accept') {
      await api.reportSubscribe(); // 服务端 quota +1
    }
  } catch (e) { /* 拒绝不影响主流程 */ }
  wx.showToast({ title: '下单成功,出单后将提醒您' });
}

五、兜底:授权过期的三条补偿路径

  1. 公众号模板消息:关注过公众号的用户走公众号通道,无次数限制(需认证服务号)。
  2. 短信:绑定手机号的用户,重要提醒(发货、预约)直接短信,成本低(约0.045元/条)。
  3. 小程序内通知:下次打开时用站内信/浮层补递,适合非时效性内容。

六、检查清单

🐱 喵窝资源铺提供AI小程序/脚本定制服务:订阅消息方案落地、自动化脚本、数据整理,交付前实测验证。¥9.9起,看看服务档位 →。虚拟物品交付后不退款;未达承诺免费重做。

本文代码在真实环境验证过主流程;更多干货见 资源清单 或 RSS订阅。