Category: WHMCS

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;
27 Jan

How to know WHMCS Admin Username for local API call?

For calling WHMCS API’s internally from the WHMCS, we have a function named
localAPI. To call this function we have to pass
1)command; What api you would like to call.
2)data
3)admin user name of the WHMCS.

A sample API command UpdateClientProduct can be called as shown below.

        $command = 'UpdateClientProduct';
        $postData = array(
            'serviceid' => 11, // your service id
            'recurringamount' => 17,// recurring amount
        );
        //if WHMCS admin name is admin
        $adminUsername = "admin"; 
        $results = localAPI(
                       $command,
                       $postData, 
                       $adminUsername
        );

We hard coded the admin user name in the above code.
But hard coding the admin user name is a bad way of coding.
The code won’t work if admin username is changed or the code is uploaded
to another WHMCS where admin username is different.

So we can use below function to get the admin name.

use Illuminate\Database\Capsule\Manager as Capsule;
function getAdminUserName() {
    $adminData = Capsule::table('tbladmins')
            ->where('disabled', '=', 0)
            ->first();
    if (!empty($adminData))
        return $adminData->username;
    else
        die('No admin exist. Why So?');
}

So the API can be called as below.

  $command = 'UpdateClientProduct';
  $postData = array(
            'serviceid' => $product['service_id'],
            'recurringamount' => $deducted_amount,
  );
  $adminUsername = getAdminUserName();
  $results = localAPI(
                 $command, 
                 $postData, 
                $adminUsername
  );
16 Jan

Calling worldsteram.nl APIs from WHMCS

Calling worldsteram.nl APIs from WHMCS.

worldsteram.nl Provides API ID, and that must be used with API Calls.
So first of all create a file named worldstream.php and it must be
uploaded to the folder /modules/servers/worldstream.

<?php
function worldstream_ConfigOptions() {
    return array(
        'API ID' => array(
            'Type' => 'text',
            'Size' => '30',
            'Default' => '_API-ID_',
            'Description' => 'REplace _API-ID_ with your API ID',
        )
    );
}

/**
 The content that we will display on the page
[server.com]/clientarea.php?action=productdetails&id=[SERVICE_ID]
**/
function worldstream_ClientArea($vars) {
    //store your api id in config option and fetch it.
    $api_id = $vars['configoption2'];
    //create a custom field to store your server id 
    //and fetch it
    if(!empty($vars['customfields']['Server ID'])){
       $server_id = $vars['customfields']['Server ID'];
    } 
    
    
      if (empty($server_id)) {
        $return_array['status'] = 0;
        return array(
            'templatefile' => 'no_serverid',
            'vars' => array(
                'response' => $return_array
            )
        );
      }

      /*
          Call the function getMyServer to get
          server details.
      */
      $result_array = getMyServer($api_id, $server_id);
      return array(
                'templatefile' => 'clientarea',
                'vars' => array(
                    'response' => $result_array
                ),
      );
}

/**
 Let's call the API get_dedicated_list 
**/
function getMyServer($api_id, $server_id) {
    $url = 'https://www.customerpanel.nl/api.php';
    $data = array(
        "api_id" => $api_id,
        "method" => "get_dedicated_list"
    );

    $options = array(
        'CURLOPT_TIMEOUT' => '300',
    );
    $response = curlCall($url, $data, $options);
    $return = array();
    $result_array = json_decode($response);
    if ($result_array->StatusCode == 1) {
        unset($result_array->StatusCode);
        unset($result_array->StatusMessage);
        foreach ($result_array as $k => $value) {
            if ($value->Server_Id == $server_id) {
                $return['Status'] = 1;
                $return['Default_Password'] =
                    $value->Default_Password;
                $return['Main_IP'] = $value->Main_IP;
                $return['IPv6'] = isset($value->IPv6)
                    ? $value->IPv6 : "None";
                $return['Max_Datatraffic'] = 
                    $value->Max_Datatraffic;
                $return['Status'] = $value->Status;
                return $return;
            } else {
                continue;
            }
        }
    }
    $return['Status'] = 0;
    return $return;
}

Create a folder named templates in the path /modules/servers/worldstream.
Within that folder create a file named clientarea.tpl.
Then store the below contents to that file.

<div class="row">
    
    <div class="col-sm-12">
        <strong class="text-center">
               Server Details:
        </strong>
    </div>
</div>

<div class="row">
    
    <div class="col-sm-5 text-right">
        IPv4 Addresses:
    </div>
    <div class="col-sm-7 text-left">
       {$response['Main_IP']}
    </div>

</div>
<div class="row">
    
    <div class="col-sm-5 text-right">
       IPv6 Addresses:
    </div>
    <div class="col-sm-7 text-left">
      {$response['IPv6']}
    </div>
</div>

<div class="row">
    
    <div class="col-sm-5 text-right">
           Max. Datatraffic:
    </div>
    <div class="col-sm-7 text-left">
       {$response['Max_Datatraffic']}GB
    </div>
</div>

<div class="row">
    
    <div class="col-sm-5 text-right">
          Default Password:
    </div>
    <div class="col-sm-7 text-left">
          {$response['Default_Password']}
    </div>
</div>

<div class="row">
    
    <div class="col-sm-5 text-right">
          Status:
    </div>
    <div class="col-sm-7 text-left">
       {$response['Status']}
    </div>
</div>
5 Dec

WHMCS 6 Database Queries

WHMCS6 use Laravel framework’s database component.
So Insert, Select, Update and Delete queries should be written in
Laravel style.

To perform the query, we should include the below code to the php file. You should replace the string path_to_init_folder with actual path to the init.php file within the WHMCS folder.

require "path_to_init_folder/init.php";
use Illuminate\Database\Capsule\Manager as Capsule;

