模板消息是小程序向用户发送服务通知的一种重要方式,它允许开发者向已授权接收通知的用户发送特定格式的消息,如订单支付成功、物流更新、活动提醒等。

(图片来源网络,侵删)
核心概念与流程
在开始写代码之前,必须理解模板消息的整个工作流程:
-
获取用户的
formId/openid:formId(旧版,已废弃):用户在小程序内点击一个<form>组件时,会生成一个formId,这个formId在有效期内(7天)可以用来发送一次模板消息。openid(新版推荐):从基础库 8.2 开始,小程序不再使用formId,取而代之的是,当用户点击一个具有open-type="getPhoneNumber"的<button>组件时,或者开发者通过其他方式(如登录后获取)获取到用户的openid,发送模板消息时,直接使用openid。- 最佳实践:用户登录后,将
openid存储在你的数据库中,与用户ID关联起来,这样你就可以随时给该用户发送消息,而不需要用户再次点击。
-
配置模板消息:
- 在微信公众平台的小程序后台,进入「开发」->「开发管理」->「模板消息」。
- 选择「添加」,搜索并添加你需要的官方模板,每个模板都有一个唯一的
template_id。 - 你可以看到模板的格式,
{{thing1.DATA}}、{{number2.DATA}}、{{thing3.DATA}}等,这些是你在发送消息时需要填充的动态内容。
-
后端PHP发送请求:
(图片来源网络,侵删)- 你的PHP服务器作为客户端,向微信的API服务端发送一个HTTPS POST请求。
- 请求中需要包含:
touser(用户的openid)、template_id(你添加的模板ID)、以及data(一个包含模板所需键值对的JSON对象)。 - 这个请求需要使用小程序的
access_token进行鉴权。
-
处理发送结果:
微信API会返回一个JSON响应,告诉你发送成功或失败,你的PHP代码需要解析这个响应并做相应处理。
前置准备工作
-
小程序后台配置:
- 登录微信公众平台。
- 进入「设置」->「开发设置」,记录下
AppID和AppSecret,这是获取access_token的凭证。 - 进入「开发」->「开发管理」->「模板消息」,添加你需要的模板,并记下
template_id。
-
服务器环境:
(图片来源网络,侵删)- 确保你的PHP环境支持
cURL扩展,因为我们将用它来发送HTTPS请求。
- 确保你的PHP环境支持
PHP实现步骤
我们将分步实现:
- 获取
access_token - 封装发送模板消息的函数
- 在业务逻辑中调用发送函数
步骤1:获取 access_token
access_token 是调用微信接口的凭证,有效期为2小时,为了提高效率,我们应该将其缓存起来,而不是每次调用都去请求微信服务器。
<?php
class WeChatService
{
private $appId;
private $appSecret;
private $accessTokenCacheFile = '/tmp/wechat_access_token.json'; // 缓存文件路径,确保可写
public function __construct($appId, $appSecret)
{
$this->appId = $appId;
$this->appSecret = $appSecret;
}
/**
* 获取 access_token
* @return string|false 返回access_token,失败返回false
*/
public function getAccessToken()
{
// 1. 尝试从缓存读取
if (file_exists($this->accessTokenCacheFile)) {
$cacheData = json_decode(file_get_contents($this->accessTokenCacheFile), true);
if ($cacheData && $cacheData['expire_time'] > time()) {
return $cacheData['access_token'];
}
}
// 2. 缓存不存在或已过期,请求微信服务器
$url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$this->appId}&secret={$this->appSecret}";
$response = $this->httpGet($url);
if (!$response) {
return false; // 请求失败
}
$result = json_decode($response, true);
if (isset($result['access_token'])) {
// 3. 获取成功,更新缓存
$cache = [
'access_token' => $result['access_token'],
'expire_time' => time() + $result['expires_in'] - 300 // 提前5分钟过期
];
file_put_contents($this->accessTokenCacheFile, json_encode($cache));
return $result['access_token'];
}
return false; // 获取失败
}
/**
* 发送HTTP GET请求
* @param string $url
* @return string|false
*/
private function httpGet($url)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 跳过SSL证书验证,生产环境建议配置证书
$output = curl_exec($ch);
curl_close($ch);
return $output;
}
}
步骤2:封装发送模板消息的函数
这是核心功能,它将组合所有必需的参数并发送请求。
// 在 WeChatService 类中添加以下方法
/**
* 发送模板消息
* @param string $touser 用户的openid
* @param string $templateId 模板ID
* @param array $data 模板数据,格式如 ['key1' => ['value' => 'value1', 'color' => '#173177']]
* @param string $page 点击模板消息后跳转的小程序页面路径,如 'pages/index/index'
* @return array|false 返回微信API的响应结果,失败返回false
*/
public function sendTemplateMessage($touser, $templateId, $data, $page = '')
{
$accessToken = $this->getAccessToken();
if (!$accessToken) {
return false;
}
$url = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token={$accessToken}";
$postData = [
'touser' => $touser,
'template_id' => $templateId,
'data' => $data,
];
// 如果提供了page,则添加到请求中
if (!empty($page)) {
$postData['page'] = $page;
}
// 添加小程序的formid(如果使用旧版逻辑,这里需要调整)
// $postData['form_id'] = '用户的form_id';
$jsonString = json_encode($postData, JSON_UNESCAPED_UNICODE);
$response = $this->httpPost($url, $jsonString);
if (!$response) {
return false;
}
return json_decode($response, true);
}
/**
* 发送HTTP POST请求
* @param string $url
* @param string $postData
* @return string|false
*/
private function httpPost($url, $postData)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 跳过SSL证书验证
$output = curl_exec($ch);
curl_close($ch);
return $output;
}
$data 数组的格式:
它必须是一个关联数组,键名需要和你在模板后台看到的 {{xxx.DATA}} 中的 xxx 保持一致,每个值也是一个关联数组,包含 value 和可选的 color。
为:购买商品:{{thing1.DATA}},金额:{{amount2.DATA}}
$data 应该是:
$data = [
'thing1' => [
'value' => 'PHP入门教程',
'color' => '#173177'
],
'amount2' => [
'value' => '99.00元',
'color' => '#173177'
]
];
步骤3:在业务逻辑中调用
你可以在任何需要发送消息的地方,比如订单支付成功后,调用这个服务。
<?php
// 假设这是你的订单处理脚本
require_once 'WeChatService.php'; // 引入服务类
// 1. 初始化服务
$wechat = new WeChatService('你的小程序AppID', '你的小程序AppSecret');
// 2. 准备发送消息的数据
// 从数据库中获取用户信息、订单信息等
$userId = 123; // 假设这是当前用户ID
$userInfo = getUserInfoFromDB($userId); // 假设这个函数从DB获取用户信息,包含openid
$orderInfo = getOrderInfoFromDB($orderId); // 假设这个函数从DB获取订单信息
$templateId = '你的模板ID'; // 从小程序后台获取
$openPage = 'pages/order/detail?order_id=' . $orderId; // 点击后跳转的页面
$templateData = [
'thing1' => [ // 对应模板中的 {{thing1.DATA}}
'value' => $orderInfo['product_name'],
'color' => '#173177'
],
'amount2' => [ // 对应模板中的 {{amount2.DATA}}
'value' => $orderInfo['total_price'] . '元',
'color' => '#173177'
],
'thing3' => [ // 对应模板中的 {{thing3.DATA}}
'value' => '商家名称',
'color' => '#173177'
]
];
// 3. 调用发送方法
$result = $wechat->sendTemplateMessage($userInfo['openid'], $templateId, $templateData, $openPage);
// 4. 处理发送结果
if ($result && $result['errcode'] == 0) {
// 发送成功
echo "模板消息发送成功!";
// 可以在这里记录发送日志
} else {
// 发送失败
$errorMsg = isset($result['errmsg']) ? $result['errmsg'] : '未知错误';
echo "模板消息发送失败!错误码:{$result['errcode']}, 错误信息:{$errorMsg}";
// 记录错误日志,方便排查
}
// --- 辅助函数 (示例) ---
function getUserInfoFromDB($userId) {
// 实际项目中,这里应该是查询数据库
// 返回的数据必须包含 'openid'
return [
'openid' => '用户的openid_from_database',
'name' => '张三'
];
}
function getOrderInfoFromDB($orderId) {
// 实际项目中,这里应该是查询数据库
return [
'product_name' => 'PHP小程序开发实战',
'total_price' => 128.00
];
}
最佳实践与注意事项
access_token缓存:如上面代码所示,务必缓存access_token,避免频繁请求微信服务器,否则会被限制。- 错误处理:微信API返回的
errcode和errmsg非常重要。errcode=0表示成功,其他值都代表不同的错误(如41030表示template_id不存在,40003表示touser的openid为空),一定要做好错误处理和日志记录。 - 频率限制:模板消息有发送频率限制,例如每个用户每月最多接收4条(具体限制以官方文档为准),不要滥用,否则可能被限制发送权限。
- 用户授权:发送模板消息的前提是用户在小程序内与你有过交互(如点击
<button open-type="getPhoneNumber">)并授权了通知权限,确保你的小程序有合理的引导让用户授权。 - 内容合规:发送的消息内容必须符合微信平台的规范,不能包含敏感、违法信息。
- 异步发送:对于非即时性要求的通知(如订单支付成功后),建议将发送任务放入消息队列(如 RabbitMQ, Redis Queue)中异步处理,避免因网络延迟影响主流程的性能。
通过以上步骤,你就可以在你的PHP后端完整地实现小程序的模板消息功能了。
