Creating a PrestaShop Payment Module

Introduction

Having been looking for a Open Source shopping cart solution I stumbled on prestashop. It is a good solution which seemed to be what I was looking for. Like a lot of modern carts and CMSes it uses system of plug-in modules to add extra functionality that doesn't come as standard. The only slight problem with this module system, is how do you create a plug-in module!? It isn't exactly obvious.
This guide will walk you through creating a simple off line payments module. By "off-line" payments I mean the module will allow the store to capture a customers payment card details during a purchase then store the details in the database for the store owner to process through their payment machine or checkout at a later time.
NOTE! Please bear in mind I am not advocating this as a payment method for live stores. There are security issues surrounding gathering payment details in this manner. I am using it as an example as it is a simple enough demonstration of creating payment module.

Intended Audience

Any one with basic PHP object orientated programming and HTML skills who would like to get an overview on how to create a prestashop module. You should be familiar with installing prestashop.

Development Environment

I created the module using open source (free) software. I used the Eclipse PHP editor PHP Development Tools (PDT) which is nice and easy to use. I also use WAMP which is a webserver and MYSQL database server package designed for easy setup. You can use any thing you feel comfortable with - I use them because they are good and free. Since I am using the WAMP server my prestashops code will be located here C:\wamp\www\prestashop and my url is http://localhost\prestashop

Getting Started

I will assume you have installed prestashop on your development computer and you are ready to begin with the basics.

Name your Module

Your module can be called anything you like. My module will be called offlinepayments as that is what is will be doing - taking off line payments.
So create a directory under your prestahop C:\wamp\www\prestashop\modules\offlinepayments see below for an example
Prestashop module

Create your module's "class" .php file

Prestashop modules must have one php file which contains the Class definition. This class definition contains the core functions of your module.
The file needs to be given the same name as your module so in my case it must called offlinepayments.php and it needs to live in the root of the modules directory.

Create the skeleton Class Entry in the file offlinepayments.php

Now we have the class file we need to create the offlinepayments class. This class is core to the module within this file. It controls the way the module works. At this stage the class is very basic it will only have enough functionality to list itself as an available module in your shop.
Open the file offlinepayments.php and paste the following code into it:

<?php

class offlinecardpayment extends PaymentModule  //this declares the class and specifies it will extend the standard payment module
{
   
    private
$_html = '';
    private
$_postErrors = array();
   
    function
__construct()
    {
       
$this->name = 'offlinecardpayment';
       
$this->tab = 'Payment';
       
$this->version = 1;

       
parent::__construct(); // The parent construct is required for translations

       
$this->page = basename(__FILE__, '.php');
       
$this->displayName = $this->l('Offline Payments Module');
       
$this->description = $this->l('Take Payment Card details for offline processing');

}
}
?>

The first part of the code

class offlinecardpayment extends PaymentModule

names the class - for it to work the class should be the same as the module name so I have to call mine offlinepayment

The next significant part of the module is the _construct function

function __construct()
{
$this->name = 'offlinecardpayment';
$this->tab = 'Payment';
$this->version = 1;

parent::__construct(); // The parent construct is required for translations

$this->page = basename(__FILE__, '.php');
$this->displayName = $this->l('Offline Payments Module');
$this->description = $this->l('Take Payment Card details for offline processing');

}

This function sets the modules name, in this case offlinecardpayment. Keep it the same as the folder and the class name.

$this->name = 'offlinecardpayment';

It also sets the area within the admin section that the module should appear in, in this case the payment. You can put your own here and prestashop will create new section. Ours is a payment module so it makes sense to keep it with the other payment modules.

$this->tab = 'Payment';

and sets a version of the module - mine is version 1

$this->version = 1;

Further down the page it gives the module a friendly name so the administrator can identify what it is easily.

$this->displayName = $this->l('Offline Payments Module');

and gives the module a simple brief description. This is displayed in the module section of the admin section of the site

$this->description = $this->l('Take Payment Card details for offline processing');

See the module listed in the admin section

Save the file and then open the administration section of your test shop.

Now navigate to the modules tab and scroll down the payment section . If all has gone well you should see the new module listed.

We have now well and truly got started as the payment module is taking shape. see the attached zip file gettingstarted.zip for the progress so far.


Making the module's install routine

Building the standard install() function

Prestashop modules have an install link. This link needs to be clicked to install the module to make it actually function within the store. NOTE! Basically, what although the module is listed in your store as an available module, you need to to click install to actually activate the module for it to be active in your store.

See below:

When that install link is clicked a function within the class named "install" is called. So for our module to be installed correctly we need to create that function within our offlinecardpayment class.

