├── .github └── FUNDING.yml ├── .gitignore ├── LICENSE ├── README.md ├── README_EN.md ├── composer.json ├── config └── easywechat.php └── src ├── EasyWeChat.php ├── Events ├── OpenPlatform │ ├── AuthorizeUpdated.php │ ├── Authorized.php │ ├── OpenPlatformEvent.php │ ├── Unauthorized.php │ └── VerifyTicketRefreshed.php └── WeChatUserAuthorized.php ├── Middleware └── OAuthAuthenticate.php ├── ServiceProvider.php └── Traits └── HandleOpenPlatformServerEvents.php /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [overtrue] 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | composer.lock 3 | .php-cs-fixer.cache 4 | .ideacghooks.lock 5 | 6 | cghooks.lock 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 overtrue 7.x 起不再默认支持 Lumen。 8 | 9 | ## 框架要求 10 | 11 | - overtrue/laravel-wechat:^7.0 -> Laravel >= 8.0 12 | - overtrue/laravel-wechat:^6.0 -> Laravel/Lumen >= 7.0 13 | - overtrue/laravel-wechat:^5.1 -> Laravel/Lumen >= 5.1 14 | 15 | ## 安装 16 | 17 | ```bash 18 | composer require overtrue/laravel-wechat:^7.2 19 | ``` 20 | 21 | ## 配置 22 | 23 | 1. 创建配置文件: 24 | 25 | ```shell 26 | php artisan vendor:publish --provider="Overtrue\\LaravelWeChat\\ServiceProvider" 27 | ``` 28 | 29 | 2. 可选,添加别名 30 | 31 | ```php 32 | 'aliases' => [ 33 | // ... 34 | 'EasyWeChat' => Overtrue\LaravelWeChat\EasyWeChat::class, 35 | ], 36 | ``` 37 | 38 | 3. 每个模块基本都支持多账号,默认为 `default`。 39 | 40 | ## 使用 41 | 42 | :rotating_light: 在中间件 `App\Http\Middleware\VerifyCsrfToken` 排除微信相关的路由,如: 43 | 44 | ```php 45 | protected $except = [ 46 | // ... 47 | 'wechat', 48 | ]; 49 | ``` 50 | 对于 Laravel 11.x 可以使用`bootstrap/app.php` 中的`$middleware->validateCsrfTokens`方法: 51 | ```php 52 | ->withMiddleware(function (Middleware $middleware) { 53 | $middleware->validateCsrfTokens(except: [ 54 | // ... 55 | 'wechat', 56 | ]); 57 | }) 58 | ``` 59 | 60 | 下面以接收普通消息为例写一个例子。 61 | 62 | 路由: 63 | 64 | ```php 65 | Route::any('/wechat', 'WeChatController@serve'); 66 | ``` 67 | 68 | > 注意:一定是 `Route::any`, 因为微信服务端认证的时候是 `GET`, 接收用户消息时是 `POST` ! 69 | 70 | 然后创建控制器 `WeChatController`: 71 | 72 | ```php 73 | getServer(); 86 | 87 | $server->with(function($message){ 88 | return "欢迎关注 overtrue!"; 89 | }); 90 | 91 | return $server->serve(); 92 | } 93 | } 94 | ``` 95 | 96 | ## OAuth 中间件 97 | 98 | 使用中间件的情况下 `app/config/easywechat.php` 中的 `oauth.callback` 就随便填写吧(因为用不着了 :smile:)。 99 | 100 | 1. 在 `app/Http/Kernel.php` 中添加路由中间件: 101 | 102 | ```php 103 | protected $routeMiddleware = [ 104 | // ... 105 | 'easywechat.oauth' => \Overtrue\LaravelWeChat\Middleware\OAuthAuthenticate::class, 106 | ]; 107 | ``` 108 | 109 | 2. 在路由中添加中间件: 110 | 111 | ```php 112 | //... 113 | Route::group(['middleware' => ['web', 'easywechat.oauth']], function () { 114 | Route::get('/user', function () { 115 | $user = session('easywechat.oauth_user.default'); // 拿到授权用户资料 116 | 117 | dd($user); 118 | }); 119 | }); 120 | ``` 121 | 122 | 中间件支持指定配置名称:`'easywechat.oauth:default'`,当然,你也可以在中间件参数指定当前的 `scopes`: 123 | 124 | ```php 125 | Route::group(['middleware' => ['easywechat.oauth:snsapi_userinfo']], function () { 126 | // ... 127 | }); 128 | 129 | // 或者指定账户的同时指定 scopes: 130 | Route::group(['middleware' => ['easywechat.oauth:default,snsapi_userinfo']], function () { 131 | // ... 132 | }); 133 | ``` 134 | 135 | 上面的路由定义了 `/user` 是需要微信授权的,那么在这条路由的**回调 或 控制器对应的方法里**, 你就可以从 `session('easywechat.oauth_user.default')` 拿到已经授权的用户信息了。 136 | 137 | ## 模拟授权 138 | 139 | 有时候我们希望在本地开发完成后线上才真实的走微信授权流程,这将减少我们的开发成本,那么你需要做以下两步: 140 | 141 | 1. 准备模拟授权资料: 142 | 2. 143 | ```php 144 | use Illuminate\Support\Arr; 145 | use Overtrue\Socialite\User as SocialiteUser; 146 | 147 | $user = new SocialiteUser([ 148 | 'id' => 'mock-openid', 149 | 'name' => 'overtrue', 150 | 'nickname' => 'overtrue', 151 | 'avatar' => 'http://example.com/avatars/overtrue.png', 152 | 'email' => null, 153 | 'original' => [], 154 | 'provider' => 'WeChat', 155 | ]); 156 | ``` 157 | 158 | > 以上字段在 scope 为 `snsapi_userinfo` 时尽可能配置齐全哦,当然,如果你的模式只是 `snsapi_base` 的话只需要 `openid` 就好了。 159 | 160 | 2. 将资料写入 session: 161 | 162 | > 注意:一定要在调用 OAuth 中间件之前写入,比如你可以创建一个全局中间件来完成这件事儿,只在开发环境启用即可。 163 | 164 | ```php 165 | session(['easywechat.oauth_user.default' => $user]); // 同理,`default` 可以更换为您对应的其它配置名 166 | ``` 167 | 168 | ## 事件 169 | 170 | > 你可以监听相应的事件,并对事件发生后执行相应的操作。 171 | 172 | - OAuth 网页授权:`Overtrue\LaravelWeChat\Events\WeChatUserAuthorized` 173 | 174 | ```php 175 | // 该事件有以下属性 176 | $event->user; // 同 session('easywechat.oauth_user.default') 一样 177 | $event->isNewSession; // 是不是新的会话(第一次创建 session 时为 true) 178 | $event->account; // 当前中间件所使用的账号,对应在配置文件中的配置项名称 179 | ``` 180 | 181 | 182 | ## 开放平台支持 183 | 184 | 您可以适用内置的 `Overtrue\LaravelWeChat\Traits\HandleOpenPlatformServerEvents` 来快速完成开放平台的服务端验证工作: 185 | 186 | *routes/web.php:* 187 | ```php 188 | Route::any('/open-platform/server', OpenPlatformController::class); 189 | ``` 190 | 191 | *app/Http/Controllers/OpenPlatformController.php:* 192 | 193 | ```php 194 | handleServerEvents($app); 209 | } 210 | } 211 | ``` 212 | 213 | Tips: 默认会根据微信开放平台的推送内容触发如下事件,你可以监听相应的事件并进行处理: 214 | 215 | - 授权方成功授权:`Overtrue\LaravelWeChat\Events\OpenPlatform\Authorized` 216 | - 授权方更新授权:`Overtrue\LaravelWeChat\Events\OpenPlatform\AuthorizeUpdated` 217 | - 授权方取消授权:`Overtrue\LaravelWeChat\Events\OpenPlatform\Unauthorized` 218 | - 开放平台推送 VerifyTicket:`Overtrue\LaravelWeChat\Events\OpenPlatform\VerifyTicketRefreshed` 219 | 220 | ```php 221 | // 事件有如下属性 222 | $message = $event->payload; // 开放平台事件通知内容 223 | ``` 224 | 225 | 配置后 `http://example.com/open-platform/server` 则为开放平台第三方应用设置的授权事件接收 URL。 226 | 227 | 228 | 更多 SDK 的具体使用请参考: 229 | 230 | ## :heart: Sponsor me 231 | 232 | [![Sponsor me](https://github.com/overtrue/overtrue/blob/master/sponsor-me.svg?raw=true)](https://github.com/sponsors/overtrue) 233 | 234 | 如果你喜欢我的项目并想支持它,[点击这里 :heart:](https://github.com/sponsors/overtrue) 235 | 236 | ## Project supported by JetBrains 237 | 238 | Many thanks to Jetbrains for kindly providing a license for me to work on this and other open-source projects. 239 | 240 | [![](https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg)](https://www.jetbrains.com/?from=https://github.com/overtrue) 241 | 242 | 243 | ## PHP 扩展包开发 244 | 245 | > 想知道如何从零开始构建 PHP 扩展包? 246 | > 247 | > 请关注我的实战课程,我会在此课程中分享一些扩展开发经验 —— [《PHP 扩展包实战教程 - 从入门到发布》](https://learnku.com/courses/creating-package) 248 | 249 | ## License 250 | 251 | MIT 252 | -------------------------------------------------------------------------------- /README_EN.md: -------------------------------------------------------------------------------- 1 | # EasyWeChat for Laravel 2 | 3 | WeChat SDK [w7corp/easywechat](https://github.com/w7corp/easywechat) wrapper for Laravel. 4 | 5 | [![Sponsor me](https://github.com/overtrue/overtrue/blob/master/sponsor-me-button-s.svg?raw=true)](https://github.com/sponsors/overtrue) 6 | 7 | ## Requirements 8 | 9 | - overtrue/laravel-wechat:^7.0 -> Laravel >= 8.0 10 | - overtrue/laravel-wechat:^6.0 -> Laravel/Lumen >= 7.0 11 | - overtrue/laravel-wechat:^5.1 -> Laravel/Lumen >= 5.1 12 | 13 | ## Installation 14 | 15 | ```bash 16 | composer require "overtrue/laravel-wechat" 17 | ``` 18 | 19 | ## Config 20 | 21 | 1. publishe the config file to `config` directory: 22 | 23 | ```shell 24 | php artisan vendor:publish --provider="Overtrue\LaravelWeChat\ServiceProvider" 25 | ``` 26 | 27 | 2. (Optional) You can add the alias to `config/app.php`: 28 | 29 | ```php 30 | 'aliases' => [ 31 | // ... 32 | 'EasyWeChat' => Overtrue\LaravelWeChat\EasyWeChat::class, 33 | ], 34 | ``` 35 | 36 | 3. Each module basically supports multiple accounts, the default name is `default`. 37 | 38 | ## Usage 39 | 40 | Ignore the CSRF check for WeChat related routes: 41 | 42 | ```php 43 | protected $except = [ 44 | // ... 45 | 'wechat', 46 | ]; 47 | ``` 48 | 49 | The following is an example written to receive a server message: 50 | 51 | Routes: 52 | 53 | ```php 54 | Route::any('/wechat', 'WeChatController@serve'); 55 | ``` 56 | 57 | > **Note** 58 | > 59 | > It must be `Route::any`, because the WeChat server validation is `GET` request, and when receiving user messages is `POST` request! 60 | 61 | Then, let's create a controller `WeChatController`: 62 | 63 | ```php 64 | getServer(); 77 | 78 | $server->with(function($message){ 79 | return "欢迎关注 overtrue!"; 80 | }); 81 | 82 | return $server->serve(); 83 | } 84 | } 85 | ``` 86 | 87 | ## OAuth middleware 88 | 89 | If you're using middleware, just fill in `oauth.callback` with any value in `app/config/wechat.php` (because you won't need it :smile:). 90 | 91 | 1. Register the middleware to `app/Http/Kernel.php`: 92 | 93 | ```php 94 | protected $routeMiddleware = [ 95 | // ... 96 | 'easywechat.oauth' => \Overtrue\LaravelWeChat\Middleware\OAuthAuthenticate::class, 97 | ]; 98 | ``` 99 | 100 | 2. Add the middleware to oauth route: 101 | 102 | ```php 103 | //... 104 | Route::group(['middleware' => ['web', 'easywechat.oauth']], function () { 105 | Route::get('/user', function () { 106 | $user = session('easywechat.oauth_user.default'); // oauth user 107 | 108 | \dd($user); 109 | }); 110 | }); 111 | ``` 112 | 113 | You can also set the config name and scopes as middleware paremeter: 114 | 115 | ```php 116 | // scopes 117 | Route::group(['middleware' => ['easywechat.oauth:snsapi_userinfo']], function () { 118 | // ... 119 | }); 120 | 121 | // account name and scopes: 122 | Route::group(['middleware' => ['easywechat.oauth:default,snsapi_userinfo']], function () { 123 | // ... 124 | }); 125 | ``` 126 | 127 | The above route defines `/user` as requiring authorization from WeChat, so in the **callback or controller method** of this route, you can get the authorized user information from `session('easywechat.oauth_user.default')`. 128 | 129 | ## Mock Authorization 130 | 131 | Sometimes we want to go real WeChat authorization process only after local development is completed online, which will reduce our development cost, then you need to do the following two steps. 132 | 133 | 1. Prepare mock authorization information: 134 | 135 | ```php 136 | use Illuminate\Support\Arr; 137 | use Overtrue\Socialite\User as SocialiteUser; 138 | 139 | $user = new SocialiteUser([ 140 | 'id' => 'mock-openid', 141 | 'name' => 'overtrue', 142 | 'nickname' => 'overtrue', 143 | 'avatar' => 'http://example.com/avatars/overtrue.png', 144 | 'email' => null, 145 | 'original' => [], 146 | 'provider' => 'WeChat', 147 | ]); 148 | ``` 149 | 150 | > If your schema is only `snsapi_base`, you only need `openid`. 151 | 152 | 2. Write the information to the session: 153 | 154 | ```php 155 | // Similarly, `default` can be replaced with your corresponding other configuration name 156 | session(['easywechat.oauth_user.default' => $user]); 157 | ``` 158 | 159 | > **Note** 160 | > 161 | > Be sure to write before calling the OAuth middleware, for example, you can create a global middleware to do this, and just enable it in the development environment. 162 | 163 | 164 | ## Events 165 | 166 | You can listen to the corresponding events and perform the corresponding actions when they occur. 167 | 168 | - **OAuth authorized**: `Overtrue\LaravelWeChat\Events\WeChatUserAuthorized` 169 | 170 | ```php 171 | // The event has the following properties 172 | $event->user; // same as session('easywechat.oauth_user.default') 173 | $event->isNewSession; // if it is a new session (true when first creating a session) 174 | $event->account; // the current account used by the middleware, corresponding to the name of the configuration item in the configuration file 175 | ``` 176 | 177 | 178 | ## Open Platform Support 179 | 180 | You can apply the built-in `Overtrue\LaravelWeChat\Traits\HandleOpenPlatformServerEvents` to quickly complete the server-side validation for the open platform: 181 | 182 | *routes/web.php:* 183 | 184 | ```php 185 | Route::any('/open-platform/server', OpenPlatformController::class); 186 | ``` 187 | 188 | *app/Http/Controllers/OpenPlatformController.php:* 189 | 190 | ```php 191 | handleServerEvents($app); 204 | } 205 | } 206 | ``` 207 | 208 | > **Note** 209 | > 210 | > By default, the following events will be triggered based on the push content of WeChat Open Platform, you can listen to the corresponding events and process them. 211 | 212 | - **Authorized party successfully authorized**: `Overtrue\LaravelWeChat\Events\OpenPlatform\Authorized` 213 | - **Authorized party updates authorization**: `Overtrue\LaravelWeChat\Events\OpenPlatform\AuthorizeUpdated` 214 | - **Authorizer deauthorized**: `Overtrue\LaravelWeChat\Events\OpenPlatform\Unauthorized` 215 | - **OpenPlatform Push VerifyTicket**: `Overtrue\LaravelWeChat\Events\OpenPlatform\VerifyTicketRefreshed` 216 | 217 | ```php 218 | // Events have the following properties 219 | $message = $event->payload; // Open Platform event notification content 220 | ``` 221 | 222 | After configuration `http://example.com/open-platform/server` is the authorized event receiving URL set for the Open Platform third party application. 223 | 224 | 225 | For more SDK specific usage, please refer to: 226 | 227 | ## :heart: Sponsor me 228 | 229 | [![Sponsor me](https://github.com/overtrue/overtrue/blob/master/sponsor-me.svg?raw=true)](https://github.com/sponsors/overtrue) 230 | 231 | If you like my project and want to support it, [click here :heart:](https://github.com/sponsors/overtrue) 232 | 233 | ## Project supported by JetBrains 234 | 235 | Many thanks to Jetbrains for kindly providing a license for me to work on this and other open-source projects. 236 | 237 | [![](https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg)](https://www.jetbrains.com/?from=https://github.com/overtrue) 238 | 239 | ## License 240 | 241 | MIT 242 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "overtrue/laravel-wechat", 3 | "description": "微信 SDK for Laravel", 4 | "keywords": [ 5 | "wechat", 6 | "weixin", 7 | "laravel", 8 | "sdk" 9 | ], 10 | "license": "MIT", 11 | "authors": [ 12 | { 13 | "name": "overtrue", 14 | "email": "anzhengchao@gmail.com" 15 | } 16 | ], 17 | "require": { 18 | "illuminate/container": "^9.0|^10.0|^11.0|^12.0", 19 | "w7corp/easywechat": "^6.0.0" 20 | }, 21 | "require-dev": { 22 | "laravel/framework": "^10.0", 23 | "jetbrains/phpstorm-attributes": "^1.0", 24 | "brainmaestro/composer-git-hooks": "dev-master", 25 | "laravel/pint": "^1.5" 26 | }, 27 | "autoload": { 28 | "psr-4": { 29 | "Overtrue\\LaravelWeChat\\": "src/" 30 | } 31 | }, 32 | "extra": { 33 | "laravel": { 34 | "providers": [ 35 | "Overtrue\\LaravelWeChat\\ServiceProvider" 36 | ] 37 | }, 38 | "hooks": { 39 | "pre-commit": [ 40 | "composer check-style" 41 | ], 42 | "pre-push": [ 43 | "composer check-style" 44 | ] 45 | } 46 | }, 47 | "scripts": { 48 | "post-update-cmd": [ 49 | "cghooks remove", 50 | "cghooks add --ignore-lock", 51 | "cghooks update" 52 | ], 53 | "post-merge": "composer install", 54 | "post-install-cmd": [ 55 | "cghooks remove", 56 | "cghooks add --ignore-lock", 57 | "cghooks update" 58 | ], 59 | "check-style": "vendor/bin/pint --test", 60 | "fix-style": "vendor/bin/pint" 61 | }, 62 | "config": { 63 | "allow-plugins": { 64 | "easywechat-composer/easywechat-composer": true 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /config/easywechat.php: -------------------------------------------------------------------------------- 1 | [ 8 | 'http' => [ 9 | 'timeout' => 5.0, 10 | ], 11 | ], 12 | 13 | /* 14 | * 公众号 15 | */ 16 | 'official_account' => [ 17 | 'default' => [ 18 | 'app_id' => env('WECHAT_OFFICIAL_ACCOUNT_APPID', ''), // AppID 19 | 'secret' => env('WECHAT_OFFICIAL_ACCOUNT_SECRET', ''), // AppSecret 20 | 'token' => env('WECHAT_OFFICIAL_ACCOUNT_TOKEN', ''), // Token 21 | 'aes_key' => env('WECHAT_OFFICIAL_ACCOUNT_AES_KEY', ''), // EncodingAESKey 22 | 23 | /* 24 | * OAuth 配置 25 | * 26 | * scopes:公众平台(snsapi_userinfo / snsapi_base),开放平台:snsapi_login 27 | * callback:OAuth授权完成后的回调页地址(如果使用中间件,则随便填写。。。) 28 | * enforce_https:是否强制使用 HTTPS 跳转 29 | */ 30 | // 'oauth' => [ 31 | // 'scopes' => array_map('trim', explode(',', env('WECHAT_OFFICIAL_ACCOUNT_OAUTH_SCOPES', 'snsapi_userinfo'))), 32 | // 'callback' => env('WECHAT_OFFICIAL_ACCOUNT_OAUTH_CALLBACK', '/examples/oauth_callback.php'), 33 | // 'enforce_https' => true, 34 | // ], 35 | 36 | /** 37 | * 接口请求相关配置,超时时间等,具体可用参数请参考: 38 | * https://github.com/symfony/symfony/blob/6.0/src/Symfony/Contracts/HttpClient/HttpClientInterface.php#L26 39 | */ 40 | //'http' => [ 41 | // 'timeout' => 5.0, 42 | // // 如果你在国外想要覆盖默认的 url 的时候才使用,根据不同的模块配置不同的 uri 43 | // 'base_uri' => 'https://api.weixin.qq.com/', 44 | //], 45 | ], 46 | ], 47 | 48 | /* 49 | * 开放平台第三方平台 50 | */ 51 | // 'open_platform' => [ 52 | // 'default' => [ 53 | // 'app_id' => env('WECHAT_OPEN_PLATFORM_APPID', ''), 54 | // 'secret' => env('WECHAT_OPEN_PLATFORM_SECRET', ''), 55 | // 'token' => env('WECHAT_OPEN_PLATFORM_TOKEN', ''), 56 | // 'aes_key' => env('WECHAT_OPEN_PLATFORM_AES_KEY', ''), 57 | 58 | /** 59 | * 接口请求相关配置,超时时间等,具体可用参数请参考: 60 | * https://github.com/symfony/symfony/blob/6.0/src/Symfony/Contracts/HttpClient/HttpClientInterface.php#L26 61 | */ 62 | // 'http' => [ 63 | // 'timeout' => 5.0, 64 | // // 如果你在国外想要覆盖默认的 url 的时候才使用,根据不同的模块配置不同的 uri 65 | // 'base_uri' => 'https://api.weixin.qq.com/', 66 | // ], 67 | // ], 68 | // ], 69 | 70 | /* 71 | * 小程序 72 | */ 73 | // 'mini_app' => [ 74 | // 'default' => [ 75 | // 'app_id' => env('WECHAT_MINI_APP_APPID', ''), 76 | // 'secret' => env('WECHAT_MINI_APP_SECRET', ''), 77 | // 'token' => env('WECHAT_MINI_APP_TOKEN', ''), 78 | // 'aes_key' => env('WECHAT_MINI_APP_AES_KEY', ''), 79 | 80 | /** 81 | * 接口请求相关配置,超时时间等,具体可用参数请参考: 82 | * https://github.com/symfony/symfony/blob/6.0/src/Symfony/Contracts/HttpClient/HttpClientInterface.php#L26 83 | */ 84 | // 'http' => [ 85 | // 'timeout' => 5.0, 86 | // // 如果你在国外想要覆盖默认的 url 的时候才使用,根据不同的模块配置不同的 uri 87 | // 'base_uri' => 'https://api.weixin.qq.com/', 88 | // ], 89 | // ], 90 | // ], 91 | 92 | /* 93 | * 微信支付 94 | */ 95 | // 'pay' => [ 96 | // 'default' => [ 97 | // 'app_id' => env('WECHAT_PAY_APPID', ''), 98 | // 'mch_id' => env('WECHAT_PAY_MCH_ID', 'your-mch-id'), 99 | // 'private_key' => '/data/private/certs/apiclient_key.pem', 100 | // 'certificate' => '/data/private/certs/apiclient_cert.pem', 101 | // 'notify_url' => 'http://example.com/payments/wechat-notify', // 默认支付结果通知地址 102 | // /** 103 | // * 证书序列号,可通过命令从证书获取: 104 | // * `openssl x509 -in application_cert.pem -noout -serial` 105 | // */ 106 | // 'certificate_serial_no' => '6F2BADBE1738B07EE45C6A85C5F86EE343CAABC3', 107 | // 108 | // 'http' => [ 109 | // 'base_uri' => 'https://api.mch.weixin.qq.com/', 110 | // ], 111 | // 112 | // // v2 API 秘钥 113 | // //'v2_secret_key' => '26db3e15cfedb44abfbb5fe94fxxxxx', 114 | // 115 | // // v3 API 秘钥 116 | // //'secret_key' => '43A03299A3C3FED3D8CE7B820Fxxxxx', 117 | // 118 | // // 注意 此处为微信支付平台证书 https://pay.weixin.qq.com/wiki/doc/apiv3/apis/wechatpay5_1.shtml 119 | // 'platform_certs' => [ 120 | // '/data/private/certs/platform_key.pem', 121 | // ], 122 | // ], 123 | // ], 124 | 125 | /* 126 | * 企业微信 127 | */ 128 | // 'work' => [ 129 | // 'default' => [ 130 | // 'corp_id' => env('WECHAT_WORK_CORP_ID', ''), 131 | // 'secret' => env('WECHAT_WORK_SECRET', ''), 132 | // 'token' => env('WECHAT_WORK_TOKEN', ''), 133 | // 'aes_key' => env('WECHAT_WORK_AES_KEY', ''), 134 | 135 | /** 136 | * 接口请求相关配置,超时时间等,具体可用参数请参考: 137 | * https://github.com/symfony/symfony/blob/6.0/src/Symfony/Contracts/HttpClient/HttpClientInterface.php#L26 138 | */ 139 | // 'http' => [ 140 | // 'timeout' => 5.0, 141 | // // 如果你在国外想要覆盖默认的 url 的时候才使用,根据不同的模块配置不同的 uri 142 | // 'base_uri' => 'https://api.weixin.qq.com/', 143 | // ], 144 | // ], 145 | // ], 146 | 147 | /* 148 | * 企业微信开放平台 149 | */ 150 | // 'open_work' => [ 151 | // 'default' => [ 152 | // 'corp_id' => env('WECHAT_OPEN_WORK_CORP_ID', ''), 153 | // 'provider_secret' => env('WECHAT_OPEN_WORK_SECRET', ''), 154 | // 'token' => env('WECHAT_OPEN_WORK_TOKEN', ''), 155 | // 'aes_key' => env('WECHAT_OPEN_WORK_AES_KEY', ''), 156 | 157 | /** 158 | * 接口请求相关配置,超时时间等,具体可用参数请参考: 159 | * https://github.com/symfony/symfony/blob/6.0/src/Symfony/Contracts/HttpClient/HttpClientInterface.php#L26 160 | */ 161 | // 'http' => [ 162 | // 'timeout' => 5.0, 163 | // // 如果你在国外想要覆盖默认的 url 的时候才使用,根据不同的模块配置不同的 uri 164 | // 'base_uri' => 'https://api.weixin.qq.com/', 165 | // ], 166 | // ], 167 | // ], 168 | ]; 169 | -------------------------------------------------------------------------------- /src/EasyWeChat.php: -------------------------------------------------------------------------------- 1 | payload[substr($name, 3)] ?? null; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Events/OpenPlatform/Unauthorized.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Overtrue\LaravelWeChat\Events; 13 | 14 | use Illuminate\Queue\SerializesModels; 15 | use Overtrue\Socialite\User; 16 | 17 | class WeChatUserAuthorized 18 | { 19 | use SerializesModels; 20 | 21 | public function __construct( 22 | public User $user, 23 | public bool $isNewSession = false, 24 | public string $account = '' 25 | ) { 26 | } 27 | 28 | public function getUser(): User 29 | { 30 | return $this->user; 31 | } 32 | 33 | public function getAccount(): string 34 | { 35 | return $this->account; 36 | } 37 | 38 | public function isNewSession(): bool 39 | { 40 | return $this->isNewSession; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Middleware/OAuthAuthenticate.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Overtrue\LaravelWeChat\Middleware; 13 | 14 | use Closure; 15 | use Illuminate\Http\Request; 16 | use Illuminate\Support\Arr; 17 | use Illuminate\Support\Facades\Session; 18 | use Illuminate\Support\Str; 19 | use JetBrains\PhpStorm\Pure; 20 | use Overtrue\LaravelWeChat\Events\WeChatUserAuthorized; 21 | 22 | /** 23 | * 仅适用于微信公众号, 企业微信的网页应用。 24 | */ 25 | class OAuthAuthenticate 26 | { 27 | public function handle( 28 | Request $request, 29 | Closure $next, 30 | string $account = 'default', 31 | string $scope = null, 32 | ?string $type = 'service' 33 | ): mixed { 34 | // 保证兼容性参数处理 35 | $prefix = ('work' !== $type) ? 'official_account' : 'work'; 36 | 37 | $sessionKey = \sprintf('easywechat.oauth_user.%s', $account); 38 | $name = \sprintf('easywechat.%s.%s', $prefix, $account); 39 | $config = config($name, []); 40 | $service = app($name); 41 | 42 | $scope = $scope ?: Arr::get($config, 'oauth.scopes', ['snsapi_base']); 43 | 44 | if (\is_string($scope)) { 45 | $scope = \array_map('trim', explode(',', $scope)); 46 | } 47 | 48 | if (Session::has($sessionKey)) { 49 | event(new WeChatUserAuthorized(session($sessionKey), false, $account)); 50 | 51 | return $next($request); 52 | } 53 | 54 | // 是否强制使用 HTTPS 跳转 55 | $enforceHttps = Arr::get($config, 'oauth.enforce_https', false); 56 | 57 | if ($request->has('code')) { 58 | session([$sessionKey => $service->getOAuth()->userFromCode($request->query('code'))]); 59 | 60 | event(new WeChatUserAuthorized(session($sessionKey), true, $account)); 61 | 62 | return redirect()->to($this->getIntendUrl($request, $enforceHttps)); 63 | } 64 | 65 | session()->forget($sessionKey); 66 | 67 | // 跳转到微信授权页 68 | return redirect()->away( 69 | $service->getOAuth()->scopes($scope)->redirect($this->getRedirectUrl($request, $enforceHttps)) 70 | ); 71 | } 72 | 73 | protected function getIntendUrl(Request $request, bool $https = false): string 74 | { 75 | $query = Arr::except($request->query(), ['code', 'state']); 76 | $url = $request->url(); 77 | 78 | if ($https) { 79 | $url = $this->ensureHttpsScheme($url); 80 | } 81 | 82 | return $url.(empty($query) ? '' : '?'.http_build_query($query)); 83 | } 84 | 85 | protected function getRedirectUrl(Request $request, bool $https = false): string 86 | { 87 | if (! $https) { 88 | return $request->fullUrl(); 89 | } 90 | 91 | return $this->ensureHttpsScheme($request->fullUrl()); 92 | } 93 | 94 | #[Pure] 95 | protected function ensureHttpsScheme(string $url): string 96 | { 97 | if (Str::startsWith($url, 'http://')) { 98 | $url = Str::replaceFirst('http', 'https', $url); 99 | } 100 | 101 | return $url; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/ServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * This source file is subject to the MIT license that is bundled 9 | * with this source code in the file LICENSE. 10 | */ 11 | 12 | namespace Overtrue\LaravelWeChat; 13 | 14 | use EasyWeChat\MiniApp\Application as MiniApp; 15 | use EasyWeChat\OfficialAccount\Application as OfficialAccount; 16 | use EasyWeChat\OpenPlatform\Application as OpenPlatform; 17 | use EasyWeChat\OpenWork\Application as OpenWork; 18 | use EasyWeChat\Pay\Application as Payment; 19 | use EasyWeChat\Work\Application as Work; 20 | use Illuminate\Support\ServiceProvider as LaravelServiceProvider; 21 | 22 | class ServiceProvider extends LaravelServiceProvider 23 | { 24 | protected function setupConfig() 25 | { 26 | $source = realpath(__DIR__.'/../config/easywechat.php'); 27 | 28 | if ($this->app->runningInConsole()) { 29 | $this->publishes([$source => \config_path('easywechat.php')], 'laravel-wechat'); 30 | } 31 | 32 | $this->mergeConfigFrom($source, 'easywechat'); 33 | } 34 | 35 | public function register() 36 | { 37 | $this->setupConfig(); 38 | 39 | $apps = [ 40 | 'official_account' => OfficialAccount::class, 41 | 'work' => Work::class, 42 | 'mini_app' => MiniApp::class, 43 | 'pay' => Payment::class, 44 | 'open_platform' => OpenPlatform::class, 45 | 'open_work' => OpenWork::class, 46 | ]; 47 | 48 | foreach ($apps as $name => $class) { 49 | if (empty(config('easywechat.'.$name))) { 50 | continue; 51 | } 52 | 53 | if (! empty(config('easywechat.'.$name.'.app_id')) || ! empty(config('easywechat.'.$name.'.corp_id'))) { 54 | $accounts = [ 55 | 'default' => config('easywechat.'.$name), 56 | ]; 57 | config(['easywechat.'.$name.'.default' => $accounts['default']]); 58 | } else { 59 | $accounts = config('easywechat.'.$name); 60 | } 61 | 62 | foreach ($accounts as $account => $config) { 63 | $this->app->bind("easywechat.{$name}.{$account}", function ($laravelApp) use ($config, $class) { 64 | $app = new $class(array_merge(config('easywechat.defaults', []), $config)); 65 | 66 | if (\is_callable([$app, 'setCache'])) { 67 | $app->setCache($laravelApp['cache.store']); 68 | } 69 | 70 | if (\is_callable([$app, 'setRequestFromSymfonyRequest'])) { 71 | $app->setRequestFromSymfonyRequest($laravelApp['request']); 72 | } 73 | 74 | return $app; 75 | }); 76 | } 77 | $this->app->alias("easywechat.{$name}.default", 'easywechat.'.$name); 78 | $this->app->alias('easywechat.'.$name, $class); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/Traits/HandleOpenPlatformServerEvents.php: -------------------------------------------------------------------------------- 1 | disableLaravelDebugbar(); 23 | 24 | $server = $application->getServer(); 25 | 26 | $server->handleAuthorized(function ($payload) { 27 | event(new Authorized($payload->toArray())); 28 | }); 29 | 30 | $server->handleUnauthorized(function ($payload) { 31 | event(new Unauthorized($payload->toArray())); 32 | }); 33 | 34 | $server->handleAuthorizeUpdated(function ($payload) { 35 | event(new AuthorizeUpdated($payload->toArray())); 36 | }); 37 | 38 | $server->handleVerifyTicketRefreshed(function ($payload) { 39 | event(new VerifyTicketRefreshed($payload->toArray())); 40 | }); 41 | 42 | if ($callback) { 43 | $callback($server); 44 | } 45 | 46 | return $server->serve(); 47 | } 48 | 49 | protected function disableLaravelDebugbar(): void 50 | { 51 | $debugbar = 'Barryvdh\Debugbar\LaravelDebugbar'; 52 | 53 | if (class_exists($debugbar)) { 54 | try { 55 | resolve($debugbar)->disable(); 56 | } catch (\Throwable) { 57 | // 58 | } 59 | } 60 | } 61 | } 62 | --------------------------------------------------------------------------------