sakuara-bj / wxbizmsgcrypt
Enterprise WeChat Callback
Requires
- php: >=7.4
- ext-dom: *
- ext-libxml: *
- ext-openssl: *
- illuminate/config: ^8.0
- illuminate/support: ^8.0|^9.0
Requires (Dev)
- phpunit/phpunit: ^8.0
This package is auto-updated.
Last update: 2026-03-27 06:58:14 UTC
README
企业微信回调消息加解密库,适用于 Laravel。基于企业微信官方加解密方案 PHP 示例封装为 Composer 包。
环境要求
- PHP >= 7.4
- 扩展:openssl、dom、libxml
- Laravel 8.x / 9.x(或兼容的 illuminate/support、illuminate/config)
安装
composer require yoolo-mine/wecom-crypto
安装后 Laravel 会自动发现并注册服务提供者,无需在 config/app.php 中手动添加。
配置
方式一:使用环境变量(推荐)
在项目 .env 中配置:
WECHAT_WORK_AES_KEY=你的EncodingAESKey(43位) WECHAT_WORK_TOKEN=你的Token WECHAT_WORK_CORP_ID=你的CorpID或应用场景对应的ReceiveId
无需发布配置文件,包会通过默认配置读取上述环境变量。
方式二:发布配置文件
如需在应用内直接修改配置,可发布配置文件到 config/wecom_crypto.php:
php artisan vendor:publish --tag=wecom-crypto-config
发布后可在 config/wecom_crypto.php 中修改 encodingAesKey、token、corpId。
Laravel 集成说明
自动发现
包在 composer.json 中声明了 extra.laravel.providers,Laravel 5.5+ 会在执行 composer install/update 时自动发现并注册 WecomCryptoServiceProvider,无需在 config/app.php 的 providers 数组中手动添加。
配置来源与优先级
- 若执行过
php artisan vendor:publish --tag=wecom-crypto-config,应用会使用config/wecom_crypto.php中的值(该文件通常通过env()读取.env)。 - 若未发布配置,包会使用包内默认配置(
mergeConfigFrom),即直接读取config('wecom_crypto.xxx'),而该值来自包自带的config/wecom_crypto.php,其中已写env('WECHAT_WORK_*', ''),因此只要在.env中配置即可生效。
多应用 / 多套回调配置
若需要对接多个企业微信应用或不同 ReceiveId(如自建应用与第三方应用),不要使用单例 app('wxcrypt'),而是在业务处按需实例化:
use YooloMine\WecomCrypto\WXBizMsgCrypt; $wxcpt = new WXBizMsgCrypt( config('wecom_crypto.token'), config('wecom_crypto.encodingAesKey'), 'different_receive_id_for_this_app' );
也可在配置文件中为不同应用定义多组 key,再按应用选择后传入 WXBizMsgCrypt。
手动注册服务提供者(可选)
若自动发现未生效(例如 Laravel 版本较旧或自定义了发现逻辑),可在 config/app.php 的 providers 数组中手动添加:
'providers' => [ // ... YooloMine\WecomCrypto\Providers\WecomCryptoServiceProvider::class, ],
注意事项
- 企业微信回调会带 URL 参数
msg_signature、timestamp、nonce,GET 校验时还有echostr;POST 消息体为加密的 XML,需将原始请求体传入DecryptMsg(如$request->getContent()),不要用已解析的$request->all()。 - 生产环境请务必在
.env中配置正确的WECHAT_WORK_AES_KEY(43 位)、WECHAT_WORK_TOKEN、WECHAT_WORK_CORP_ID,并避免将上述信息提交到版本库。
使用
在 Laravel 中通过容器使用
包注册了单例 wxcrypt,可直接注入或从容器解析:
use YooloMine\WecomCrypto\CallBack\ErrorCode; // 解析单例(使用 .env 或 config/wecom_crypto.php 中的配置) $wxcpt = app('wxcrypt');
验证回调 URL(VerifyURL)
企业微信在配置回调 URL 时会发起 GET 校验,用本方法验证并返回需回显的明文:
$result = $wxcpt->VerifyURL( $request->query('msg_signature'), $request->query('timestamp'), $request->query('nonce'), $request->query('echostr') ); if ($result === ErrorCode::IllegalAesKey) { // EncodingAESKey 长度不是 43 } if ($result === ErrorCode::ValidateSignatureError) { // 签名校验失败 } if (is_int($result) && $result < 0) { // 其他错误码,见下方「错误码」 } // 成功:$result 为解密后的明文字符串,直接返回给企业微信 return response($result);
解密回调消息(DecryptMsg)
收到企业微信 POST 的加密消息时,先解密再处理:
$sMsg = ''; $errCode = $wxcpt->DecryptMsg( $request->query('msg_signature'), $request->query('timestamp'), $request->query('nonce'), $request->getContent(), $sMsg ); if ($errCode !== ErrorCode::OK) { // 根据错误码处理,见下方「错误码」 return response()->json(['error' => $errCode], 400); } // $sMsg 为解密后的 XML 字符串 $xml = simplexml_load_string($sMsg); // 后续业务逻辑...
加密回复消息(EncryptMsg)
需要对企业微信回复加密消息时:
$sEncryptMsg = ''; $errCode = $wxcpt->EncryptMsg( $replyXmlString, // 要回复的 XML 明文 $request->query('timestamp'), $request->query('nonce'), $sEncryptMsg ); if ($errCode !== ErrorCode::OK) { return response()->json(['error' => $errCode], 500); } // $sEncryptMsg 为加密后的完整 XML,可直接返回 return response($sEncryptMsg)->header('Content-Type', 'text/xml');
不使用 Laravel 容器时
也可直接实例化,适用于非 Laravel 或需要多套配置的场景:
use YooloMine\WecomCrypto\WXBizMsgCrypt; $wxcpt = new WXBizMsgCrypt( 'your_token', 'your_encoding_aes_key_43_chars', 'your_corp_id_or_receive_id' ); // 之后调用 VerifyURL、DecryptMsg、EncryptMsg 同上
示例:路由与控制器
以下为在企业微信后台配置的「接收消息服务器」对应的路由与控制器示例,便于直接接入 GET 校验与 POST 消息。
路由
在 routes/web.php 或 routes/api.php 中定义(企业微信要求回调 URL 可公网访问,若用 web.php 需注意 CSRF 排除):
use App\Http\Controllers\WecomCallbackController; // 企业微信应用回调:GET 用于校验 URL,POST 用于接收消息 Route::match(['get', 'post'], 'wecom/callback', [WecomCallbackController::class, 'handle']) ->name('wecom.callback');
若使用 routes/web.php 且启用了 CSRF,需在 app/Http/Middleware/VerifyCsrfToken.php 的 $except 中排除该 URI,例如:
protected $except = [ 'wecom/callback', ];
控制器示例
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use YooloMine\WecomCrypto\CallBack\ErrorCode; class WecomCallbackController extends Controller { /** * 企业微信回调入口:GET 校验 URL,POST 接收加密消息 */ public function handle(Request $request) { $wxcpt = app('wxcrypt'); if ($request->isMethod('get')) { return $this->verifyUrl($request, $wxcpt); } return $this->receiveMessage($request, $wxcpt); } /** * 验证回调 URL(企业微信后台配置 URL 时触发) */ protected function verifyUrl(Request $request, $wxcpt) { $result = $wxcpt->VerifyURL( $request->query('msg_signature'), $request->query('timestamp'), $request->query('nonce'), $request->query('echostr') ); if (is_int($result) && $result !== 0) { return response('', 400); } return response($result); } /** * 接收企业微信 POST 的加密消息,解密后处理 */ protected function receiveMessage(Request $request, $wxcpt) { $sMsg = ''; $errCode = $wxcpt->DecryptMsg( $request->query('msg_signature'), $request->query('timestamp'), $request->query('nonce'), $request->getContent(), $sMsg ); if ($errCode !== ErrorCode::OK) { return response()->json(['errcode' => $errCode], 400); } $xml = simplexml_load_string($sMsg); if ($xml === false) { return response()->json(['errcode' => -1, 'errmsg' => 'invalid xml'], 400); } // 根据 MsgType 等字段处理业务(文本、事件等) $msgType = (string) ($xml->MsgType ?? ''); switch ($msgType) { case 'text': $content = (string) ($xml->Content ?? ''); // 可选:构造回复 XML 后调用 EncryptMsg 再返回 return $this->replyText($request, $wxcpt, $xml, $content); case 'event': return $this->handleEvent($xml); default: return response(''); } } protected function replyText(Request $request, $wxcpt, $xml, string $content) { $from = (string) ($xml->FromUserName ?? ''); $to = (string) ($xml->ToUserName ?? ''); $createTime = time(); $replyXml = sprintf( '<xml><ToUserName><![CDATA[%s]]></ToUserName><FromUserName><![CDATA[%s]]></FromUserName><CreateTime>%s</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[%s]]></Content></xml>', $from, $to, $createTime, htmlspecialchars('收到:' . $content) ); $sEncryptMsg = ''; $errCode = $wxcpt->EncryptMsg( $replyXml, $request->query('timestamp'), $request->query('nonce'), $sEncryptMsg ); if ($errCode !== ErrorCode::OK) { return response()->json(['errcode' => $errCode], 500); } return response($sEncryptMsg)->header('Content-Type', 'text/xml; charset=UTF-8'); } protected function handleEvent($xml) { $event = (string) ($xml->Event ?? ''); // 处理订阅、点击等事件 return response(''); } }
企业微信后台配置
- 进入企业微信管理后台 → 应用管理 → 自建应用(或对应应用)→ 接收消息。
- 设置「接收消息服务器」URL 为:
https://你的域名/wecom/callback(与上面路由一致)。 - 填写 Token、EncodingAESKey,并保存后使用「验证」按钮;验证通过即会请求上述 GET 接口并校验通过。
错误码
与企业微信官方文档一致,可使用 YooloMine\WecomCrypto\CallBack\ErrorCode 常量判断:
| 常量 | 值 | 说明 |
|---|---|---|
ErrorCode::OK |
0 | 成功 |
ErrorCode::ValidateSignatureError |
-40001 | 签名验证错误 |
ErrorCode::ParseXmlError |
-40002 | xml 解析失败 |
ErrorCode::ComputeSignatureError |
-40003 | sha 加密生成签名失败 |
ErrorCode::IllegalAesKey |
-40004 | EncodingAESKey 非法 |
ErrorCode::ValidateCorpidError |
-40005 | ReceiveId 校验错误 |
ErrorCode::EncryptAESError |
-40006 | AES 加密失败 |
ErrorCode::DecryptAESError |
-40007 | AES 解密失败 |
ErrorCode::IllegalBuffer |
-40008 | 解密后得到的 buffer 非法 |
ErrorCode::EncodeBase64Error |
-40009 | base64 加密失败 |
ErrorCode::DecodeBase64Error |
-40010 | base64 解密失败 |
ErrorCode::GenReturnXmlError |
-40011 | 生成 xml 失败 |
协议与参考
- 加解密协议与返回码以企业微信官方文档为准。
- 本包采用 MIT 协议。