This is what our install function will look like:

public function install()
{
if (!parent::install()
OR !$this->createPaymentcardtbl() //calls function to create payment card table
            OR !$this->registerHook('invoice')
OR !$this->registerHook('payment')
OR !$this->registerHook('paymentReturn'))
return false;
return true;
}

Here is a breakdown of what the function does.
OR !$this->createPaymentcardtbl()

This line will call a function called createPaymentcardtbl that creates an additional table in the prestashop stores database for storing the payment card details. NOTE! At present that function does not exist so we are going to create that function further in this article.

OR !$this->registerHook('invoice')

This line tells prestashop that we want this module to register itself with the invoice hook. This will allow us to create functionality to display the payment card details against each order. If you don't know what a hook is then see appendices for details.

OR !$this->registerHook('payment')

This line tells prestashop that we want this module to register itself with the payment "hook". It will run the function hookPayment.

OR !$this->registerHook('paymentReturn')

This line tells prestashop that we want this module to register itself with the payment return hook. This is the hook which allow us to add functionality to the part after the payment is complete. Something like displaying a "thanks for ordering message" for example.

Creating a payment card database table

Now the install function is complete. Next we build the createPaymentcardtbl function.

This function creates a table within the prestashop database to hold the payment card details related to the each order taken. It is a very basic table which is designed to hold the following information

id_payment = primary key
id_order = holds the order information to associate the payment details with the order this was taken with.
cardholder_name = hold the card holders name
cardnumber = holds the card number details
expiry date = the holds cards expiry date.

function createPaymentcardtbl()
{
/**Function called by install -
* creates the "order_paymentcard" table required for storing payment card details
     * Column Descriptions: id_payment the primary key.
     * id order: Stores the order number associated with this payment card
     * cardholder_name: Stores the card holder name
     * cardnumber: Stores the card number
     * expiry date: Stores date the card expires
     */
       
                    $db = Db::getInstance();
            $query = "CREATE TABLE `"._DB_PREFIX_."order_paymentcard` (
`id_payment` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`id_order` INT NOT NULL ,
`cardholdername` TEXT NOT NULL ,
`cardnumber` TEXT NOT NULL
) ENGINE = MYISAM ";
           
        $db->Execute($query);

return true;
}

Testing the Install

Click the "install" button next to the Offline Payments.

If successful you should see a message displayed that says "Module installed successfully"

We can also see within our prestastore database that the payment module is now listed as a module. Below is an example (using phpmyadmin) which shows that our module has been added to the list of prestashop modules - we can verify that by the fact it has been added to the prestashop modules table.

We can also check to see if our module has been registered with the hooks by displaying the hook_module table.

If the prestashop module is listed in the module table and within the hook_module table it's a good sign our install function is working well.


Displaying our module as a payment option in the checkout

Since we have a registered our module with the payment hook a function which tells the payment hook to do something. The hook payment expects a function called hookPayment by default so thats what we need to create. This is where the hook payment allows us to have our module appear (image below)

Create the hookPayment() function

All we need our hook payment screen to do is present a very simple template called "payment.tpl" that lets our users select the offline option and when selected take them to the payment form called "payment.php" (we need to create payment.php)

Here is our hookPayment function. Make sure you create it within your offlinecardpayment class.

/**
* hookPayment($params)
* Called in Front Office at Payment Screen - displays user this module as payment option
*/
function hookPayment($params)
{
global $smarty;

$smarty->assign(array(
            'this_path' => $this->_path,
            'this_path_ssl' => Configuration::get('PS_FO_PROTOCOL').$_SERVER['HTTP_HOST'].__PS_BASE_URI__."modules/{$this->name}/"));

return $this->display(__FILE__, 'payment.tpl');
}

The first line

global $smarty;

creates a $smarty template

$smarty->assign(array( 'this_path' => $this->_path,
'this_path_ssl' => Configuration::get('PS_FO_PROTOCOL').$_SERVER['HTTP_HOST'].__PS_BASE_URI__."modules/{$this->name}/"));

sends the url information to the new smarty template

and finally calls the template

return $this->display(__FILE__, 'payment.tpl');

Creating the payment.tpl smarty template file

<p class="payment_module">

<a href="{$this_path}payment.php" title="{l s='Pay with your Credit or Debit Card' mod='offlinecardpayment'}">

<img src="{$this_path}cards.png" alt="{l s='Pay with your Credit or Debit Card' mod='offlinecardpayment'}" />

{l s='Pay with a credit card' mod='offlinecardpayment'}

</a>

</p>

This is a simple template file that will display the Offline Payment Module as a payment option. The most important part of the template is the line:

