Category: WHMCS

3 Oct

WHMCS Merchant Gateway Development

This type of WHMCS gateways can be used for payment using credit card. Let’s create a payment gateway with name test. For that we have to create a file named test.php and then we have to upload it to the folder modules/gateways.

Live mode and test mode.

Each payment gateway provides some test credit card details, we can use that for test payment.
In test mode, for any payment there won’t be any actual transaction.
In live mode, we have to use real credit card details and real transaction will happen in this mode. Payment gateway will give us both live url and test url for real and test payments respectively.

Our gateway module will decide weather we have to do real or test transactions based on the settings in the module config.

'testMode' => array(
            'FriendlyName' => 'Test Mode',
            'Type' => 'yesno',
            'Description' => 'Tick to enable test mode',
        ),

If we select testMode, then only test payments will work. If it’s unselected real transaction will happen.

Initially the test.php file looks like

<?php

if (!defined("WHMCS")) {
    die("This file cannot be accessed directly");
}

function test_MetaData()
{
    return array(
        'DisplayName' => 'Test payment gateway',
        'APIVersion' => '1.0',
        'DisableLocalCredtCardInput' => true,
        'TokenisedStorage' => false,
    );
}

function test_config()
{
    return array(
        'FriendlyName' => array(
            'Type' => 'System',
            'Value' => 'Test payment gateway',
        ),
        'live_url' => array(
            'FriendlyName' => 'Live URL',
            'Type' => 'text',
            'Size' => '25',
            'Default' => '',
            'Description' => 'Enter your live url here',
        ),
        'test_url' => array(
            'FriendlyName' => 'Test URL',
            'Type' => 'text',
            'Size' => '25',
            'Default' => '',
            'Description' => 'Enter your test url here',
        ),
        'testMode' => array(
            'FriendlyName' => 'Test Mode',
            'Type' => 'yesno',
            'Description' => 'Tick to enable test mode',
        ),
        'user_id' => array(
            'FriendlyName' => 'User Id',
            'Type' => 'text',
            'Size' => '25',
            'Default' => '',
            'Description' => 'Enter your user id here',
        ),
    );
}


function test_capture($params) {

        if($params['testMode'] == 'on') { //testmode
          $url = $params['test_url'];
        } else { // live mode
          $url = $params['live_url'];
        }
}
?>

We have to add payment code into the function, test_capture($params).
Inside that we have to use payment transfer code. Each gateway API will be diffrent, so the payment transfer code will be different for each gateway.

Let’s write a payment transfer code for an arbitrary gateway.

function test_capture($params) {
     $postfields = [
        'invoiceid' => $params['invoiceid'],
        'amount' => $params['amount'],
        'currency' => $params['currency'],
        'cardnumber' => $params['cardnum'],
        'cardexpiry' => $params['cardexp'],
        'cardcvv' => $params['cccvv'],
    ];

    if($params['testMode'] == 'on') { //testmode
          $url = 'https://test.myarbitrarygateway/api/payment';
    } else { // live mode
          $url = 'https://myarbitrarygateway/api/payment';
    }

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postfields));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    $response = curl_exec($ch);
    curl_close($ch);

    $data = json_docode($response);

    return array(
        'status' => ($data->success == 1) ? 'success' : 'declined',
        'rawdata' => $data,
        'transid' => $data->transaction_id,
        'fees' => $data->fees,
    ); 
}
25 Sep

What is mean by WHMCS hook?

Standard way of modifying WHMCS features is using hook. WHMCS code is encrypted and we couldn’t change it. In addition to it, modifying core file is a bad idea. When we update WHMCS to the latest version all of our core file changes will be removed. So hook is a nice way to customize WHMCS features.

There are many hooks available in WHMCS. Available WHMCS hooks can be found in the link
https://developers.whmcs.com/hooks/hook-index/

Some of them can be used to run a piece of code just after an action is happened(ClientAdd executes just after a client is added to WHMCS). Some of them can be used used to run a piece of code just before an action(PreDeleteClient executes just before a client is deleted). There are hooks that can be used to run on certain pages(ClientAreaPageHome executes on the client area homepage).

Hooks can be include in the WHMCS in two ways.
1)We can create a php file with any name and can upload to the folder
/includes/hooks/.
2)We can create a file named hooks.php as a part of the addon module.

To get a better idea, let’s go through some examples now.
For now let’s just upload sample files to the folder /includes/hooks/.

