Jump to content

Automatic server selection based on client "Location" choice


Recommended Posts

  • WHMCS Technical Analyst

For companies operating hosting infrastructure in multiple countries, it is often desirable to allow the customer to choose where they would like their account to be hosted.

There are several ways in which this could be done. 

One possible solution would be to create separate products for each location, and assign the appropriate Server Group to each one. However, this would mean that you would have to maintain several instances of the same product; if you wanted to update the product in the future, you would have to do so for each instance.

Another solution, and the one that I will be covering in this post, is to create one product that allows users to select their location via a Custom Field. This means that all accounts with the same 'plan' are assigned to the same product, which is more streamlined for administrative purposes, and more logical for the end user. However, WHMCS is not able to automatically select the correct location for a new account in this scenario because it doesn't understand the concept of a 'location'. By default, WHMCS can only pick a server from the Server Group assigned under the product's Module Settings - this wouldn't work for this scenario.

Therefore we are going to need to write an action hook that can automatically select a server based on the location specified by the client.

This guide assumes that you have already added all of your Servers to WHMCS, and that they are all working as expected.  

Step 1: Create the a new product (and group if applicable) for your hosting plan

The first thing that you need to do is create a new product for this hosting plan via System Settings > Products/Services, as per our documentation. Make sure to setup pricing and your module settings according to your requirements.

Step 2: Add a new custom field

With your product now setup, you should create a new Custom Field for the product called Location. You can do this in the Custom Fields tab in your product settings.

Something like the following would work nicely:

image.png.24a4693cba18aeef0d378186d87d79f4.png

Step 3: Create the action hook that will assign the correct server

In this example, we will only have three servers - one per selectable location. These are:

  • Server ID 1: VPS node in London (UK)
  • Server ID 2: VPS node in Paris (FR)
  • Server ID 3: VPS node in New York (US)

Keep in mind that you will need to substitute these IDs with the correct ones for your hosting environment.

To achieve the desired outcome, we will be making use of the PreModuleCreate hook, which is called prior to the Create command being run.

To get started, create a new file at /includes/hooks called set_serverid_by_location_hook.php

With this file created, paste in the following content:

<?php

add_hook('PreModuleCreate', 1, function($vars) {
    // Check if the product ID matches a supported product
    $productId = $vars['pid'];
    $supportedProducts = array(1, 2, 3); // List of supported product IDs

    // If it doesn't, do nothing
    if (!in_array($productId, $supportedProducts)) {
        return;
    }

    // Define serverid and location pairings statically
    $serverIdMappings = array(
        'London (UK)' => '1',
        'Paris (FR)' => '2',
        'New York (US)' => '3',
        // Add more location-serverid pairs as needed
    );

    // Get the Location custom field value
    $location = isset($vars['customfields']['Location']) ? $vars['customfields']['Location'] : '';

    // Check if the location is supported
    if (!isset($serverIdMappings[$location])) {
        logActivity("Server ID not found for location: $location");
        return;
    }

    // Override parameters for account creation
    return array(
        'serverid' => $serverIdMappings[$location],
        // Add any other parameters you want to override here
    );
});

?>

This script will set the server ID based on the Location defined by the customer.

If you have multiple servers per location and want to assign one randomly, then the above will need to be modified slightly. Something like this should do the trick (the modified sections have comments - the other parts do not):

<?php

add_hook('PreModuleCreate', 1, function($vars) {
    $productId = $vars['pid'];
    $supportedProducts = array(1, 2, 3);

    if (!in_array($productId, $supportedProducts)) {
        return;
    }

    // Define serverid and location pairings
    // This time, there can be multiple server IDs per location.
    $serverIdMappings = array(
        'London (UK)' => array('serverid1', 'serverid2', 'serverid3'), // Multiple server IDs for London
        'Paris (FR)' => array('serverid4', 'serverid5'), // Multiple server IDs for Paris
        'New York (US)' => array('serverid6', 'serverid7'), // Multiple server IDs for New York
        // Add more location-serverid pairs as needed
    );

    $location = isset($vars['customfields']['Location']) ? $vars['customfields']['Location'] : '';

    if (!isset($serverIdMappings[$location])) {
        logActivity("Server IDs not found for location: $location");
        return;
    }

    // Get a random server ID from the list of server IDs for the location
    $randomServerId = $serverIdMappings[$location][array_rand($serverIdMappings[$location])];

    return array(
        'serverid' => $randomServerId,
    );
});

