PrestaShop 1.7 had its first stable release back in 2016. Since it was a major release, most modules and themes built for the 1.6 version won’t work with 1.7, so many users refused to upgrade their stores. In December 2018 the core team made available for download a new minor release, 1.7.5.0, with many improvements, bug fixes and new features based largely on feedback from the community.
If you haven’t upgraded your store yet, it may be a great time to do so. The next minor release, 1.7.6.0, will be out next spring
On this tutorial, I will show you how to create a new custom payment module that will work for all 1.7 versions (I am using PrestaShop 1.7.5.0).
1 – Declaring a new module
All PrestaShop modules are stored under its modules/ directory. Whenever want to create a new module, all we have to do is to add a new subdirectory and a PHP file with the same name. This PHP file will contain our module declaration and is also responsible for displaying configuration options on the admin and deliver content for the frontend and backend hooks.
- Create a subdirectory named prestapay inside of modules/.
- Create a new file named prestapay.php inside of the newly created directory.
- Copy the content below and paste it on prestapay.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
<?php /** * PrestaPay - A Sample Payment Module for PrestaShop 1.7 * * This file is the declaration of the module. * * @author Andresa Martins <[email protected]> * @license https://opensource.org/licenses/afl-3.0.php */ if (!defined('_PS_VERSION_')) { exit; } class PrestaPay extends PaymentModule { private $_html = ''; private $_postErrors = array(); public $address; /** * PrestaPay constructor. * * Set the information about this module */ public function __construct() { $this->name = 'prestapay'; $this->tab = 'payments_gateways'; $this->version = '1.0'; $this->author = 'Andresa Martins'; $this->controllers = array('payment', 'validation'); $this->currencies = true; $this->currencies_mode = 'checkbox'; $this->bootstrap = true; $this->displayName = 'PrestaPay'; $this->description = 'Sample Payment module developed for learning purposes.'; $this->confirmUninstall = 'Are you sure you want to uninstall this module?'; $this->ps_versions_compliancy = array('min' => '1.7.0', 'max' => _PS_VERSION_); parent::__construct(); } /** * Install this module and register the following Hooks: * * @return bool */ public function install() { return parent::install(); } /** * Uninstall this module and remove it from all hooks * * @return bool */ public function uninstall() { return parent::uninstall(); } /** * Returns a string containing the HTML necessary to * generate a configuration screen on the admin * * @return string */ public function getContent() { return $this->_html; } } |
This file consists of a class with the same name as our module (PrestaPay), which extends the class PaymentModule. For a basic module declaration, we only need four functions: __construct() , install() , uninstall() and getContent().
__construct()
Contains the declaration data of our module. This information will be displayed in the backend in all places where the module is listed.
- $this->name — A string representing the name of the module. It should be unique on your store and it will be used in the module URL.
- $this->tab — Category of this module. For a full list, see the file prestashop/controllers/admin/AdminModulesController.php
- $this->version — Version of your module
- $this->author — The author’s name
- $this->controllers — Array containing a declaration of all controllers of this module. We will create the controller files later.
- $this->currencies — A bool of whether this module can be enabled/disabled for specific currencies.
- $this->currencies_mode — Defines how the currency configuration will be displayed on the backend (checkbox or radio).
- $this->bootstrap — A bool of whether this module uses bootstrapped controllers.
- $this->displayName — The module name
- $this->description — The module description
- $this->confirmUninstall — A message to be displayed when the user tries to uninstall this module
- $this->ps_versions_compliancy — An Array containing the min/max PrestaShop version supported by this module.
install()
This function is executed whenever the module is installed. We will use this function to register all the hooks necessary for our module, but it can also be used to perform database operations (e.g., create a new database table, add a field to an existing table).
uninstall()
Similar to install(), this function will run whenever a module is uninstalled.
getContent()
Returns the HTML necessary to create a configuration page in the backend. We will work on this function later on this tutorial.
2 – Adding an icon to the module
We also need to add an icon to our module. It is not required, but it is a good practice to give an identity to the module. All you have to do is to add a logo.png file to the main directory of your module. The recommended logo size is 128 x 128px. It is also recommended to use an image with a transparent background.
I got the piggy bank icon from Flat Icon.
3 – Installing the module
Now that we have the basic structure for our module, we can install it on PrestaShop. In the backend, go to Modules > Module Catalog. Use the search input to find our module (PrestaPay). Our new module should be displayed as shown below:
Click on Install . Now that the module has been installed, you should be able to see it on Modules > Module Manager .
4 – Working with hooks
Our module has been installed but no information is being displayed on the frontend yet. That happens because we did not register our module with the appropriated hooks.
A “hook” is a way to associate our code with a specific PrestaShop event. Most of the hooks are used to display information on a page, but there are also hooks that are triggered before or after a certain action. You can see a full list of hooks here.
For this tutorial, we will register and implement the following hooks:
- paymentOptions — Displays this module as a payment option during the checkout
- paymentReturn — Displays a given HTML code on the order confirmation page
Registering the new hooks is very easy – we only need to add a couple of lines of code to the install() function:
1 2 3 4 5 6 7 8 9 10 11 |
/** * Install this module and register the following Hooks: * * @return bool */ public function install() { return parent::install() && $this->registerHook('paymentOptions') && $this->registerHook('paymentReturn'); } |
This is telling PrestaShop that we want our custom content to be displayed in the hooks paymentOptions and paymentReturn. PrestaShop will then register both hooks and, whenever it is necessary to display one of them, it will look for a function hook<nameOfTheHook>() in the main class of our module. That function should return the content to be displayed in that hook.
The following function will tell PrestaShop to display our module as a payment option during the checkout step:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
/** * Display this module as a payment option during the checkout * * @param array $params * @return array|void */ public function hookPaymentOptions($params) { /* * Verify if this module is active */ if (!$this->active) { return; } /** * Form action URL. The form data will be sent to the * validation controller when the user finishes * the order process. */ $formAction = $this->context->link->getModuleLink($this->name, 'validation', array(), true); /** * Assign the url form action to the template var $action */ $this->smarty->assign(['action' => $formAction]); /** * Load form template to be displayed in the checkout step */ $paymentForm = $this->fetch('module:prestapay/views/templates/hook/payment_options.tpl'); /** * Create a PaymentOption object containing the necessary data * to display this module in the checkout */ $newOption = new PrestaShop\PrestaShop\Core\Payment\PaymentOption; $newOption->setModuleName($this->displayName) ->setCallToActionText($this->displayName) ->setAction($formAction) ->setForm($paymentForm); $payment_options = array( $newOption ); return $payment_options; } |
Note that in this function we are also loading a template file, payment_options.tpl, so we should create it as well:
1 2 3 4 5 6 7 8 9 10 11 12 |
{* * PrestaPay - A Sample Payment Module for PrestaShop 1.7 * * Form to be displayed in the payment step * * @license https://opensource.org/licenses/afl-3.0.php *} <form method="post" action="{$action}"> <P>This is the checkout template of our payment module.</P> <P>We can add payment instructions and form fields to it.</P> </form> |
Now we have everything we need to display our module in the payment step, but it is necessary to reset our module in the dashboard. Resetting the module will run uninstall() and install() again, which will register the hooks we just created.
Go to Modules > Module Manager. Find the module PrestaPay and click on Reset :
Visit your store and place an order. In the payment step, you should be able to see an option to pay using PrestaPay:
If you try to place an order now, it is not going to work. That happens because we did not create the Validation controller yet (we will talk about it later). Before we can proceed to that step, let’s implement the paymentReturn hook:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
/** * Display a message in the paymentReturn hook * * @param array $params * @return string */ public function hookPaymentReturn($params) { /** * Verify if this module is enabled */ if (!$this->active) { return; } return $this->fetch('module:prestapay/views/templates/hook/payment_return.tpl'); } |
1 2 3 4 5 6 7 8 9 10 11 |
{* * PrestaPay - A Sample Payment Module for PrestaShop 1.7 * * HTML to be displayed in the order confirmation page * * @license https://opensource.org/licenses/afl-3.0.php *} <h1>Congratulations!</h1> <P>You have just completed an order using PrestaPay.</P> |
We are done with the hooks for now, but before we can place an order, we need to create a Validation Controller.
5 – Creating a validation controller
In the context of a payment module, a validation controller is where the payment is processed and the cart object is converted into an actual order and inserted into the database. This controller is also the place where you can:
- Connect to a remote API and process the payment
- Update the order status
- Send order emails to the customers or admins
- Calculate discounts
- Insert/update information in the database
I’ve added comments to each part of the code so you can understand everything that PrestaShop is doing:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
<?php /** * PrestaPay - A Sample Payment Module for PrestaShop 1.7 * * Order Validation Controller * * @author Andresa Martins <[email protected]> * @license https://opensource.org/licenses/afl-3.0.php */ class PrestapayValidationModuleFrontController extends ModuleFrontController { /** * Processa os dados enviados pelo formulário de pagamento */ public function postProcess() { /** * Get current cart object from session */ $cart = $this->context->cart; $authorized = false; /** * Verify if this module is enabled and if the cart has * a valid customer, delivery address and invoice address */ if (!$this->module->active || $cart->id_customer == 0 || $cart->id_address_delivery == 0 || $cart->id_address_invoice == 0) { Tools::redirect('index.php?controller=order&step=1'); } /** * Verify if this payment module is authorized */ foreach (Module::getPaymentModules() as $module) { if ($module['name'] == 'prestapay') { $authorized = true; break; } } if (!$authorized) { die($this->l('This payment method is not available.')); } /** @var CustomerCore $customer */ $customer = new Customer($cart->id_customer); /** * Check if this is a vlaid customer account */ if (!Validate::isLoadedObject($customer)) { Tools::redirect('index.php?controller=order&step=1'); } /** * Place the order */ $this->module->validateOrder( (int) $this->context->cart->id, Configuration::get('PS_OS_PAYMENT'), (float) $this->context->cart->getOrderTotal(true, Cart::BOTH), $this->module->displayName, null, null, (int) $this->context->currency->id, false, $customer->secure_key ); /** * Redirect the customer to the order confirmation page */ Tools::redirect('index.php?controller=order-confirmation&id_cart='.(int)$cart->id.'&id_module='.(int)$this->module->id.'&id_order='.$this->module->currentOrder.'&key='.$customer->secure_key); } } |
Note: if you are going to connect to a remote API to process the payment, it is important to do it after the order is placed.
We can now proceed to the frontend and place our first order. If everything works fine, you should be able to see the message we previously created for the paymentReturn hook:
Note: there is another hook under the “Your order is confirmed” message. That hook is named orderConfirmation.
In the next part of this tutorial, you will learn how to manipulate form fields in the payment step, create configuration fields for our module in the dashboard and send a custom e-mail to the customers.