Let’s add a hook ‘ClientAreaPage’ to a file named sample.php,
then let’s upload to the folder /includes/hooks/.
(see https://developers.whmcs.com/hooks-reference/client-area-interface/#clientareapage)

<?php
add_hook('ClientAreaPage', 1, function($vars) {
    echo "this is from clieant area hook";
    exit;
});

Then take any page in the client side, you will get a blank page with content
this is from clieant area hook. As we have exit in the code, nothing else will be displayed.

Now let’s modify the hook as shown below.

<?php
add_hook('ClientAreaPage', 1, function($vars) {
    echo "<pre>"; print_r($vars);
    exit;
});

Now the page will display an array.

To remove the hook, just delete the file from the folder /hooks/hook-index/.

Now let’s see another hook, ClientAreaPageCart.
Add the below code to a file named cart_hook.php and then upload to the folder
/includes/hooks/.

<?php
add_hook('ClientAreaPageCart', 1, function($vars) {
        echo "<pre>"; print_r($vars);
        exit;
});

This hook won’t run in all of the client are pages. But it will run in all of the
cart pages. For example take the url /cart.php then you will see an array printed in it.

20 Sep

WHMCS Addon development

WHMCS addon can be used to develop additional features in your WHMCS.

Here briefly explaining steps required to develop a simple addon, that just print some strings.
That’s enough to get started. After developing this simple addon you can move on to complex addons.

Addon name and folder name

Name of addon do have importance while developing a WHMCS addon.
Let’s name our addon as hello_world.

Then we have to create a folder named hello_world.
Within the folder, develop a file named hello_world.php.
Name used for the folder and file is hello_world, which is the name of the addon.
We have to upload the folder to the WHMCS folder /modules/addons/.

Functions to be defined in hello_world.php

We have to define some functions in the file hello_world.php.
Prefix for those functions should be same as the addon name.
As our addon name is hello_world, prefix for those functions
must be hello_world_.

Those functions are listed below.
1)hello_world_config
2)hello_world_activate
3)hello_world_deactivate
4)hello_world_output
5)hello_world_clientarea

hello_world_config

This function just returns an array.
Here we define addon name, version author etc.

function hello_world_config() {
    $configarray = array(
        "name" => "My first whmcs addon",
        "description" => "Just a sample addon, that just prints some strings",
        "version" => "1.0",
        "author" => "WHMCSTools.com",
        "language" => "english",
    );
    return $configarray;
}

hello_world_activate

This function is called when the addon module is activated from the admin side.
We can write table creation code within the function.

function hello_world_activate () {
    return array(
        'status' => 'success',
        'description' => 'Addon activated'
    );
}

hello_world_deactivate

This function is called when we are deactivating the addon from the admin side.
All addon table drop code should be added here.

function hello_world_deactivate() {
    return array(
        'status' => 'success',
        'description' => 'Addon deactivated'
    );
}

hello_world_output

Code written within this function will be executed when we take the url
www.ourwebsite.com/admin/addonmodules.php?module=hello_world

function hello_world_output($vars) {
    echo "just printing this in admin side";
}

hello_world_clientarea

This function is called when we take the url,
www.ourwebsite.com/index.php?m=hello_world

To display some contents in the client area, we have to create a smarty tpl file.
In the function hello_world_clientarea, we specified tpl file name as
‘templatefile’ => ‘template/clienthome’. So we have to create a folder named
template inside the folder /modules/addons/hello_world.
Then we have to create a file named clienthome.tpl in the folder template.
Note that we specified templatefile as ‘templatefile’ => ‘template/clienthome’, (no extension here) but we created file name as template/clienthome.tpl with the extension .tpl.

We have to pass parameters to the tpl file from the function hello_world_clientarea.
In the function we are passing the smarty key value pair as shown below.

'vars' => array(
            'name' => 'X ',
            'address' => 'Y',
            'phone' => 'Z',
),

Then in the tpl file we just have to write {$name} to display the name.

function hello_world_clientarea($vars) {
 
    $modulelink = $vars['modulelink'];
 
    return array(
        'pagetitle' => 'Addon Module',
        'breadcrumb' => array('index.php?m=hello_world'=>'Hello World Addon'),
        'templatefile' => 'template/clienthome', #smarty file name without .tpl extension
        'requirelogin' => true, # accepts true/false
        'forcessl' => false, # accepts true/false
        'vars' => array(  #smarty key value pair
            'name' => 'X ',
            'address' => 'Y',
            'phone' => 'Z',
        ),
    );
 
}

Content of the file template/clienthome.tpl is

    <h4> My Details </h4>

    <ul>
        <li>My name is : {$name} </li>
       <li>My address is : {$address} </li>
        <li>My phone number is : {$phone} </li>
    </ul>
20 Sep