?>

The result of this will be that the server is provisioned in the appropriate location at the time of module creation. Keep in mind that changing the value of the Location field will not update the location of the account. To do this, you will need to migrate the account to the new server.

The above code worked at the time of writing. Please keep in mind that this is a customisation to WHMCS, and as such our Support team won't be able to troubleshoot any issues that you encounter with it - this extends beyond the remit of our support package.

Any questions or feedback on the above are always welcome.

Link to comment
Share on other sites

  • 2 weeks later...

Hey, thanks for the solution.

I tried following the guide and the code but it appears it's partially not working.

For example, I order product 1, selected location 1, and it launched on location 1. And when ordering again, product 1 and location 1, it launches account on location 2. And then ordering again follows the pattern. One time it's location 1, then location 2, then again location 1, and then location 2, and so on regardless of the chosen location.

Here's the code I tried modifying with debugging.

<?php

add_hook('AfterModuleCreate', 1, function($vars) {
    // Debugging: Log information to help diagnose the issue
    logActivity("Debugging: Hook triggered for product ID: " . $vars['params']['pid']);
    
    // Check if the product ID matches a supported product
    $productId = $vars['params']['pid'];
    $supportedProducts = array(1, 2, 4, 5, 6); // List of supported product IDs

    // Debugging: Log the product ID to check if it's correct
    logActivity("Debugging: Product ID is $productId");

    // If it doesn't, do nothing
    if (!in_array($productId, $supportedProducts)) {
        return;
    }

    // Define serverid and location pairings statically
    $serverIdMappings = array(
        'West Cost (US)' => '1',
        'Germany (EU)' => '2',
        // Add more location-serverid pairs as needed
    );

    // Get the Location custom field value
    $location = isset($vars['params']['customfields']['Location']) ? $vars['params']['customfields']['Location'] : '';

    // Debugging: Log the location value to check if it's correct
    logActivity("Debugging: Location is $location");

    // Check if the location is supported
    if (!isset($serverIdMappings[$location])) {
        logActivity("Debugging: Server ID not found for location: $location");
        return;
    }

    // Debugging: Log the selected server ID
    logActivity("Debugging: Selected Server ID is {$serverIdMappings[$location]}");

    // Override parameters for account creation
    return array(
        'serverid' => $serverIdMappings[$location],
        // Add any other parameters you want to override here
    );
});



?>

 

Then, I checked the ActivityLog and it's showing correct values as chosen.

Debugging: Hook triggered for product ID: 1
Debugging: Product ID is 1
Debugging: Location is West Cost (US)
Debugging: Selected Server ID is 1

But, it launched the cPanel account on Server ID 2 (Germany).

And, here are my settings for Product 1

hzajcZm4To6mINwQphrpzQ.png

 

Any solution please?

Link to comment
Share on other sites

  • 4 weeks later...

Hello joshq,

I think I caught the issue.

The reason could be that in Servers -> Group, the Fill Type is set to "Add to the least full server" and the other option is "Fill active server until full then switch to next least used".

But the script is not overriding the Fill Type and forcing users to follow the Fill Type regardless of the user selected location.

Is there a way to override Fill Type and enforce what user has been selected?

 

Link to comment
Share on other sites

  • 4 weeks later...
  • 2 months later...
  • 2 weeks later...

Hi everyone,

Anyone know if a similar approach is available to select Server Group based on custom field?

The above looks like its selecting the Server ID directly, would want to just select the Server Group and let WHMCS do the job selecting. (Fill Least Full)


 

Link to comment
Share on other sites

  • 1 month later...

 

13 hours ago, Achievement In Motion said:

Whats the best method to just show the server ping prior to choosing and allow the client to make their final choice?

Could provide a test IP address with each stated server locale.

I.E.:

US - 10.10.10.10

NL - 10. 11.11.11

BR - 10.12.12.12

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use & Guidelines and understand your posts will initially be pre-moderated