做源码交易和转卡码平台的朋友都清楚一个痛点:用户付了钱,卡密或下载链接却要自己回到页面刷新查询,很多用户找不到入口就流失了,甚至误以为没发货而申请退款。而支付宝小程序模板消息(formId 机制)下线之后,很多人一下子不知道用什么方式在支付后主动触达用户。这篇文章就完整讲一遍 支付宝小程序订阅消息 的实战接入:从模板申请、前端授权,到后端 Python 发送、发送记录与重试,最后落地到"支付成功自动推送卡密"这个最实用的场景。

一、为什么是订阅消息:模板消息的迁移背景

早期支付宝小程序用的是模板消息,靠前端收集 formId 触发推送。随着平台对用户隐私和骚扰治理收紧,formId 机制已逐步下线,取而代之的是订阅消息(subscribe message)。订阅消息的核心变化有三点:

  • 必须由用户主动点击授权才能发送,无法静默推送
  • 一次授权对应一条消息(一次性订阅),发完即失效
  • 发送由服务端 API 完成,前端只负责引导授权

对商城、转卡码这类"支付后发货"的场景,订阅消息反而更合适:用户在支付成功页顺手点一次授权,系统在发货瞬间把卡密推过去,触达率远高于让用户自己回访页面。

二、准备工作:开通能力与申请模板

第一步在支付宝开放平台后台完成:进入「小程序开发 → 开发设置 → 消息推送」,开通订阅消息能力,然后创建模板。注意类目要和小程序的服务类目匹配,比如电商类目才能申请"订单发货通知"类模板。申请通过后会得到一个模板 ID(entityId),形如 8f0c4f8c2c0d4f4d9c2f...,前后端都要用到。

三、前端:支付成功后引导授权

前端用 my.requestSubscribeMessage 唤起授权弹窗,把模板 ID 放进 entityIds 数组。注意这个 API 只能在用户点击事件的回调里调用,不能在页面 onLoad 里静默调用。以支付宝小程序 axml 为例:

<!-- pay-success.axml -->
<view class="card">
  <text>支付成功,点击领取卡密</text>
  <button size="default" onTap="onSubscribe">开启发货通知</button>
</view>
// pay-success.js 用户点击事件回调中唤起授权
Page({
  onSubscribe() {
    my.requestSubscribeMessage({
      entityIds: ['8f0c4f8c2c0d4f4d9c2f...'], // 申请到的模板 ID
      success: (res) => {
        // res.subscribeStatus[模板ID] === 'accept' 表示授权成功
        const status = res.subscribeStatus['8f0c4f8c2c0d4f4d9c2f...'];
        if (status === 'accept') {
          my.showToast({ content: '发货后将第一时间通知您' });
        }
      },
      fail: () => {
        // 用户拒绝也不影响下单,发货时降级为站内信/短信
        console.log('user rejected subscribe');
      }
    });
  }
})

这里有个容易被忽略的坑:授权状态不要只存在前端。用户授权后,要把结果上报给后端(比如随订单状态一起提交),后端发送时才知道"这条订单能不能走订阅消息"。建议在订单表上加一个 subscribe_status 字段。

四、后端:Python 发送订阅消息

后端发送走开放平台 API alipay.open.app.mini.templatemessage.subscribe,推荐直接使用官方 alipay-sdk-python,RSA2 签名、时间戳、nonce 这些细节都封装好了。安装后代码如下:

pip install alipay-sdk-python
from alipay.aop.api.DefaultAlipayClient import DefaultAlipayClient
from alipay.aop.api.request.AlipayOpenAppMiniTemplatemessageSubscribeRequest \
    import AlipayOpenAppMiniTemplatemessageSubscribeRequest

# 初始化客户端:应用 ID、应用私钥、支付宝公钥、开放平台网关
client = DefaultAlipayClient(
    gateway="https://openapi.alipay.com/gateway.do",
    app_id="2026XXXXXXXXXXXX",
    private_key=open("/path/app_private_key.pem").read(),
    alipay_public_key=open("/path/alipay_public_key.pem").read(),
    sign_type="RSA2",
    charset="utf-8",
)

def send_subscribe_message(template_id, data, scene="order_delivery"):
    req = AlipayOpenAppMiniTemplatemessageSubscribeRequest()
    # biz_content:data 为模板字段名到值的映射,scene 标识业务场景
    req.biz_content = {
        "data": data,  # 例如 {"卡密": {"value": "ABC-123-XXX"}}
        "scene": scene,
    }
    resp = client.execute(req)
    # 返回 code=10000 表示发送成功
    if resp.code == "10000":
        return True
    # 常见失败:40004 业务校验失败 / 用户未授权 / 消息已失效
    log.error("subscribe send failed: %s %s", resp.code, resp.msg)
    return False