How to create and drop tables in WHMCS 7 using PHP 7?

We might have to create a table from WHMCS addon while activating the module.
Also we might have to drop the table while deactivating the module.
Here we are going to write PHP 7 compatible codes to create and drop the table.

Create a table

Assume that we have to create a table named sample_table
with the following fields.
a)table_id
It’s an auto increment field, also it’s primary key of the table.

b)age
This field is an integer

c)name
This field should store string and maximum character length is 100.

d)created_at
It’s a date field

c)amount
It’s float field with two decimal points.

d)description
It’s a text field.

Then we can use below code for that.

 use Illuminate\Database\Capsule\Manager as Capsule;
 if (!Capsule::schema()->hasTable('sample_table')) {
        Capsule::schema()->create('sample_table', function($table) {
            $table->increments('table_id');
            $table->integer('age');
            $table->string('name', 100);
            $table->date('created_at');
            $table->float('amount', 8, 2);
            $table->text('description');
        });
    }

Drop the table.

To drop the table we can use the below code.

#add the below line only once in a file
use Illuminate\Database\Capsule\Manager as Capsule;
Capsule::schema()->dropIfExists('sample_table');
21 Aug

WHMCS Report Development

We can easily create a custom report in WHMCS.
First of all we have to create a file with any name and then we have to upload it to the folder modules/reports.

Let’s write a report to list down invoices that are paid between two dates.
Name of the file is invoice_report.php.
And it should be uploaded to the folder, modules/reports.

Th sample report code is below.

<?php

if (!defined("WHMCS")) die("This file cannot be accessed directly");

use Illuminate\Database\Capsule\Manager as Capsule;

# Report Title
$reportdata["title"] = "Invoice Report";

# Report Description
$reportdata["description"] = "";

$start_date = "2017-01-01";
$end_date =  date("Y-m-d");  

$reportdata["tableheadings"] = array(
      "ID", 
      "DATE PAID", 
      "SUBTOTAL",
      "TAX", 
      "TOTAL", 
      "PAYMENT TYPE",
);
 if(!empty($start_date) && !empty($end_date)) {
        $invoices = Capsule::table('tblinvoices')
              ->whereBetween('datepaid', array($start_date, $end_date))
              ->where('status', 'Paid')
              ->get();

        foreach($invoices as $invoice) {
	         $reportdata["tablevalues"][] = array(
                    $invoice->id,
                    $invoice->datepaid,         
                    $invoice->subtotal,
                    $invoice->tax + $invoice->tax2,
                    $invoice->total,
                    $invoice->paymentmethod,
	         );
       }
}

We have a ready made sales total report tool, with one time cost of $25.
Please Contact Us by filling the form. We will get back to you asap.

5 Jul

Multi Level Product Category (Product Subcategory) in WHMCS

By default in WHMCS, only one level product categories are possible. Assume that you have a category
US and another Category Spain. And under US you have another category California Items
and Under Spain you have another category named Madrid Items.

Then currently you have to create two categories named California Items and Madrid Items as two categories. Then you can add products under California Items and Madrid Items. But there is no way to add subcategory in WHMCS. We developed a module, which allow to add a parent category and then number of child category to it.Anybody looking for such a module, please contact.

 

16 May

CyberSource Payment Gateway Module for WHMCS

We have to use Cybersource’s SOAP Toolkit for the WHMCS payment gateway module.Using the toolkit we can develop a Merchant Gateway as explained in the link https://developers.whmcs.com/payment-gateways/merchant-gateway/

The SOAP toolkit details can be found in the URL https://www.cybersource.com/developers/getting_started/integration_methods/soap_toolkit_api. Also read the PDF http://apps.cybersource.com/library/documentation/dev_guides/SOAP_Toolkits/SOAP_toolkits.pdf

We are not covering basics of Payment gateway module development for WHMCS in this post. But we are explaining basics of Cybersource’s API here. Sample code with PHP can be downloaded from the URL www.cybersource.com/support_center/implementation/downloads/soap_api/sample_files/sample_php.zip

It’s better to store subscription id, returning from the cybersource and use that for recurring payments.In this way we can avoid repeated entry of credit card details. From the _storeremote($params) function of your WHMCS payment gateway code, we can create the subscription_id. Below briefly explaining code to produce the subscription id.

