微信模板訊息是服務號觸達用戶的重要管道(如訂單通知、付款成功、發貨提醒)。本文提供基於 ThinkPHP 框架的完整封裝類別,涵蓋 Access Token 管理、模板發送、錯誤處理。
<?php
namespace Org\Weixin;
/**
* 微信模板訊息發送類
* 依賴:ThinkPHP S() 快取、cURL 擴充
*/
class OrderPush {
protected $appid;
protected $secret;
protected $accessToken;
public function __construct($appid, $secret) {
$this->appid = $appid;
$this->secret = $secret;
$this->accessToken = $this->getToken($appid, $secret);
}
// ========== HTTP 請求封裝 ==========
protected function requestPost($url, $data) {
if (empty($url) || empty($data)) return false;
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $data,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => false,
CURLOPT_SSL_VERIFYPEER => false, // 生產環境建議驗證憑證
CURLOPT_TIMEOUT => 10,
]);
$response = curl_exec($ch);
$error = curl_error($ch);
curl_close($ch);
if ($error) {
// 記錄日誌:$error
return false;
}
return $response;
}
protected function requestGet($url) {
if (empty($url)) return false;
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_TIMEOUT => 10,
]);
$response = curl_exec($ch);
curl_close($ch);
return $response;
}
// ========== Access Token 管理(含快取) ==========
protected function getToken($appid, $secret) {
// 1. 嘗試從快取讀取
$cacheKey = "wx_access_token_{$appid}";
if (S($cacheKey)) {
return S($cacheKey);
}
// 2. 向微信請求新 Token
$url = "https://api.weixin.qq.com/cgi-bin/token" . http_build_query([
'grant_type' => 'client_credential',
'appid' => $appid,
'secret' => $secret,
]);
$response = $this->requestGet($url);
if (!$response) return false;
$result = json_decode($response, true);
if (json_last_error() !== JSON_ERROR_NONE || !isset($result['access_token'])) {
// 記錄錯誤:$result['errcode'], $result['errmsg']
return false;
}
$token = $result['access_token'];
// 3. 快取 7200 秒(微信 Token 有效期 2 小時,提前刷新)
S($cacheKey, $token, 7200);
return $token;
}
// ========== 發送模板訊息 ==========
/**
* 發送模板訊息
*
* @param string $touser 接收者 OpenID
* @param string $template_id 模板 ID(在公眾平台配置)
* @param string $url 點擊訊息跳轉連結
* @param array $data 模板資料(需符合模板定義的鍵名)
* @param string $topcolor 頂部顏色(預設 #7B68EE)
* @return bool 發送是否成功
*/
public function doSend($touser, $template_id, $url, $data, $topcolor = '#7B68EE') {
if (!$this->accessToken) {
// 記錄:Token 獲取失敗
return false;
}
// 1. 組裝訊息結構
$template = [
'touser' => $touser,
'template_id' => $template_id,
'url' => $url,
'topcolor' => $topcolor,
'data' => $data,
];
// 2. JSON 編碼(注意:微信要求 UTF-8,不轉義 Unicode)
$json = json_encode($template, JSON_UNESCAPED_UNICODE);
// 3. 發送請求
$apiUrl = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={$this->accessToken}";
$response = $this->requestPost($apiUrl, $json);
if (!$response) return false;
$result = json_decode($response, true);
// 4. 判斷結果(errcode 0 為成功)
if (isset($result['errcode']) && $result['errcode'] == 0) {
return true;
} else {
// 記錄失敗:$result['errcode'], $result['errmsg']
// 常見錯誤:40037 模板ID錯誤、43004 需要用戶關注、45009 發送頻率超限
return false;
}
}
}
?>
<?php
// 假設模板內容為:
// {{first.DATA}}
// 商品:{{name.DATA}}
// 金額:{{amount.DATA}}
// 時間:{{time.DATA}}
// {{remark.DATA}}
$data = [
'first' => [
'value' => "您好,您的訂單已付款成功!",
'color' => "#173177"
],
'name' => [
'value' => "iPhone 15 Pro Max 256GB 深空灰",
'color' => "#173177"
],
'amount' => [
'value' => "¥ 9,999.00",
'color' => "#E74C3C" // 金額用紅色突出
],
'time' => [
'value' => date('Y-m-d H:i:s'),
'color' => "#173177"
],
'remark' => [
'value' => "預計 1-3 個工作日發貨,請留意查收。",
'color' => "#173177"
],
];
?>
<?php
namespace Home\Controller;
use Think\Controller;
use Org\Weixin\OrderPush;
class OrderController extends Controller {
public function notifyUser() {
$appid = C('WEIXIN_APPID');
$secret = C('WEIXIN_SECRET');
$pusher = new OrderPush($appid, $secret);
$openid = 'oUpF8uMuAJ...'; // 用戶 OpenID
$templateId = 'TM_00123...'; // 模板 ID
$detailUrl = 'https://shop.example.com/order/12345';
$data = [ /* 如上方模板資料結構 */ ];
$success = $pusher->doSend($openid, $templateId, $detailUrl, $data);
if ($success) {
$this->ajaxReturn(['status' => 1, 'msg' => '模板訊息發送成功']);
} else {
$this->ajaxReturn(['status' => 0, 'msg' => '發送失敗,請檢查日誌']);
}
}
}
?>
| 項目 | 說明 |
|---|---|
| Access Token 快取 | 全域唯一、2小時有效、每日調用上限 2000 次,必須快取,避免重複請求導致封禁 |
| Token 刷新策略 | 建議提前 5-10 分鐘刷新(快取設定 7100-7150 秒),避免邊界失效 |
| 模板審核 | 模板需在微信公眾平台「模板庫」選用並提交審核,通過後獲得 template_id |
| 用戶授權 | 僅能發送給已關注公眾號的用戶(OpenID 來源:網頁授權、掃碼關注、客服訊息) |
| 發送頻率 | 同一用戶同一模板 1 分鐘 1 次、1 小時 5 次、1 天 20 次(視帳號等級調整) |
| 資料編碼 | 使用 JSON_UNESCAPED_UNICODE 避免中文被轉義為 \uXXXX,減少封包大小 |
| 錯誤碼處理 | 建立錯誤碼對照表,針對 43004(未關注)、45009(頻率限制) 等做重試或降級 |
微信模板訊息雖 API 簡單,但生產環境涉及 Token 管理、頻率控制、錯誤重試、記錄追蹤等工程化課題,建議封裝為獨立服務模組統一管理。