微信多客服功能允許公眾號將用戶對話轉接至人工客服介面。開發模式下,只需在收到用戶訊息時回傳特定 XML,即可觸發轉接,後續訊息自動導向客服系統。
當用戶發送訊息時,公眾號回傳:
1399197672
verifyUrl(); // 首次驗證
} else {
$this->responseMsg(); // 正常訊息處理
}
}
private function verifyUrl(): void {
$signature = $_GET['signature'] ?? '';
$timestamp = $_GET['timestamp'] ?? '';
$nonce = $_GET['nonce'] ?? '';
$echostr = $_GET['echostr'] ?? '';
$tmpArr = [self::TOKEN, $timestamp, $nonce];
sort($tmpArr, SORT_STRING);
if (sha1(implode($tmpArr)) === $signature) {
echo $echostr;
}
exit;
}
private function responseMsg(): void {
$postStr = file_get_contents('php://input');
if (empty($postStr)) { exit; }
$this->log("RECV: $postStr");
$postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
$msgType = trim($postObj->MsgType);
$result = match ($msgType) {
'event' => $this->handleEvent($postObj),
'text' => $this->handleText($postObj),
'image', 'voice', 'video', 'location', 'link' => $this->defaultReply(),
default => $this->defaultReply(),
};
$this->log("SEND: $result");
echo $result;
}
/** 事件處理:關注、掃碼、菜單點擊等 */
private function handleEvent($obj): string {
switch (trim($obj->Event)) {
case 'subscribe':
return $this->replyNews($obj, [[
'Title' => '歡迎關注',
'Description' => "使用方法:\n1. 輸入訂單號查詢物流\n2. 輸入「客服」聯繫人工",
'PicUrl' => 'https://example.com/welcome.jpg',
'Url' => ''
]]);
case 'CLICK': // 自定義菜單點擊
if ($obj->EventKey === 'CONTACT_SERVICE') {
return $this->transferToService($obj);
}
break;
}
return $this->defaultReply();
}
/** 文字訊息處理:關鍵字觸發客服 */
private function handleText($obj): string {
$keyword = trim($obj->Content);
$triggers = ['你好', '在嗎', '有人嗎', '客服', '投訴', '人工'];
foreach ($triggers as $word) {
if (str_contains($keyword, $word)) {
return $this->transferToService($obj);
}
}
// 其他業務邏輯...
if ($keyword === '時間') {
return $this->replyText($obj, date('Y-m-d H:i:s'));
}
return $this->defaultReply();
}
/** 核心:轉接多客服 */
private function transferToService($obj): string {
$tpl = <<
%s
XML;
return sprintf($tpl, $obj->FromUserName, $obj->ToUserName, time());
}
/** 回覆文字 */
private function replyText($obj, string $content): string {
$tpl = <<
%s
XML;
return sprintf($tpl, $obj->FromUserName, $obj->ToUserName, time(), $content);
}
/** 回覆圖文 */
private function replyNews($obj, array $articles): string {
$itemTpl = <<
XML;
$items = '';
foreach ($articles as $a) {
$items .= sprintf($itemTpl, $a['Title'], $a['Description'], $a['PicUrl'], $a['Url']);
}
$tpl = <<
%s
%d
$items
XML;
return sprintf($tpl, $obj->FromUserName, $obj->ToUserName, time(), count($articles));
}
private function defaultReply(): string {
return $this->replyText((object)['FromUserName'=>'','ToUserName'=>''], '收到,感謝您的訊息。');
}
private function log(string $msg): void {
// 實際專案請接入 Monolog 或寫入資料庫
$file = __DIR__ . '/wechat.log';
file_put_contents($file, date('[Y-m-d H:i:s] ') . $msg . "\n", FILE_APPEND);
}
}
// 入口
$app = new WechatCallback();
$app->handleRequest();
CLICK)回傳 transfer_customer_service 無效,必須使用 VIEW 類型菜單連結至客服頁面(https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=...&scene=123)class WechatServiceApi {
private $accessToken;
public function __construct(string $appId, string $secret) {
$this->accessToken = $this->getAccessToken($appId, $secret);
}
/** 獲取客服列表 */
public function getKfList(): array {
$url = "https://api.weixin.qq.com/cgi-bin/customservice/getkflist?access_token={$this->accessToken}";
return json_decode(file_get_contents($url), true)['kf_list'] ?? [];
}
/** 新增客服帳號 */
public function addKf(string $account, string $nickname, string $password): bool {
$url = "https://api.weixin.qq.com/customservice/kfaccount/add?access_token={$this->accessToken}";
$data = ['kf_account' => $account, 'nickname' => $nickname, 'password' => md5($password)];
$res = $this->postJson($url, $data);
return $res['errcode'] === 0;
}
/** 發送客服訊息(主動推送) */
public function sendMessage(string $openId, string $msgType, array $content): bool {
$url = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token={$this->accessToken}";
$data = ['touser' => $openId, 'msgtype' => $msgType, $msgType => $content];
$res = $this->postJson($url, $data);
return $res['errcode'] === 0;
}
private function getAccessToken(string $appId, string $secret): string {
// 實際請快取至 Redis,避免頻繁請求
$url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=$appId&secret=$secret";
return json_decode(file_get_contents($url), true)['access_token'] ?? '';
}
private function postJson(string $url, array $data): array {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($data, JSON_UNESCAPED_UNICODE),
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_RETURNTRANSFER => true,
]);
return json_decode(curl_exec($ch), true);
}
}