English (United Kingdom)

Payment Plugins

This documentation explains you how to create custom payment plugins to extend the functions of the Vik plugins and collect payments through your own bank. To know how to install and activate it, please read this article (you should skip the 1st step): https://vikwp.com/support/knowledge-base/payment-plugins/how-to-download-and-install-a-payment-plugin

This should be the structure of your payment plugin:

Installation Payment Files

The installation files are two: the "Main installation" file, and the file "utils.php".

Main Installation file

This file is needed during the installation phase. It defines the properties of the plugin, like the name, the version or the author, and it attaches the payment plugin to the hooks of the component; by this way, the payment plugin will be recognized by the Vik Plugin as part of it. The file must be named vik*paymentname*.php (all in lowercase: in this case, the name should be "vikmypay.php") You can see an example below:

<?php
/*
Plugin Name:  VikMyPay
Description:  MyPay integration to collect payments through the Vik plugins
Version:      1.0.0
Author:       E4J s.r.l.
Author URI:   https://vikwp.com
License:      GPL2
License URI:  https://www.gnu.org/licenses/gpl-2.0.html
Text Domain:  vikmypay
Domain Path:  /languages
*/

// No direct access
defined('ABSPATH') or die('No script kiddies please!');

// require utils functions
require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . 'utils.php';
define('VIKMYPAY_LANG', basename(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'languages');
 
define('VIKMYPAYVERSION', '1.0.0');

add_action('init', function() {
    JFactory::getLanguage()->load('vikmypay', VIKMYPAY_LANG);
});

/**
 * VIKRESTAURANTS HOOKS
 */

/**
 * Pushes the mypay gateway within the supported payments of VikRentCar plugin.
 *
 * @param   array   $drivers  The current list of supported drivers.
 *
 * @return  array   The updated drivers list.
 */
add_filter('get_supported_payments_vikrestaurants', function($drivers)
{
    $driver = vikmypay_get_payment_path('vikrestaurants');

    // make sure the driver exists
    if ($driver)
    {
        $drivers[] = $driver;
    }

    return $drivers;
});

/**
 * Loads mypay payment handler when dispatched by Vikrestaurants.
 *
 * @param   array   &$drivers   A list of payment classnames.
 * @param   string  $payment    The name of the invoked payment.
 *
 * @return  void
 *
 * @see     JPayment
 */
add_action('load_payment_gateway_vikrestaurants', function(&$drivers, $payment)
{
    // make sure the classname hasn't been generated yet by a different hook
    // and the request payment matches 'mypay' string
    if ($payment == 'mypay')
    {
        $classname = vikmypay_load_payment('vikrestaurants');

        if ($classname)
        {
            $drivers[] = $classname;
        }
    }
}, 10, 2);

/**
 * VIKRENTITEMS HOOKS
 */

/**
 * Pushes the mypay gateway within the supported payments of VikRentCar plugin.
 *
 * @param   array   $drivers  The current list of supported drivers.
 *
 * @return  array   The updated drivers list.
 */
add_filter('get_supported_payments_vikrentitems', function($drivers)
{
    $driver = vikmypay_get_payment_path('vikrentitems');

    // make sure the driver exists
    if ($driver)
    {
        $drivers[] = $driver;
    }

    return $drivers;
});

/**
 * Loads mypay payment handler when dispatched by vikrentitems.
 *
 * @param   array   &$drivers   A list of payment classnames.
 * @param   string  $payment    The name of the invoked payment.
 *
 * @return  void
 *
 * @see     JPayment
 */
add_action('load_payment_gateway_vikrentitems', function(&$drivers, $payment)
{
    // make sure the classname hasn't been generated yet by a different hook
    // and the request payment matches 'mypay' string
    if ($payment == 'mypay')
    {
        $classname = vikmypay_load_payment('vikrentitems');

        if ($classname)
        {
            $drivers[] = $classname;
        }
    }
}, 10, 2);

/**
 * VIKRENTCAR HOOKS
 */

/**
 * Pushes the mypay gateway within the supported payments of VikRentCar plugin.
 *
 * @param   array   $drivers  The current list of supported drivers.
 *
 * @return  array   The updated drivers list.
 */
add_filter('get_supported_payments_vikrentcar', function($drivers)
{
    $driver = vikmypay_get_payment_path('vikrentcar');

    // make sure the driver exists
    if ($driver)
    {
        $drivers[] = $driver;
    }

    return $drivers;
});

/**
 * Loads mypay payment handler when dispatched by Vikrentcar.
 *
 * @param   array   &$drivers   A list of payment classnames.
 * @param   string  $payment    The name of the invoked payment.
 *
 * @return  void
 *
 * @see     JPayment
 */
add_action('load_payment_gateway_vikrentcar', function(&$drivers, $payment)
{
    // make sure the classname hasn't been generated yet by a different hook
    // and the request payment matches 'mypay' string
    if ($payment == 'mypay')
    {
        $classname = vikmypay_load_payment('vikrentcar');

        if ($classname)
        {
            $drivers[] = $classname;
        }
    }
}, 10, 2);

/**
 * VIKAPPOINTMENTS HOOKS
 */

/**
 * Pushes the mypay gateway within the supported payments of VikAppointments plugin.
 *
 * @param   array   $drivers  The current list of supported drivers.
 *
 * @return  array   The updated drivers list.
 */
add_filter('get_supported_payments_vikappointments', function($drivers)
{
    $driver = vikmypay_get_payment_path('vikappointments');

    // make sure the driver exists
    if ($driver)
    {
        $drivers[] = $driver;
    }

    return $drivers;
});

/**
 * Loads mypay payment handler when dispatched by VikAppointments.
 *
 * @param   array   &$drivers   A list of payment classnames.
 * @param   string  $payment    The name of the invoked payment.
 *
 * @return  void
 *
 * @see     JPayment
 */
add_action('load_payment_gateway_vikappointments', function(&$drivers, $payment)
{
    // make sure the classname hasn't been generated yet by a different hook
    // and the request payment matches 'mypay' string
    if ($payment == 'mypay')
    {
        $classname = vikmypay_load_payment('vikappointments');

        if ($classname)
        {
            $drivers[] = $classname;
        }
    }
}, 10, 2);

/**
 * VIKBOOKING HOOKS
 */

/**
 * Pushes the mypay gateway within the supported payments of VikBooking plugin.
 *
 * @param   array   $drivers  The current list of supported drivers.
 *
 * @return  array   The updated drivers list.
 */
add_filter('get_supported_payments_vikbooking', function($drivers)
{
    $driver = vikmypay_get_payment_path('vikbooking');

    // make sure the driver exists
    if ($driver)
    {
        $drivers[] = $driver;
    }

    return $drivers;
});

/**
 * Loads mypay payment handler when dispatched by VikBooking.
 *
 * @param   array   &$drivers   A list of payment instances.
 * @param   string  $payment    The name of the invoked payment.
 *
 * @return  void
 *
 * @see     JPayment
 */
add_action('load_payment_gateway_vikbooking', function(&$drivers, $payment)
{
    // make sure the classname hasn't been generated yet by a different hook
    // and the request payment matches 'mypay' string
    if ($payment == 'mypay')
    {
        $classname = vikmypay_load_payment('vikbooking');

        if ($classname)
        {
            $drivers[] = $classname;
        }
    }
}, 10, 2);

/**
 * Filters the array containing the logo details to let VikBooking
 * retrieves the correct image.
 *
 * In order to change the image logo, it is needed to inject the
 * image path and URI within the $logo argument.
 *
 * @param   array   $logo   An array containing the following information:
 *                          - name  The payment name;
 *                          - path  The payment logo base path;
 *                          - uri   The payment logo base URI.
 *
 * @return  array   The updated logo information.
 */
add_filter('vikbooking_oconfirm_payment_logo', function($logo)
{
    if ($logo['name'] == 'mypay')
    {
        $logo['path'] = VIKMYPAY_DIR . DIRECTORY_SEPARATOR . 'vikbooking' . DIRECTORY_SEPARATOR . 'mypay.png';
        $logo['uri']  = VIKMYPAY_URI . 'vikbooking/mypay.png';
    }

    return $logo;
});
File "utils.php"

Along with the installation file, you must create the "utils.php" file. As the name suggests, this file is an helper, and it contains functions or constraints that are not related with the functioning of the payment method. It's optional, but it's recommended to make a better division and organization of the code. Please find the example below:

<?php
/**
 * @package     VikMyPay
 * @subpackage  core
 * @author      E4J s.r.l.
 * @copyright   Copyright (C) 2018 VikWP All rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 * @link        https://vikwp.com
 */

// No direct access
defined('ABSPATH') or die('No script kiddies please!');

// Define plugin base path
define('VIKMYPAY_DIR', dirname(__FILE__));
// Define plugin base URI
define('VIKMYPAY_URI', plugin_dir_url(__FILE__));

/**
 * Imports the file of the gateway and returns the classname
 * of the file that will be instantiated by the caller.
 *
 * @param   string  $plugin  The name of the caller.
 *
 * @return  mixed   The classname of the payment if exists, otherwise false.
 */
function vikmypay_load_payment($plugin)
{
    if (!JLoader::import("{$plugin}.mypay", VIKMYPAY_DIR))
    {
        // there is not a version available for the given plugin
        return false;
    }

    return ucwords($plugin) . 'MyPayPayment';
}

/**
 * Returns the path in which the payment is located.
 *
 * @param   string  $plugin  The name of the caller.
 *
 * @return  mixed   The path if exists, otherwise false.
 */
function vikmypay_get_payment_path($plugin)
{
    $path = VIKMYPAY_DIR . DIRECTORY_SEPARATOR . $plugin . DIRECTORY_SEPARATOR . 'mypay.php';

    if (!is_file($path))
    {
        // there is not a version available for the given plugin
        return false;
    }

    return $path;
}
Main File

This is the main file; it will contain all the necessary functions necessary to initialize, validate and confirm the transaction.

Payment Schema

The payment framework of VikBooking can be extended by creating a PHP file declaring the *PaymentMethodName*Payment class.
You'll also need the creation of another file, specific for the component you're developing, and place it into a folder with the name of the component. This last file will override the main properties of the general one, making it ready to be executed by the component.
You can install the payment method by compressing it into a zip file and installing it as a plugin in the Add Plugin page. You can reach its folder by the following path: /wp-content/plugins/PaymentMethodName
In order to develop the class *PaymentMethodName*Payment please follow the code example below.

<?php

defined('ABSPATH') or die('No script kiddies please!');

JLoader::import('adapter.payment.payment');


abstract class AbstractMyPayPayment extends JPayment{
    
    
    protected function buildAdminParameters() {
        return array();
    }
    
    public function __construct($alias, $order, $params = array()) {
        parent::__construct($alias, $order, $params);
    }
    
    protected function beginTransaction() {
        /** See the code below to build this method */
    }
    
    protected function validateTransaction(JPaymentStatus &$status) {
        /** See the code below to build this method */
        return array();

    }

    protected function complete($esit = 0) {
        /** See the code below to build this method */
    }
}
Parameter Constructors

The parameters form for the administrator section can be built through the buildAdminParameters() static method. This is useful to fill in private data or credentials, required for the creation and the validation of the payment/transaction. A Merchant ID and a Signature Key are an example of parameters that should be visible as well as editable in the back-end.
The parameters of the form are returned as an array with the following structure:

[
	"param_1" : [
		"label" : "Label 1",
		"type" 	: "text",
	],
	"param_2" : [
		"label" : "Label 2",
		"type" 	: "select",
	],
]
  • param_1 - is one key of the array that must be unique (required).
    It represents the name of the parameter to use.
  • label - indicates the text to assign for the parameter in the administrator section (optional).
    By placing a double slash (//) after the label, the text after will be displayed as a tip next to the field of the parameter (like "Merchant ID//this is a tip to display...").
  • type - is used to render the right type of input field in the administrator section (required).
    The values allowed for the type are the followings: custom, select, text.
  • html - will be used only when the type of the parameter is custom (optional).
  • options - is an array containing all the possible values to use in the dropdown (required only when the type is select).
protected function buildAdminParameters() {
    $logo_img = VIKMYPAY_URI . 'mypay/mypay-logo.jpg';
    return array(   
        'logo' => array(
            'label' => __('','vikbooking'),
            'type' => 'custom',
            'html' => '<img src="'.$logo_img.'"/>'
        ),
        'merchantid' => array(
            'label' => __('Merchant ID','vikbooking'),
            'type' => 'text'
        ),
        'testmode' => array(
            'label' => __('Test Mode','vikbooking'),
            'type' => 'select',
            'options' => array('Yes', 'No'),
        ),
    );
}

The code above will generate a parameters form in the administrator section as follows.

If your form doesn't come up, probably there is a syntax error on your file.
When you fill in the admin form, the parameters are stored in an array called by the function getParams() and you are able to get the values with the instructions below:

$merchant_id = $this->getParam('merchantid'); /* returns "539264823539" */
$test = $this->getParam('testmode'); /* returns "Yes" */
Order Info Object

The Order Info object is a mapped key/val array with the order information needed to complete the payment process, which parameters can be get by using the function get('parameter'). You can see below all the available keys of the array.

Param Type Description
details array Array containing sub-parameters for the record of this reservation.
custmail alphanumeric The email address of the customer.
transaction_currency char(3) The currency of the amount (3-letter ISO 4217 code). The default is EUR.
return_url string The return url to come back to your shop from the bank on successful transactions.
error_url string The error url to come back to your shop from the bank on failed transactions.
notify_url string The notification url to validate the transaction data sent from the bank. This URL invokes the validatePayment method of your gateway.
total_to_pay decimal The total amount to pay as decimal (ex. 135.50).

You can retrieve the information of the Order Info array with the example below.

$uniq_id = $this->get('sid')."-".$this->get('ts');

Begin Transaction

The method beginTransaction() of the object VikBookingPayment is invoked every time a user visits the page of a reservation with PENDING Status. Here you need to echo the HTML form that points to the payment creation url of your bank gateway.
In this method it is also possible to make calls via cURL to retrieve tokens or to self submit the form to receive additional info to send to the bank.

protected function beginTransaction()
{    
    $merchant_id = $this->getParam('merchantid');
    
    $action_url = "https://yourbankgateway.com/";
    if( $this->getParam('testmode') == 'Yes' ) {
        $action_url = "https://test.yourbankgateway.com/";
    }

    $form='<form action="'.$action_url.'" method="post">';
    // put here all the required fields of your gateway
    $form.='<input type="hidden" name="your_post_data_merchantid" value="'.$merchant_id.'"/>';
    $form.='<input type="hidden" name="your_post_data_amount" value="'.$this->get('total_to_pay').'"/>';
    $form.='<input type="hidden" name="your_post_data_notifyurl" value="'.$this->get('notify_url').'"/>';
    $form.='<input type="hidden" name="your_post_data_description" value="'.$this->get('transaction_name').'"/>';
    // print a button to submit the payment form
    $form.='<input type="submit" name="_submit" value="Pay Now!" />';
    $form.='</form>';
    
    echo $form;
}
Validate Transaction

The validateTransaction() method is used to validate the transaction details sent from the bank. This method is invoked by the system every time the NotifyURL is visited (the one described in the beginTransaction() method). Usually the data are sent via POST method, and you can access them by simply using the $_POST super-global variable. Some gateways require a signature validation to make sure the transaction wasn't corrupted. The signature validation can be created only following the instructions on the official documentation of your bank.

This method must return a key/value array with the status of the transaction. The possible keys of the array are the followings.

Param Type Description
verified boolean The status of the transaction. 1/true in case of success, otherwise 0/false.
tot_paid decimal The real amount paid by the customer, should be returned by the Gateway (ex, 102.75).
log string A log message sent to the administrator in case of failure. The log can contain any value returned from the Bank and it should be as specific as possible. When not empty, the system will store the logs in the db and they will be visible from the order details page of the back-end.

Any different values in the returned array will be completely ignored during the validation of the transaction.

protected function validateTransaction(JPaymentStatus &$status)
{
    $log = '';
    
    /** In case of error the log will be sent via email to the admin */
    
    $response = $_POST['status'];
    /** Process your gateway response here */
    if($response == 'success') {
        $status->verified(); 
        /** Set a value for the value paid */
        $status->paid( $_POST['amount']);
    } else {
        $status->appendLog( "Transaction Error!\n".$_POST['error_msg']);
    }
    //stop iteration
    return true;
}
Complete

The complete($esit) method is generally used to display a message and to redirect the gateway to the order summary view in case of success or failure. Please notice that this method should be in your Class only in case the Bank Gateway will not redirect the user to the ReturnURL. Also, this method, if it exists in the Class, will be invoked by the system after the validateTransaction(). The $esit argument is a boolean value that represents the status of the transaction. In case of success (1/true) you should print a positive message and redirect the gateway to the return_url address, otherwise (0/false) you should print an error message and redirect the gateway to the error_url address. Remember also to put an exit or die rule at the end of the method to completely stop the flow.

protected function complete($res = 0)
{
    $app = JFactory::getApplication();

    if ($res)
    {
        $url = $this->get('return_url');

        // display successful message
        $app->enqueueMessage(__('Thank you! Payment successfully received.', 'vikmypay'));
    }
    else
    {
        $url = $this->get('error_url');

        // display error message
        $app->enqueueMessage(__('It was not possible to verify the payment. Please, try again.', 'vikmypay'));
    }

    JFactory::getApplication()->redirect($url);
}
Component File

This file is needed to the component to make the Payment Plugin work. It needs to be in a folder, called with the name of the component in lowercase (ex. "vikbooking"), and the PHP file has to be called as the main one (ex. "mypay.php"). In this file you override the main file with specific parameters of the component you're developing this Payment Plugin, but you can also add new methods specific for that component; in this case, we've added some hooks of VikBooking in order to save the 'total_to_pay' and to retrieve it before the transaction validation. You'll have to create one of this files for each component in which you will import the Payment Plugin. In the example below, made for VikBooking, you can find an override of the constructor, and either new methods defined specifically for the component to which the file belong to. Please find the example below:

<?php
/**
 * @package     Vikmypay
 * @subpackage  vikbooking
 * @author      E4J s.r.l.
 * @copyright   Copyright (C) 2018 VikWP All rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 * @link        https://vikwp.com
 */

defined('ABSPATH') or die('No script kiddies please!');

JLoader::import('mypay', VIKMYPAY_DIR);

// Prepend the deposit message before the payment form (only if specified).
add_action('payment_after_begin_transaction_vikbooking', function(&$payment, &$html)
{
    // make sure the driver is mypay
    if (!$payment->isDriver('mypay'))
    {
        return;
    }

    if ($payment->get('leave_deposit'))
    {
        $html = '<p class="vbo-leave-deposit">
            <span>' . JText::_('VBLEAVEDEPOSIT') . '</span>' . 
            $payment->get('currency_symb') . ' ' . number_format($payment->get('total_to_pay'), 2) . 
        '</p><br/>' . $html;
    }
/**
     * Force the system to avoid using the cache for transient.
     * The previous value will be reset after terminating the callback.
     *
     * 
     */
    $was_using_cache = wp_using_ext_object_cache(false);


    // save the total to pay within a transient (should not work on a multisite, try using `set_site_transient`)
    $transient = set_transient('vikmypay_vikbooking_' . $payment->get('oid') . '_' . $payment->get('sid'), $payment->get('total_to_pay'), 10 * MINUTE_IN_SECONDS);

// restore cache flag
    wp_using_ext_object_cache($was_using_cache);
    //if transient saving fails
    if(!$transient){
        $txname = $payment->get('sid') . '-' . $payment->get('oid') . '.tx';
        $fp = fopen(VIKMYPAY_DIR . DIRECTORY_SEPARATOR . 'VikMyPay' . DIRECTORY_SEPARATOR . $txname , 'w+');
        fwrite($fp, $payment->get('total_to_pay'));
        fclose($fp);
    }
}, 10, 2);

/// Retrieve the total amount  from the static transaction file.
add_action('payment_before_validate_transaction_vikbooking', function($payment)
{
    // make sure the driver is MyPay
    if (!$payment->isDriver('mypay'))
    {
        return;
    }
    $txname = $payment->get('sid') . '-' . $payment->get('oid') . '.tx';
    $txdata = '';

    $path = VIKMYPAY_DIR . DIRECTORY_SEPARATOR . 'MyPay' . DIRECTORY_SEPARATOR . $txname;
    /**
     * Force the system to avoid using the cache for transient.
     * The previous value will be reset after terminating the callback.
     *
     */
    $was_using_cache = wp_using_ext_object_cache(false);

    $transient = 'vikmypay_vikbooking_' . $payment->get('oid') . '_' . $payment->get('sid');

    // get session ID from transient (should not work on a multisite, try using `get_site_transient`)
    $data = get_transient($transient);
    /**
     *  Check if transient exists: if it doesn't exist, then attempt to recover the needed data from the file.
     *  
     *
     */

    if ($data) {

        
        // set total to pay as it is probably missing
        $payment->set('total_to_pay', $data);

        // always attempt to delete transient
        delete_transient($transient);
        // restore cache flag
        wp_using_ext_object_cache($was_using_cache);
        
    } else if (is_file($path)) {
        $fp = fopen($path, 'rb');
        $txdata = fread($fp, filesize($path));
        fclose($fp);

        if (!empty($txdata))
        {
            $payment->set('total_to_pay', $txdata);
        }
        else
        {
            // if not set, specify an empty value to pay
            $payment->set('total_to_pay', $payment->get('total_to_pay', 0));
        }
        // remove transaction file
        unlink($path);
    }
        
    
});

// VikBooking doesn't have a return_url to use within the afterValidation method.
// Use this hook to construct it and route it following the shortcodes standards.
add_action('payment_on_after_validation_vikbooking', function(&$payment, $res)
{
    // make sure the driver is mypay
    if (!$payment->isDriver('mypay'))
    {
        return;
    }

    $url = 'index.php?option=com_vikbooking&task=vieworder&sid=' . $payment->get('sid') . '&ts=' . $payment->get('ts');

    $model      = JModel::getInstance('vikbooking', 'shortcodes', 'admin');
    $itemid     = $model->all('post_id');
    
    if (count($itemid))
    {
        $url = JRoute::_($url . '&Itemid=' . $itemid[0]->post_id, false);
    }

    JFactory::getApplication()->redirect($url);
    exit;
}, 10, 2);

/**
 * This class is used to collect payments in VikBooking plugin
 * by using the mypay gateway.
 *
 * @since 1.0
 */
class VikBookingMyPayPayment extends AbstractMyPayPayment
{
    /**
     * @override
     * Class constructor.
     *
     * @param   string  $alias   The name of the plugin that requested the payment.
     * @param   mixed   $order   The order details to start the transaction.
     * @param   mixed   $params  The configuration of the payment.
     */
    public function __construct($alias, $order, $params = array())
    {
        parent::__construct($alias, $order, $params);

        $details = $this->get('details', array());

        $this->set('oid', $this->get('id', null));
        
        if (!$this->get('oid'))
        {
            $this->set('oid', isset($details['id']) ? $details['id'] : 0);
        }

        if (!$this->get('sid'))
        {
            $this->set('sid', isset($details['sid']) ? $details['sid'] : 0);
        }

        if (!$this->get('ts'))
        {
            $this->set('ts', isset($details['ts']) ? $details['ts'] : 0);
        }

        if (!$this->get('custmail'))
        {
            $this->set('custmail', isset($details['custmail']) ? $details['custmail'] : '');
        }
    }
}