├── .gitignore ├── LICENSE ├── README.md ├── composer.json ├── config └── whmcs.php └── src ├── Client.php ├── Facades └── Whmcs.php ├── UserProvider.php ├── Whmcs.php ├── WhmcsServiceProvider.php └── WhmcsUser.php /.gitignore: -------------------------------------------------------------------------------- 1 | composer.lock 2 | /.idea 3 | /vendor 4 | /.vscode 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Laravel WHMCS-UP 2 | ===== 3 | 4 | [![Latest Stable Version](https://poser.pugx.org/sburina/laravel-whmcs-up/v/stable)](https://packagist.org/packages/sburina/laravel-whmcs-up) 5 | [![Total Downloads](https://poser.pugx.org/sburina/laravel-whmcs-up/downloads)](https://packagist.org/packages/sburina/laravel-whmcs-up) 6 | [![License](https://poser.pugx.org/sburina/laravel-whmcs-up/license)](https://packagist.org/packages/sburina/laravel-whmcs-up) 7 | 8 | This package provides several useful functions for integration of your Laravel application with WHMCS (up to version 7.9): 9 | 10 | - WHMCS API client 11 | - WHMCS user provider 12 | - WHMCS Auto-Login 13 | 14 | ## Installation 15 | 16 | Install the package through [Composer](http://getcomposer.org/). 17 | 18 | - Run the Composer require command from the Terminal: 19 | 20 | ```bash 21 | composer require sburina/laravel-whmcs-up 22 | ``` 23 | 24 | - Run `composer update` to pull in the files. 25 | 26 | ### After Laravel 5.5 27 | 28 | No additional steps required. 29 | 30 | ### Before Laravel 5.5 31 | 32 | - Add the service provider and alias of the package. To do this, open your `config/app.php` file. 33 | 34 | - Add a new line to the `providers` array: 35 | 36 | ```php 37 | Sburina\Whmcs\WhmcsServiceProvider::class 38 | ``` 39 | 40 | - And optionally add a new line to the `aliases` array: 41 | 42 | ```php 43 | 'Whmcs' => Sburina\Whmcs\Facades\Whmcs::class, 44 | ``` 45 | 46 | - From the command-line run: 47 | 48 | ```bash 49 | php artisan vendor:publish --provider=Sburina\Whmcs\WhmcsServiceProvider 50 | ``` 51 | 52 | - Open `config\whmcs.php` to see the available configuration options. The preferred way of configuring the package is via the environment variables in your project's `.env` file. 53 | 54 | Now you can use the package in your Laravel project. 55 | 56 | ## Usage 57 | 58 | This package defines several important WHMCS methods with custom signature, providing somewhat easier use and code completion. These methods are: 59 | 60 | ```php 61 | // Getters 62 | sbGetProducts($pid = null, $gid = null, $module = null); 63 | sbGetClients($limitstart = null, $limitnum = null, $sorting = null, $search = null); 64 | sbGetClientsDetails($email = null, $clientid = null, $stats = false); 65 | 66 | // Login 67 | sbValidateLogin($email, $password2); 68 | 69 | // AutoLogin 70 | getAutoLoginUrl($goto = null); 71 | redirectAutoLogin($goto = null); 72 | ``` 73 | 74 | All other WHMCS API methods can be used magically by calling the `\Whmcs::{WHMCS_API_METHOD}` facade. 75 | This also works with all the custom API functions stored in your WHMCS API folder. For complete specification of WHMCS API methods please take a look at the [WHMCS API Index](https://developers.whmcs.com/api/api-index/). 76 | 77 | ### Examples 78 | 79 | Get user's detail using our method: 80 | 81 | ```php 82 | \Whmcs::sbGetClientsDetails($email); 83 | ``` 84 | 85 | The same thing, using the original WHMCS API method via a magic call: 86 | 87 | ```php 88 | \Whmcs::GetClientsDetails([ 89 | 'email' => 'jane.doe@example.com' 90 | ]); 91 | ``` 92 | 93 | Obtain a list of client purchased products 94 | 95 | ```php 96 | \Whmcs::GetClientsProducts([ 97 | 'clientid' => 18122013 98 | ]); 99 | ``` 100 | 101 | Retrieve a specific invoice 102 | 103 | ```php 104 | \Whmcs::GetInvoice([ 105 | 'invoiceid' => 100001 106 | ]); 107 | ``` 108 | 109 | If you for any reason don't like facades, you can use the `app()` helper. 110 | 111 | ```php 112 | $whmcs = app('whmcs'); 113 | $whmcs->GetInvoice([ 114 | 'invoiceid' => 100001 115 | ]); 116 | ``` 117 | 118 | ## Authenticating against WHMCS user base 119 | 120 | If your Laravel application doesn't have its own user base, but you still need to authenticate users before allowing them to access certain pages (_NOT_ WHMCS pages), there are few additional steps to take: 121 | 122 | - Register the user provider in your AuthServiceProvider boot() method: 123 | 124 | ```php 125 | public function boot() 126 | { 127 | $this->registerPolicies(); 128 | 129 | Auth::provider('whmcs', function () { 130 | return new \Sburina\Whmcs\UserProvider(); 131 | }); 132 | } 133 | ``` 134 | - In `config/auth.php` define the new provider: 135 | ```php 136 | 'providers' => [ 137 | 'users' => [ 138 | 'driver' => 'whmcs', 139 | ], 140 | ``` 141 | - in the same file, the `web` guard is already configured to use `users` provider, so there's no need to change anything. You could decide to give the provider a different name, in which case you'd need to define the same name for the appropriate guard. 142 | 143 | Now you can simply use the existing Laravel `Auth::routes()` with already provided `auth` pages, exactly the same way as if you had a local user base. 144 | 145 | On successful login, the `session_key` named in `config/whmcs.php` (default: `user`) will be populated with user data retrieved from WHMCS, and the login session will start as usual. `auth()->check()` and `auth()->guest()` will work, and `auth()->user()` will return the instance of `WhmcsUser` class with attributes populated with user's data. User's data won't be retrieved from WHMCS again while the login session is in progress and the session key `user` exists. 146 | 147 | On logout, the session key `user` will be destroyed and the login session will end. 148 | 149 | ## Remote login / redirect 150 | 151 | User that logged in into your Laravel application this way will not be automatically logged in to WHMCS! To redirect the authenticated user to any protected WHMCS page and log them into WHMCS automatically at the same time, you can use: 152 | ```php 153 | return \Whmcs::redirectAutoLogin(); 154 | ``` 155 | 156 | `config/whmcs.php` option `autoauth.goto` determines the default URI for such redirects. You can override the default by adding the argument to this method: 157 | ```php 158 | return \Whmcs::redirectAutoLogin('cart.php'); 159 | ``` 160 | 161 | If you'd prefer just getting the login URL and sending it to the user from your own code, you can do it like so: 162 | ```php 163 | $url = \Whmcs::getAutoLoginUrl(); 164 | ``` 165 | 166 | Again, you can override the default URI: 167 | ```php 168 | $url = \Whmcs::getAutoLoginUrl('cart.php'); 169 | ``` 170 | 171 | To learn more about this feature and how to enable it in WHMCS, see [WHMCS AutoAuth](https://docs.whmcs.com/AutoAuth). 172 | 173 | ## Support 174 | 175 | [Please open an issue in github](https://github.com/sburina/laravel-whmcs-up/issues) 176 | 177 | ## License 178 | 179 | This package is released under the MIT License. See the bundled 180 | [LICENSE](https://github.com/sburina/laravel-whmcs-up/blob/master/LICENSE) file for details. 181 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sburina/laravel-whmcs-up", 3 | "description": "WHMCS API client and user provider for Laravel", 4 | "type": "library", 5 | "license": "MIT", 6 | "minimum-stability": "stable", 7 | "authors": [ 8 | { 9 | "name": "Sinisa Burina", 10 | "email": "sburina@gmail.com" 11 | } 12 | ], 13 | "require": { 14 | "ext-json": "*", 15 | "laravel/framework": "^5 || ^6 || ^7 || ^8 || ^9" 16 | }, 17 | "require-dev": { 18 | }, 19 | "autoload": { 20 | "psr-4": { 21 | "Sburina\\Whmcs\\": "src/" 22 | } 23 | }, 24 | "extra": { 25 | "laravel": { 26 | "providers": [ 27 | "Sburina\\Whmcs\\WhmcsServiceProvider" 28 | ], 29 | "aliases": { 30 | "Whmcs": "Sburina\\Whmcs\\Facades\\Whmcs" 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /config/whmcs.php: -------------------------------------------------------------------------------- 1 | env('WHMCS_URL', 'http://localhost/whmcs'), 13 | 14 | /* 15 | |-------------------------------------------------------------------------- 16 | | API Credentials 17 | |-------------------------------------------------------------------------- 18 | | 19 | | Prior to WHMCS verison 7.2, authentication was validated based on admin 20 | | login credentials, and not API Authentication Credentials. This method of 21 | | authentication is still supported for backwards compatibility but may be 22 | | deprecated in a future version of WHMCS. 23 | | 24 | | Supported auth types': "api", "password" 25 | | 26 | | @see https://developers.whmcs.com/api/authentication/ 27 | | 28 | */ 29 | 'auth' => [ 30 | 'type' => env('WHMCS_AUTH_TYPE', 'api'), 31 | 32 | 'api' => [ 33 | 'identifier' => env('WHMCS_API_ID', ''), 34 | 'secret' => env('WHMCS_API_SECRET', ''), 35 | ], 36 | 37 | 'password' => [ 38 | 'username' => env('WHMCS_ADMIN_USERNAME', ''), 39 | 'password' => env('WHMCS_ADMIN_PASSWORD', ''), 40 | ], 41 | ], 42 | 43 | /* 44 | |-------------------------------------------------------------------------- 45 | | API Credentials 46 | |-------------------------------------------------------------------------- 47 | | 48 | | an access key can be configured to allow IP restrictions to be bypassed. 49 | 50 | | It works by defining a secret key/passphrase in the WHMCS configuration.php 51 | | file which is then passed into all API calls. To configure it, add a line 52 | | as follows to your configuration.php file in the root WHMCS directory. 53 | | 54 | | @see https://developers.whmcs.com/api/access-control/ 55 | | 56 | */ 57 | 'api_access_key' => env('WHMCS_API_ACCESS_KEY', ''), 58 | 59 | /* 60 | |-------------------------------------------------------------------------- 61 | | AutoAuth 62 | |-------------------------------------------------------------------------- 63 | | Auth Key to automatically login the user to WHMCS if already loogged in 64 | | to this app. Option "goto" allows you to redirect user to a specific WHMCS 65 | | page after successful login. 66 | | 67 | | @see https://docs.whmcs.com/AutoAuth 68 | | 69 | */ 70 | 'autoauth' => [ 71 | 'key' => env('WHMCS_AUTOAUTH_KEY'), 72 | 'goto' => 'clientarea.php?action=products', 73 | ], 74 | 75 | /* 76 | |-------------------------------------------------------------------------- 77 | | Session key to store WHMCS user record 78 | |-------------------------------------------------------------------------- 79 | | 80 | | After successful validation, we store the retrieved WHMCS user record to 81 | | the following session key: 82 | | 83 | */ 84 | 'session_key' => env('WHMCS_SESSION_USER_KEY', 'user'), 85 | 86 | /* 87 | |-------------------------------------------------------------------------- 88 | | Convert numbers from strings to floats in results 89 | |-------------------------------------------------------------------------- 90 | | 91 | | WHMCS API returns numbers (prices, etc..) as strings in its JSON results. 92 | | This option will reformat the response so all the numbers with two decimals 93 | | will be converted to floats in the resulting array. If you just need to 94 | | display the results, leave the option turned off. 95 | | 96 | | Default: false 97 | | 98 | */ 99 | 'use_floats' => false, 100 | 101 | /* 102 | |-------------------------------------------------------------------------- 103 | | Return the results as associative arrays 104 | |-------------------------------------------------------------------------- 105 | | 106 | | true: get result as an associative array. 107 | | false: get the result as a stdClass object. 108 | | 109 | */ 110 | 'result_as_array' => true, 111 | ]; 112 | -------------------------------------------------------------------------------- /src/Client.php: -------------------------------------------------------------------------------- 1 | 30, 23 | 'debug' => false, 24 | 'http_errors' => true, 25 | 'base_uri' => rtrim(config('whmcs.url'), '/').'/includes/api.php', 26 | 'verify' => false, 27 | 'headers' => [ 28 | 'User-Agent' => 'sburina/laravel-whmcs-up', 29 | ], 30 | ]; 31 | 32 | if (!empty(config('whmcs.api_access_key'))) $options['query'] = ['accesskey' => config('whmcs.api_access_key')]; 33 | 34 | $this->client = new GuzzleClient($options); 35 | } 36 | 37 | /** 38 | * @param array $data 39 | * 40 | * @return array 41 | */ 42 | public function post($data) 43 | { 44 | try { 45 | $fp = array_merge( 46 | config('whmcs.auth.'.config('whmcs.auth.type')), 47 | ['responsetype' => 'json'], 48 | $data 49 | ); 50 | 51 | $response = $this->client->post('', ['form_params' => $fp]); 52 | $contents = $response->getBody()->getContents(); 53 | 54 | if (config('whmcs.use_floats', false)) { 55 | $contents = preg_replace('/":"(-?\d+\.\d\d)"/', '":\1', $contents); 56 | } 57 | 58 | return json_decode($contents, config('whmcs.result_as_array', true)); 59 | } catch (ConnectException $e) { 60 | 61 | return ['result' => 'error', 'message' => $e->getMessage()]; 62 | } catch (Exception $e) { 63 | 64 | return ['result' => 'error', 'message' => $e->getMessage()]; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Facades/Whmcs.php: -------------------------------------------------------------------------------- 1 | client = app('whmcs'); 19 | } 20 | 21 | 22 | /** 23 | * Retrieve a user by their unique identifier. 24 | * 25 | * @param mixed $identifier 26 | * 27 | * @return WhmcsUser|null 28 | */ 29 | public function retrieveById($identifier) 30 | { 31 | $userAttributes = []; 32 | if (session()->has(config('whmcs.session_key'))) { 33 | $userAttributes = session()->get(config('whmcs.session_key')); 34 | } else { 35 | $res = (array) $this->client->sbGetClientsDetails(null, $identifier); 36 | if (Arr::has($res, 'result') && $res['result'] === 'success') { 37 | $userAttributes = (array) $res['client']; 38 | } 39 | } 40 | 41 | return (sizeof($userAttributes) > 0) ? new WhmcsUser($userAttributes) : null; 42 | } 43 | 44 | /** 45 | * Retrieve a user by their unique identifier and "remember me" token. 46 | * 47 | * @param mixed $identifier 48 | * @param string $token 49 | * 50 | * @return null 51 | */ 52 | public function retrieveByToken($identifier, $token) 53 | { 54 | return null; 55 | } 56 | 57 | /** 58 | * Update the "remember me" token for the given user in storage. 59 | * 60 | * @param Authenticatable $user 61 | * @param string $token 62 | * 63 | * @return void 64 | */ 65 | public function updateRememberToken(Authenticatable $user, $token) 66 | { 67 | // 68 | } 69 | 70 | /** 71 | * Retrieve a user by the given credentials. 72 | * 73 | * @param array $credentials 74 | * 75 | * @return WhmcsUser|null 76 | */ 77 | public function retrieveByCredentials(array $credentials) 78 | { 79 | $userAttributes = []; 80 | if (session()->has(config('whmcs.session_key'))) { 81 | $userAttributes = session()->get(config('whmcs.session_key')); 82 | } else { 83 | $res = (array) $this->client->sbGetClientsDetails($credentials['email']); 84 | if (Arr::has($res, 'result') && $res['result'] === 'success') { 85 | $userAttributes = (array) $res['client']; 86 | } 87 | } 88 | 89 | return (sizeof($userAttributes) > 0) ? new WhmcsUser($userAttributes) : null; 90 | } 91 | 92 | /** 93 | * Validate a user against the given credentials. 94 | * 95 | * @param Authenticatable $user 96 | * @param array $credentials 97 | * 98 | * @return bool 99 | */ 100 | public function validateCredentials(Authenticatable $user, array $credentials) 101 | { 102 | /** @var array $res */ 103 | $res = (array) $this->client->sbValidateLogin( 104 | $credentials['email'], 105 | $credentials['password'] 106 | ); 107 | 108 | if (Arr::has($res, 'result') && $res['result'] === 'success') { 109 | session()->put(config('whmcs.session_key'), $this->retrieveByCredentials($credentials)->getAttributes()); 110 | 111 | return true; 112 | } 113 | 114 | return false; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/Whmcs.php: -------------------------------------------------------------------------------- 1 | reflector = new ReflectionClass(__CLASS__); 22 | } 23 | 24 | /** 25 | * Magic call to any other WHMCS API methods available. 26 | * 27 | * @see https://developers.whmcs.com/api/api-index/ 28 | * 29 | * @param $name 30 | * @param $args 31 | * 32 | * @return array|null 33 | */ 34 | public function __call($name, $args) 35 | { 36 | $params = $args[0]; 37 | $params['action'] = $name; 38 | 39 | return (new Client)->post($params); 40 | } 41 | 42 | /** 43 | * Retrieve configured products matching provided criteria. 44 | * 45 | * @param int|null $pid Obtain a specific product id configuration. Can be a list of ids comma separated 46 | * @param int|null $gid Retrieve products in a specific group id 47 | * @param string|null $module Retrieve products utilising a specific module 48 | * 49 | * @return array 50 | */ 51 | public function sbGetProducts($pid = null, $gid = null, $module = null) 52 | { 53 | return (new Client)->post([ 54 | 'action' => 'getProducts', 55 | 'pid' => $pid, 56 | 'gid' => $gid, 57 | 'module' => $module, 58 | ]); 59 | } 60 | 61 | /** 62 | * @param int|null $limitstart The offset for the returned log data (default: 0) 63 | * @param int|null $limitnum The number of records to return (default: 25) 64 | * @param string $sorting The direction to sort the results. ASC or DESC. Default: ASC 65 | * @param string|null $search The search term to look for at the start of email, firstname, 66 | * lastname, fullname or companyname 67 | * 68 | * @return array 69 | */ 70 | public function sbGetClients($limitstart = null, $limitnum = null, $sorting = null, $search = null) 71 | { 72 | return (new Client)->post([ 73 | 'action' => 'getClients', 74 | 'limitstart' => $limitstart, 75 | 'limitnum' => $limitnum, 76 | 'sorting' => $sorting, 77 | 'search' => $search, 78 | ]); 79 | } 80 | 81 | /** 82 | * Obtain the Clients Details for a specific client. 83 | * 84 | * Either email or clientid is required! 85 | * 86 | * @param string|null $email 87 | * @param int|null $clientid 88 | * @param bool $stats 89 | * 90 | * @return array 91 | */ 92 | public function sbGetClientsDetails($email = null, $clientid = null, $stats = false) 93 | { 94 | return (new Client)->post([ 95 | 'action' => 'getClientsDetails', 96 | 'email' => $email, 97 | 'clientid' => $clientid, 98 | 'stats' => $stats, 99 | ]); 100 | } 101 | 102 | /** 103 | * Validate client login credentials. 104 | * 105 | * @param string $email Client or Sub-Account Email Address 106 | * @param string $password2 Password to validate 107 | * 108 | * Response Parameters: 109 | * Parameter Type Description 110 | * result string The result of the operation: success or error 111 | * userid int Client ID 112 | * contactid int Contact ID if credentials match with a Sub-Account 113 | * passwordhash string Login session token - returned if Two-Factor Authentication is not required 114 | * twoFactorEnabled bool True if Two-Factor Authentication is enabled for the given account 115 | * 116 | * @return array 117 | */ 118 | public function sbValidateLogin($email, $password2) 119 | { 120 | return (new Client)->post([ 121 | 'action' => 'ValidateLogin', 122 | 'email' => $email, 123 | 'password2' => $password2, 124 | ]); 125 | } 126 | 127 | /** 128 | * Generate the AutoLogin URL for WHMCS 129 | * 130 | * @param string|null $goto URI part to redirect after login 131 | * 132 | * @return string 133 | */ 134 | public function getAutoLoginUrl($goto = null) 135 | { 136 | if (auth()->check()) { 137 | // Define WHMCS URL & AutoAuth Key 138 | $whmcsurl = rtrim(config('whmcs.url'), '/').'/dologin.php'; 139 | $timestamp = time(); 140 | $email = auth()->user()->email; 141 | $hash = sha1($email.$timestamp.config('whmcs.autoauth.key')); # Generate Hash 142 | // Generate AutoAuth URL & Redirect 143 | $url = $whmcsurl 144 | ."?email=$email×tamp=$timestamp&hash=$hash&goto=" 145 | .urlencode($goto ?? config('whmcs.autoauth.goto')); 146 | 147 | return $url; 148 | } else { 149 | return '/'; 150 | } 151 | } 152 | 153 | /** 154 | * Redirect to AutoLogin URL 155 | * 156 | * @param string|null $goto 157 | * 158 | * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector 159 | */ 160 | public function redirectAutoLogin($goto = null) 161 | { 162 | return redirect($this->getAutoLoginUrl($goto)); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/WhmcsServiceProvider.php: -------------------------------------------------------------------------------- 1 | isLaravel()) { 20 | $source = dirname(__DIR__).'/config/whmcs.php'; 21 | $this->publishes([$source => config_path('whmcs.php')]); 22 | $this->mergeConfigFrom($source, 'whmcs'); 23 | } 24 | } 25 | 26 | /** 27 | * Register the service provider. 28 | * 29 | * @return void 30 | */ 31 | public function register() 32 | { 33 | $this->registerWhmcs(); 34 | } 35 | 36 | /** 37 | * register Whmcs. 38 | */ 39 | public function registerWhmcs() 40 | { 41 | $this->app->singleton('whmcs', function () { 42 | return new Whmcs(); 43 | }); 44 | $this->app->alias('whmcs', Whmcs::class); 45 | } 46 | 47 | /** 48 | * @return array 49 | */ 50 | public function provides() 51 | { 52 | return [ 53 | 'whmcs', 54 | ]; 55 | } 56 | 57 | /** 58 | * @return bool 59 | */ 60 | protected function isLaravel() 61 | { 62 | return !preg_match('/lumen/i', $this->app->version()); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/WhmcsUser.php: -------------------------------------------------------------------------------- 1 | attributes; 72 | } 73 | 74 | /** 75 | * Get the non-existent "remember me" token value. 76 | * 77 | * @return null 78 | */ 79 | public function getRememberToken() 80 | { 81 | return null; 82 | } 83 | } 84 | --------------------------------------------------------------------------------