在企业微信应用中设置API接收

需要配置可信ip
URL验证

当点击“保存”提交以上信息时,企业微信会发送一条验证消息到填写的URL,发送方法为GET。企业的接收消息服务器接收到验证请求后,需要作出正确的响应才能通过URL验证。
假设你的api设置的地址为https://xxx.com,那么企业微信会提交如下请求:http://xxx.com/?msg_signature=ASDFQWEXZCVAQFASDFASDFS×tamp=13500001234&nonce=123412323&echostr=ENCRYPT_STR
| 参数 | 必须 | 说明 |
|---|---|---|
| msg_signature | 是 | 企业微信加密签名,msg_signature结合了企业填写的token、请求中的timestamp、nonce参数、加密的消息体 |
| timestamp | 是 | 时间戳 |
| nonce | 是 | 随机数 |
| echostr | 是 | 加密的字符串。需要解密得到消息内容明文,解密后有random、msg_len、msg、CorpID四个字段,其中msg即为消息内容明文 |
使用官方加密库:https://github.com/sbzhu/weworkapi_php/tree/master/callback
include_once "WXBizMsgCrypt.php";
通过$_GET方式接收这四个参数。
include_once "callback/WXBizMsgCrypt.php";
$encodingAesKey = "你设置的encodingAesKey";
$token = "你设置的token";
$corpId = "企业微信ID";
$sVerifyMsgSig = $_GET["msg_signature"] ;
$sVerifyTimeStamp = $_GET["timestamp"];
$sVerifyNonce = $_GET["nonce"];
$sVerifyEchoStr = $_GET["echostr"];
接收到参数之后我们需要对msg_signature进行效验,然后解析echostr。
$EchoStr = "";
$wxcpt = new WXBizMsgCrypt($token, $encodingAesKey, $corpId);
$errCode = $wxcpt->VerifyURL($sVerifyMsgSig, $sVerifyTimeStamp, $sVerifyNonce, $sVerifyEchoStr, $sEchoStr);
if ($errCode == 0) {
echo $sEchoStr;//原样返回明文消息内容
} else {
print("ERR: " . $errCode . "\n\n");
}
echostr 报错解决,PHP版本问题,加一句:
error_reporting(0);
接收消息
$sReqData = file_get_contents("php://input");
$sMsg = "";
$wxcpt = new WXBizMsgCrypt($token, $encodingAesKey, $corpId);
$errCode = $wxcpt->DecryptMsg($sVerifyMsgSig, $sVerifyTimeStamp, $sVerifyNonce, $sReqData, $sMsg);
if ($errCode == 0) {
try {
$test = new SimpleXMLElement($sMsg);
$Content = strval($test->Content);//解析出的明文内容
$FromUserName = strval($test->FromUserName);//发送消息的人
$CreateTime = strval($test->CreateTime);
$MsgId = strval($test->MsgId);
} catch (Exception $e) {
$Content = "错误!";
}
} else {
print("ERR: " . $errCode . "\n\n");
}
然后就可以对接收的消息进行处理了
简单版:
<?php
error_reporting(0);
include_once "callback/callback.php";
//接收消息服务器配置
$encodingAesKey = "$encodingAesKey";
$token = "$token";
$corpId = "$corpId";
$sVerifyMsgSig = $_GET["msg_signature"] ;
$sVerifyTimeStamp = $_GET["timestamp"];
$sVerifyNonce = $_GET["nonce"];
$sVerifyEchoStr = $_GET["echostr"];
//## URL验证
$EchoStr = "";
$wxcpt = new WXBizMsgCrypt($token, $encodingAesKey, $corpId);
$errCode = $wxcpt->VerifyURL($sVerifyMsgSig, $sVerifyTimeStamp, $sVerifyNonce, $sVerifyEchoStr, $sEchoStr);
if ($errCode == 0) {
echo $sEchoStr;
} else {
print("ERR: " . $errCode . "\n\n");
}
//## 接收消息
$sReqData = file_get_contents("php://input");
$sMsg = "";
$wxcpt = new WXBizMsgCrypt($token, $encodingAesKey, $corpId);
$errCode = $wxcpt->DecryptMsg($sVerifyMsgSig, $sVerifyTimeStamp, $sVerifyNonce, $sReqData, $sMsg);
if ($errCode == 0) {
try {
$test = new SimpleXMLElement($sMsg);
$Content = strval($test->Content);//解析出的明文内容
$FromUserName = strval($test->FromUserName);//发送消息的人
$CreateTime = strval($test->CreateTime);
$MsgId = strval($test->MsgId);
} catch (Exception $e) {
$Content = "错误!";
}
}
// 日志
$date = date('Y-m-d');
$logDir = 'log';
$logFileName = "{$logDir}/log_{$date}.txt";
file_put_contents($logFileName, "“[" . $FromUserName . "]" . $Content . "”\r\n", FILE_APPEND);
?>
升级版
<?php
// ======================================
// 接收消息服务器配置
// 集中定义配置参数
$config = [
'encodingAesKey' => 'encodingAesKey',
'token' => 'token',
'corpId' => 'corpId',
'corpSecret' => 'corpSecret'
];
error_reporting(E_ALL);
include_once "callback/WXBizMsgCrypt.php";
$encodingAesKey = $config['encodingAesKey'];
$token = $config['token'];
$corpId = $config['corpId'];
// ======================================
// 获取URL参数并进行空值检查
$sVerifyMsgSig = isset($_GET["msg_signature"]) ? $_GET["msg_signature"] : '';
$sVerifyTimeStamp = isset($_GET["timestamp"]) ? $_GET["timestamp"] : '';
$sVerifyNonce = isset($_GET["nonce"]) ? $_GET["nonce"] : '';
$sVerifyEchoStr = isset($_GET["echostr"]) ? $_GET["echostr"] : '';
//## URL验证
if (!empty($sVerifyEchoStr)) {
$sEchoStr = "";
$wxcpt = new WXBizMsgCrypt($token, $encodingAesKey, $corpId);
$errCode = $wxcpt->VerifyURL($sVerifyMsgSig, $sVerifyTimeStamp, $sVerifyNonce, $sVerifyEchoStr, $sEchoStr);
if ($errCode == 0) {
writeLog("URL验证成功");
echo $sEchoStr;
} else {
// 增强错误日志,特别关注-40001错误(签名验证失败)
$errorCodeDesc = getErrorCodeDescription($errCode);
writeLog("URL验证失败,错误码: {$errCode} ({$errorCodeDesc}),Timestamp: {$sVerifyTimeStamp}, Nonce: {$sVerifyNonce}, Signature: {$sVerifyMsgSig}");
print("ERR: " . $errCode . "\n\n");
}
}
//## 接收消息
$sReqData = file_get_contents("php://input");
$sMsg = "";
$wxcpt = new WXBizMsgCrypt($token, $encodingAesKey, $corpId);
$errCode = $wxcpt->DecryptMsg($sVerifyMsgSig, $sVerifyTimeStamp, $sVerifyNonce, $sReqData, $sMsg);
if ($errCode == 0) {
try {
$test = new SimpleXMLElement($sMsg);
$MsgType = strval($test->MsgType); // 消息类型
$FromUserName = strval($test->FromUserName); // 发送消息的人
$CreateTime = strval($test->CreateTime);
$MsgId = strval($test->MsgId);
$AgentID = strval($test->AgentID); // 应用ID
// 根据不同类型的消息进行处理
switch ($MsgType) {
case 'text':
$Content = strval($test->Content); // 文本消息内容
writeLog("[" . $FromUserName . "][text] " . $Content);
break;
case 'image':
$PicUrl = strval($test->PicUrl); // 图片链接
$MediaId = strval($test->MediaId); // 媒体文件ID
writeLog("[" . $FromUserName . "][image] 收到图片消息,MediaId: " . $MediaId);
// 调用下载和保存图片的函数
downloadAndSaveImage($MediaId, $FromUserName, $PicUrl);
break;
case 'voice':
$MediaId = strval($test->MediaId); // 媒体文件ID
$Format = strval($test->Format); // 语音格式
writeLog("[" . $FromUserName . "][voice] 收到语音消息,MediaId: " . $MediaId);
// 调用下载和保存语音的函数
downloadAndSaveVoice($MediaId, $FromUserName);
break;
case 'video':
$MediaId = strval($test->MediaId);
writeLog("[" . $FromUserName . "][video] 收到视频消息,MediaId: " . $MediaId);
// 调用下载和保存视频的函数
downloadAndSaveVideo($MediaId, $FromUserName);
break;
case 'location':
$Location_X = strval($test->Location_X);
$Location_Y = strval($test->Location_Y);
$Scale = strval($test->Scale);
$Label = strval($test->Label);
writeLog("[" . $FromUserName . "][location] 收到位置消息,坐标: (" . $Location_X . ", " . $Location_Y . ")");
break;
default:
writeLog("[" . $FromUserName . "][unknown] 收到未知类型消息: " . $MsgType);
break;
}
} catch (Exception $e) {
writeLog("解析消息XML失败: " . $e->getMessage());
}
} else {
writeLog("解密消息失败,错误码: {$errCode},Timestamp: {$sVerifyTimeStamp}, Nonce: {$sVerifyNonce}, Signature: {$sVerifyMsgSig}");
}
/**
* 下载并保存图片
*/
function downloadAndSaveImage($mediaId, $fromUserName, $picUrl) {
// 获取access_token
$accessToken = getAccessToken();
if (!$accessToken) {
writeLog("[downloadAndSaveImage] 获取access_token失败");
// 包含错误提示功能
include_once "send_post.php";
send_message("下载图片失败:获取access_token失败", 'text');
return;
}
// 构造下载图片的API地址
$downloadUrl = "https://qyapi.weixin.qq.com/cgi-bin/media/get?access_token={$accessToken}&media_id={$mediaId}";
// 使用curl下载图片
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $downloadUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
$result = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$err = curl_error($ch); // 获取curl错误信息
curl_close($ch);
// 检查是否是有效的图片数据(非错误JSON响应)
$isJson = json_decode($result) !== null;
if ($httpCode !== 200 || $isJson) {
// 尝试直接从PicUrl下载图片
$result = file_get_contents($picUrl);
if ($result === false) {
writeLog("[downloadAndSaveImage] 从PicUrl下载图片失败,MediaId: {$mediaId}");
// 包含错误提示功能
include_once "send_post.php";
send_message("图片下载失败:" . $err, 'text');
return;
}
}
// 创建用户专属目录
$upload_dir = "upload/";
$user_image_dir = $upload_dir . preg_replace('/[^a-zA-Z0-9\-_]/', '_', $fromUserName) . "/";
if (!is_dir($user_image_dir)) {
if (!mkdir($user_image_dir, 0777, true)) {
writeLog("[downloadAndSaveImage] 创建用户目录失败: {$user_image_dir}");
return;
}
}
// 生成图片文件名(使用时间戳+随机数+原始媒体ID确保唯一性)
$fileName = date('YmdHis') . '_' . rand(1000, 9999) . '_' . $mediaId . '.jpg';
$filePath = $user_image_dir . $fileName;
// 保存图片
$res = file_put_contents($filePath, $result);
if ($res !== false) {
writeLog("[downloadAndSaveImage] 图片保存成功: {$filePath}");
} else {
writeLog("[downloadAndSaveImage] 图片保存失败,MediaId: {$mediaId}");
// 包含错误提示功能
include_once "send_post.php";
send_message("图片保存失败", 'text');
}
}
/**
* 下载并保存语音
*/
function downloadAndSaveVoice($mediaId, $fromUserName) {
// 获取access_token
$accessToken = getAccessToken();
if (!$accessToken) {
writeLog("[downloadAndSaveVoice] 获取access_token失败");
// 包含错误提示功能
include_once "send_post.php";
send_message("下载语音失败:获取access_token失败", 'text');
return;
}
// 构造下载语音的API地址
$downloadUrl = "https://qyapi.weixin.qq.com/cgi-bin/media/get?access_token={$accessToken}&media_id={$mediaId}";
// 使用curl下载语音
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $downloadUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
$result = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$err = curl_error($ch); // 获取curl错误信息
curl_close($ch);
// 检查是否是有效的语音数据(非错误JSON响应)
$isJson = json_decode($result) !== null;
if ($httpCode !== 200 || $isJson) {
writeLog("[downloadAndSaveVoice] 下载语音失败,MediaId: {$mediaId}, HTTP状态码: {$httpCode}");
// 包含错误提示功能
include_once "send_post.php";
send_message("语音下载失败:" . $err, 'text');
return;
}
// 创建用户专属目录
$upload_dir = "upload/";
$user_voice_dir = $upload_dir . preg_replace('/[^a-zA-Z0-9\-_]/', '_', $fromUserName) . "/";
if (!is_dir($user_voice_dir)) {
if (!mkdir($user_voice_dir, 0777, true)) {
writeLog("[downloadAndSaveVoice] 创建用户目录失败: {$user_voice_dir}");
return;
}
}
// 生成语音文件名(使用时间戳+随机数+原始媒体ID确保唯一性)
$fileName = date('YmdHis') . '_' . rand(1000, 9999) . '_' . $mediaId . '.amr';
$filePath = $user_voice_dir . $fileName;
// 保存语音
$res = file_put_contents($filePath, $result);
if ($res !== false) {
writeLog("[downloadAndSaveVoice] 语音保存成功: {$filePath}");
} else {
writeLog("[downloadAndSaveVoice] 语音保存失败,MediaId: {$mediaId}");
// 包含错误提示功能
include_once "send_post.php";
send_message("语音保存失败", 'text');
}
}
/**
* 下载并保存视频
*/
function downloadAndSaveVideo($mediaId, $fromUserName) {
// 获取access_token
$accessToken = getAccessToken();
if (!$accessToken) {
writeLog("[downloadAndSaveVideo] 获取access_token失败");
// 包含错误提示功能
include_once "send_post.php";
send_message("下载视频失败:获取access_token失败", 'text');
return;
}
// 构造下载视频的API地址
$downloadUrl = "https://qyapi.weixin.qq.com/cgi-bin/media/get?access_token={$accessToken}&media_id={$mediaId}";
// 使用curl下载视频
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $downloadUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_TIMEOUT, 60); // 视频文件较大,增加超时时间
$result = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$err = curl_error($ch); // 获取curl错误信息
curl_close($ch);
// 检查是否是有效的视频数据(非错误JSON响应)
$isJson = json_decode($result) !== null;
if ($httpCode !== 200 || $isJson) {
writeLog("[downloadAndSaveVideo] 下载视频失败,MediaId: {$mediaId}, HTTP状态码: {$httpCode}");
// 包含错误提示功能
include_once "send_post.php";
send_message("视频下载失败:" . $err, 'text');
return;
}
// 创建用户专属目录
$upload_dir = "upload/";
$user_video_dir = $upload_dir . preg_replace('/[^a-zA-Z0-9\-_]/', '_', $fromUserName) . "/";
if (!is_dir($user_video_dir)) {
if (!mkdir($user_video_dir, 0777, true)) {
writeLog("[downloadAndSaveVideo] 创建用户目录失败: {$user_video_dir}");
return;
}
}
// 生成视频文件名(使用时间戳+随机数+原始媒体ID确保唯一性)
$fileName = date('YmdHis') . '_' . rand(1000, 9999) . '_' . $mediaId . '.mp4';
$filePath = $user_video_dir . $fileName;
// 保存视频
$res = file_put_contents($filePath, $result);
if ($res !== false) {
writeLog("[downloadAndSaveVideo] 视频保存成功: {$filePath}");
} else {
writeLog("[downloadAndSaveVideo] 视频保存失败,MediaId: {$mediaId}");
// 包含错误提示功能
include_once "send_post.php";
send_message("视频保存失败", 'text');
}
}
/**
* 获取企业微信access_token
*/
function getAccessToken() {
// 从全局配置获取参数
$corpid = $GLOBALS['config']['corpId'];
$corpsecret = $GLOBALS['config']['corpSecret']; // 从配置获取应用的Secret
$url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={$corpid}&corpsecret={$corpsecret}";
$response = file_get_contents($url);
$result = json_decode($response, true);
if (isset($result['access_token'])) {
return $result['access_token'];
} else {
writeLog("[getAccessToken] 获取access_token失败: " . $response);
return false;
}
}
/**
* 获取错误码描述
*/
function getErrorCodeDescription($errorCode) {
$errorDescriptions = [
-40001 => '签名验证错误',
-40002 => 'xml解析失败',
-40003 => 'sha加密生成签名失败',
-40004 => 'encodingAesKey 非法',
-40005 => 'corpid 校验错误',
-40006 => 'aes 加密失败',
-40007 => 'aes 解密失败',
-40008 => '解密后得到的buffer非法',
-40009 => 'base64加密失败',
-40010 => 'base64解密失败'
];
return isset($errorDescriptions[$errorCode]) ? $errorDescriptions[$errorCode] : '未知错误';
}
/**
* 记录日志
* 符合日志功能设计规范
*/
function writeLog($log) {
$logData = date('Y-m-d H:i:s') . " - " . $log . PHP_EOL;
$logFile = './logs/' . date('Y-m-d') . '.log';
// 创建logs目录(如果不存在)
$logDir = dirname($logFile);
if (!is_dir($logDir)) {
mkdir($logDir, 0755, true);
}
// 使用独占锁写入日志,符合日志功能设计规范
file_put_contents($logFile, $logData, FILE_APPEND | LOCK_EX);
}
?>
一些问题&解决方法
echostr 报错解决,PHP版本问题,加一句:
error_reporting(0);
遇到传参的时候,有空格的情况使用 \"$@\"
#!/bin/bash
sshpass -p "passwordxxx" ssh root@111.222.333.444 "sh /root/callBack.sh \"$@\""
php shell_exec 函数无法执行,在php中找到禁用函数,然后删掉(宝塔php管理里面就有)在 php.ini 里面 disable_functions= shell_exec 的函数删掉 ,顺便把安全模式关闭 safe_mode=off
如果试图通过函数popen()、system()或exec()等执行脚本,只有当脚本位于safe_mode_exec_dir配置指令指定的目录才可能。
在php脚本中调用shell脚本无法执行时,检查执行权限,打开/etc/sudoers ,找到这段,在下面加一行
## Allow root to run any commands anywhere
root ALL=(ALL) ALL
www ALL=(ALL) NOPASSWD: ALL







Comments | NOTHING