PHP公众号模板消息推送

wen PHP项目 6

PHP实现微信公众号模板消息推送:从入门到实战(含完整代码)

目录导读

  1. 模板消息的核心价值与适用场景
  2. 前置准备:公众号权限与模板申请
  3. 推送流程原理解析(Access Token与消息结构)
  4. PHP完整实现代码(封装类+调用示例)
  5. 高频问题解答(Q&A)
  6. 性能优化与错误排查实战

模板消息的核心价值与适用场景

在微信生态中,模板消息是公众号向用户发送服务通知的重要通道,常用于订单状态变更、账户提醒、活动通知等场景,与客服消息(48小时窗口)不同,模板消息不受时间限制,但受模板行业类目用户主动触发(如一键授权)约束。

PHP公众号模板消息推送

核心优势

  • 高触达率:直接出现在用户聊天列表,显示“服务通知”入口
  • 结构化展示:支持关键词高亮、跳转链接、自定义颜色
  • 合规性强:不打扰用户,仅推送用户订阅过的内容

典型场景:电商发货提醒、课程开课通知、会员到期预警、预约成功确认。


前置准备:公众号权限与模板申请

  1. 账号类型:必须为已认证服务号(订阅号无模板消息权限)。
  2. 开通接口:登录[微信公众平台] → 「功能」→「模板消息」→ 选择行业(每月可修改1次)→ 从模板库中选用2个关键词模板。
  3. 关键参数
    • AppID、AppSecret(开发 → 基本配置)
    • 模板ID(模板消息页面获取,格式如 tmpl_xxxxx
    • 用户OpenID(需用户关注并通过OAuth或JS-SDK授权获取)

推送流程原理解析

获取全局Access Token

GET https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
  • 有效期7200秒,需缓存复用(建议存Redis或文件),避免频繁请求。

构造模板消息负载

{
    "touser": "OPENID",
    "template_id": "TEMPLATE_ID",
    "url": "https://example.com/detail",
    "miniprogram": { "appid": "小程序APPID", "pagepath": "pages/index" },
    "data": {
        "keyword1": { "value": "订单号", "color": "#173177" },
        "keyword2": { "value": "商品名称" }
    }
}
  • data 中的 keywordN 必须对应模板中定义的关键词顺序(通常是3-5个)。

发送请求

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

PHP完整实现代码

封装类 WechatTemplate.php

<?php
class WechatTemplate {
    private $appid;
    private $secret;
    private $access_token;
    private $token_cache_file = 'access_token.json';
    public function __construct($appid, $secret) {
        $this->appid = $appid;
        $this->secret = $secret;
        $this->access_token = $this->getAccessToken();
    }
    // 获取并缓存Token
    private function getAccessToken() {
        if (file_exists($this->token_cache_file)) {
            $data = json_decode(file_get_contents($this->token_cache_file), true);
            if ($data['expires'] > time() + 7200) {
                return $data['token'];
            }
        }
        $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$this->appid}&secret={$this->secret}";
        $res = json_decode(file_get_contents($url), true);
        if (isset($res['access_token'])) {
            file_put_contents($this->token_cache_file, json_encode([
                'token' => $res['access_token'],
                'expires' => time() + $res['expires_in']
            ]));
            return $res['access_token'];
        }
        throw new Exception('获取AccessToken失败: ' . $res['errmsg']);
    }
    // 发送模板消息
    public function sendTemplate($openid, $template_id, $data, $url = '', $miniprogram = []) {
        $msg = [
            'touser' => $openid,
            'template_id' => $template_id,
            'data' => $data
        ];
        if (!empty($url)) $msg['url'] = $url;
        if (!empty($miniprogram)) $msg['miniprogram'] = $miniprogram;
        $json = json_encode($msg, JSON_UNESCAPED_UNICODE);
        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_URL => "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={$this->access_token}",
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => $json,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER => ['Content-Type: application/json']
        ]);
        $result = curl_exec($ch);
        $errno = curl_errno($ch);
        curl_close($ch);
        if ($errno) return ['errcode' => -1, 'errmsg' => "curl错误: $errno"];
        $decoded = json_decode($result, true);
        return $decoded; // 返回 ercode=0 表示成功
    }
}

调用示例

require 'WechatTemplate.php';
$wechat = new WechatTemplate('你的APPID', '你的APPSECRET');
$openid = '用户OpenID';
$template_id = 'tmpl_xxxxxxxxxxxx';
$data = [
    'keyword1' => ['value' => '20240101001', 'color' => '#173177'],
    'keyword2' => ['value' => 'iPhone 15 Pro'],
    'keyword3' => ['value' => '已发货,顺丰快递']
];
$result = $wechat->sendTemplate($openid, $template_id, $data, 'https://example.com/order');
if ($result['errcode'] === 0) {
    echo "推送成功!";
} else {
    echo "失败:{$result['errmsg']}";
}

高频问题解答(Q&A)

Q1: 模板消息能否主动发送给没有互动的用户?
答:不能,必须满足“用户触发”条件,用户点击公众号菜单、提交表单、完成支付等,至少90天内用户有过一次互动,且每次互动可触发1-3条模板消息(具体规则见官方文档)。

Q2: 如何实现无限次推送?
答:可结合“一次性订阅”协议(subscribe场景),每次用户授权即可获得一次推送机会,适用于高频率场景。

Q3: 发送时提示“invalid template_id”或“41030”?
答:检查模板ID是否与当前公众号匹配,或是否已删除,注意模板必须与所选行业匹配,且关键词数量需与data中一致。

Q4: 用户未关注公众号,能否推送?
答:不能,用户必须关注服务号并拥有OpenID,若用户取关,则推送会返回 errcode: 43004

Q5: 如何调试 errcode: 40001(invalid credential)?
答:确认AppSecret未泄露,且Token未过期(缓存时间建议小于7200秒),可临时用 file_get_contents 获取Token并打印验证。


性能优化与错误排查

优化建议

  • Token缓存:使用Redis或共享内存,避免每次请求都调API
  • 批量推送:用 curl_multi 并发发送,提升效率(注意频率限制:每分钟最高10万次全量推送)
  • 日志监控:记录每次推送的 errmsg,针对 45009(并发过多)实施退避策略

常见错误码速查

错误码 含义 解决方案
40001 Token无效 重新获取
40037 模板ID错误 核对模板
43004 用户未关注 引导关注
45009 接口调用超限 降低频率,等待1分钟
47003 参数格式错误 检查 data 字段结构

掌握PHP公众号模板消息推送,本质是理解Token生命周期管理消息负载结构,本文提供了可直接运行的封装类,只需替换AppID/Secret即可上线,建议实际开发中增加异常重试机制,并监控微信回调的推送结果(msg_status 字段)以优化内容质量。

进阶思考:结合微信云开发或消息队列(如RabbitMQ)实现异步推送,可应对高并发场景,且不易触发接口限流,切勿将用户OpenID嵌入前端,防止泄露。


(本文所有示例均通过PHP 7.4+环境下测试,兼容PHP 8.0)

抱歉,评论功能暂时关闭!