function cybersourcegateway_storeremote($params) {
    require_once("cybersource_lib/vendor/autoload.php");
    $referenceCode = '<order number>';
    $options = array();
    
    $client = new CybsSoapClient($options, $params);
    $request = $client->createRequest($referenceCode);
    
    $ccAuthService = new stdClass();
    $ccAuthService->run = 'true';
    $request->ccAuthService = $ccAuthService;

    $billTo = new stdClass();
    $billTo->firstName = "<First name here>";
    $billTo->lastName = "<Last name here>";
    $billTo->street1 = "
<address here>";
    $billTo->city = "<city here>";
    $billTo->state = "<State here>";
    $billTo->postalCode = "<Post code here>";
    $billTo->country = "<Country here>";
    $billTo->email = "<Email here>";
    $billTo->ipAddress = "<Ip address here>";
    $request->billTo = $billTo;
 
    
    $card = new stdClass();
    $card->accountNumber = "<card number here>";

    $card->expirationMonth = "<expiry month here>";
    $card->expirationYear = "<expiry year here>";
    $card->cardType = "<card type here>";
    $request->card = $card;

    $purchaseTotals = new stdClass();
    $purchaseTotals->currency = $params['currency'];
    $purchaseTotals->grandTotalAmount = $params['amount'];
    $request->purchaseTotals = $purchaseTotals;

    $recurringSubscriptionInfo = new stdClass();
    $recurringSubscriptionInfo->frequency = "on-demand";
    $request->recurringSubscriptionInfo = $recurringSubscriptionInfo;

    $paySubscriptionCreateService = new stdClass();
    $paySubscriptionCreateService->run = 'true';
    $request->paySubscriptionCreateService = $paySubscriptionCreateService;

    $reply = $client->runTransaction($request);
    $subscription_id = $reply->paySubscriptionCreateReply->subscriptionID;

	

 if ($reply->decision == 'ACCEPT' && $reply->reasonCode == '100') {
    return array(
        "status" => "success",
        "gatewayid" => $subscription_id,
        "rawdata" => json_encode($reply)
    );
 }
else {
        return array(
            "status" => "failed",
            "gatewayid" => $subscription_id,
            "rawdata" => json_encode($reply)
        );
    }
}

Then in your capture function, call API as given below.

function cybersourcegateway_capture($params) {
   require_once("cybersource_lib/vendor/autoload.php");
    $referenceCode = $params['referencecode'];
    $options = array();
    $client = new CybsSoapClient($options, $params);

    $request = $client->createRequest($referenceCode);
    $ccAuthService = new stdClass();
    $ccAuthService->run = 'true';
    $request->ccAuthService = $ccAuthService;

    $ccCaptureService = new stdClass();
    $ccCaptureService->run = 'true';
    $request->ccCaptureService = $ccCaptureService;

    $purchaseTotals = new stdClass();
    $purchaseTotals->currency = $params['currency'];
    $purchaseTotals->grandTotalAmount = $params['amount'];
    $request->purchaseTotals = $purchaseTotals;


    $recurringSubscriptionInfo = new stdClass();
    $recurringSubscriptionInfo->subscriptionID = $params['gatewayid'];
    $request->recurringSubscriptionInfo = $recurringSubscriptionInfo;


    $reply2 = $client->runTransaction($request);

    if ($reply2->decision == 'ACCEPT' && $reply2->reasonCode == '100') {
        return array(
            "status" => "success",
            "transid" => $reply2->requestID,
            "rawdata" => json_encode($reply2)
        );
    } else {
        return array(
            "status" => "failed",
            "transid" => $reply2->requestID,
            "rawdata" => json_encode($reply2)
        );
    }
}

Those who would like to hear more about the gateway development, post a comment. Will reply asap.

And those who are looking for a ready made module, please contact us from the link Request a quote. We will get back to you asap.

3 Apr

WHMCS 6 Addon Module Development Using Smarty

Main aim of this post is not to discuss all steps of WHMCS addon module development. We are discussing two points that are not well explained in many blogs.

1)How to create and delete tables using new WHMCS database component. As full_query is deprecated, we have to use Illuminate Database. This post explains how to do table creation and deletion using WHMCS Illuminate Database.

2)When creating WHMCS addon without using Smarty, We are making html code complicated with mix of single and double quotes. So in this post we are discussing how to write HTML content using Smarty.

Assume that we have to develop an addon with name ‘example’. Steps to create the addon module is explained below.

1)create a folder named `example`.
In the folder create two files.

a)example.php
b)hooks.php
Also create folders named `lang` and `templates`

2)Now let’s open the file example.php and add the below code.

  <?php

if (!defined("WHMCS"))
    die("This file cannot be accessed directly");

use Illuminate\Database\Capsule\Manager as Capsule;

   function example_config() {
    $configarray = array(
        "name" => "Sample Addon",
        "description" => "Sample Addon is just an addon to explain you",
        "version" => "1.0",
        "author" => "WHMCSTools",
        "language" => "english",
    );

    return $configarray;
}


