Payment plugins

Contents...

Payment plugins are used to manage payments for customers placing orders on a website. An opportunity to provide a payment is offered, when an order has been completed, in the form of a page where a customer can perform certain actions to provide payment; e.g., complete a payment form or proceed to the payment service website.

In the Webasyst framework the basis for development of payment plugins is provided by the system kernel. The framework offers a set of methods and a special data exchange format for retrieving order details and processing callbacks from payment gateways. Payment plugins are supported at the framework’s system level rather than at the application level. This allows various applications (e.g., Shop-Script 5) to rely on the same payment plugins installed in the system.

Take a look at the source code of PayPal payment plugin as an example (GitHub repository).

The source code of payment plugins resides inside wa-plugins/payment/ directory. Each plugin is stored in a separate subdirectory containing implementation of a plugin class extending base class waPayment and implementing interfaces waIPayment, waIPaymentCapture, waIPaymentRefund, waIPaymentCancel, which provide templates for public methods payment(), capture(), cancel(), and refund() ­for processing the corresponding types of payment transactions (if supported by the corresponding gateway; otherwise they must return an error code).

The name of the subdirectory with plugin files is the plugin’s unique identifier, which is used to form the correct name for the main class and the file it is contained in. Below is provided an example of the correct class and file names for some plugin with yourpayment identifier:

wa-plugins/payment/yourpayment/lib/yourpaymentPayment.class.php

<?php

class yourpaymentPayment extends waPayment
{

...

}

In addition to the main plugin class extending base class waPayment, each plugin also requires configuration files for correct functioning, which, like for apps and plugins, must be placed inside the lib/config/ subdirectory (see descriptions of plugin configuration files below in this article).

The development of a payment plugin is very similar to that of an application plugin with the exception that, unlike the development of an app plugin, creation of a payment plugin often requires only implementation of one or several main methods of one base class, which are listed below.

Methods

In methods of the main plugin class you can access values of plugin settings specified in its configuration file lib/config/settings.php by using private class fields of the form $this->field_name. Instead of field_name specify the id of the settings field whose value you need to obtain.

payment()

This method generates the main content of the page which offers a customer to initiate the payment process; e.g., enter payment credentials or proceed to a secure payment page. In the latter case a form with hidden element is often used, which is sent to the payment gateway when a customer clicks on a button or a link.

The following methods are required if your payment plugin implements integration with a payment gateway which supports callbacks to the online store; e.g., to validate the received order details or to transmit additional data to the online store such as the payment transaction status.

callbackInit()

This method allows extracting the id of the app which should process the callback and the payment plugin settings values specified for that app. Below is the general example of how such a method should look in your plugin class:

protected function callbackInit($request)
{
    $this->order_id = null; //extract the order id from callback params
    $this->app_id = null; //do the same for app id
    $this->merchant_id = null;//do the same for payment plugin settings specified for that app

    return parent::callbackInit($request); //mandatory call of parent class method at the end
}

callbackHandler()

This method must describe the algorithm processing callbacks from the payment gateway. Such callbacks must be sent to URLs of the form http://yourdomain.com/payments.php/[plugin_id]/. Additional parameters can be passed to a payment plugin by means of URL parameters, which are usually accessible as $_GET array items. It is an established practice to use the transaction_result variable with the following values:

  • result: payment systems callback
  • success or failure: user redirect back to website upon successful or failed payment attempt

Here is an example of a URL to which a payment system should send its callbacks: http://yourdomain.com/payments.php/[plugin_id]/?transaction_result=result

The main purpose of this method is to call the transaction handler provided by the app utilizing your payment plugin:

$this->execAppCallback($state, $transaction_data)

Instead of $state specify the desired transaction status using one of the following constants of system class waPayment: CALLBACK_PAYMENT, CALLBACK_REFUND, CALLBACK_CONFIRMATION, CALLBACK_CAPTURE, CALLBACK_DECLINE, CALLBACK_CANCEL, CALLBACK_CHARGEBACK; e.g.:

$result = $this->execAppCallback(self::CALLBACK_PAYMENT, $transaction_data);

