WHMCS Technical Analyst WHMCS Sachin Posted Wednesday at 02:01 PM WHMCS Technical Analyst Share Posted Wednesday at 02:01 PM (edited) Hello WHMCS Community, I wanted to share a useful PHP script that I created to add clients from a CSV file to WHMCS using the internal API. This script is designed to be run on the same server (and directory) where WHMCS is installed, ensuring that it is secure and prevents unauthorized use. Prerequisites: 1. WHMCS API Credentials: Ensure you have your WHMCS API identifier and secret. 2. CSV File: Prepare a CSV file with the following columns: firstname, lastname, email, address1, city, state, postcode, country, phonenumber, password. PHP Script: Here is the PHP script to read the CSV file and add clients to WHMCS: <?php // CONFIGURATION $secretKey = 'CHANGE_ME_TO_A_RANDOM_32_CHAR_STRING'; // REQUIRED: Script will refuse to run if unchanged // Enter a valid WHMCS Admin username for the API context. $adminUsername = 'YourAdminUsername'; // FILE CHECK & OPEN CSV $csvPath = 'clients.csv'; // SECURITY CHECK if ($secretKey === 'CHANGE_ME_TO_A_RANDOM_32_CHAR_STRING') { die("<h3>Security Error</h3><p>You must change the <code>$secretKey</code> value inside the script before running it.</p>"); } if (!isset($_GET['key']) || $_GET['key'] !== $secretKey) { die("<h3>Access Denied</h3><p>Invalid or missing key.</p>"); } // INITIALIZE WHMCS ENVIRONMENT require_once 'init.php'; // Prevents the script from timing out on large files set_time_limit(0); ini_set('memory_limit', '512M'); // Force output to the browser so you see progress instantly if (function_exists('apache_setenv')) { @apache_setenv('no-gzip', 1); } @ini_set('zlib.output_compression', 0); @ini_set('implicit_flush', 1); for ($i = 0; $i < ob_get_level(); $i++) { ob_end_flush(); } ob_implicit_flush(1); echo "<html><body style='font-family: sans-serif; padding: 20px;'>"; echo "<h2>Starting Client Import...</h2><hr>"; // FILE CHECK if (!file_exists($csvPath)) { die("<p style='color:red; font-weight:bold;'>Error: The required file <code>clients.csv</code> was not found in the WHMCS folder.</p>"); } $file = fopen($csvPath, 'r'); fgetcsv($file); // Skip header row $count = 0; // PROCESS ROWS while (($data = fgetcsv($file)) !== false) { $count++; $values = [ 'firstname' => $data[0] ?? '', 'lastname' => $data[1] ?? '', 'email' => $data[2] ?? '', 'address1' => $data[3] ?? '', 'city' => $data[4] ?? '', 'state' => $data[5] ?? '', 'postcode' => $data[6] ?? '', 'country' => $data[7] ?? '', 'phonenumber' => $data[8] ?? '', 'password' => $data[9] ?? '', 'noemail' => true, ]; // WHMCS API CALL $results = localAPI('AddClient', $values, $adminUsername); // OUTPUT RESULT if ($results['result'] === 'success') { echo "<div style='color:green;'>[OK] Row $count: Added Client ID {$results['clientid']} ({$values['email']})</div>"; } else { echo "<div style='color:red;'>[FAIL] Row $count: {$values['email']} - Error: {$results['message']}</div>"; } flush(); } fclose($file); echo "<hr><h3>Import Process Complete.</h3>"; echo "</body></html>"; ?> Running the Script 1. Save the script as add_clients.php and place it in your WHMCS root directory. 2. Ensure the clients.csv file is in the same directory. 3. Run the script from the command line or through your browser. 4. NOTE: Once the work is done, ENSURE that the script is deleted and NOT left on the server. This is provided on an as is basis. But I will try to answer any queries if anyone faces any issues with this. Hope it comes in handy. Edited Thursday at 03:19 PM by WHMCS Sachin Code Update for security 0 Quote Link to comment Share on other sites More sharing options...
bear Posted Wednesday at 02:27 PM Share Posted Wednesday at 02:27 PM 24 minutes ago, WHMCS Sachin said: You must run it by adding "?run=true" to the URL Security check? Appreciate the effort, but that's not terribly secure, especially when you spit out the answer in the failure routine if it's not included. 0 Quote Link to comment Share on other sites More sharing options...
WHMCS Technical Analyst WHMCS Sachin Posted Thursday at 07:46 AM Author WHMCS Technical Analyst Share Posted Thursday at 07:46 AM 17 hours ago, bear said: Security check? Appreciate the effort, but that's not terribly secure, especially when you spit out the answer in the failure routine if it's not included. Fair point, but it is mostly to stop any random bots from triggering it accidentally, and it can be easily modified by the person to remove it from the output once they are setting it up. 0 Quote Link to comment Share on other sites More sharing options...
bear Posted Thursday at 11:33 AM Share Posted Thursday at 11:33 AM I'd suggest that your average user that grabs a script like this will not change anything in it, especially since they're not (apparently) being told to. I've seen users not change a "CHANGE_THIS" in scripts, then ask why it's not working. I've also seen them ignore the "remove this file after use", so relying on that might not be advisable either. Best practice is not to spoon feed, keep this outside the publicly accessible directories, and instead of that simple URL parameter you reveal, make it a "secret key" that is defined within the script, with a note to change it (or better still they make their own that must be x long and have this or that in it; if empty, fail with "key needed"). Safety first. 1 Quote Link to comment Share on other sites More sharing options...
WHMCS Technical Analyst WHMCS Sachin Posted Thursday at 03:20 PM Author WHMCS Technical Analyst Share Posted Thursday at 03:20 PM 3 hours ago, bear said: I'd suggest that your average user that grabs a script like this will not change anything in it, especially since they're not (apparently) being told to. I've seen users not change a "CHANGE_THIS" in scripts, then ask why it's not working. I've also seen them ignore the "remove this file after use", so relying on that might not be advisable either. Best practice is not to spoon feed, keep this outside the publicly accessible directories, and instead of that simple URL parameter you reveal, make it a "secret key" that is defined within the script, with a note to change it (or better still they make their own that must be x long and have this or that in it; if empty, fail with "key needed"). Safety first. Excellent points. Feedback taken and code updated 🙂 0 Quote Link to comment Share on other sites More sharing options...
bear Posted Thursday at 03:51 PM Share Posted Thursday at 03:51 PM (edited) Well done, and good to see "CHANGE_ME_TO_A_RANDOM_32_CHAR_STRING" is not 32 long. 😉 EDITED: Saw the check for the unchanged key. Edited Thursday at 04:10 PM by bear 0 Quote Link to comment Share on other sites More sharing options...
WHMCS Technical Analyst WHMCS Sachin Posted Friday at 06:28 AM Author WHMCS Technical Analyst Share Posted Friday at 06:28 AM 14 hours ago, bear said: Well done, and good to see "CHANGE_ME_TO_A_RANDOM_32_CHAR_STRING" is not 32 long. 😉 EDITED: Saw the check for the unchanged key. Iteration is how we learn, dont we? 0 Quote Link to comment Share on other sites More sharing options...
bear Posted Friday at 11:42 AM Share Posted Friday at 11:42 AM 5 hours ago, WHMCS Sachin said: Iteration is how we learn, dont we? ? At any rate, 32 characters doesn't appear to be required (any length works, as long as it's not the given one, and as it's a GET, the parameter of the key needs to be in the URL as before, yes (script.php?key=xxxxx)? Without, it gets the invalid key error. You've removed that explanation/step. If you like, once this is working and explained, remove my comments. Just trying to help and it's kind of blown up your kind gesture of this script. Apologies. 0 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.