<a href="{$this_path}payment.php" title="{l s='Pay with your Credit or Debit Card' mod='offlinecardpayment'}">

this calls the payment.php file which will take our customer's payment card details.

We also create an image file called card.png This is optional the logo that will be displayed as part of our payment option.
And it is called here in in our payment.tpl file

<img src="{$this_path}cards.png" alt="{l s='Pay with your Credit or Debit Card' mod='offlinecardpayment'}" />

So with this template file we should now see out payment option being displayed as an option at the payment stage of the checkout process see below:

Now our payment option is availalbe to our user's we need to create the actual page that is displayed when the payment option is clicked on. As part of this process a form will be displayed for the user to enter their card details and then store them in the database for later viewing.

If you recall it was payment.php So that is the name of the next file we need to create in our payment modules directory.

Okay, so create a new file called payment.php and place the following code into it:

<?php

include(dirname(__FILE__).'/../../config/config.inc.php');
include(
dirname(__FILE__).'/../../header.php');
include(
dirname(__FILE__).'/offlinecardpayment.php');

if (!
$cookie->isLogged())
   
Tools::redirect('authentication.php?back=order.php');
   
$offlinecardpayment = new offlinecardpayment();
echo
$offlinecardpayment->execPayment($cart);

include_once(
dirname(__FILE__).'/../../footer.php');

?>

This file will call some files it requires for operation -
include(dirname(__FILE__).'/../../config/config.inc.php');
include(dirname(__FILE__).'/../../header.php');
include(dirname(__FILE__).'/offlinecardpayment.php');

the first config.inc.php is prestashop's configuration file, the second is the header used build the store and finally we include our offlinecardpayment.php so we have access to our module's class file.

Next we ensure the user is logged in if they aren't we direct them to an authentication

if (!$cookie->isLogged())
    Tools::redirect('authentication.php?back=order.php');

We then call this function
$offlinecardpayment = new offlinecardpayment();
echo $offlinecardpayment->execPayment($cart);

Which calls our offlinecardpayment class and then calls the execPayment function
which is a method/function of our offlinecardpayment. At this stage the function doesn't exist so we need to create it.

Open the offlinecardpayment.php file and within it paste the following code:

/*
* This function will check display the card details form payment_execution.tpl
* It will check if the submit button on the form has been pressed and submit the card details to the database
*/

public function execPayment($cart)
{
if (!$this->active)
return ;
  
global $cookie, $smarty;

$smarty->assign(array(
'this_path' => $this->_path,
'this_path_ssl' => (Configuration::get('PS_SSL_ENABLED') ? 'https://' : 'http://').htmlspecialchars($_SERVER['HTTP_HOST'], ENT_COMPAT, 'UTF-8').__PS_BASE_URI__.'modules/'.$this->name.'/'
));

return $this->display(__FILE__, 'payment_execution.tpl');
}

This is the paymentExec function we have added which will begin the payment process and if you look at this line of code:

return $this->display(__FILE__, 'payment_execution.tpl');

you can see it is calling the payment_execution.tpl which is the "form" that your user will enter their payment detail in. And at this stage it doesn't exist. So let's get on and make this file.

Displaying the Card Payment Entry details form

Create a new file called payment_execution.tpl. Open it in and a paste the following smarty markup code

capture name=path}{l s='Shipping'}{/capture}
{include file=$tpl_dir./breadcrumb.tpl}
<h2>{l s='Order summary' mod='offlinecardpayment'}</h2>

{assign var='current_step' value='payment'}
{include file=$tpl_dir./order-steps.tpl}

<h3>{l s='Payment Card Details' mod='offlinecardpayment'}</h3>

<form action="{$this_path_ssl}validation.php" method="post">
<table border="0">

<tr>

<td>

{l s='Name on Card:' mod='offlinecardpayment'}
</td>
<td>
<input type="text" name="cardholderName" id="cardholderName" value="{$cardholderName}"/>

</td>

</tr>

<tr>
<td>
{l s='Credit Card Number:' mod='offlinecardpayment'}
</td>
<td>
<input type="text" name="cardNumber" id="cardNumber" value="{$cardNumber}" />
</td>
</tr>

</table>

<p class="cart_navigation">
<a href="{$base_dir_ssl}order.php?step=3" class="button_large">{l s='Other payment methods' mod='offlinecardpayment'}</a>
<input type="submit" name="paymentSubmit" value="{l s='Submit Order' mod='creditcard'}" class="exclusive_large" />
</p>
</form>

With this template our payment option will now display a payment card entry form when selected. Its a fairly simple affair but it is enough to collect the username and credit card number. Of course in real life you would most likely want to collect other items such as expiry date and valid from date and perhaps card type. But for now this is an effective enough example.

