├── .editorconfig ├── LICENSE.md ├── composer.json ├── config └── whmcs.php ├── phpstan.neon.dist └── src ├── Auth ├── AbstractAuth.php ├── AuthFactory.php ├── AuthInterface.php └── Method │ ├── PasswordAuth.php │ └── TokenAuth.php ├── Facades └── Whmcs.php ├── HttpClient └── HttpClientBuilderFactory.php ├── WhmcsFactory.php ├── WhmcsManager.php └── WhmcsServiceProvider.php /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.php] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | indent_style = space 9 | indent_size = 4 -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "darthsoup/laravel-whmcs", 3 | "description": "WHMCS API interface for Laravel 7.0 and up", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "Kevin Krummnacker", 8 | "email": "kevin.krummnacker@gmail.com" 9 | } 10 | ], 11 | "require": { 12 | "php": "^7.4|^8.0", 13 | "ext-json": "*", 14 | "illuminate/support": "^8.0|^9.0|^10.0|^11.0|^12.0", 15 | "illuminate/session": "^8.0|^9.0|^10.0|^11.0|^12.0", 16 | "illuminate/events": "^8.0|^9.0|^10.0|^11.0|^12.0", 17 | "darthsoup/php-whmcs-api": "~1.3", 18 | "graham-campbell/manager": "^5.1" 19 | }, 20 | "require-dev": { 21 | "guzzlehttp/guzzle": "^7.0", 22 | "http-interop/http-factory-guzzle": "^1.0", 23 | "mockery/mockery": "~1.3", 24 | "nunomaduro/larastan": "^1.0.4", 25 | "orchestra/testbench": "^5.0|^6.0|^7.0|^8.0|^10.0", 26 | "phpunit/phpunit": "^9.3|^11.5.3" 27 | }, 28 | "autoload": { 29 | "psr-4": { 30 | "DarthSoup\\Whmcs\\": "src/" 31 | } 32 | }, 33 | "autoload-dev": { 34 | "psr-4": { 35 | "DarthSoup\\Tests\\Whmcs\\": "tests/" 36 | } 37 | }, 38 | "scripts": { 39 | "analyse": "vendor/bin/phpstan analyse" 40 | }, 41 | "suggest": { 42 | "guzzlehttp/guzzle": "A PSR-18 compatible HTTP Client (^7.0)" 43 | }, 44 | "extra": { 45 | "laravel": { 46 | "providers": [ 47 | "DarthSoup\\Whmcs\\WhmcsServiceProvider" 48 | ], 49 | "aliases": { 50 | "Whmcs": "DarthSoup\\Whmcs\\Facades\\Whmcs" 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /config/whmcs.php: -------------------------------------------------------------------------------- 1 | 'primary', 18 | 19 | /* 20 | |-------------------------------------------------------------------------- 21 | | WHMCS Connections 22 | |-------------------------------------------------------------------------- 23 | | 24 | | Define your whmcs connections here. 25 | | Do not add the path `/includes/api.php` to the URL, it will be added 26 | | automatically. 27 | | 28 | | Note that WHMCS only support these two authentication methods. 29 | | Methods: "password", "token" 30 | | 31 | */ 32 | 33 | 'connections' => [ 34 | 35 | 'primary' => [ 36 | 'method' => env('WHMCS_AUTH_TYPE', 'password'), 37 | 'url' => env('WHMCS_API_URL', 'https://url.to.whmcs.tld/path_to_whmcs'), 38 | 'username' => env('WHMCS_USERNAME', 'YOUR_USERNAME'), 39 | 'password' => env('WHMCS_PASSWORD', 'YOUR_PASSWORD'), 40 | 'access_key' => env('WHMCS_ACCESSKEY') 41 | ], 42 | 43 | 'secondary' => [ 44 | 'method' => env('WHMCS_AUTH_TYPE', 'token'), 45 | 'url' => env('WHMCS_API_URL', 'https://url.to.whmcs.tld/path_to_whmcs'), 46 | 'identifier' => env('WHMCS_API_IDENTIFIER', 'YOUR_API_IDENTIFIER'), 47 | 'secret' => env('WHMCS_API_SECRET', 'YOUR_API_SECRET'), 48 | 'access_key' => env('WHMCS_API_ACCESSKEY') 49 | ], 50 | 51 | ], 52 | 53 | /* 54 | |-------------------------------------------------------------------------- 55 | | Timeout Settings 56 | |-------------------------------------------------------------------------- 57 | | 58 | | Set the connection timeout and general timeout in seconds for the HTTP 59 | | client below, integer values only. 60 | | 61 | */ 62 | 63 | 'connect_timeout' => 10, 64 | 65 | 'timeout' => 30, 66 | ]; 67 | -------------------------------------------------------------------------------- /phpstan.neon.dist: -------------------------------------------------------------------------------- 1 | includes: 2 | - ./vendor/nunomaduro/larastan/extension.neon 3 | 4 | parameters: 5 | level: 5 6 | paths: 7 | - src 8 | - config 9 | -------------------------------------------------------------------------------- /src/Auth/AbstractAuth.php: -------------------------------------------------------------------------------- 1 | client = $client; 16 | 17 | return $this; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Auth/AuthFactory.php: -------------------------------------------------------------------------------- 1 | client) { 17 | throw new InvalidArgumentException('The client instance was not given to the auth process.'); 18 | } 19 | 20 | if (!array_key_exists('username', $config)) { 21 | throw new InvalidArgumentException('The password authenticator requires a username.'); 22 | } 23 | if (!array_key_exists('password', $config)) { 24 | throw new InvalidArgumentException('The password authenticator requires a password.'); 25 | } 26 | 27 | $this->client->authenticate( 28 | $config['username'], 29 | $config['password'], 30 | Client::AUTH_LOGIN_CREDENTIALS 31 | ); 32 | 33 | if (Arr::has($config, 'access_key') && null !== $config['access_key']) { 34 | $this->client->accessKey($config['access_key']); 35 | } 36 | 37 | return $this->client; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Auth/Method/TokenAuth.php: -------------------------------------------------------------------------------- 1 | client) { 17 | throw new InvalidArgumentException('The client instance was not given to the auth process.'); 18 | } 19 | 20 | if (!array_key_exists('identifier', $config)) { 21 | throw new InvalidArgumentException('The token authenticator requires a identifier.'); 22 | } 23 | if (!array_key_exists('secret', $config)) { 24 | throw new InvalidArgumentException('The token authenticator requires a secret.'); 25 | } 26 | 27 | $this->client->authenticate( 28 | $config['identifier'], 29 | $config['secret'], 30 | Client::AUTH_API_CREDENTIALS 31 | ); 32 | 33 | if (Arr::has($config, 'access_key') && null !== $config['access_key']) { 34 | $this->client->accessKey($config['access_key']); 35 | } 36 | 37 | return $this->client; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Facades/Whmcs.php: -------------------------------------------------------------------------------- 1 | httpClient = $httpClient; 37 | $this->requestFactory = $requestFactory; 38 | $this->streamFactory = $streamFactory; 39 | $this->uriFactory = $uriFactory; 40 | } 41 | 42 | public function make(): Builder 43 | { 44 | return new Builder($this->httpClient, $this->requestFactory, $this->streamFactory, $this->uriFactory); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/WhmcsFactory.php: -------------------------------------------------------------------------------- 1 | builder = $builder; 24 | $this->auth = $auth; 25 | } 26 | 27 | public function make(array $config): Client 28 | { 29 | $client = $this->getClient($this->getBuilder($config)); 30 | 31 | if (!array_key_exists('method', $config)) { 32 | throw new InvalidArgumentException('The whmcs factory requires an auth method.'); 33 | } 34 | 35 | if ($url = Arr::get($config, 'url')) { 36 | $client->url($url); 37 | } 38 | 39 | if ($config['method'] === 'none') { 40 | return $client; 41 | } 42 | 43 | return $this->auth->make($config['method'])->with($client)->authenticate($config); 44 | } 45 | 46 | protected function getBuilder(array $config): Builder 47 | { 48 | $builder = $this->builder->make(); 49 | 50 | if ($backoff = Arr::get($config, 'backoff')) { 51 | $builder->addPlugin( 52 | new RetryPlugin(['retries' => $backoff === true ? 2 : $backoff]) 53 | ); 54 | } 55 | 56 | return $builder; 57 | } 58 | 59 | protected function getClient(Builder $builder): Client 60 | { 61 | return new Client($builder); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/WhmcsManager.php: -------------------------------------------------------------------------------- 1 | getConnections() 13 | * @method \DarthSoup\WhmcsApi\Api\Addons addons() 14 | * @method \DarthSoup\WhmcsApi\Api\Affiliates affiliates() 15 | * @method \DarthSoup\WhmcsApi\Api\Authentication authentication() 16 | * @method \DarthSoup\WhmcsApi\Api\Billing billing() 17 | * @method \DarthSoup\WhmcsApi\Api\Client client() 18 | * @method \DarthSoup\WhmcsApi\Api\Custom custom() 19 | * @method \DarthSoup\WhmcsApi\Api\Domains domains() 20 | * @method \DarthSoup\WhmcsApi\Api\Orders orders() 21 | * @method \DarthSoup\WhmcsApi\Api\Products products() 22 | * @method \DarthSoup\WhmcsApi\Api\Servers servers() 23 | * @method \DarthSoup\WhmcsApi\Api\Service service() 24 | * @method \DarthSoup\WhmcsApi\Api\System system() 25 | * @method \DarthSoup\WhmcsApi\Api\Users users() 26 | */ 27 | class WhmcsManager extends AbstractManager 28 | { 29 | protected WhmcsFactory $factory; 30 | 31 | public function __construct(Repository $config, WhmcsFactory $factory) 32 | { 33 | parent::__construct($config); 34 | $this->factory = $factory; 35 | } 36 | 37 | protected function createConnection(array $config): Client 38 | { 39 | return $this->factory->make($config); 40 | } 41 | 42 | public function getFactory(): WhmcsFactory 43 | { 44 | return $this->factory; 45 | } 46 | 47 | protected function getConfigName(): string 48 | { 49 | return 'whmcs'; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/WhmcsServiceProvider.php: -------------------------------------------------------------------------------- 1 | app instanceof Application && $this->app->runningInConsole()) { 28 | $this->publishes([$source => config_path('whmcs.php')]); 29 | } 30 | 31 | $this->mergeConfigFrom($source, 'whmcs'); 32 | } 33 | 34 | /** 35 | * Register the service provider. 36 | * 37 | * @return void 38 | */ 39 | public function register() 40 | { 41 | $this->registerHttpClientFactory(); 42 | $this->registerAuthFactory(); 43 | $this->registerWhmcsFactroy(); 44 | $this->registerManager(); 45 | $this->registerBindings(); 46 | } 47 | 48 | /** 49 | * Register the http client factory class. 50 | */ 51 | protected function registerHttpClientFactory(): void 52 | { 53 | $this->app->singleton('whmcs.httpclientfactory', function (Container $app) { 54 | $psrFactory = new PsrHttpFactory(); 55 | 56 | return new HttpClientBuilderFactory( 57 | new GuzzleClient([ 58 | 'connect_timeout' => $app['config']->get('whmcs.connect_timeout', 10), 59 | 'timeout' => $app['config']->get('whmcs.timeout', 30), 60 | ]), 61 | $psrFactory, 62 | $psrFactory, 63 | $psrFactory, 64 | ); 65 | }); 66 | 67 | $this->app->alias('whmcs.httpclientfactory', HttpClientBuilderFactory::class); 68 | } 69 | 70 | /** 71 | * Register the auth factory class. 72 | */ 73 | protected function registerAuthFactory(): void 74 | { 75 | $this->app->singleton('whmcs.authfactory', function () { 76 | return new AuthFactory(); 77 | }); 78 | 79 | $this->app->alias('whmcs.authfactory', AuthFactory::class); 80 | } 81 | 82 | /** 83 | * Register the whmcs factory class. 84 | */ 85 | public function registerWhmcsFactroy(): void 86 | { 87 | $this->app->singleton('whmcs.factory', function (Container $app) { 88 | $builder = $app['whmcs.httpclientfactory']; 89 | $auth = $app['whmcs.authfactory']; 90 | 91 | return new WhmcsFactory($auth, $builder); 92 | }); 93 | 94 | $this->app->alias('whmcs.factory', WhmcsFactory::class); 95 | } 96 | 97 | /** 98 | * Register the whmcs factory class. 99 | */ 100 | public function registerManager(): void 101 | { 102 | $this->app->singleton('whmcs', function (Container $app) { 103 | $config = $app['config']; 104 | $factory = $app['whmcs.factory']; 105 | 106 | return new WhmcsManager($config, $factory); 107 | }); 108 | 109 | $this->app->alias('whmcs', WhmcsManager::class); 110 | } 111 | 112 | /** 113 | * Register the bindings. 114 | */ 115 | protected function registerBindings(): void 116 | { 117 | $this->app->bind('whmcs.connection', function (Container $app) { 118 | /** @var WhmcsManager $manager */ 119 | $manager = $app['whmcs']; 120 | 121 | return $manager->connection(); 122 | }); 123 | 124 | $this->app->alias('whmcs.connection', Client::class); 125 | } 126 | 127 | /** 128 | * @return array 129 | */ 130 | public function provides(): array 131 | { 132 | return [ 133 | 'whmcs.httpclientfactory', 134 | 'whmcs.authfactory', 135 | 'whmcs.factory', 136 | 'whmcs', 137 | 'whmcs.connection', 138 | ]; 139 | } 140 | } 141 | --------------------------------------------------------------------------------