WHMCS Technical Analyst WHMCS Sachin Posted November 26 WHMCS Technical Analyst Share Posted November 26 (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 November 27 by WHMCS Sachin Code Update for security 0 Quote Link to comment Share on other sites More sharing options...
bear Posted November 26 Share Posted November 26 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 November 27 Author WHMCS Technical Analyst Share Posted November 27 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 November 27 Share Posted November 27 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 November 27 Author WHMCS Technical Analyst Share Posted November 27 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 November 27 Share Posted November 27 (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 November 27 by bear 0 Quote Link to comment Share on other sites More sharing options...
WHMCS Technical Analyst WHMCS Sachin Posted November 28 Author WHMCS Technical Analyst Share Posted November 28 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 November 28 Share Posted November 28 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.