In the case when a payment is registered as received during the callback processing the array returned by the execAppCallback() method will contain value true with the 'result' key.

Call app’s transaction handler only after you have verified that received data are correct and valid using checksum, signature, etc. Use of non-verified data may cause saving of inconsistent payment information to the database!

After processing a callback from a payment system, method callbackHandler may redirect the customer to the specified URL or display a web page generated by the specified template file. For that, the method must return an array of values corresponding to each of these two options as shown below:

  • Redirect:
    return array(
        'redirect' => 'URL', //specify the required URL to redirect the customer
    );
  • Display a web page:
    return array(
        'header' => array(), //optional array of HTTP headers, which will be added to the server response by means of method addHeader() of class waResponse
        'template' => 'path/to/template.html', //optional path to the template which should be used to generate and display a web page; by default, wa-system/webasyst/templates/actions/payments/Payments.html is used
        'some_var' => 'some_value', //arbitrary variable to be used in the template
    );

formalizeData()

Method formalizeData() is good for transforming requests received as callbacks from the payment gateway to the format suitable for saving transaction information in the Webasyst database.

image ($order_data)

Implement this method in the main plugin class if it must support payments by QR code.

Parameters
  • $order_data: Instance of the waOrder class corresponding to the order for which payment is required.

The method must return an array with the following keys (for any or for both of them):

  • image_url: Absolute URL of a QR code image.
  • image_data_url: Data to display a QR code in the “data URL” format.

Configuration files

plugin.php

File plugin.php is plugin’s main configuration file used to store basic information: plugin name, description, version, etc. as shown below:

<?php

return array(
    'name'        => ...,
    'description' => ...,
    'icon'        => ...,
    'logo'        => ...,
    'vendor'      => ...,
    'version'     => ...,
    'type'        => ...,
);

Field descriptions

  • name: plugin name; if plugin’s localization file contains a string with key matching the specified name, then the name will be automatically translated;
  • description: plugin description; if plugin’s localization file contains a string with key matching the specified description, then the description will be automatically translated;
  • icon: path to plugin icon file, relative to main plugin directory; e.g., img/paypal16.png (path to standard 16x16 px large plugin icon file);
  • logo: path to plugin logo file, relative to main plugin directory; e.g., img/paypal.png (path to 60x32 px large logo file, standard for payment plugins);
  • vendor: numerical developer id available in your developer account in Webasyst Customer Center;
  • version: string version value; e.g., '1.0.0';
  • type: payment plugin type, one of the following constants of system class waPayment: TYPE_CARD (accept bank card payments directly on your website), TYPE_ONLINE (accept payments on payment page provided by payment gateway), or TYPE_MANUAL (manual payment processing by website administrator); e.g., waPayment::TYPE_ONLINE.

requirements.php

File requirements.php is used to specify additional system requirements applicable to the functioning of your payment plugin (e.g., availability of extra PHP extensions or configuration parameters, or installed Webasyst apps). See a detailed description of the system requirements configuration file.

settings.php

File settings.php is used for automatic generation of settings interface for a payment plugin displayed in the backend of the app utilizing the plugin. Read a detailed description of the plugin settings file.

guide.php

This file is used to display various non-editable parameters to users to enable them to copy and paste those data to their merchant account settings fields on the payment gateway website; e.g.: callback URL for updating order statuses, plugin setup instance id, name of required character set or data transfer method.

File guide.php must be a valid PHP file containing an array of parameters for each such field as shown below:

<?php

return array(
    array(
        'title'       => '',
        'description' => '',
        'value'       => '',
    ),
    array(
        'title'       => '',
        'description' => '',
        'value'       => '',
    ),
    ...
);

Each sub-array corresponds to one field. Sub-array keys have the following meanings:

  • title: arbitrary field name
  • description: optional field description
  • value: text to be displayed to user. In this text several useful variables may be used, which are automatically replaced with actual values:

    %RELAY_URL%: payment gateway callback handler URL

    %HTTP_RELAY_URL%: payment gateway callback handler URL with http:// protocol name

    %HTTPS_RELAY_URL%: payment gateway callback handler URL with https:// protocol name (to use such URLs, you must ensure that your Webasyst-powered website is available via https://)

    %APP_ID%: id of the app utilizing this payment plugin

    %MERCHANT_ID%: payment plugin setup instance id