Now let’s see how we can add a row to a table named my_sample_table.
We have two fields to insert. one is key and other one is value.

#Insert a row

$insert_array = array(
      "name" => $_REQUEST['name'],
      "relid" => $_REQUEST['productid']
);

Capsule::table('my_sample_table')
    ->insert(
         $insert_array
);

/*
  If you would like to get unique id of
  last inserted row
*/

$insert_array = array(
   "name" => $_REQUEST['name'],
   "relid" => $_REQUEST['productid']
);
$row_id = Capsule::table('my_sample_table')
    ->insertGetId(
        $insert_array
);

To update a table field, we can write the query as given below.

#Update a row
Capsule::table('tblinvoices')
   ->where('id', '=', $invoice_id)
   ->update(array('status' => 'Paid'));
#Select multiple rows
$data = Capsule::table('tbldomains')
   ->where('domain', '=', 'mydomain.com')
   ->get();
#Select a row
$data = Capsule::table('tbldomains')
   ->where('domain', '=', 'mydomain.com')
   ->first();
#delete  row
Capsule::table('my_sample_table')
    ->where('id', '=', 22)
    ->delete();

Sample Join Query

If we have to join two tables discount_system and tblproductgroups on a condition tblproductgroups.id = discount_system.pg_id, then we can write the join query as written below. For any select query we can just select intrested field’s values. In the below query we are just selecting three field values, they are tblproductgroups.name, discount_system.discount_type and discount_system.id.

    $discount_details = Capsule::table('discount_system')
         ->join(
             'tblproductgroups',
             'tblproductgroups.id', 
                  '=', 'discount_system.pg_id'
         )
         ->select(                             
               'tblproductgroups.name',
               'discount_system.discount_type',
               'discount_system.id'
        )
        ->get();

To know more about the query,
Go to Laravel Query Tutorial

24 Nov

Simplify Commerce Basics Using PHP SDK

Simplify Commerce is a payment gateway brought to you by MasterCard.
They have SDK for most of the programming languages. Here i am going to explain about PHP SDK of the Simplify Commerce. Using Simplify Commerce we can do payments using token. With this approach we don’t have to pass card number and also we are not storing the cc number in the database.

First of all you have to download the SDK and include it in your code as shown below. Link to download the SDK is
https://www.simplify.com/commerce/sdk/php/simplifycommerce-sdk-php-1.6.0.tgz

require_once 'Simplify.php'

In your Simplify account, you will have public key and private key for both live and sandbox. For testing use sandbox keys and when you go live have to use the live keys.

        require_once 'Simplify.php'
        if($is_test_mode){
                $pub_key = $sandbox_pubkey;
                $prive_key $sandbox_privkey;
        } else {
                $pub_key = $live_pubkey;
                $prive_key = $live_privkey;
        }
        try{
                Simplify::$publicKey = $pub_key;
                Simplify::$privateKey = $prive_key;
        }catch (Simplify_ApiException $e) {
                die($e->getMessage());
        }


How to get Simplify token?

You can create a token that can be used only once using the below code. You won’t be able to use the token more than once.

  $card = array(
      'addressState' => $state,
      'expMonth' => $exp_m,
      'expYear' => $exp_y,
      'addressCity' => $city,
      'cvc' => $cvc,
      'number' => $card_num
  );
  try {
        $cardToken =  Simplify_CardToken::createCardToken(
          array(
              'card' => $card 
          )
        );
        $token = $cardToken->id;
  } catch (Simplify_ApiException $e) {
        die( $e->getMessage());
  }

How to create a customer using the simplify token?
You can create a customer using the below code. You can see that we are using the token created. The below API will return a customer id
and we can use that for multiple transactions. Even though the token id can be used only once, the customer id can be used many times.

  
try {
    $data = array(
        'token' => $token,
        'email' => $email,
        'name' =>  $name,
        'reference' => $reference
    );
    $customer = Simplify_Customer::createCustomer(
        $data
    );
    $customer_id = $customer->id;
}catch (Simplify_ApiException $e){
    die( $e->getMessage());
}

How to make a payment using the simplify customer id?
The sample payment code is below. One important point to remember is, if you want to make a payment of $10, then we have to pass amount as
10 * 100, not 10. So for $10, we have pass amount as 1000 and currency as USD. We have to pass the $customer_id we generated in the previous step. We can pass anything as reference. Normally we pass order id or invoice id as reference.

  
$payment_data = array(
    'amount' =>  $amount * 100,
    'customer' => $customer_id,
    'description' => $description,
    'currency' => 'USD',
    'reference' => $invoice_id
);
try {
   $payment = Simplify_Payment::createPayment(
       $payment_data
   );
} catch (Simplify_ApiException $e) {
   die( $e->getMessage());
}

How to add a monthly subscription for the simplify customer id?
You can add a recurring payment from a customer as below.
amount and customer id, we already discussed in the above step.
If the customer will pay monthly, we have to set frequency as MONTHLY and frequencyPeriod as 1. If the customer will pay bimonthly then frequency should be MONTHLY and frequencyPeriod should be 2.
For semiannual payments set frequency=MONTHLY, frequencyPeriod=6.

Possible values for frequency are DAILY, WEEKLY, MONTHLY and YEARLY.

  
$subscription_data = array(
   'amount' =>  $amount * 100,
   'customer' => $customer_id,
   'frequency' => $frequency,
   'name' => $name,
   'frequencyPeriod' => $frequencyPeriod
);
try {
   $subscription  = Simplify_Subscription::createSubscription(
      $subscription_data
   );
} catch (Simplify_ApiException $e) {
   die( $e->getMessage());
}

Developed a WHMCS payment gateway module using the Simplify Commerce PHP SDK. Please contact me if you need it.