# 发货时组装模板字段并发送
ok = send_subscribe_message(
    "8f0c4f8c2c0d4f4d9c2f...",
    {"商品": {"value": "转卡码系统V3 完整源码"},
     "卡密": {"value": order.card_no},
     "提示": {"value": "请在 个人中心-我的卡密 中查看"}},
)

三个实战要点:一是 data 的字段名必须和后台模板里定义的字段完全一致,多一个少一个都会报参数校验错误;二是 code=10000 才算成功,其他返回码要进重试或降级流程;三是发送前先查一下订单的 subscribe_status,没授权的用户直接跳过,别白白浪费一次调用。

五、发送记录与失败重试

消息发送必须留痕,否则出了问题无从排查。建一张发送记录表:

CREATE TABLE subscribe_msg_log (
  id          BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  order_no    VARCHAR(64)  NOT NULL COMMENT '订单号',
  template_id VARCHAR(64)  NOT NULL COMMENT '模板ID',
  content     JSON         NOT NULL COMMENT '发送内容快照',
  status      TINYINT      NOT NULL DEFAULT 0 COMMENT '0待发送 1成功 2失败 3重试中',
  retry_count TINYINT      NOT NULL DEFAULT 0,
  err_code    VARCHAR(16)  DEFAULT NULL,
  created_at  DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at  DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  KEY idx_order (order_no),
  KEY idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订阅消息发送记录';

发送流程建议做成先落库、后发送:发货事务里先插入一条 status=0 的记录,再由支付回调后的异步任务扫描发送,成功后更新状态。再配一个定时重试任务,每 5 分钟扫一次 status=2retry_count < 3 的记录重发,就能覆盖大部分瞬时失败。

六、与转卡码系统集成:支付回调 → 自动推送

把上面几块串起来,就是转卡码平台的标准发货通知链路:支付宝异步通知(notify_url)验签通过 → 订单状态机迁移为已支付 → 生成卡密入库 → 检查用户订阅授权 → 组装模板字段发送订阅消息 → 写发送日志。核心代码如下:

# 支付回调处理(伪代码)
def on_pay_notify(order):
    # 1. 验签 + 幂等处理(防重复通知)
    if not verify_alipay_notify(request.form):
        return "failure"
    if order.status == "PAID":
        return "success"
    # 2. 状态迁移 + 生成卡密 + 落库发货
    card = issue_card(order)
    # 3. 用户授权过订阅消息就推送,否则降级为站内信
    if order.subscribe_status == "accept":
        send_subscribe_message(TEMPLATE_ID, {
            "商品": {"value": order.product_name},
            "卡密": {"value": card.code},
        })
    else:
        create_station_letter(order, card)
    return "success"

这套链路在我们的 转卡码系统 V3 里是开箱即用的:小程序端已封装授权弹窗,后端内置订阅消息发送与降级策略,支付回调、卡密生成、消息推送一条龙。如果你主要做 H5 生意,转卡码系统 V2 的站内信 + 短信通知方案同样成熟,无需小程序也能完成发货触达。

七、避坑清单

  • 订阅授权必须在点击事件中唤起,onLoad/onShow 里调用会直接失败
  • 一次授权 = 一条消息,发完即失效;可在"我的订单"页常驻授权入口,引导用户多次授权
  • 模板字段名、类型必须与后台模板一致,否则发送报参数校验错误
  • 发送结果以 code=10000 为准,前端 success 回调不代表发送成功
  • 授权状态要落库,前端状态不可信;用户拒绝授权时准备站内信/短信降级方案
  • 不要滥用:订阅消息用于发货、售后等强相关场景,营销类内容容易触发平台处罚

八、小结

订阅消息是目前支付宝小程序里唯一合规、稳定、免费的服务端触达通道。把"支付 → 授权 → 发货 → 推送"这条链路做好,卡密领取率和复购率都能明显提升。模板申请、前端授权、后端发送、日志重试四步走完,你的小程序就能在用户付款后第一时间把货送到手里。需要现成方案的话,欢迎到 源码商城 看看转卡码系统 V3 的小程序端实现,或者用 Codex Desktop 帮你把这套代码半小时内写完。