Example of file guide.php:

<?php

return array(
    array(
        'title'       => 'Successful payment notification URL',
        'description' => '',
        'value'       => '%HTTPS_RELAY_URL%',
    ),
    array(
        'title'       => 'Failed transaction notification URL',
        'description' => '',
        'value'       => '%HTTPS_RELAY_URL%?transaction_result=failure',
    ),
    array(
        'title'       => 'Data transfer encoding',
        'description' => 'Specify this value when setting up connection using new integration protocol',
        'value'       => 'UTF-8',
    ),
);

Custom controls for frontend

A payment plugin can display additional controls including custom ones. To do so, you need to implement public method customFields() in the plugin class. This method must return an array of sub-arrays, each representing an additional control. Using argument $order you can access various properties of the current order to generate additional frontend fields and their values. Here is an example:

public function customFields(waOrder $order)
{
    return array(
        'field1' => array(
            'value'        => $default_field_value, //if necessary
            'title'        => _wp('First field name'),
            'control_type' => waHtmlControl::INPUT,
        ),
        'field2' => array(
            'value'        => $default_field_value, //if necessary
            'title'        => _wp('Second field name'),
            'control_type' => 'MyPaymentPluginControl', //custom control key
        ),
        //...
    );
}

For 'control_type' parameters you may specify a custom control key, which can be any string of HTML or JavaScript code.

To implement a custom control, the following is required:

  1. Add a public method to your plugin class to return the desired control HTML code.
  2. At the beginning of customFields() method, add the following call:

    $this->registerControl('MyPaymentPluginControl', array($this, 'myPaymentPluginControl'));

    In this example, 'MyPaymentPluginControl' as the first parameter of the registerControl() method is your custom control key, and 'myPaymentPluginControl' is its implementation method name.

Custom control keys and their implementation methods can be named freely.

Passing tax rates to payment system

Tax rates are available if an app using a payment plugin passes it on to the plugin. Tax rate values are passed as percents; e.g., value 18 means that the tax rate is 18%.

Order items

Tax rates are passed individually for each order item, which are available in payment() method via waOrder->items field of waOrder class instance containing formalized order data.

An order item is represented by an associative array. Array entry with 'tax_rate' key contains an item’s tax rate value as a number or null. Value 0 means zero tax rate. Value null means that no tax rate is applicable to an order item. If there is no such entry at all, then it means that an app does not support passing of tax rates to a payment plugin.

Arbitrary positive value of item array entry 'tax_included' denotes that the tax value is included in the item cost.

Shipping cost

Shipping-related information is not contained in order items and must be retrieved separately. Tax rate applicable for the shipping cost is available in class field waOrder->shipping_tax_rate. The flag of tax amount included in the shipping cost is contained in waOrder->shipping_tax_included field.

If a fiscalization service accepts shipping-related information as part of order data array, you can prepare that information using this example as a basis:

$item = array(
    'quantity'     => 1,
    'name'         => $order->shipping_name,
    'amount'       => $order->shipping,
    'tax_rate'     => $order->shipping_tax_rate,
    'tax_included' => ($order->shipping_tax_included !== null) ? $order->shipping_tax_included : true,
);

App support

Passing of tax rates for order items is supported by Shop-Script as of ver. 7.2.9.
Passing of tax rates for shipping cost is supported by Shop-Script as of ver. 7.2.18.

Tips

Various useful URLs for setting up integration between your website and the payment system can be obtained in plugin’s PHP code using the following methods:

Values of variables acceptable in file guide.php:

  • %RELAY_URL%
    $this->getRelayUrl();
  • %HTTP_RELAY_URL%
    $this->getRelayUrl(false);
  • %HTTPS_RELAY_URL%
    $this->getRelayUrl(true);

