微信模板消息已停用,当前唯一合法主动触达方式是用户主动授权的订阅消息,需满足已备案、7天内触发授权、模板ID后台申请三个前提。

微信模板消息推送前必须确认的三个前提
微信模板消息已停用多年,2022年起所有新注册公众号/小程序默认禁用该接口,存量应用也逐步下线。当前唯一合法的主动触达方式是「订阅消息」,且必须由用户在小程序或公众号内主动勾选授权。直接调用 POST @#@#@#@#@#@#@#@#@#@0 会返回 {"errcode":48002,"errmsg":"template message is not supported"}。
- 必须是已备案的小程序或服务号(订阅号不支持)
- 用户需在最近7天内通过
button.open-type="subscribe"触发过一次授权 - 模板ID需在微信公众平台后台手动申请,且仅限与用户行为强相关的类目(如订单提醒、预约成功)
用Gin实现订阅消息发送的核心流程
Gin本身不处理微信通信,重点是封装好HTTP请求并正确签名。关键在于构造符合微信要求的JSON体,并用 access_token 认证。
- 先调用
@#@#@#@#@#@#@#@#@#@1获取access_token(需缓存,有效期2小时) - 再 POST 到
@#@#@#@#@#@#@#@#@#@2 - 请求体必须包含
to_user(openid)、template_id、page(跳转路径)、data(字段值对象)
func sendSubscribeMsg(c *gin.Context) {
accessToken := getAccessToken() // 自行实现缓存逻辑
url := "https://www.php.cn/link/606ecba42fcf5ee2852e5d6c188a08a2?access_token=" + accessToken
<pre class="brush:php;toolbar:false;">payload := map[string]interface{}{
"touser": "oAbc123xyz...", // 用户 openid
"template_id": "zYxWvU...", // 后台申请的模板ID
"page": "pages/order/detail?id=123",
"data": map[string]map[string]string{
"thing1": {"value": "iPhone 15"},
"time2": {"value": "2024-06-15 14:30"},
},
}
jsonBytes, _ := json.Marshal(payload)
resp, _ := http.Post(url, "application/json", bytes.NewBuffer(jsonBytes))
// 解析 resp.Body 看 errcode 是否为 0}
Gin中容易忽略的两个坑
微信接口对字段名大小写和嵌套结构极其敏感,稍错就返回 errcode:41028(模板参数不匹配)或 errcode:43101(用户拒绝接收)。
- 模板字段名必须全小写,且与后台配置的关键词完全一致(如后台填的是
thing1,就不能传THING1或thing_1) -
data里每个字段必须是{"value": "xxx"}结构,不能直接写字符串或省略value键 -
access_token缓存失效时未重试会导致批量失败,建议用sync.Once或 Redis 做双重校验
调试时最有效的验证步骤
线上出问题基本都卡在 openid 或模板权限上,别急着改代码。
- 用 curl 手动测试:先用开发者工具抓到用户的
openid,再拼一个最简 payload 发送 - 检查模板是否“已通过审核”,状态为“启用”而非“待审核”或“已驳回”
- 查看用户是否在
mp.weixin.qq.com的「消息与通知」里关闭了该公众号/小程序的订阅权限
微信的错误码不报具体字段,只说“参数错误”,实际八成是 touser 不对或 template_id 复制漏了字符。把请求体打印出来,逐个字段跟后台模板设置对照一遍,比读文档快得多。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











