浏览代码

Merge branch 'liuzj-1000975-dev' into test

# Conflicts:
#	modules/Common/Errors/Errors.php
liuzejian 1 年之前
父节点
当前提交
e10d27cb05

+ 2 - 1
composer.json

@@ -21,7 +21,8 @@
         "phpoffice/phpspreadsheet": "^1.28",
         "predis/predis": "^2.1",
         "qiniu/php-sdk": "^7.4",
-        "vinkla/hashids": "*"
+        "vinkla/hashids": "*",
+        "w7corp/easywechat": "^6.12"
     },
     "require-dev": {
         "fakerphp/faker": "^1.9.1",

+ 78 - 0
modules/Channel/Http/Controllers/WechatOpenPlatformController.php

@@ -0,0 +1,78 @@
+<?php
+
+namespace Modules\Channel\Http\Controllers;
+
+use Catch\Base\CatchController;
+use EasyWeChat\OpenPlatform\Application;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\DB;
+use Modules\Channel\Models\WechatAuthorizationInfo;
+use Modules\Channel\Services\WechatOpenPlatform\WechatOpenPlatformService;
+use Modules\Common\Errors\Errors;
+use Modules\Common\Exceptions\CommonBusinessException;
+use Modules\User\Http\Controllers\UserTrait;
+
+class WechatOpenPlatformController extends CatchController
+{
+    use UserTrait;
+    // 授权跳转页
+    public function preauth(Request $request) {
+        $currentUser = $this->getCurrentUser();
+        $componentInfo = WechatOpenPlatformService::getComponentInfoByCompanyUid($currentUser->pid);
+        $app = WechatOpenPlatformService::buildApplication($componentInfo);
+        $url = $app->createPreAuthorizationUrl(url('/api/channel/openPlatform/auth/'.$componentInfo->app_id), []);
+        return $url;
+    }
+
+    public function auth(Request $request, $component_appid) {
+        $auth_code = $request->input('auth_code');
+        $componentInfo = WechatOpenPlatformService::getComponentInfoByAppid($component_appid);
+        $app = WechatOpenPlatformService::buildApplication($componentInfo);
+        $client = $app->getClient();
+        $response = $client->post('/cgi-bin/component/api_query_auth', [
+            'component_appid' => $component_appid,
+            'authorization_code' => $auth_code
+        ]);
+        $authorizer_appid = $response['authorization_info']['authorizer_appid'];
+        $authorizer_refresh_token = $response['authorization_info']['authorizer_refresh_token'];
+        $currentUser = $this->getCurrentUser();
+        WechatAuthorizationInfo::updateOrCreate([
+            'authorizer_appid' => $authorizer_appid,
+            'component_appid' => $component_appid,
+            'user_id' => $currentUser->id,
+            'puser_id' => $currentUser->pid,
+        ], [
+            'authorizer_refresh_token' => $authorizer_refresh_token
+        ]);
+
+        return view('wechat.openPlatform.authSuccess')->with('url', url('/'));
+    }
+
+    /**
+     * 处理授权事件
+     * @param Request $request
+     * @param $component_appid
+     */
+    public function authorCommand(Request $request, $component_appid) {
+        $componentInfo = WechatOpenPlatformService::getComponentInfoByAppid($component_appid);
+        $app = WechatOpenPlatformService::buildApplication($componentInfo);
+        $server = $app->getServer();
+
+        $server->handleAuthorized(function($message, \Closure $next) {
+            myLog('authorCommand')->info('handleAuthorized', ['message' => $message]);
+            return $next($message);
+        });
+
+
+        return $server->serve();
+    }
+
+    public function infoCommand(Request $request, $authorizer_appid, $component_appid) {
+        $componentInfo = WechatOpenPlatformService::getComponentInfoByAppid($component_appid);
+        $app = WechatOpenPlatformService::buildApplication($componentInfo);
+
+        $server = $app->getServer();
+
+        return $server->serve();
+    }
+}

+ 12 - 0
modules/Channel/Models/WechatAuthorizationInfo.php

@@ -0,0 +1,12 @@
+<?php
+
+namespace Modules\Channel\Models;
+
+
+
+class WechatAuthorizationInfo extends BaseModel
+{
+    protected $table = 'wechat_authorization_infos';
+
+    protected $guarded = [];
+}

+ 61 - 0
modules/Channel/Services/WechatOpenPlatform/WechatOpenPlatformService.php

@@ -0,0 +1,61 @@
+<?php
+
+namespace Modules\Channel\Services\WechatOpenPlatform;
+
+use EasyWeChat\OpenPlatform\Application;
+use Illuminate\Support\Facades\DB;
+use Modules\Common\Errors\Errors;
+use Modules\Common\Exceptions\CommonBusinessException;
+
+class WechatOpenPlatformService
+{
+    public static function getComponentInfoByCompanyUid($companyUid) {
+        $componentInfo = DB::table('wechat_open_platform_infos')
+            ->where([
+                'company_uid' => $companyUid,
+                'is_enabled' => 1,
+            ])->orderBy('id', 'desc')
+            ->first();
+        if(!$componentInfo) {
+            CommonBusinessException::throwError(Errors::OPENPLATFORM_COMPANY_INFO_NOT_EXISTS);
+        }
+        return $componentInfo;
+    }
+
+    public static function getComponentInfoByAppid($componentAppid) {
+        $componentInfo =  DB::table('wechat_open_platform_infos')
+            ->where([
+                'app_id' => $componentAppid,
+                'is_enabled' => 1,
+            ])->orderBy('id', 'desc')
+            ->first();
+        if(!$componentInfo) {
+            CommonBusinessException::throwError(Errors::OPENPLATFORM_COMPANY_INFO_NOT_EXISTS);
+        }
+        return $componentInfo;
+    }
+
+    /**
+     * 构造app
+     * @param $componentInfo
+     * @return Application
+     * @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
+     */
+    public static function buildApplication($componentInfo) {
+        if(!$componentInfo) {
+            CommonBusinessException::throwError(Errors::OPENPLATFORM_COMPANY_INFO_NOT_EXISTS);
+        }
+        $config = [
+            'app_id' => $componentInfo->app_id, // 开放平台账号的 appid
+            'secret' => $componentInfo->secret,   // 开放平台账号的 secret
+            'token' => $componentInfo->token,  // 开放平台账号的 token
+            'aes_key' => $componentInfo->aes_key,   // 明文模式请勿填写 EncodingAESKey
+            'http' => [
+                'throw'  => true, // 状态码非 200、300 时是否抛出异常,默认为开启
+                'timeout' => 5.0,
+                'retry' => true, // 使用默认重试配置
+            ],
+        ];
+        return new Application($config);
+    }
+}

+ 12 - 0
modules/Channel/routes/route.php

@@ -4,6 +4,7 @@ use Illuminate\Support\Facades\Route;
 use Modules\Channel\Http\Controllers\AdvertiserController;
 use Modules\Channel\Http\Controllers\PayTemplateController;
 use Modules\Channel\Http\Controllers\UserMiniprogramController;
+use Modules\Channel\Http\Controllers\WechatOpenPlatformController;
 
 Route::prefix('channel')->group(function () {
     Route::post('advertiser/add', [AdvertiserController::class, 'addAdvertiser']);
@@ -47,5 +48,16 @@ Route::prefix('channel')->group(function () {
     Route::any('promotions/options', [\Modules\Channel\Http\Controllers\OrdersController::class, 'promotionsOptions']);
     Route::any('miniprogram/use_list', [\Modules\Channel\Http\Controllers\OrdersController::class, 'userUseList']);
     Route::any('promotions/users', [\Modules\Channel\Http\Controllers\OrdersController::class, 'promotionsUsers']);
+
+
+    /**
+     * 第三方开放平台
+     */
+    Route::prefix('openPlatform')->group(function(){
+        Route::get('auth/{component_appid}', [WechatOpenPlatformController::class, 'auth'])->middleware(['roleCheck:optimizer']);
+        Route::get('preauth', [WechatOpenPlatformController::class, 'preauth']);
+        Route::post('authorCommand/{component_appid}', [WechatOpenPlatformController::class, 'authorCommand'])->withoutMiddleware(config('catch.route.middlewares'));;
+        Route::post('infoCommand/{authorizer_appid}/{component_appid}', [WechatOpenPlatformController::class, 'infoCommand'])->withoutMiddleware(config('catch.route.middlewares'));;
+    });
 });
 

+ 1 - 0
modules/Common/Errors/Errors.php

@@ -35,4 +35,5 @@ class Errors
     public const  SYNC_WECHAT_NOT_OK = [500303, '剧集没有成功同步到微信'];
     public const  MINIPROGRAM_OWNER_ACCOUNT_ROLE_ERROR= [5000501, '所分配的账号不是投放公司账号'];
     public const  GET_WECHAT_MEDIA_LINK_ERROR = [500304, '获取短剧播放链接失败'];
+    public const  OPENPLATFORM_COMPANY_INFO_NOT_EXISTS= [5000601, '公司没有对应的开放平台信息'];
 }

+ 34 - 0
modules/Common/Support/Wechat/Crypt/ErrorCode.php

@@ -0,0 +1,34 @@
+<?php
+namespace Modules\Common\Support\Wechat\Crypt;
+/**
+ * error code 说明.
+ * <ul>
+ *    <li>-40001: 签名验证错误</li>
+ *    <li>-40002: xml解析失败</li>
+ *    <li>-40003: sha加密生成签名失败</li>
+ *    <li>-40004: encodingAesKey 非法</li>
+ *    <li>-40005: appid 校验错误</li>
+ *    <li>-40006: aes 加密失败</li>
+ *    <li>-40007: aes 解密失败</li>
+ *    <li>-40008: 解密后得到的buffer非法</li>
+ *    <li>-40009: base64加密失败</li>
+ *    <li>-40010: base64解密失败</li>
+ *    <li>-40011: 生成xml失败</li>
+ * </ul>
+ */
+class ErrorCode
+{
+	public static $OK = 0;
+	public static $ValidateSignatureError = -40001;
+	public static $ParseXmlError = -40002;
+	public static $ComputeSignatureError = -40003;
+	public static $IllegalAesKey = -40004;
+	public static $ValidateAppidError = -40005;
+	public static $EncryptAESError = -40006;
+	public static $DecryptAESError = -40007;
+	public static $IllegalBuffer = -40008;
+	public static $EncodeBase64Error = -40009;
+	public static $DecodeBase64Error = -40010;
+	public static $GenReturnXmlError = -40011;
+}
+

+ 52 - 0
modules/Common/Support/Wechat/Crypt/PKCS7Encoder.php

@@ -0,0 +1,52 @@
+<?php
+namespace Modules\Common\Support\Wechat\Crypt;
+
+/**
+ * PKCS7Encoder class
+ *
+ * 提供基于PKCS7算法的加解密接口.
+ */
+class PKCS7Encoder
+{
+	public static $block_size = 32;
+
+	/**
+	 * 对需要加密的明文进行填充补位
+	 * @param $text 需要进行填充补位操作的明文
+	 * @return 补齐明文字符串
+	 */
+	function encode($text)
+	{
+		$block_size = PKCS7Encoder::$block_size;
+		$text_length = strlen($text);
+		//计算需要填充的位数
+		$amount_to_pad = PKCS7Encoder::$block_size - ($text_length % PKCS7Encoder::$block_size);
+		if ($amount_to_pad == 0) {
+			$amount_to_pad = PKCS7Encoder::$block_size;
+		}
+		//获得补位所用的字符
+		$pad_chr = chr($amount_to_pad);
+		$tmp = "";
+		for ($index = 0; $index < $amount_to_pad; $index++) {
+			$tmp .= $pad_chr;
+		}
+		return $text . $tmp;
+	}
+
+	/**
+	 * 对解密后的明文进行补位删除
+	 * @param decrypted 解密后的明文
+	 * @return 删除填充补位后的明文
+	 */
+	function decode($text)
+	{
+
+		$pad = ord(substr($text, -1));
+		if ($pad < 1 || $pad > 32) {
+			$pad = 0;
+		}
+		return substr($text, 0, (strlen($text) - $pad));
+	}
+
+}
+

+ 103 - 0
modules/Common/Support/Wechat/Crypt/Prpcrypt.php

@@ -0,0 +1,103 @@
+<?php
+namespace Modules\Common\Support\Wechat\Crypt;
+
+/**
+ * Prpcrypt class
+ *
+ * 提供接收和推送给公众平台消息的加解密接口.
+ */
+class Prpcrypt
+{
+	public $key;
+
+	public function __construct($k)
+	{
+		$this->key = base64_decode($k . "=");
+	}
+
+	/**
+	 * 对明文进行加密
+	 * @param string $text 需要加密的明文
+	 * @return string 加密后的密文
+	 */
+	public function encrypt($text, $appid)
+	{
+
+		try {
+			//获得16位随机字符串,填充到明文之前
+			$random = $this->getRandomStr();
+			$text = $random . pack("N", strlen($text)) . $text . $appid;
+			//使用自定义的填充方式对明文进行补位填充
+			$pkc_encoder = new PKCS7Encoder;
+			$text = $pkc_encoder->encode($text);
+
+            $encrypted = @openssl_encrypt($text, 'aes-128-cbc', $this->key, 0);
+
+			//print(base64_encode($encrypted));
+			//使用BASE64对加密后的字符串进行编码
+			return array(ErrorCode::$OK, base64_encode($encrypted));
+		} catch (Exception $e) {
+			//print $e;
+			return array(ErrorCode::$EncryptAESError, null);
+		}
+	}
+
+	/**
+	 * 对密文进行解密
+	 * @param string $encrypted 需要解密的密文
+	 * @return string 解密得到的明文
+	 */
+	public function decrypt($encrypted, $appid)
+	{
+
+		try {
+			//使用BASE64对需要解密的字符串进行解码
+			$ciphertext_dec = base64_decode($encrypted);
+            $decrypted = openssl_decrypt($ciphertext_dec, 'aes-128-cbc', $this->key);
+		} catch (Exception $e) {
+			return array(ErrorCode::$DecryptAESError, null);
+		}
+
+
+		try {
+			//去除补位字符
+			$pkc_encoder = new PKCS7Encoder;
+			$result = $pkc_encoder->decode($decrypted);
+			//去除16位随机字符串,网络字节序和AppId
+			if (strlen($result) < 16)
+				return "";
+			$content = substr($result, 16, strlen($result));
+			$len_list = unpack("N", substr($content, 0, 4));
+			$xml_len = $len_list[1];
+			$xml_content = substr($content, 4, $xml_len);
+			$from_appid = substr($content, $xml_len + 4);
+		} catch (Exception $e) {
+			//print $e;
+			return array(ErrorCode::$IllegalBuffer, null);
+		}
+		if ($from_appid != $appid)
+			return array(ErrorCode::$ValidateAppidError, null);
+		return array(0, $xml_content);
+
+	}
+
+
+	/**
+	 * 随机生成16位字符串
+	 * @return string 生成的字符串
+	 */
+	function getRandomStr()
+	{
+
+		$str = "";
+		$str_pol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
+		$max = strlen($str_pol) - 1;
+		for ($i = 0; $i < 16; $i++) {
+			$str .= $str_pol[mt_rand(0, $max)];
+		}
+		return $str;
+	}
+
+}
+
+?>

+ 4 - 0
modules/Common/Support/Wechat/Crypt/ReadMe.txt

@@ -0,0 +1,4 @@
+注意事项:
+1.WXBizMsgCrypt.php文件提供了WXBizMsgCrypt类的实现,是用户接入企业微信的接口类。Sample.php提供了示例以供开发者参考。errorCode.php, pkcs7Encoder.php, sha1.php, xmlparse.php文件是实现这个类的辅助类,开发者无须关心其具体实现。
+2.WXBizMsgCrypt类封装了 DecryptMsg, EncryptMsg两个接口,分别用于开发者解密以及开发者回复消息的加密。使用方法可以参考Sample.php文件。
+3.加解密协议请参考微信公众平台官方文档。

+ 36 - 0
modules/Common/Support/Wechat/Crypt/SHA1.php

@@ -0,0 +1,36 @@
+<?php
+
+namespace Modules\Common\Support\Wechat\Crypt;
+
+/**
+ * SHA1 class
+ *
+ * 计算公众平台的消息签名接口.
+ */
+class SHA1
+{
+	/**
+	 * 用SHA1算法生成安全签名
+	 * @param string $token 票据
+	 * @param string $timestamp 时间戳
+	 * @param string $nonce 随机字符串
+	 * @param string $encrypt 密文消息
+	 */
+	public function getSHA1($token, $timestamp, $nonce, $encrypt_msg)
+	{
+		//排序
+		try {
+			$array = array($encrypt_msg, $token, $timestamp, $nonce);
+			sort($array, SORT_STRING);
+			$str = implode($array);
+			return array(ErrorCode::$OK, sha1($str));
+		} catch (Exception $e) {
+			//print $e . "\n";
+			return array(ErrorCode::$ComputeSignatureError, null);
+		}
+	}
+
+}
+
+
+?>

+ 145 - 0
modules/Common/Support/Wechat/Crypt/WXBizMsgCrypt.php

@@ -0,0 +1,145 @@
+<?php
+namespace Modules\Common\Support\Wechat\Crypt;
+/**
+ * 对公众平台发送给公众账号的消息加解密示例代码.
+ *
+ * @copyright Copyright (c) 1998-2014 Tencent Inc.
+ */
+
+
+/**
+ * 1.第三方回复加密消息给公众平台;
+ * 2.第三方收到公众平台发送的消息,验证消息的安全性,并对消息进行解密。
+ */
+class WXBizMsgCrypt
+{
+	private $token;
+	private $encodingAesKey;
+	private $appId;
+
+	/**
+	 * 构造函数
+	 * @param $token string 公众平台上,开发者设置的token
+	 * @param $encodingAesKey string 公众平台上,开发者设置的EncodingAESKey
+	 * @param $appId string 公众平台的appId
+	 */
+	public function __construct($token, $encodingAesKey, $appId)
+	{
+		$this->token = $token;
+		$this->encodingAesKey = $encodingAesKey;
+		$this->appId = $appId;
+	}
+
+	/**
+	 * 将公众平台回复用户的消息加密打包.
+	 * <ol>
+	 *    <li>对要发送的消息进行AES-CBC加密</li>
+	 *    <li>生成安全签名</li>
+	 *    <li>将消息密文和安全签名打包成xml格式</li>
+	 * </ol>
+	 *
+	 * @param $replyMsg string 公众平台待回复用户的消息,xml格式的字符串
+	 * @param $timeStamp string 时间戳,可以自己生成,也可以用URL参数的timestamp
+	 * @param $nonce string 随机串,可以自己生成,也可以用URL参数的nonce
+	 * @param &$encryptMsg string 加密后的可以直接回复用户的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字符串,
+	 *                      当return返回0时有效
+	 *
+	 * @return int 成功0,失败返回对应的错误码
+	 */
+	public function encryptMsg($replyMsg, $timeStamp, $nonce, &$encryptMsg)
+	{
+		$pc = new Prpcrypt($this->encodingAesKey);
+
+		//加密
+		$array = $pc->encrypt($replyMsg, $this->appId);
+		$ret = $array[0];
+		if ($ret != 0) {
+			return $ret;
+		}
+
+		if ($timeStamp == null) {
+			$timeStamp = time();
+		}
+		$encrypt = $array[1];
+
+		//生成安全签名
+		$sha1 = new SHA1;
+		$array = $sha1->getSHA1($this->token, $timeStamp, $nonce, $encrypt);
+		$ret = $array[0];
+		if ($ret != 0) {
+			return $ret;
+		}
+		$signature = $array[1];
+
+		//生成发送的xml
+		$xmlparse = new XMLParse;
+		$encryptMsg = $xmlparse->generate($encrypt, $signature, $timeStamp, $nonce);
+		return ErrorCode::$OK;
+	}
+
+
+	/**
+	 * 检验消息的真实性,并且获取解密后的明文.
+	 * <ol>
+	 *    <li>利用收到的密文生成安全签名,进行签名验证</li>
+	 *    <li>若验证通过,则提取xml中的加密消息</li>
+	 *    <li>对消息进行解密</li>
+	 * </ol>
+	 *
+	 * @param $msgSignature string 签名串,对应URL参数的msg_signature
+	 * @param $timestamp string 时间戳 对应URL参数的timestamp
+	 * @param $nonce string 随机串,对应URL参数的nonce
+	 * @param $postData string 密文,对应POST请求的数据
+	 * @param &$msg string 解密后的原文,当return返回0时有效
+	 *
+	 * @return int 成功0,失败返回对应的错误码
+	 */
+	public function decryptMsg($msgSignature, $timestamp = null, $nonce, $postData, &$msg)
+	{
+		if (strlen($this->encodingAesKey) != 43) {
+			return ErrorCode::$IllegalAesKey;
+		}
+
+		$pc = new Prpcrypt($this->encodingAesKey);
+
+		//提取密文
+		$xmlparse = new XMLParse;
+		$array = $xmlparse->extract($postData);
+		$ret = $array[0];
+
+		if ($ret != 0) {
+			return $ret;
+		}
+
+		if ($timestamp == null) {
+			$timestamp = time();
+		}
+
+		$encrypt = $array[1];
+		$touser_name = $array[2];
+
+		//验证安全签名
+		$sha1 = new SHA1;
+		$array = $sha1->getSHA1($this->token, $timestamp, $nonce, $encrypt);
+		$ret = $array[0];
+
+		if ($ret != 0) {
+			return $ret;
+		}
+
+		$signature = $array[1];
+		if ($signature != $msgSignature) {
+			return ErrorCode::$ValidateSignatureError;
+		}
+
+		$result = $pc->decrypt($encrypt, $this->appId);
+		if ($result[0] != 0) {
+			return $result[0];
+		}
+		$msg = $result[1];
+
+		return ErrorCode::$OK;
+	}
+
+}
+

+ 52 - 0
modules/Common/Support/Wechat/Crypt/XMLParse.php

@@ -0,0 +1,52 @@
+<?php
+namespace Modules\Common\Support\Wechat\Crypt;
+
+/**
+ * XMLParse class
+ *
+ * 提供提取消息格式中的密文及生成回复消息格式的接口.
+ */
+class XMLParse
+{
+
+	/**
+	 * 提取出xml数据包中的加密消息
+	 * @param string $xmltext 待提取的xml字符串
+	 * @return string 提取出的加密消息字符串
+	 */
+	public function extract($xmltext)
+	{
+		libxml_disable_entity_loader(true);
+		try {
+			$xml = new \DOMDocument();
+			$xml->loadXML($xmltext);
+			$array_e = $xml->getElementsByTagName('Encrypt');
+			$array_a = $xml->getElementsByTagName('ToUserName');
+			$encrypt = $array_e->item(0)->nodeValue;
+			$tousername = $array_a->item(0)->nodeValue;
+			return array(0, $encrypt, $tousername);
+		} catch (Exception $e) {
+			//print $e . "\n";
+			return array(ErrorCode::$ParseXmlError, null, null);
+		}
+	}
+
+	/**
+	 * 生成xml消息
+	 * @param string $encrypt 加密后的消息密文
+	 * @param string $signature 安全签名
+	 * @param string $timestamp 时间戳
+	 * @param string $nonce 随机字符串
+	 */
+	public function generate($encrypt, $signature, $timestamp, $nonce)
+	{
+		$format = "<xml>
+<Encrypt><![CDATA[%s]]></Encrypt>
+<MsgSignature><![CDATA[%s]]></MsgSignature>
+<TimeStamp>%s</TimeStamp>
+<Nonce><![CDATA[%s]]></Nonce>
+</xml>";
+		return sprintf($format, $encrypt, $signature, $timestamp, $nonce);
+	}
+
+}

+ 41 - 0
modules/Common/Support/Wechat/Crypt/demo.php

@@ -0,0 +1,41 @@
+<?php
+namespace Modules\Common\Support\Wechat\Crypt;
+
+
+
+// 第三方发送消息给公众平台
+$encodingAesKey = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG";
+$token = "pamtest";
+$timeStamp = "1409304348";
+$nonce = "xxxxxx";
+$appId = "wxb11529c136998cb6";
+$text = "<xml><ToUserName><![CDATA[oia2Tj我是中文jewbmiOUlr6X-1crbLOvLw]]></ToUserName><FromUserName><![CDATA[gh_7f083739789a]]></FromUserName><CreateTime>1407743423</CreateTime><MsgType><![CDATA[video]]></MsgType><Video><MediaId><![CDATA[eYJ1MbwPRJtOvIEabaxHs7TX2D-HV71s79GUxqdUkjm6Gs2Ed1KF3ulAOA9H1xG0]]></MediaId><Title><![CDATA[testCallBackReplyVideo]]></Title><Description><![CDATA[testCallBackReplyVideo]]></Description></Video></xml>";
+
+
+$pc = new WXBizMsgCrypt($token, $encodingAesKey, $appId);
+$encryptMsg = '';
+$errCode = $pc->encryptMsg($text, $timeStamp, $nonce, $encryptMsg);
+if ($errCode == 0) {
+	print("加密后: " . $encryptMsg . "\n");
+} else {
+	print($errCode . "\n");
+}
+
+$xml_tree = new DOMDocument();
+$xml_tree->loadXML($encryptMsg);
+$array_e = $xml_tree->getElementsByTagName('Encrypt');
+$array_s = $xml_tree->getElementsByTagName('MsgSignature');
+$encrypt = $array_e->item(0)->nodeValue;
+$msg_sign = $array_s->item(0)->nodeValue;
+
+$format = "<xml><ToUserName><![CDATA[toUser]]></ToUserName><Encrypt><![CDATA[%s]]></Encrypt></xml>";
+$from_xml = sprintf($format, $encrypt);
+
+// 第三方收到公众号平台发送的消息
+$msg = '';
+$errCode = $pc->decryptMsg($msg_sign, $timeStamp, $nonce, $from_xml, $msg);
+if ($errCode == 0) {
+	print("解密后: " . $msg . "\n");
+} else {
+	print($errCode . "\n");
+}

+ 25 - 0
modules/Common/Support/Wechat/OpenPlatform/OPApplication.php

@@ -0,0 +1,25 @@
+<?php
+
+namespace Modules\Common\Support\Wechat\OpenPlatform;
+
+use EasyWeChat\OpenPlatform\Application;
+
+class OPApplication extends Application
+{
+    public function getAuthorizerAccessToken(string $appId, string $refreshToken): string
+    {
+        $component_appid = $this->getAccount()->getAppId();
+        $cacheKey = sprintf('open-platform.authorizer_access_token.%s.%s.%s', $component_appid, $appId, md5($refreshToken));
+
+        /** @phpstan-ignore-next-line */
+        $authorizerAccessToken = (string) $this->getCache()->get($cacheKey);
+
+        if (! $authorizerAccessToken) {
+            $response = $this->refreshAuthorizerToken($appId, $refreshToken);
+            $authorizerAccessToken = (string) $response['authorizer_access_token'];
+            $this->getCache()->set($cacheKey, $authorizerAccessToken, intval($response['expires_in'] ?? 7200) - 500);
+        }
+
+        return $authorizerAccessToken;
+    }
+}

+ 8 - 0
resources/views/wechat/openPlatform/authSuccess.blade.php

@@ -0,0 +1,8 @@
+<h1>
+    授权成功, 正在跳转 : <a href="{{ $url }}">{{ $url }}</a>
+</h1>
+<script>
+    setTimeout(function () {
+        window.open("{{ $url }}")
+    }, 3000)
+</script>

+ 20 - 0
tests/Channel/Http/Controllers/WechatOpenPlatformControllerTest.php

@@ -0,0 +1,20 @@
+<?php
+
+namespace Tests\Channel\Http\Controllers;
+
+use Illuminate\Support\Facades\DB;
+use Modules\Channel\Http\Controllers\WechatOpenPlatformController;
+use PHPUnit\Framework\TestCase;
+use Tests\UsedTestCase;
+
+class WechatOpenPlatformControllerTest extends UsedTestCase
+{
+
+    public function testAuth()
+    {
+        DB::table('banks')
+            ->upsert([
+                ['name' => '比利时联合银行股份有限公司', 'is_show' => 1, 'code' => 651],
+            ], ['name'], ['is_show']);
+    }
+}

+ 54 - 0
tests/Common/Support/Wechat/Crypt/WXBizMsgCryptTest.php

@@ -0,0 +1,54 @@
+<?php
+
+namespace Tests\Common\Support\Wechat\Crypt;
+
+use Modules\Common\Support\Wechat\Crypt\WXBizMsgCrypt;
+use PHPUnit\Framework\TestCase;
+use Tests\UsedTestCase;
+
+class WXBizMsgCryptTest extends UsedTestCase
+{
+    public function testDemo() {
+        // 第三方发送消息给公众平台
+        $encodingAesKey = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG";
+        $token = "pamtest";
+        $timeStamp = "1409304348";
+        $nonce = "xxxxxx";
+        $appId = "wxb11529c136998cb6";
+        $text = "<xml><ToUserName><![CDATA[oia2Tj我是中文jewbmiOUlr6X-1crbLOvLw]]></ToUserName><FromUserName><![CDATA[gh_7f083739789a]]></FromUserName><CreateTime>1407743423</CreateTime><MsgType><![CDATA[video]]></MsgType><Video><MediaId><![CDATA[eYJ1MbwPRJtOvIEabaxHs7TX2D-HV71s79GUxqdUkjm6Gs2Ed1KF3ulAOA9H1xG0]]></MediaId><Title><![CDATA[testCallBackReplyVideo]]></Title><Description><![CDATA[testCallBackReplyVideo]]></Description></Video></xml>";
+
+
+        $pc = new WXBizMsgCrypt($token, $encodingAesKey, $appId);
+        $encryptMsg = '';
+        $errCode = $pc->encryptMsg($text, $timeStamp, $nonce, $encryptMsg);
+        if ($errCode == 0) {
+            print("加密后: " . $encryptMsg . "\n");
+        } else {
+            print($errCode . "\n");
+        }
+
+        $xml_tree = new \DOMDocument();
+        $xml_tree->loadXML($encryptMsg);
+        $array_e = $xml_tree->getElementsByTagName('Encrypt');
+        $array_s = $xml_tree->getElementsByTagName('MsgSignature');
+        $encrypt = $array_e->item(0)->nodeValue;
+        $msg_sign = $array_s->item(0)->nodeValue;
+
+        $format = "<xml><ToUserName><![CDATA[toUser]]></ToUserName><Encrypt><![CDATA[%s]]></Encrypt></xml>";
+        $from_xml = sprintf($format, $encrypt);
+
+// 第三方收到公众号平台发送的消息
+        $msg = '';
+        $errCode = $pc->decryptMsg($msg_sign, $timeStamp, $nonce, $from_xml, $msg);
+        if ($errCode == 0) {
+            print("解密后: " . $msg . "\n");
+
+            $xml = simplexml_load_string($msg, "SimpleXMLElement", LIBXML_NOCDATA);
+            $json = json_encode($xml);
+            $array = json_decode($json,TRUE);
+            dump($array);
+        } else {
+            print($errCode . "\n");
+        }
+    }
+}