Token 驗證成功,但發送消息給訂閱號無任何回復。經排查,核心原因有三:
官方示例代碼中僅調用 $wechatObj->valid(),**未調用 $wechatObj->responseMsg()**,自然無法處理用戶消息。
valid() 方法中有 echo $echoStr,若不註釋掉 $wechatObj->valid(),該輸出會混入 responseMsg() 的 XML 回復,導致微信服務器解析失敗。
Token 僅需驗證一次,驗證通過後務必註釋或刪除該調用。
微信要求 XML 回復為 UTF-8 編碼。官方 Demo 默認 ANSI,直接上傳可驗證通過;但改為 UTF-8 後 Token 驗證反而失敗。
**解決:Token 驗證階段用 ANSI 文件通過驗證後,將代碼另存為 UTF-8 覆蓋上傳**,並確保 responseMsg() 輸出的 XML 頭聲明編碼:
$textTpl = "<xml> <ToUserName><![CDATA[%s]]></ToUserName> <FromUserName><![CDATA[%s]]></FromUserName> <CreateTime>%s</CreateTime> <MsgType><![CDATA[%s]]></MsgType> <Content><![CDATA[%s]]></Content> <FuncFlag>0</FuncFlag> </xml>";
<?php
define('TOKEN', 'your_token_here');
$wechatObj = new WechatCallbackApiTest();
// $wechatObj->valid(); // 驗證通過後註釋掉
$wechatObj->responseMsg();
class WechatCallbackApiTest {
public function valid() {
$echoStr = $_GET['echostr'];
if ($this->checkSignature()) {
header('Content-Type: text/plain');
echo $echoStr;
exit;
}
}
public function responseMsg() {
$postStr = $GLOBALS['HTTP_RAW_POST_DATA'];
// $postStr = file_get_contents('php://input'); // 兼容 php://input
file_put_contents('log.txt', $postStr . "\n", FILE_APPEND); // 日誌調試
if (!empty($postStr)) {
libxml_disable_entity_loader(true); // 防 XXE
$postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
$fromUsername = $postObj->FromUserName;
$toUsername = $postObj->ToUserName;
$keyword = trim($postObj->Content);
$time = time();
$textTpl = "<xml>
<ToUserName><![CDATA[%s]]></ToUserName>
<FromUserName><![CDATA[%s]]></FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[%s]]></Content>
<FuncFlag>0</FuncFlag>
</xml>";
if (!empty($keyword)) {
$msgType = 'text';
$contentStr = 'Welcome to wechat world!';
$resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr);
echo $resultStr;
} else {
echo 'Input something...';
}
} else {
echo '';
exit;
}
}
private function checkSignature() {
if (!defined('TOKEN')) throw new Exception('TOKEN not defined!');
$signature = $_GET['signature'];
$timestamp = $_GET['timestamp'];
$nonce = $_GET['nonce'];
$token = TOKEN;
$tmpArr = [$token, $timestamp, $nonce];
sort($tmpArr, SORT_STRING);
$tmpStr = implode($tmpArr);
$tmpStr = sha1($tmpStr);
return $tmpStr === $signature;
}
}
file_put_contents('log.txt', ...) 記錄原始 POST 數據。php://input 替代 $GLOBALS['HTTP_RAW_POST_DATA'](PHP 5.6+ 棄用,7.0+ 移除)。