WHMCS V 8.10 sell service with a customer existing Subdomain
WHMCS is a website software package that sells IT services.
We use this software to support our online and hosting service in China.
https://webtest.accesstochina.com
From time to time, we find we need to add features to the WHMCS software to enable us to sell services or information not currently supported by the WHMCS " out-of-the-box” software.
The good news is that WHMCS has many development tools to support making changes as required.
Below are my notes on how to allow services to be sold with a customer's existing subdomain.
I assume you understand the WHMCS development manual, PHP, PHP templates (TPL) and WHCS hooks.
Background
When selling a service, you often add a domain name; e.g., cloud VPS service typically requires a domain/subdomain to locate the service. Example of a domain sold with a service;
WHMCS supports the following domain sources;
-
- New domain purchased from ISP with WHMCS have many integrated suppliers
- Domain you have already purchased
- Domain transferred into your ISP domain supplier account
- Customers own domain
- Subdomain to one existing domain, e.g., customer-sub-domain.b2b66.icu (b2b66.icu is a domain own my business). It is not a subdomain owned by the customer's domain.
WHMCS currently does not support customers using their subdomain.
Changes needed WHMCS
Four files need creating/changing.
-
- Based on using WHMCS standard template:
-
- twenty-one --> orderform --> configureproductdomain.tpl
-
- javascript
- twenty-one --> orderform --> standard_cart --> js --> scripts.min.js
- Two new hooks
- ShoppingCartValidateDomain
- ShoppingCartViewCartOutput
- Based on using WHMCS standard template:
Recommended steps
Please note that these are the steps I took to make changes. I am publishing them as a guide, and they should be tested in a backup development system. Each install of WHMCS can be different based on the version and modules installed.
If you do not have development services, I recommend not installing these changes in your production system. You can request a development license key from WHMCS, create a copy of your production system, and check they work in your service environment.
1. Create a complete backup of your WHMCS system
2. Make a copy of the twenty-one orderform start_cart directory
-
- templates/orderforms/standart_cart à templates/orderforms/standard_cart2
- In WHMCS system setting à general settings à ordering (tab) select new template name (in this case, standard_cart2). In doing this, you can return to the original template if needed)
3. Create a new field in the template configureproductdomain.tpl for the subdomain;
-
- Search the template for “{if $owndomainenabled}”. This will give the section of coding needing a change.
- Add a new input field for the subdomain. Do not call the field “subdomain”. WHMCS already uses this name. I used “subd” and “owndomainsubd”;
<div>
<input type="text" id="owndomainsubd" value="{$subd}" placeholder="Subdomain" class="form-control" autocapitalize="none" data-toggle="tooltip" data-placement="top" data-trigger="manual" title="Subdomain" />
</div>
<input type="text" id="owndomainsld" value="{$sld}" placeholder="Domain" class="form-control" autocapitalize="none" data-toggle="tooltip" data-placement="top" data-trigger="manual" title="{lang key='orderForm.enterDomain'}" />
-
- Review and adjust as required and update the CSS if needed
4. Update the scripts.min.js to make the new data available to Hook
-
- We recommend that you reformat the scripts.min.js file into a readable format. I copied the JS code into the JavaScript Beautifier. There are many tools on the Internet for reformatting coding.
- In the routine, add line “subd”
jQuery(".domain-selection-options .option").removeClass("option-selected"), jQuery(this).parents(".option").addClass("option-selected"), jQuery(".domain-input-group").hide(), jQuery("#domain" + jQuery(this).val()).show()
}), jQuery("#frmProductDomain").submit(function(e) e.preventDefault();
var t = jQuery(this).find('button[type="submit"]'),
e = jQuery("#DomainSearchResults"),
n = jQuery("#spotlightTlds"),
o = jQuery("#domainSuggestions"),
f = jQuery("#btnDomainContinue"),
i = jQuery(".domain-selection-options input:checked").val(),
a = jQuery("#" + i + "sld"),
r = a.val(),
s = "",
subd = jQuery("#owndomainsubd").val(),
d = jQuery("#frmProductDomainPid").val(),
l = "",
m = jQuery("#idnLanguageSelector");
-
- Second change to the JS, again adding “subd”;
jQuery(".domain-selection-options .option").removeClass("option-selected"), jQuery(this).parents(".option").addClass("option-selected"), jQuery(".domain-input-group").hide(), jQuery("#domain" + jQuery(this).val()).show()
}), jQuery("#frmProductDomain").submit(function(e) {
e.preventDefault();
var t = jQuery(this).find('button[type="submit"]'),
e = jQuery("#DomainSearchResults"),
n = jQuery("#spotlightTlds"),
o = jQuery("#domainSuggestions"),
f = jQuery("#btnDomainContinue"),
i = jQuery(".domain-selection-options input:checked").val(),
a = jQuery("#" + i + "sld"),
r = a.val(),
s = "",
subd = jQuery("#owndomainsubd").val(),
d = jQuery("#frmProductDomainPid").val(),
l = "",
m = jQuery("#idnLanguageSelector");
-
- Save the JS file and check that the template is still functioning as before. The subdomain will not be processed until the hooks are created. It is good to check that the changes have not added new bugs to the system.
5. Create ShoppingCartValidateDomain hook
-
- Create a PHP file in /includes/hooks/ directory e.g. subdomainvalidate.php
<?php
add_hook('ShoppingCartValidateDomain', 1, function($vars) {
/// Clear session values
$_SESSION['subd'] = '';
$_SESSION['newdomain'] = '';
// logActivity('POST Data: ' . var_export($_POST, true));
/// set domain
$newdomain = isset($_POST['domain']) ? $_POST['domain'] : '';
logActivity('ShoppingCartValidateDomain - New Domain: ' . $newdomain);
$_SESSION['newdomain'] = $newdomain;
/// set subdomain
$subd = isset($_POST['subd']) ? $_POST['subd'] : '';
// logActivity('ShoppingCartValidateDomain - Subdomain: ' . $subd);
$_SESSION['subd'] = $subd;
// logActivity('Session Data: ' . print_r($_SESSION, true), 0);
});
-
- The hook is only performed when validating a domain name.
- This script collects the data from the template and creates session values for the next hook.
- The logActivity is helpful if you need to debug.
6. Create ShoppingCartViewCartOutput hook
-
- Create a PHP file in /includes/hooks/ directory e.g. viewcartoutput.php
<?php
if (isset($_SESSION['cart']['products']) && isset($_SESSION['subd']) && !empty($_SESSION['subd'])) {
foreach ($_SESSION['cart']['products'] as $key => $product) {
if (isset($product['domain']) && $product['domain'] === $_SESSION['newdomain']) {
$_SESSION['cart']['products'][$key]['domain'] = $_SESSION['subd'] . '.' . $product['domain'];
} } } });
-
- The hook will perform each time the cart is open
- It will add the subdomain name to the correct domain name and not all domain names in the cart.
- Below is an example of the output
Last modified: Version 1.1 - 25 January 2025