URLs for returning customers from payment gateway’s website:

$this->getAdapter()->getBackUrl($type, $data);

Instead of $type, specify the desired action type in the form of one of the following constants of system class waAppPayment: URL_SUCCESS (successful payment), URL_DECLINE (declined payment), URL_FAIL (payment failure), URL_CHECKOUT (return to checkout page), URL_PRINTFORM (display a printable form). Instead of $data you must specify an array of transaction data. Example:

$return_url = $this->getAdapter()->getBackUrl(waAppPayment::URL_SUCCESS, $transaction_data);
The app utilizing your payment plugin must have support for the return URL type which you specify when calling method getBackUrl().

Controllers & actions

A payment plugin can have its own controllers to process user requests. Plugin’s controllers must extends one of the following system classes:

  • waJsonActions
  • waJsonController
  • waLongActionController
  • waSystemPluginActions
  • waSystemPluginAction

Requests to own controllers can be sent, for instance, on the plugin settings page.

Before a controller begins to process a user request in the backend, user access rights are automatically verified.

URLs to send requests to plugin controllers can be obtained by means of plugin’s main class method getInteractionUrl($action = 'default', $module = 'backend').

HTML template files for action classes must be stored at paths formed according to the templates/actions/[module]/[Module][Action].html pattern.

Additional PHP classes

To ensure that plugin’s additional classes are auto-loaded, their files must be stored in lib/classes/ directory and named by the payment[Plugin_id][Class_name].class.php pattern. Class names must be formed by the payment[Plugin_id][Class_name] pattern. Here [Plugin_id] is a plugin ID and [Class_name] is an arbitrary class name part added by the developer.

Database

General plugin settings

Common settings, valid for all apps, can be stored in wa_app_settings table. You can save and read such values using these built-in methods:

  • setGeneralSettings($name, $value)
  • getGeneralSettings($name = null, $default = '')

Such settings are saved to the database table with app_id field values formed by the webasyst.payment.[id] pattern. Here [id] is the plugin ID.

The following settings names, being reserved identifiers, cannot be used by a plugin developer:

  • update_time: used by meta updates,
  • sync_time: used by scheduled tasks,
  • sync_success_time: used by scheduled tasks,
  • sync_failure_time: used by scheduled tasks.

Own database tables

Payment plugin’s tables must be named using the wa_payment_[plugin_id] prefix.

The structure of tables must be described in file lib/config/db.php in the same format as for apps and their plugins.

To access values in its table, a plugin can use waSystemPluginModel, a child class of waModel. In this case you do not need to create model classes for of plugin’s tables. You can use method setPlugin(waSystemPlugin $plugin, $table = null) of that class to get an instance of waModel for a specified table name and start working with the data in that table.

If, however, you need custom methods which are not available in waModel, this is not a good option for you. In this case you need to create model classes for your plugin’s tables. They can extend waSystemPluginModel class, too.

In the main plugin class method getModel($table = null) is available. You can pass a table name to it and get an instance of that table’s model class, if there is such a class in the plugin, or an instance of waSystemPluginModel for the specified table name. To specify a table name, use only its variable name part after the mandatory prefix.

If no table name is specified, then the method returns a model class instance for the plugin table whose name consists of the plugin’s mandatory prefix only. This can be useful in the cases when a payment plugin uses only one database table.

Data files

Payment plugins can store their data in files located in wa-data/ directory. To access such files, you can use these built-in methods of the main plugin class:

  • getDataPath($path = null, $public = false, $create = true): get a path to a file,
  • getDataUrl($path = null, $absolute = false): get a public file URL.

Plugins configuration parameters

  • Description of functionality to be supported by an app in file app.php:
    • rights: required access rights level for users who are allowed to interact with the plugin’s controllers. If empty or not specified, then administrative access rights for an app are required. For shop app, access rights level settings must be enabled to allow users access to app settings and payment plugin settings.
  • Descriptions of callback URLs must be contained in file guide.php.

Meta updates

Meta update files must be stored in the lib/updates/ directory.