This form has its action set to validation.php. validation.php is the code that will be taking the details the user enters and adds it to our order.

<form action="{$this_path_ssl}validation.php" method="post">

As you probably guessed we need a validation.php for us to post to so lets go ahead and create it.
Create a file called validation.php in your modules directory and paste the following code into it.

<?php

include(dirname(__FILE__).'/../../config/config.inc.php');
include(
dirname(__FILE__).'/../../header.php');
include(
dirname(__FILE__).'/offlinecardpayment.php');
           

/* Gather submitted payment card details */
$cardholderName     = $_POST['cardholderName'];
$cardNumber         = $_POST['cardNumber'];


$currency = new Currency(intval(isset($_POST['currency_payement']) ? $_POST['currency_payement'] : $cookie->id_currency));
$total = floatval(number_format($cart->getOrderTotal(true, 3), 2, '.', ''));

$offlinecardpayment = new offlinecardpayment();
$offlinecardpayment->validateOrder($cart->id_PS_OS_PREPARATION_, $total, $offlinecardpayment->displayName, NULL, NULL, $currency->id);
$order = new Order($offlinecardpayment->currentOrder);
$offlinecardpayment->writePaymentcarddetails($order->id, $cardholderName, $cardNumber);
   
Tools::redirectLink(__PS_BASE_URI__.'order-confirmation.php?id_cart='.$cart->id.'&id_module='.$offlinecardpayment->id.'&id_order='.$offlinecardpayment->currentOrder.'&key='.$order->secure_key);

?>

This part of the validation calls a function to write the information entered by our user to the database in the paymentcard table.

$offlinecardpayment = new offlinecardpayment();
$offlinecardpayment->validateOrder($cart->id,  _PS_OS_PREPARATION_, $total, $offlinecardpayment->displayName, NULL, NULL, $currency->id);
$order = new Order($offlinecardpayment->currentOrder);
$offlinecardpayment->writePaymentcarddetails($order->id, $cardholderName, $cardNumber);

With this information written to the database we have successfully captured our users payment information for off line processing. Which is what we wanted.

Of course we need to add it to our offlinepayments core class. So please add that now

     *  Call this function to save the payment card details to the payment card table
     */

