├── .gitattributes ├── .gitignore ├── .php_cs ├── .phpstorm.meta.php ├── .travis.yml ├── LICENSE ├── README.md ├── composer.json ├── phpunit.xml ├── publish └── wechat.php └── src ├── ConfigProvider.php ├── EasyWechat.php ├── Factory.php └── Helper.php /.gitattributes: -------------------------------------------------------------------------------- 1 | /tests export-ignore -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | composer.lock 3 | *.cache 4 | *.log 5 | .idea -------------------------------------------------------------------------------- /.php_cs: -------------------------------------------------------------------------------- 1 | setRiskyAllowed(true) 14 | ->setRules([ 15 | '@PSR2' => true, 16 | '@Symfony' => true, 17 | '@DoctrineAnnotation' => true, 18 | '@PhpCsFixer' => true, 19 | 'header_comment' => [ 20 | 'commentType' => 'PHPDoc', 21 | 'header' => $header, 22 | 'separate' => 'none', 23 | 'location' => 'after_declare_strict', 24 | ], 25 | 'array_syntax' => [ 26 | 'syntax' => 'short' 27 | ], 28 | 'list_syntax' => [ 29 | 'syntax' => 'short' 30 | ], 31 | 'concat_space' => [ 32 | 'spacing' => 'one' 33 | ], 34 | 'blank_line_before_statement' => [ 35 | 'statements' => [ 36 | 'declare', 37 | ], 38 | ], 39 | 'general_phpdoc_annotation_remove' => [ 40 | 'annotations' => [ 41 | 'author' 42 | ], 43 | ], 44 | 'ordered_imports' => [ 45 | 'imports_order' => [ 46 | 'class', 'function', 'const', 47 | ], 48 | 'sort_algorithm' => 'alpha', 49 | ], 50 | 'single_line_comment_style' => [ 51 | 'comment_types' => [ 52 | ], 53 | ], 54 | 'yoda_style' => [ 55 | 'always_move_variable' => false, 56 | 'equal' => false, 57 | 'identical' => false, 58 | ], 59 | 'phpdoc_align' => [ 60 | 'align' => 'left', 61 | ], 62 | 'multiline_whitespace_before_semicolons' => [ 63 | 'strategy' => 'no_multi_line', 64 | ], 65 | 'class_attributes_separation' => true, 66 | 'combine_consecutive_unsets' => true, 67 | 'declare_strict_types' => true, 68 | 'linebreak_after_opening_tag' => true, 69 | 'lowercase_constants' => true, 70 | 'lowercase_static_reference' => true, 71 | 'no_useless_else' => true, 72 | 'no_unused_imports' => true, 73 | 'not_operator_with_successor_space' => true, 74 | 'not_operator_with_space' => false, 75 | 'ordered_class_elements' => true, 76 | 'php_unit_strict' => false, 77 | 'phpdoc_separation' => false, 78 | 'single_quote' => true, 79 | 'standardize_not_equals' => true, 80 | 'multiline_comment_opening_closing' => true, 81 | ]) 82 | ->setFinder( 83 | PhpCsFixer\Finder::create() 84 | ->exclude('vendor') 85 | ->in(__DIR__) 86 | ) 87 | ->setUsingCache(false); 88 | -------------------------------------------------------------------------------- /.phpstorm.meta.php: -------------------------------------------------------------------------------- 1 | 假设您的域名为 `nxx.cloud` 那么请登录微信公众平台 “开发者中心” 修改 “URL(服务器配置)” 为: `http://nxx.cloud/wechat`。 23 | 24 | 路由: 25 | 26 | ```php 27 | Router::addRoute(['GET', 'POST', 'HEAD'], '/wechat', 'App\Controller\WeChatController@serve'); 28 | ``` 29 | 30 | > 注意:一定是 `Router::addRoute`, 因为微信服务端认证的时候是 `GET`, 接收用户消息时是 `POST` ! 31 | 32 | 然后创建控制器 `WeChatController`: 33 | 34 | ```php 35 | server->push(function ($message) { 64 | return "欢迎关注 EasyWechat!"; 65 | }); 66 | // 一定要用Helper::Response去转换 67 | return Helper::Response($app->server->serve()); 68 | } 69 | } 70 | ``` 71 | 72 | > 上面例子里的 在return的时候必须调用``Naixiaoxin\HyperfWechat\Helper::Response``去转换,否则会报错。 73 | 74 | ### 我们有以下方式获取 SDK 的服务实例 75 | 76 | ##### 使用外观 77 | 78 | ```php 79 | use \Naixiaoxin\HyperfWechat\EasyWechat; 80 | $officialAccount = EasyWechat::officialAccount(); // 公众号 81 | $work = EasyWechat::work(); // 企业微信 82 | $payment = EasyWechat::payment(); // 微信支付 83 | $openPlatform = EasyWechat::openPlatform(); // 开放平台 84 | $miniProgram = EasyWechat::miniProgram(); // 小程序 85 | 86 | // 均支持传入配置账号名称以及配置 87 | EasyWeChat::officialAccount('foo',[]); // `foo` 为配置文件中的名称,默认为 `default`。`[]` 可覆盖账号配置 88 | //... 89 | ``` 90 | 91 | 更多 SDK 的具体使用请参考:https://easywechat.com 92 | 93 | ## License 94 | 95 | MIT 96 | 97 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "naixiaoxin/hyperf-wechat", 3 | "type": "library", 4 | "license": "MIT", 5 | "keywords": [ 6 | "php", 7 | "hyperf", 8 | "wechat" 9 | ], 10 | "description": "Wechat SDK For Hyperf", 11 | "autoload": { 12 | "psr-4": { 13 | "Naixiaoxin\\HyperfWechat\\": "src/" 14 | } 15 | }, 16 | "autoload-dev": { 17 | "psr-4": { 18 | "HyperfTest\\": "tests" 19 | } 20 | }, 21 | "require": { 22 | "php": ">=7.2", 23 | "ext-swoole": ">=4.4", 24 | "hyperf/guzzle": "~1.1.0|^2.0.0", 25 | "hyperf/super-globals": "~1.1.0|^2.0.0", 26 | "overtrue/wechat": "^4.2" 27 | }, 28 | "require-dev": { 29 | "friendsofphp/php-cs-fixer": "^2.14", 30 | "phpstan/phpstan": "^0.10.5", 31 | "hyperf/testing": "~1.1.0|^2.0.0", 32 | "swoole/ide-helper": "^4.4" 33 | }, 34 | "config": { 35 | "sort-packages": true 36 | }, 37 | "scripts": { 38 | "test": "co-phpunit -c phpunit.xml --colors=always", 39 | "analyze": "phpstan analyse --memory-limit 300M -l 0 ./src", 40 | "cs-fix": "php-cs-fixer fix $1" 41 | }, 42 | "extra": { 43 | "hyperf": { 44 | "config": "Naixiaoxin\\HyperfWechat\\ConfigProvider" 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | ./tests/ 14 | 15 | -------------------------------------------------------------------------------- /publish/wechat.php: -------------------------------------------------------------------------------- 1 | [ 15 | /* 16 | * 指定 API 调用返回结果的类型:array(default)/collection/object/raw/自定义类名 17 | */ 18 | 'response_type' => 'array', 19 | // 日志 20 | 'log' => [ 21 | 'default' => 'single', 22 | 'channels' => [ 23 | 'single' => [ 24 | 'driver' => env('WECHAT_LOG_DRIVER', 'errorlog'), 25 | 'level' => env('WECHAT_LOG_LEVEL', 'debug'), 26 | 'path' => env('WECHAT_LOG_DRIVER', BASE_PATH . '/runtime/logs/hyperf.log'), 27 | ], 28 | ], 29 | ], 30 | ], 31 | 32 | 'official_account' => [ 33 | 'default' => [ 34 | // AppID 35 | 'app_id' => env('WECHAT_OFFICIAL_ACCOUNT_APPID', 'your-app-id'), 36 | // AppSecret 37 | 'secret' => env('WECHAT_OFFICIAL_ACCOUNT_SECRET', 'your-app-secret'), 38 | // Token 39 | 'token' => env('WECHAT_OFFICIAL_ACCOUNT_TOKEN', 'your-token'), 40 | // EncodingAESKey 41 | 'aes_key' => env('WECHAT_OFFICIAL_ACCOUNT_AES_KEY', ''), 42 | ], 43 | ], 44 | //第三方开发平台 45 | //'open_platform' => [ 46 | // 'default' => [ 47 | // 'app_id' => env('WECHAT_OPEN_PLATFORM_APPID', ''), 48 | // 'secret' => env('WECHAT_OPEN_PLATFORM_SECRET', ''), 49 | // 'token' => env('WECHAT_OPEN_PLATFORM_TOKEN', ''), 50 | // 'aes_key' => env('WECHAT_OPEN_PLATFORM_AES_KEY', ''), 51 | // ], 52 | //], 53 | //小程序 54 | //'mini_program' => [ 55 | // 'default' => [ 56 | // 'app_id' => env('WECHAT_MINI_PROGRAM_APPID', ''), 57 | // 'secret' => env('WECHAT_MINI_PROGRAM_SECRET', ''), 58 | // 'token' => env('WECHAT_MINI_PROGRAM_TOKEN', ''), 59 | // 'aes_key' => env('WECHAT_MINI_PROGRAM_AES_KEY', ''), 60 | // ], 61 | //], 62 | //支付 63 | //'payment' => [ 64 | // 'default' => [ 65 | // 'sandbox' => env('WECHAT_PAYMENT_SANDBOX', false), 66 | // 'app_id' => env('WECHAT_PAYMENT_APPID', ''), 67 | // 'mch_id' => env('WECHAT_PAYMENT_MCH_ID', 'your-mch-id'), 68 | // 'key' => env('WECHAT_PAYMENT_KEY', 'key-for-signature'), 69 | // 'cert_path' => env('WECHAT_PAYMENT_CERT_PATH', 'path/to/cert/apiclient_cert.pem'), // XXX: 绝对路径!!!! 70 | // 'key_path' => env('WECHAT_PAYMENT_KEY_PATH', 'path/to/cert/apiclient_key.pem'), // XXX: 绝对路径!!!! 71 | // 'notify_url' => 'http://example.com/payments/wechat-notify', // 默认支付结果通知地址 72 | // ], 73 | // // ... 74 | //], 75 | //企业微信 76 | //'work' => [ 77 | // 'default' => [ 78 | // 'corp_id' => 'xxxxxxxxxxxxxxxxx', 79 | // 'agent_id' => 100020, 80 | // 'secret' => env('WECHAT_WORK_AGENT_CONTACTS_SECRET', ''), 81 | // //... 82 | // ], 83 | //], 84 | //企业开放平台 85 | //'open_work' => [ 86 | // 'default' => [ 87 | // //参考EasyWechat官方文档 88 | // //https://www.easywechat.com/docs/4.1/open-work/index 89 | // ], 90 | //], 91 | //小微商户 92 | //'micro_merchant' => [ 93 | // 'default' => [ 94 | // //参考EasyWechat官方文档 95 | // //https://www.easywechat.com/docs/4.1/micro-merchant/index 96 | // ], 97 | //], 98 | ]; 99 | -------------------------------------------------------------------------------- /src/ConfigProvider.php: -------------------------------------------------------------------------------- 1 | [ 21 | ], 22 | 'commands' => [ 23 | ], 24 | 'annotations' => [ 25 | 'scan' => [ 26 | 'paths' => [ 27 | __DIR__, 28 | ], 29 | ], 30 | ], 31 | 'publish' => [ 32 | [ 33 | 'id' => 'config', 34 | 'description' => 'The config for wechat.', 35 | 'source' => __DIR__ . '/../publish/wechat.php', 36 | 'destination' => BASE_PATH . '/config/autoload/wechat.php', 37 | ], 38 | ], 39 | ]; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/EasyWechat.php: -------------------------------------------------------------------------------- 1 | get(Factory::class)->{$functionName}(...$args); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Factory.php: -------------------------------------------------------------------------------- 1 | 'official_account', 38 | 'work' => 'work', 39 | 'miniProgram' => 'mini_program', 40 | 'payment' => 'payment', 41 | 'openPlatform' => 'open_platform', 42 | 'openWork' => 'open_work', 43 | 'microMerchant' => 'micro_merchant', 44 | ]; 45 | 46 | /** 47 | * @var ContainerInterface 48 | */ 49 | protected $container; 50 | 51 | /** 52 | * @var ConfigInterface 53 | */ 54 | protected $config; 55 | 56 | /** 57 | * @var CacheInterface 58 | */ 59 | protected $cache; 60 | 61 | public function __construct(ContainerInterface $container) 62 | { 63 | $this->container = $container; 64 | $this->config = $container->get(ConfigInterface::class); 65 | $this->cache = $container->get(CacheInterface::class); 66 | } 67 | 68 | public function __call($functionName, $args) 69 | { 70 | $accountName = $args[0] ?? 'default'; 71 | $accountConfig = $args[1] ?? []; 72 | if (!isset($this->configMap[$functionName])) { 73 | throw new \Exception('方法不存在'); 74 | } 75 | $configName = $this->configMap[$functionName]; 76 | $config = $this->getConfig(sprintf('wechat.%s.%s', $configName, $accountName), $accountConfig); 77 | $app = \EasyWeChat\Factory::$functionName($config); 78 | $app->rebind('cache', $this->cache); 79 | $app['guzzle_handler'] = CoroutineHandler::class; 80 | $app->rebind('request', $this->getRequest()); 81 | return $app; 82 | } 83 | 84 | /** 85 | * 获得配置. 86 | */ 87 | private function getConfig(string $name, array $config = []): array 88 | { 89 | $defaultConfig = $this->config->get('wechat.defaults', []); 90 | $moduleConfig = $this->config->get($name, []); 91 | return array_merge($moduleConfig, $defaultConfig, $config); 92 | } 93 | 94 | /** 95 | * 获取Request请求 96 | */ 97 | private function getRequest(): Request 98 | { 99 | $request = $this->container->get(RequestInterface::class); 100 | //return $this->container->get(RequestInterface::class); 101 | $uploadFiles = $request->getUploadedFiles() ?? []; 102 | $files = []; 103 | foreach ($uploadFiles as $k => $v) { 104 | $files[$k] = $v->toArray(); 105 | } 106 | return new Request( 107 | $request->getQueryParams(), 108 | $request->getParsedBody(), 109 | [], 110 | $request->getCookieParams(), 111 | $files, 112 | $request->getServerParams(), 113 | // is_array($_SERVER) ? $_SERVER : $_SERVER->toArray(), 114 | $request->getBody()->getContents() 115 | ); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/Helper.php: -------------------------------------------------------------------------------- 1 | withBody(new SwooleStream($response->getContent()))->withStatus($response->getStatusCode()); 26 | foreach ($response->headers->all() as $key => $item) { 27 | $psrResponse = $psrResponse->withHeader($key, $item); 28 | } 29 | return $psrResponse; 30 | } 31 | } 32 | --------------------------------------------------------------------------------