sakuara-bj/wxbizmsgcrypt

Enterprise WeChat Callback

Maintainers

Package info

github.com/yoolo-mine/wxbizmsgcrypt

pkg:composer/sakuara-bj/wxbizmsgcrypt

Statistics

Installs: 776

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 0

v1.0.1 2026-02-27 06:40 UTC

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 中修改 encodingAesKeytokencorpId

Laravel 集成说明

自动发现

包在 composer.json 中声明了 extra.laravel.providers,Laravel 5.5+ 会在执行 composer install/update 时自动发现并注册 WecomCryptoServiceProvider无需config/app.phpproviders 数组中手动添加。

配置来源与优先级

  1. 若执行过 php artisan vendor:publish --tag=wecom-crypto-config,应用会使用 config/wecom_crypto.php 中的值(该文件通常通过 env() 读取 .env)。
  2. 若未发布配置,包会使用包内默认配置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.phpproviders 数组中手动添加:

'providers' => [
    // ...
    YooloMine\WecomCrypto\Providers\WecomCryptoServiceProvider::class,
],

注意事项

  • 企业微信回调会带 URL 参数 msg_signaturetimestampnonce,GET 校验时还有 echostr;POST 消息体为加密的 XML,需将原始请求体传入 DecryptMsg(如 $request->getContent()),不要用已解析的 $request->all()
  • 生产环境请务必在 .env 中配置正确的 WECHAT_WORK_AES_KEY(43 位)、WECHAT_WORK_TOKENWECHAT_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.phproutes/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('');
    }
}

企业微信后台配置

  1. 进入企业微信管理后台 → 应用管理 → 自建应用(或对应应用)→ 接收消息。
  2. 设置「接收消息服务器」URL 为:https://你的域名/wecom/callback(与上面路由一致)。
  3. 填写 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 失败

协议与参考