function writePaymentcarddetails($id_order, $cardholderName, $cardNumber)
{
$db = Db::getInstance();
$result = $db->Execute('
INSERT INTO `ps_order_paymentcard`
( `id_order`, `cardholdername`,`cardnumber`)
VALUES
("'.intval($id_order).'","'.$cardholderName.'","'.$cardNumber.'")');
return;
}

Thats all Folks

Well that's almost done, there is just a bit more work though - now that our users can enter their payment details we need to be able to retrieve them in our administration panel.

Displaying the order info in the admin panel

We need to hook into the invoice part of the administration panel. We have already declared we want to access the invoice hook earlier in our install() function

OR !$this->registerHook('invoice')

This tells prestashop we want to hook into the invoice and that it should look for a standard invoice function called hookInvoice

So let's go ahead and add this function to our core offlinepayment class

function hookInvoice($params)
{
$id_order = $params['id_order'];

global $smarty;
$paymentCarddetails = $this->readPaymentcarddetails($id_order);

$smarty->assign(array(
    'cardHoldername'          => $paymentCarddetails['cardholdername'],
'cardNumber'         => $paymentCarddetails['cardnumber'],
'id_order' => $id_order,
'this_page' => $_SERVER['REQUEST_URI'],
'this_path' => $this->_path,
            'this_path_ssl' => Configuration::get('PS_FO_PROTOCOL').$_SERVER['HTTP_HOST'].__PS_BASE_URI__."modules/{$this->name}/"));
return $this->display(__FILE__, 'invoice_block.tpl');

}

This function is quite simple it calls another function called readPaymentcarddetails() which retrieves the appropriate payment details from the database

    /*
     *  Call this function to read the payment card details from the payment card table
     */
function readPaymentcarddetails($id_order)
{
$db = Db::getInstance();
$result = $db->ExecuteS('
SELECT * FROM `ps_order_paymentcard`
WHERE `id_order` ="'.intval($id_order).'";');
return $result[0];
}

and then displays them in the invoice part of the administration panel. It does this by calling a template file called invoice_block.tpl

So go ahead and create a that file in your modules directory. And put the following code into it.

<fieldset style="width: 400px;">

<legend>

     {l s='Payment Card Information' mod='offlinecardpayment'}

</legend>

<div id="info" border: solid red 1px;">
<table>
<tr><td>Card Holder Name:</td> <td>{$cardHoldername}</td></tr>
<tr><td>Card Number:</td> <td>{$cardNumber}</td></tr>
</table>
</div>

</fieldset>

Now payment details are displayed in the invoice for the order.

Prestashop Invoice

That's that we now have create a module which will take a users payment card details during the prestashop order process. We have also enabled our administrator to retrieve them in the invoice for processing. I hope this tutorial is useful to presta people out there - if it gives you a an idea and a bit of headstart for your own module development efforts then it has achieved it's goal.

Extra HomeWork

Well we have a working module but it is a little basic. We could do with adding some extra bits and pieces. For example we don't have any validation on the form - to make sure that the user doesn't enter a load of nonsense insead of valid card details. Also we need to create a paymentReturn function that will give our user some feedback to let them know their payment had been successful. Also storing creditcard numbers in plain text is not the done thing -we should encrypt first at a minimum.

Files

These are the files for each of the chapters. Each file contains the files and functions of the named chapter. The finished module is offlinecardpayment.zip

AttachmentSize
gettingstarted.zip566 bytes
Making_the_modules_install_routine.zip1.08 KB
offlinecardpayment.zip21.08 KB

Duplicate payment module

Hi this is a very helpful tutorial for a beginners like me using prestashop.
By the way I have a problem on my test site, I duplicated the bankwire module folder and rename it to 123bank module (only those who have 123bank account can use this module for payment). All works fine but not on the last step of ordering products. After clicking I confirm my order I got this error : Hack attempt (OrderHistory -> id_order_state is empty) . Can you help me resolve this problem? Thanks.

500 Internal Server Error on payment.php

Hi!
I'd like to start by thanking you for this excellent tutorial. Clear and concise. And have to make a few custom payment modules but I'm running into a certain number of hick-ups.
I started out by duplicating the cheque payment module entirely, and activated it in my back-office. Everthing fine so far, except when I try to use the payment module, only part of my theme renders properly and I get a 500 internal server error on payment.php. The permissions are all set correctly on the server. And the cheque module works fine. So I'm having a hard-time figuring out why this is the case. So was wondering if you ran into a similar problem.

Regards,

Nighthawk.

500 errors

Hello Nighthawk,
As you probably know 500 is a server error. This often means apache or php didn't like your file or your code. Do you have access to the apache access or error logs? These are often useful for working out the problem. Sorry I can't help more but the apache logs are really useful for this kind of problem.

an error occurred while processing this directive

on local host - all is fine
on my website - i get an error occurred while processing this directive
when selecting the payment method.

can anyone help?

localhost fine?

That suggests that your code is working okay if it works locally. Although it is worth checking you haven't hard coded a local IP in your code. Otherwise can you check your apache logs if they are available? I use them a lot for troubleshooting annoying errors like the one you describe

table creation

Hi
Great tutorial!!! exactly what i needed!
i'm even making it thinner, to give ability to give your credit card over the phone.
but i have a problem. i can't see the table that is being created!
the install pass good, but still, i don't see an empty table.
have ideas?

thanks

all is fine. thanks

my mistake

Thanks

Thanks

Hook is..

Hello,

A hook in the case of prestashop and programming in general, is a way of altering the functionality of a program without actually changing the original or core code. For example if you want to add a module that was going to appear in the the right column of prestashop you would connect to the hook called rightColumn. So the code contained in your homegrown module would appear in the right column. It is a bit confusing at first and it seems more complex than it actually is.

hook

Could someone explain me what hook is?

Good Tutorial

Good tutorial but why you focus on payment module and not general module

thanks

thanks for the info is so nice to see your tutorial.
in other hand you have a problem in the link of the files that fail because need to remove the
from the link

best regards

new module for check payment

I'd like to redo the Cheque module and simply respell everything "Check." I was wondering if creating a new module using your instructions would accomplish that? Check payments are handled a little differently and the instrcutions are different, so I wasn;t sure using the offiline credit card payment module would be an ok place to start.

I'm looking to do this because I cannot get the translations to "stick" in the Back Office every time I try to rename Cheque>Check. (I think it has something to do with my hosting provider, but I can't nail down the issue.)

I also tried changing all the references to Cheque in the original module, but that didn't work either. Just trying to figure out some options.

Cheque 2 Check

Hello,

If all you need to do is simply rename it I think you could copy the module and rename the folder cheque to check. Change all references to cheque to check in the files. Then uninstall the original cheque module and install your new cheque module. It will work just fine except it is now called Check. I did the same with shopping cart module becasue I wanted to alter it but did not want to hack the original module. I hope his makes sense and is useful

Cheers

Dave

Powered by Drupal, an open source content management system