├── .gitignore ├── src ├── helper.php ├── CacheBridge.php ├── Command │ └── Config.php ├── Behavior │ └── AppInit.php ├── Facade.php ├── Middleware │ └── OauthMiddleware.php └── config.php ├── composer.json ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | /vendor/ 3 | composer.lock -------------------------------------------------------------------------------- /src/helper.php: -------------------------------------------------------------------------------- 1 | 4 | * @copyright 2017-2018 耐小心 5 | */ 6 | 7 | //加入Hook 8 | \think\facade\Hook::add('app_init', \Naixiaoxin\ThinkWechat\Behavior\AppInit::class); -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "naixiaoxin/think-wechat", 3 | "description": "EasyWechat For Thnkphp5.1+", 4 | "type": "think-extend", 5 | "keywords": ["wechat", "weixin","easywechat","thinkphp","think", "sdk"], 6 | "require": { 7 | "overtrue/wechat": "~4.0", 8 | "topthink/framework": "~5.1" 9 | }, 10 | "license": "MIT", 11 | "authors": [{ 12 | "name": "耐小心", 13 | "email": "qiqizjl@qq.com" 14 | }], 15 | "autoload": { 16 | "psr-4": { 17 | "Naixiaoxin\\ThinkWechat\\": "src/" 18 | }, 19 | "files": [ 20 | "src/helper.php" 21 | ] 22 | }, 23 | "extra": { 24 | "think-config": { 25 | "wechat": "src/config.php" 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright (c) SeanWang 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /src/CacheBridge.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2017-2018 耐小心 7 | */ 8 | 9 | namespace Naixiaoxin\ThinkWechat; 10 | 11 | use Psr\SimpleCache\CacheInterface; 12 | use think\Cache; 13 | 14 | class CacheBridge implements CacheInterface 15 | { 16 | protected $cache = null; 17 | 18 | public function __construct(Cache $cache) 19 | { 20 | $this->cache = $cache; 21 | } 22 | 23 | public function get($key, $default = null) 24 | { 25 | return $this->cache->get($key, $default); 26 | } 27 | 28 | public function set($key, $value, $ttl = null) 29 | { 30 | return $this->cache->set($key, $value, $ttl); 31 | } 32 | 33 | public function delete($key) 34 | { 35 | return $this->cache->rm($key); 36 | } 37 | 38 | public function clear() 39 | { 40 | return $this->cache->clear(); 41 | } 42 | 43 | public function getMultiple($keys, $default = null) 44 | { 45 | throw new \Exception("not support"); 46 | } 47 | 48 | public function setMultiple($values, $ttl = null) 49 | { 50 | throw new \Exception("not support"); 51 | } 52 | 53 | public function deleteMultiple($keys) 54 | { 55 | foreach ($keys as $key) { 56 | $this->delete($keys); 57 | } 58 | } 59 | 60 | public function has($key) 61 | { 62 | return $this->cache->has($key); 63 | } 64 | 65 | } -------------------------------------------------------------------------------- /src/Command/Config.php: -------------------------------------------------------------------------------- 1 | 10 | // +---------------------------------------------------------------------- 11 | 12 | namespace Naixiaoxin\ThinkWechat\Command; 13 | 14 | use think\console\Command; 15 | use think\console\Input; 16 | use think\console\Output; 17 | 18 | class Config extends Command 19 | { 20 | 21 | /** 22 | * {@inheritdoc} 23 | */ 24 | protected function configure() 25 | { 26 | $this->setName('wechat:config') 27 | ->setDescription('send wechat config to config dir'); 28 | } 29 | 30 | protected function execute(Input $input, Output $output) 31 | { 32 | if (file_exists(env('config_path') . 'wechat.php')) { 33 | $output->error('file is exist'); 34 | return; 35 | } 36 | $fileContent = file_get_contents(env('vendor_path') . 'naixiaoxin/think-wechat/src/config.php'); 37 | file_put_contents(env('config_path') . 'wechat.php', $fileContent); 38 | $output->info('send success'); 39 | return; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Behavior/AppInit.php: -------------------------------------------------------------------------------- 1 | 5 | * @copyright 2017-2018 耐小心 6 | */ 7 | 8 | namespace Naixiaoxin\ThinkWechat\Behavior; 9 | 10 | use EasyWeChat\MiniProgram\Application as MiniProgram; 11 | use EasyWeChat\OfficialAccount\Application as OfficialAccount; 12 | use EasyWeChat\OpenPlatform\Application as OpenPlatform; 13 | use EasyWeChat\Payment\Application as Payment; 14 | use EasyWeChat\Work\Application as Work; 15 | use EasyWeChat\OpenWork\Application as OpenWork; 16 | use EasyWeChat\MicroMerchant\Application as MicroMerchant; 17 | use Naixiaoxin\ThinkWechat\CacheBridge; 18 | 19 | class AppInit 20 | { 21 | protected $apps 22 | = [ 23 | 'official_account' => OfficialAccount::class, 24 | 'work' => Work::class, 25 | 'mini_program' => MiniProgram::class, 26 | 'payment' => Payment::class, 27 | 'open_platform' => OpenPlatform::class, 28 | 'open_work' => OpenWork::class, 29 | 'micro_merchant' => MicroMerchant::class, 30 | ]; 31 | 32 | /* 33 | * useing 34 | * app( $module_name) 35 | * or 36 | * app( $module_name, [ app_id => 'test' ]) 37 | */ 38 | 39 | public function run() 40 | { 41 | $wechat_default = config('wechat.default') ? config('wechat.default') : []; 42 | foreach ($this->apps as $name => $app) { 43 | if (!config('wechat.' . $name)) { 44 | continue; 45 | } 46 | $configs = config('wechat.' . $name); 47 | foreach ($configs as $config_name => $module_default) { 48 | bind('wechat.' . $name . '.' . $config_name, function ($config = [] ) use ($app, $module_default, $wechat_default) { 49 | //合并配置文件 50 | $account_config = array_merge($module_default, $wechat_default, $config); 51 | $account_app = app($app, ['config' => $account_config]); 52 | if (config('wechat.default.use_tp_cache')) { 53 | $account_app['cache'] = app(CacheBridge::class); 54 | } 55 | return $account_app; 56 | }); 57 | } 58 | if (isset($configs['default'])) { 59 | bind('wechat.' . $name, 'wechat.' . $name . '.default'); 60 | } 61 | } 62 | 63 | } 64 | } -------------------------------------------------------------------------------- /src/Facade.php: -------------------------------------------------------------------------------- 1 | 4 | * @copyright 2017-2018 耐小心 5 | */ 6 | 7 | namespace Naixiaoxin\ThinkWechat; 8 | 9 | use think\Facade as ThinkFacade; 10 | 11 | /** 12 | * Class Facade. 13 | * 14 | * @author overtrue 15 | */ 16 | class Facade extends ThinkFacade 17 | { 18 | /** 19 | * 默认为 Server. 20 | * 21 | * @return string 22 | */ 23 | public static function getFacadeAccessor() 24 | { 25 | return 'wechat.official_account'; 26 | } 27 | 28 | /** 29 | * @return \EasyWeChat\OfficialAccount\Application 30 | */ 31 | public static function officialAccount($name = '',$config = []) 32 | { 33 | return $name ? app('wechat.official_account.' . $name, ["config"=>$config]) : app('wechat.official_account', ["config"=>$config]); 34 | } 35 | 36 | /** 37 | * @return \EasyWeChat\Work\Application 38 | */ 39 | public static function work($name = '',$config = []) 40 | { 41 | return $name ? app('wechat.work.' . $name, ["config"=>$config]) : app('wechat.work', ["config"=>$config]); 42 | } 43 | 44 | /** 45 | * @return \EasyWeChat\Payment\Application 46 | */ 47 | public static function payment($name = '',$config = []) 48 | { 49 | return $name ? app('wechat.payment.' . $name, ["config"=>$config]) : app('wechat.payment', ["config"=>$config]); 50 | } 51 | 52 | /** 53 | * @return \EasyWeChat\MiniProgram\Application 54 | */ 55 | public static function miniProgram($name = '',$config = []) 56 | { 57 | return $name ? app('wechat.mini_program.' . $name, ["config"=>$config]) : app('wechat.mini_program', ["config"=>$config]); 58 | } 59 | 60 | /** 61 | * @return \EasyWeChat\OpenPlatform\Application 62 | */ 63 | public static function openPlatform($name = '',$config = []) 64 | { 65 | return $name ? app('wechat.open_platform.' . $name, ["config"=>$config]) : app('wechat.open_platform', ["config"=>$config]); 66 | } 67 | 68 | 69 | /** 70 | * @return \EasyWeChat\OpenWork\Application 71 | */ 72 | public static function openWork($name = '',$config = []) 73 | { 74 | return $name ? app('wechat.open_work.' . $name, ["config"=>$config]) : app('wechat.open_work', ["config"=>$config]); 75 | } 76 | 77 | /** 78 | * @return \EasyWeChat\MicroMerchant\Application 79 | */ 80 | public static function microMerchant($name = '',$config = []) 81 | { 82 | return $name ? app('wechat.micro_merchant.' . $name, ["config"=>$config]) : app('wechat.micro_merchant', ["config"=>$config]); 83 | } 84 | 85 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # thinkphp-wechat 2 | [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fqiqizjl%2Fthink-wechat.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fqiqizjl%2Fthink-wechat?ref=badge_shield) 3 | 4 | 微信SDK For ThinkPHP 5.1 基于[overtrue/wechat](https://github.com/overtrue/wechat) 5 | 6 | ## 框架要求 7 | ThinkPHP5.1(中间件要求支持ThinkPHP5.1.6+) 8 | 9 | ## 安装 10 | ~~~ 11 | composer require naixiaoxin/think-wechat 12 | ~~~ 13 | 14 | ## 配置 15 | 1. 修改配置文件 16 | 修改项目根目录下config/wechat.php中对应的参数 17 | 18 | 2. 每个模块基本都支持多账号,默认为 default。 19 | 20 | 21 | ## 使用 22 | ### 接受普通消息 23 | 新建一个Controller,我这边用的是Note 24 | ```php 25 | server->push(function($message){ 40 | return 'hello,world'; 41 | }); 42 | $app->server->serve()->send(); 43 | } 44 | } 45 | ``` 46 | ### 获得SDK实例 47 | #### 使用facade 48 | ```php 49 | use Naixiaoxin\ThinkWechat\Facade; 50 | 51 | $officialAccount = Facade::officialAccount(); // 公众号 52 | $work = Facade::work(); // 企业微信 53 | $payment = Facade::payment(); // 微信支付 54 | $openPlatform = Facade::openPlatform(); // 开放平台 55 | $miniProgram = Facade::miniProgram(); // 小程序 56 | $openWork = Facade::openWork(); // 企业微信第三方服务商 57 | $microMerchant = Facade::microMerchant(); // 小微商户 58 | ``` 59 | 以上均支持传入自定义账号:例如 60 | ```php 61 | $officialAccount = Facade::officialAccount('test'); // 公众号 62 | ``` 63 | 64 | 以上均支持传入自定义账号+配置(注:这里的config和配置文件中账号的格式相同):例如 65 | ```php 66 | $officialAccount = Facade::officialAccount('',$config); // 公众号 67 | ``` 68 | 69 | ### Oauth登录中间件(ThinkPHP5.1.6+) 70 | 使用中间件情况下,config的oauth.callback可以随便写~,反正是直接获取了当前URL 71 | ```php 72 | \think\facade\Route::rule('user','usere')->middleware(\Naixiaoxin\ThinkWechat\Middleware\OauthMiddleware::class); 73 | ``` 74 | 75 | 上面的路由定义了 /user 是需要微信授权的,那么在这条路由的回调 或 控制器对应的方法里, 你就可以从 session('wechat_oauth_user_default') 拿到已经授权的用户信息了。 76 | 77 | 78 | 关于ThinkPHP5.1的中间件使用方法不在叙述,详情可以查看[官方文档](https://www.kancloud.cn/manual/thinkphp5_1/564279) 79 | 80 | #### 中间件参数说明 81 | 由于ThinkPHP中间件只支持一个参数,所以以`:`做分割 82 | 83 | 支持传入account账号别名以及scope类型 84 | 85 | 若不传入`account`,会使用`default`账号 86 | 87 | 若不传入`scope`,会使用配置文件中的`oauth.scope` 88 | 89 | 支持一下两种方式 90 | ``` 91 | default:snsapi_base 92 | snsapi_base 93 | ``` 94 | 95 | ### HOOK 96 | > 你可以监听相应的事件,并对事件发生后执行相应的操作。 97 | - OAuth授权 `wechat_oauth` 98 | 99 | ```php 100 | // 该事件有以下属性 101 | $params['user']; // 同 session('wechat_oauth_user_default') 一样 102 | $params['is_new']; // 是不是新的会话(第一次创建 session 时为 true) 103 | ``` 104 | 更多 SDK 的具体使用请参考:https://easywechat.com 105 | 106 | ## 参考项目 107 | - [overtrue/laravel-wechat](https://raw.githubusercontent.com/overtrue/laravel-wechat) 108 | 109 | ## License 110 | 111 | MIT 112 | 113 | [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fqiqizjl%2Fthink-wechat.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fqiqizjl%2Fthink-wechat?ref=badge_large) 114 | -------------------------------------------------------------------------------- /src/Middleware/OauthMiddleware.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2017-2018 耐小心 7 | */ 8 | 9 | namespace Naixiaoxin\ThinkWechat\Middleware; 10 | 11 | use EasyWeChat\OfficialAccount\Application; 12 | use think\facade\Hook; 13 | use think\facade\Log; 14 | use think\Request; 15 | use think\facade\Session; 16 | 17 | class OauthMiddleware 18 | { 19 | 20 | /** 21 | * 执行中间件 22 | * 23 | * @param Request $request 24 | * @param \Closure $next 25 | * @param null $param 26 | * @return mixed|\think\response\Redirect 27 | */ 28 | public function handle(Request $request, \Closure $next, $param = null) 29 | { 30 | 31 | $params = $this->getParam($param); 32 | $account = $params["account"]; 33 | $scopes = $params["scopes"]; 34 | //定义session 35 | $session_key = 'wechat_oauth_user_' . $account; 36 | $session = Session::get($session_key); 37 | /** @var Application $officialAccount */ 38 | $officialAccount = app(\sprintf('wechat.official_account.%s', $account)); 39 | if (!$scopes) { 40 | $scopes = config(\sprintf('wechat.official_account.%s.oauth.scopes', $account)); 41 | } 42 | if (!$scopes){ 43 | $scopes = ['snsapi_base']; 44 | } 45 | if (is_string($scopes)) { 46 | $scopes = array_map('trim', explode(',', $scopes)); 47 | } 48 | Log::info(json_encode($session)); 49 | if (!$session) { 50 | if ($request->get('code')) { 51 | $session = $officialAccount->oauth->user(); 52 | Session::set($session_key, $session); 53 | Hook::listen('wechat_oauth', [ 54 | 'user' => $session, 55 | 'is_new' => true, 56 | ]); 57 | //跳转到登录 58 | Log::info($this->getTargetUrl($request)); 59 | return redirect($this->getTargetUrl($request)); 60 | } 61 | $url = $officialAccount->oauth->scopes($scopes)->redirect($request->url(true))->getTargetUrl(); 62 | return redirect($url); 63 | } 64 | Hook::listen('wechat_oauth', [ 65 | 'user' => $session, 66 | 'is_new' => false, 67 | ]); 68 | return $next($request); 69 | } 70 | 71 | 72 | /** 73 | * @param $params 74 | * @return array 75 | */ 76 | protected function getParam($params) 77 | { 78 | //定义初始化 79 | $res["account"] = "default"; 80 | $res['scopes'] = null; 81 | if (!$params) { 82 | return $res; 83 | } 84 | //解析 85 | $result = explode(":", $params); 86 | $account = ""; 87 | $scopes = ""; 88 | if (isset($result[0])) { 89 | $account = $result[0]; 90 | } 91 | if (isset($result[1])) { 92 | $scopes = $result[1]; 93 | } 94 | if ($account) { 95 | if (strstr($account, "sns")) { 96 | $res["scopes"] = $account; 97 | } else { 98 | $res["account"] = $account; 99 | } 100 | } 101 | if ($scopes) { 102 | $res["scopes"] = $scopes; 103 | } 104 | return $res; 105 | } 106 | 107 | /** 108 | * Build the target business url. 109 | * 110 | * @param Request $request 111 | *¬ 112 | * @return string 113 | */ 114 | protected function getTargetUrl($request) 115 | { 116 | $param = $request->get(); 117 | if (isset($param['code'])) { 118 | unset($param['code']); 119 | } 120 | if (isset($param['state'])) { 121 | unset($param['state']); 122 | } 123 | return $request->baseUrl() . (empty($param) ? '' : '?' . http_build_query($param)); 124 | } 125 | } -------------------------------------------------------------------------------- /src/config.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2017-2018 耐小心 7 | */ 8 | 9 | return [ 10 | /* 11 | * 默认配置,将会合并到各模块中 12 | */ 13 | 'default' => [ 14 | /* 15 | * 指定 API 调用返回结果的类型:array(default)/object/raw/自定义类名 16 | */ 17 | 'response_type' => 'array', 18 | /* 19 | * 使用 ThinkPHP 的缓存系统 20 | */ 21 | 'use_tp_cache' => true, 22 | /* 23 | * 日志配置 24 | * 25 | * level: 日志级别,可选为: 26 | * debug/info/notice/warning/error/critical/alert/emergency 27 | * file:日志文件位置(绝对路径!!!),要求可写权限 28 | */ 29 | 'log' => [ 30 | 'level' => env('WECHAT_LOG_LEVEL', 'debug'), 31 | 'file' => env('WECHAT_LOG_FILE', app()->getRuntimePath()."log/wechat.log"), 32 | ], 33 | ], 34 | 35 | //公众号 36 | 'official_account' => [ 37 | 'default' => [ 38 | // AppID 39 | 'app_id' => env('WECHAT_OFFICIAL_ACCOUNT_APPID', 'your-app-id'), 40 | // AppSecret 41 | 'secret' => env('WECHAT_OFFICIAL_ACCOUNT_SECRET', 'your-app-secret'), 42 | // Token 43 | 'token' => env('WECHAT_OFFICIAL_ACCOUNT_TOKEN', 'your-token'), 44 | // EncodingAESKey 45 | 'aes_key' => env('WECHAT_OFFICIAL_ACCOUNT_AES_KEY', ''), 46 | /* 47 | * OAuth 配置 48 | * 49 | * scopes:公众平台(snsapi_userinfo / snsapi_base),开放平台:snsapi_login 50 | * callback:OAuth授权完成后的回调页地址(如果使用中间件,则随便填写。。。) 51 | */ 52 | //'oauth' => [ 53 | // 'scopes' => array_map('trim', 54 | // explode(',', env('WECHAT_OFFICIAL_ACCOUNT_OAUTH_SCOPES', 'snsapi_userinfo'))), 55 | // 'callback' => env('WECHAT_OFFICIAL_ACCOUNT_OAUTH_CALLBACK', '/examples/oauth_callback.php'), 56 | //], 57 | ], 58 | ], 59 | 60 | //第三方开发平台 61 | //'open_platform' => [ 62 | // 'default' => [ 63 | // 'app_id' => env('WECHAT_OPEN_PLATFORM_APPID', ''), 64 | // 'secret' => env('WECHAT_OPEN_PLATFORM_SECRET', ''), 65 | // 'token' => env('WECHAT_OPEN_PLATFORM_TOKEN', ''), 66 | // 'aes_key' => env('WECHAT_OPEN_PLATFORM_AES_KEY', ''), 67 | // ], 68 | //], 69 | 70 | //小程序 71 | //'mini_program' => [ 72 | // 'default' => [ 73 | // 'app_id' => env('WECHAT_MINI_PROGRAM_APPID', ''), 74 | // 'secret' => env('WECHAT_MINI_PROGRAM_SECRET', ''), 75 | // 'token' => env('WECHAT_MINI_PROGRAM_TOKEN', ''), 76 | // 'aes_key' => env('WECHAT_MINI_PROGRAM_AES_KEY', ''), 77 | // ], 78 | //], 79 | 80 | //支付 81 | //'payment' => [ 82 | // 'default' => [ 83 | // 'sandbox' => env('WECHAT_PAYMENT_SANDBOX', false), 84 | // 'app_id' => env('WECHAT_PAYMENT_APPID', ''), 85 | // 'mch_id' => env('WECHAT_PAYMENT_MCH_ID', 'your-mch-id'), 86 | // 'key' => env('WECHAT_PAYMENT_KEY', 'key-for-signature'), 87 | // 'cert_path' => env('WECHAT_PAYMENT_CERT_PATH', 'path/to/cert/apiclient_cert.pem'), // XXX: 绝对路径!!!! 88 | // 'key_path' => env('WECHAT_PAYMENT_KEY_PATH', 'path/to/cert/apiclient_key.pem'), // XXX: 绝对路径!!!! 89 | // 'notify_url' => 'http://example.com/payments/wechat-notify', // 默认支付结果通知地址 90 | // ], 91 | // // ... 92 | //], 93 | 94 | //企业微信 95 | //'work' => [ 96 | // 'default' => [ 97 | // 'corp_id' => 'xxxxxxxxxxxxxxxxx', 98 | // 'agent_id' => 100020, 99 | // 'secret' => env('WECHAT_WORK_AGENT_CONTACTS_SECRET', ''), 100 | // //... 101 | // ], 102 | //], 103 | 104 | 105 | //企业开放平台 106 | //'open_work' => [ 107 | // 'default' => [ 108 | // //参考EasyWechat官方文档 109 | // //https://www.easywechat.com/docs/4.1/open-work/index 110 | // ], 111 | //], 112 | 113 | //小微商户 114 | //'micro_merchant' => [ 115 | // 'default' => [ 116 | // //参考EasyWechat官方文档 117 | // //https://www.easywechat.com/docs/4.1/micro-merchant/index 118 | // ], 119 | //], 120 | ]; 121 | --------------------------------------------------------------------------------