//Table is creating here
function example_activate() {
   if (!Capsule::schema()->hasTable('sample_table')) {
        Capsule::schema()->create('sample_table', function($table) {
            $table->increments('table_id');
            $table->integer('age');
            $table->string('name', 100);
            $table->date('created_at');
            $table->float('amount', 8, 2);
            $table->text('description');
        });
    }

    return array('status' => 'success', 'description' => 'Module activated');
}
function example_deactivate() {

    Capsule::schema()->dropIfExists('sample_table');
    return array('status' => 'success', 'description' => 'Module deactivated');

}

function example_output($vars) {
}

function example_clientarea($vars) {
}

3)Now we can write what we have to display in admin side of the module
in the function example_output() {.

For example we can define the function as

function example_output($vars) {
   echo '<p>The date & time are currently ' . date("Y-m-d H:i:s") . '</p>';
}

But if we have much more HTML content to display, we have to create a big string and then echo it. Better way to display the HTML content is using a Smarty file.

For that let’s create a file named time.tpl in the folder templates.
Let’s discuss the template usage with the same simple content. But we can extend that for complex contents too.

Content of the tpl file is

 <p>The date & time are currently {$date}</p>

Now let’s discuss how to use the tpl from the example_output function.

 function example_output($vars) {
   $smarty = new Smarty();
   $smarty->assign('date', date("Y-m-d H:i:s") );
   $smarty->caching = false;
   $smarty->compile_dir = $GLOBALS['templates_compiledir'];
   $smarty->display(dirname(__FILE__) . '/templates/time.tpl');
 }
20 Mar

WHMCS Hooks Priority

WHMCS Hooks are a nice way to write custom code.

We can add hook functions to a php file (File name can be anything, let’s assume our file name as test.php) and it should be uploaded to the folder /includes/hooks.

All WHMCS hooks are listed in the page https://developers.whmcs.com/hooks/hook-index/.

Now let’s explain WHMCS hooks priority. We can add custom code to be executed on every WHMCS client area page to the below hook.

<?php
add_hook('ClientAreaPage', 1, function($vars) { 
    // 1 is the priority value
    echo "From priority value 1<br>";
});

In the above hook code, we added code to be executed on the client area page as

 echo "From priority value 1<br>";

We can add same hook functions many times.
Let’s add another ClientAreaPage hook as shown below.

add_hook('ClientAreaPage', 2, function($vars) { 
     //2 is the priority value
     echo "From priority value 2<br>";
});

So the first ClientAreaPage hook has priority 1, second hook as priority 2.
So we will get the below out put.


From priority value 1
From priority value 2

Hook with priority value 1 is executed first,then hook with priority value 2 is executed.

9 Feb

How to get WHMCS product price details by query?

If you would like to know the price details of a product in WHMCS by query,
you can use the below query.

First you have to get the currency id. It’s the Id of the currency chosen by the user.

I wrote a function named getCurrencyId to get the currency ID.

function getCurrencyId() {
    $uid = $_SESSION['uid'];
    if (empty($uid)) {
        if (!empty($_SESSION['currency'])) {
            return $_SESSION['currency'];
        } else {
            return 1;
        }
    }
    $userData = Capsule::table('tblclients')
            ->select('currency')
            ->where('id', '=', $uid)
            ->first();
    if (!empty($userData))
        return $userData->currency;
    else
	return 1;
}

Assume that we would like to know price details of the product with id = 10.
Then we have to join tables tblproducts and tblpricing on a condition
‘tblproducts.id’, ‘=’, ‘tblpricing.relid’.

$currency_id = getCurrencyId();
$pid = 10;
$product_details = Capsule::table('tblproducts')
  ->join('tblpricing', 
      'tblproducts.id', '=', 'tblpricing.relid')
  ->where('tblpricing.type', '=', 'product')
  ->where('tblproducts.id', $pid)
  ->where('tblpricing.currency', $currency_id)
  ->select('tblproducts.*', 'tblpricing.*')
  ->first();

From the result array $product_details
we can get monthly, quarterly, semiannually, annually, biennially and triennially
as given below. If no price is entered for a particular period, you will get price as -1.

$monthly_price = $product_details->monthly;
if($monthly_price != -1 ) {
   echo "monthly price is" . $monthly_price;
}
//do -1 check all of the periods.

$quarterly_price = $product_details->quarterly;
$semiannually_price = $product_details->semiannually;
$annually_price = $data->annually;
$biennially_price = $data->biennially;
$triennially_price = $data->triennially;