4PSA VoipNow® Automation Payment Developer Guide

Manual Version 63748.4 at 2010/03/03 14:17:56

For suggestions regarding this manual contact:

docs@4psa.com

All rights reserved

Distribution of this work or derivative of this work is prohibited unless prior written permission is obtained from the copyright holder.

VoipNow is a Registered Trademark of Rack-Soft, Inc.

4PSA is a Registered Trademark of Rack-Soft, Inc.

Asterisk is a Trademark of Digium, Inc.

Linux is a Registered Trademark of Linus Torvalds.

All other trademarks and copyrights are property of their respective owners.

Table of Contents

Preface
Who Should Read This Guide
Introduction
Creating a Plug-in
The Plug-in Structure
Logical Structure
Physical Structure
The Interaction Between VoipNow and the Plug-in
Interface Interactions Description
Automation Store Interactions Description
Plug-in Methods
Commonly Utilized Plug-in Methods
Plug-in XML Configure Syntax
Plug-in Example
Setup File
Requirements File
Input Requirements
Language File
Plug-in Class

Preface

Who Should Read This Guide

This reference guide should be read by the developers of third-party applications whose purpose is to implement the new payment processors into the 4PSA VoipNow Automation store.

This Payment Plug-in Reference Guide provides the information needed for creating a 4PSA VoipNow Automation Payment Plug-in. It contains a comprehensive description of the system defined interfaces and classes.

Introduction

This guide offers guidelines and references for anyone wishing to modify, extend, or contribute to the 4PSA VoipNow Automation payment plug-ins. Third-party products can interact with the system and may offer more payment options, according to their needs. Beginning with 4PSA VoipNow 2.0.2, developers can build payment plug-ins and upload them using the control panel.

A VoipNow Automation Payment Plug-in is a set of functions, written in the PHP scripting language, that defines the interaction of both VoipNow Automation and the Automation Store with a certain gateway or payment processor.

The Creating a Plug-in chapter of this Guide goes through the basic steps of creating a VoipNow Automation Payment Plug-in, explaining the logical and physical structure of a plug-in and the way it interacts with the system.

Creating a Plug-in

This guide assumes you are already familiar with the 4PSA VoipNow functionality and the PHP programming. This chapter describes the basic steps to be taken in the development of a new plug-in.

The Plug-in Structure

Logical Structure

A VoipNow Plug-in consists of the following parts:

Payment Plug-in Manager - This plug-in part is actually a configuration file with which the VoipNow Automation interface generates a setup page for the plug-in. It contains the setup/setup.xml file and the language pack files from the /language folder.

Front-end - This part of the plug-in is present in the VoipNow Automation store and generates the payment inputs based on the requirements.xml file content (which is saved in the database and can be modified) and on the language pack files from the /language folder.

Gateway Interaction Methods - These methods form a class that enhances the communication between VoipNow Automation and the gateway that needs to be implemented. It will be located in the index.php file.

Physical Structure

A plug-in is composed of one or more files and folders and it is uploaded as an archive in the VoipNow control panel. Each developer may create plug-ins with different structures, but all developers must follow a basic folder structure in order to be registered in the VoipNow. Aside from the files and folders represented in the image below, the plug-in may contain a set of back-end custom files and folders, entirely designed by the developer. These files define auxiliary functions that are used internally and that are necessary for the plug-in's purpose.

The folder structure of a VoipNow Plug-in consists of the following parts:

The index.php File

Containing an index.php file in the plug-in root is a compulsory demand for every plug-in. This file will contain the main gateway or the payment processor interaction class. When a payment request is made, either 4PSA VoipNow Automation or the Automation Store requires this file in order to initiate the class that will handle the communication with the payment processor. The file's structure is:

index.php   
<?php 
            class VoipNowPaymentPlugin extends OnlinePaymentInterface implements OnlinePayment {
        // gateway interaction methods
    }
?> 

The Language Folder

This folder contains the plug-in's language pack files. A plug-in can customize the layout of certain configuration areas or can cause JavaScript or HTML alerts to be displayed. All the output messages must be displayed in the language that the customer has selected from the interface. The user's language support can be achieved from plug-ins as follows:

  1. Create a folder named /language in the plug-in root.

  2. Create one file for each language defined in the 4PSA VoipNow control panel and place it inside the /language folder.

    Note

    You must name the file with the language code in order to have effect. (E.g.: en.php)

  3. Define an array with the language pack messages inside each language file. The structure of the array is : key => value.

    <?php 
        $plugin_msg_arr = array();
        $plugin_msg_arr['field_name'] = 'This message is displayed as label in Payment Plug-in Management Page.';
    	$plugin_msg_arr['verification_alert'] = 'This message is displayed as alert in case of failed verification.';
    ?> 

    Note

    In order to be functional, you must keep the array name.

  4. Use the OnlinePaymentInterface::translate method described in the section called “Plug-in Methods” to display a message in the current user language.

    <?php 
    	class VoipNowPaymentPlugin extends OnlinePaymentInterface implements OnlinePayment {
    		final public function AuthorizePayment($params) {
    			// ...
    			if (!isset($params['CreditCardNumber'])) {
    				// ...
    				$message = self :: translate('error_missing_credit_card_number');
    				// we can use the method this way because our class extends OnlinePaymentInterface
    				return self :: RaiseError($code, $message);
    			}
    			// ...
    		}
        }						
    ?> 

The setup.xml File

This file will provide configuration information for the plug-in within the 4PSA VoipNow Automation interface. The file is located under the /setup folder.

After the plug-in is registered, it must be configured in order to be used. The configuration page from 4PSA VoipNow Automation interface will require the setup.xml file in order to generate the setup form.

The XML syntax for the setup file is presented in the the section called “Setup File”.

The requirements.xml File

This file will provide information about the fields that are to be displayed under 4PSA VoipNow Automation store, for each method that has been used. After the plug-in is configured and set in use, if the user will choose to pay with this plug-in, the requirements. xml file will provide details about the required fields specific to each method.

This file also uses the XML syntax, very similar with the setup.xml file's syntax.

The required_inc.php File

This file will specify the required inputs for each plug-in method. The file contains an array having as indexes the names of the plug-in's methods and as values arrays containing the required fields' names. E.g.:

<?php 
							
							
	$_required_params = array(
		'AuthorizePatyment' => array(
			'CreditCardType',
			'CreditCardNumber',
			'CardExpMonth',
			'CardExpYear',
			
			'InvoiceID',
			'OrderTotal',
			'IPAddress',
			
			'PayerFirstName',
			'PayerLastName',
			'PayerEmail',
			'PayerCountry'
		),
		// ...
	);
	
?> 

The list of input parameters for each plug-in method can be found in the section called “Plug-in Methods”.

The License File

This file contains plug-in license information.

If you want to distribute the plug-in under the General Public License (GPL) agreement, include the following paragraph:

Copyright (C) {year} {name of author}
This program is free software: you can redistribute it and/or modify 
it under the terms of the GNU General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.
					
This program is distributed in the hope that it will be useful, 
but WITHOUT ANY WARRANTY; without even the implied warranty of 
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
See the GNU General Public License for more details. 

You should have received a copy of the GNU General Public License 
along with this program. If not, see http://www.gnu.org/licenses/.

The Meta File

Each plug-in must contain a meta file with standard recognition plug-in information. 4PSA VoipNow does not allow uploading and running plug-ins without the following set of informations:

  • Name
  • Version
  • Author

The file content format is the following:

Name: Name of the plug-in
          Version: The plug-in's version, e.g.: 1.0
          Author: Name of the plug-in author
          Homepage: The plug-in's home page
          Update: The date when the plug-in was last modified

The Interaction Between VoipNow and the Plug-in

The execution of a payment plug-in is initiated when a payment or a refund command is triggered inside the 4PSA VoipNow Automation interface. This will instantiate the payment plug-in class and execute one of the payment methods available in that class. Further more, another type of interaction between VoipNow interface and the plug-in files is the configure operation. During the configuration process, the VoipNow interface will directly access the setup and requirements XML files and it will display the required information based on the formated data from those files.

Interface Interactions Description

The interactions between 4PSA VoipNow Automation interface and the payment plug-ins are being recognized at two levels:

  • VoipNow Payment Plug-in Setup Interface
  • VoipNow Payment Plug-in Usage

VoipNow Payment Plug-in Setup Interface

The Plug-in Setup Interface will load the information from the setup.xml file and will generate a form that contains the inputs needed to set up the plug-in's requirements in order to be able to connect to the gateway.

Further more, the Plug-in Setup Interface will load the information from the requirements.xml file in order to be able to setup the 4PSA VoipNow Automation store interface inputs for each payment plug-in method.

VoipNow Payment Plug-in Usage

After a client has made his first payment and the product has been deployed, the amount of money paid for it needs to be captured. This will be done from the VoipNow Automation Interface, using one of the available plug-in methods.

This will be done from the Orders and the Invoices pages. There, for each invoice, the user can capture the payment, refund a part of the money, etc.

Automation Store Interactions Description

A payment plug-in's execution is triggered when an user is trying to complete a payment operation. At that moment, an instance of the payment plug-in class will be created and, afterwards, a specific payment method will be called. The method will communicate with the gateway or with the payment processor sending via different methods (usually SOAP or XML RPC) informations about the payment and will grab the processor's response towards the given data. The response will be formated and returned to the Automation store.

Also, the Automation store will be able load the xml requirements from the database (this will be the same as the content of the requirements.xml file or a modified version of this content) in order to be able to create a payment form for the user.

Plug-in Methods

Depending of it's type, a payment plug-in must implement a simple set of methods that are defined within the OnlinePayment interface.

<?php 
	interface OnlinePaymentInterface {
		public function PreAuthorisePayment(array $params);
		public function ProcessPreAuthorisePayment(array $params);	
		public function AuthorisePayment(array $params);
		public function CapturePayment(array $params);
		public function GetTransactionDetails(array $params);
		public function RefundTransaction(array $params);
		public function RecurringPayment(array $params);
    public function CheckSubscriptionValidity(array $params);
    public function Void(array $params);
	}
?> 

Each method must have a set of parameters. Some of these parameters will be required all the time. The required ones will be mentioned in the required_inc.php file and verified to be valid. The input for these methods has a shape of an array.

<?php 
	$plugin = new VoipNowPaymentPlugin();
	$params_authorise = array(
		'CreditCardNumber' => '4111111111111111',
		'CardExpMont' => '09',
		'CardExpYear' => '2010',
		'CreditCardType' => 'Visa',
		'OrderTotal' => '20.21',
		'Currency' => 'USD';
	);
	$plugin->AuthorizePayment($params_authorize);
?> 

Also, each method has a required set of parameters that must be returned by the coder. A method's output has the shape of an array. Considering the fact that a class method must return one or more outputs for each transaction that takes place inside it (see the section called “Plug-in Methods”), the array will have the following shape:

<?php 
	$result = array();
	$result[METHOD_NAME_CONSTANT] = array(
		'output_index' => 'output_value',
		// ...
	);
?> 

where METHOD_NAME_CONSTANT is a constant from OnlinePaymentInterface class. E.g:

<?php 
	class VoipNowPaymentPlugin extends OnlinePaymentInterface implements OnlinePayment {
		final public function AuthorizePayment($params) {
			// ...
			if (!isset($params['CreditCardNumber'])) {
				// ...
				$message = self :: translate('error_missing_credit_card_number');
				// we can use the method this way because our class extends OnlinePaymentInterface
				return self :: RaiseError($code, $message);
			}
			// ...
			$result = array();
			$result[parent::method_auth] = array();
			// ...
			$result[parent::method_auth]['CardNumberEnding'] = 'value';	// the last 4 digits of the card number
			$result[parent::method_auth]['CardExpMonth'] = 'value';		// the expire 
			$result[parent::method_auth]['CardExpYear'] = 'value';		// date of the card
			return $result;
		}
    }		
?> 

AuthorizePayment Method

This method is only meant to block a certain amount of money on the client's card account that can be later withdrawn by the merchant with the help of a Capture operation.

The method's available input parameters are:

  • CreditCardNumber - the client's card number.
  • CardExpYear - the card expiration year.
  • CardExpMonth - the card expiration month.
  • CreditCardType - the card type (e.g.: Visa, MasterCard, Maestro, etc.).
  • CardSecurityCode - the card unique code that is available only on the physical card (e.g. last the three digit code on the back of a VISA card).
  • OrderTotal - the total amount of the order placed on the public store.
  • Currency - the currency used for the transaction.
  • IPAddress - specifies the buyer's IP address (used for security purposes).
  • InvoiceID - the unique identification number of the invoice created for the order placed on the public store.
  • PayerFirstName - the client's first name.
  • PayerLastName - the client's last name.
  • PayerAddress - the client's address: street name, number.
  • PayerCountry - the client's country.
  • PayerCity - the client's city.
  • PayerState - the client's state.
  • PayerPostalCode - the client's postal code.
  • PayerEmail - the client's email address.
  • PayerCompany - the client's company, if he represents one.
  • PayerPhoneNumber - the client's phone number.
  • PayerFaxNumber - the customer's fax number.
  • PayerTaxAccountNumber - the customer's tax account number.
  • PayerBankName - the name of the bank used by the client.
  • PayerBankAccount - the client's bank account number.
  • TransactionID - for the other payment methods that do not use card details (check and wire transfer) but a subscription, you can use this parameter to pass the credentials.

Requested method output parameters are:

  • ACK - will specify if the outcome of the request was success or failure.
  • BankRef - the possible bank response regarding the transaction.
  • Date - the date when the transaction was made. This should be an integer number (see php time function).
  • MerchantID - the merchant's identification number.
  • APIRequest - the API request in the form that is sent to the processor.
  • APIResponse - the API response in the form that is received from the processor.
  • Error - if the processor will return an error, it will be displayed here. This parameter is an array itself with the following indexes:
    • code - the error code (the code returned by the gateway or the error's internal code that appeared inside the plug-in).
    • location - the error location.
    • message - the error message.
    • severity - the error severity.
  • Amount - the amount of the invoice.
  • Currency - the currency used for the transaction.
  • TransactionID - the identification number returned by the Authorize operation for the specific transaction.
  • SubscriptionID - the subscription's identification number.
  • CardNumberEnding - the last four digits of the card number used by the customer to pay for the order.
  • CardExpMonth - the client's card expiration month.
  • CardExpYear - the client's card expiration year.

CapturePayment Method

This method is meant to retrieve the amount of money that was blocked in the client's bank account by the Authorize operation.

The available input parameters for this method are:

  • Currency - the currency used for the transaction.
  • OrderTotal - the total amount of the order placed on the public store.
  • TransactionID - the identification number returned by the Authorize operation for the specific transaction.

The requested output parameters for this method are:

  • ACK - will specify if the outcome of the request was success or failure.
  • BankRef - the possible bank response regarding the transaction.
  • Date - the date when the transaction was made (this should be an integer number (see php time function).
  • MerchantID - the merchant's identification number.
  • APIRequest - the API request in the form that is sent to the processor.
  • APIResponse - the API response in the form that is received from the processor.
  • Error - if the processor will return an error, it will be displayed here. This parameter is an array itself with the following indexes:
    • code - the error code (the code returned by the gateway or the error's internal code that appeared inside the plug-in).
    • location - the error location.
    • message - the error message.
    • severity - the error severity.
  • Amount - the amount of the invoice.
  • Currency - the currency used for the transaction.
  • TransactionID - the identification number returned by the Capture operation for the specific transaction.

GetTrasactionDetails Method

This method is meant to obtain information about a certain transaction. If the method is not supported by the gateway, it should return null or an empty array.

The available input parameters for this method are:

  • TransactionID - the identification number of any of the operations made before this one.

RefundTransaction Method

This method is meant to refund a certain amount of money (less than or equal to the amount that was payed in the Capture operation) to the client.

The available input parameters for this method are:

  • Currency - the currency used for the transaction.
  • OrderTotal - the total amount of the order placed on the public store.
  • TransactionID - the identification number returned by the Capture operation for the specific transaction.
  • InvoiceID - the identification number of the refunded invoice.

The requested output parameters for this method are:

  • ACK - will specify if the outcome of the request was success or failure.
  • BankRef - the possible bank response regarding the transaction.
  • Date - the date when the transaction was made (this should be an integer number (see php time function).
  • MerchantID - the merchant's identification number.
  • APIRequest - the API request in the form that is sent to the processor.
  • APIResponse - the API response in the form that is received from the processor.
  • Error - if the processor will return an error, it will be displayed here. This parameter is an array itself with the following indexes:
    • code - the error code (the code returned by the gateway or the error's internal code that appeared inside the plug-in).
    • location - the error location.
    • message - the error message.
    • severity - the error severity.
  • Amount - the amount of the invoice.
  • Currency - the currency used for the transaction.
  • TransactionID - the identification number returned by the Capture operation for the specific transaction.

RecurringPayment Method

The recurring payment is actually a referenced capture method, made using the subscription given by the AuthorizePayment plug-in method.

The available input parameters for this method are:

  • Currency - the currency used for the transaction.
  • OrderTotal - the total amount of the order placed on the public store.
  • InvoiceID - the identification number of the invoice being payed.
  • SubscriptionID - the identification number of the subscription returned by the AuthorizePayment plug-in method.

The requested output parameters for this method are:

  • ACK - will specify if the outcome of the request was success or failure.
  • BankRef - the possible bank response regarding the transaction.
  • Date - the date when the transaction was made (this should be an integer number (see php time function).
  • MerchantID - the merchant's identification number.
  • APIRequest - the API request in the form that is sent to the processor.
  • APIResponse - the API response in the form that is received from the processor.
  • Error - if the processor will return an error, it will be displayed here. This parameter is an array itself with the following indexes:
    • code - the error code (the code returned by the gateway or the error's internal code that appeared inside the plug-in).
    • location - the error location.
    • message - the error message.
    • severity - the error severity.
  • Amount - the amount of the invoice.
  • Currency - the currency used for the transaction.
  • TransactionID - the identification number returned by the Capture operation for the specific transaction.

VoidPayment Method

This method cancels the payment of an existing invoice. The payment status of the voided invoice can be only pending or authorized since the void method only releases the money that were blocked in the customer's bank account when the invoice was authorized.

The available input parameters for this method are:

  • Currency - the currency used for the transaction.
  • OrderTotal - the total amount of the order placed on the public store.
  • TransactionID - the identification number returned by the Authorize operation for the specific transaction.
  • InvoiceID - the identification number of the refunded invoice.

The requested output parameters for this method are:

  • ACK - will specify if the outcome of the request was success or failure.
  • BankRef - the possible bank response regarding the transaction.
  • Date - the date when the transaction was made (this should be an integer number (see php time function).
  • MerchantID - the merchant's identification number.
  • APIRequest - the API request in the form that is sent to the processor.
  • APIResponse - the API response in the form that is received from the processor.
  • Error - if the processor will return an error, it will be displayed here. This parameter is an array itself with the following indexes:
    • code - the error code (the code returned by the gateway or the error's internal code that appeared inside the plug-in).
    • location - the error location.
    • message - the error message.
    • severity - the error severity.
  • Amount - the amount of the invoice.
  • Currency - the currency used for the transaction.
  • TransactionID - the identification number returned by the Authorize operation for the specific transaction.

Creating a Subscription Method

For many gateways it is not possible to realize a referenced transaction using a transaction ID from operations already completed. In this case, there is a need for a work-around. Many of these gateways offer account management APIs which can be used afterwards for payments. Therefor, a new method that handles the account creation must be added to the payment plug-in class. This function will be called from the AuthorizePayment class method, but only if the Authorize call was successful. It is useless to create a subscription with all the details if the payment operation has failed.

After calling the subscription method from within the AuthorizePayment class method, if the subscription was a success, the SubscriptionID value that is returned by this method must be given a returned unique token that identifies the newly created subscription.

Considering this, the RecurringPayment class method will be made using this SubscriptionID. Be aware, though: this class method is only a referenced capture transaction and does not offer the gateway the possibility to charge the client freely. Every operation must be triggered from within the new plug-in and not by the gateway.

Commonly Utilized Plug-in Methods

For each plug-in there must be some common methods for returning errors, for finding the plug-in's root folder or for storing its common elements. To make things easier and for security purposes, every plug-in must contain a class named OnlinePaymentInterface.

<?php 
	class OnlinePaymentInterface {
	
		const method_preauth
		const method_processauth
		const method_auth
		const method_capture
		const method_refund
		const method_partial_refund
		const method_recurrent
		const method_subscribe
		
		public function LoadLanguagePack()
		
		public function GetPaymentPluginRoot()
		
		public function GetPluginParams()
		
		public function Translate(string $lp_key)
		
		public function RaiseError($code = '', $message = '', $location = ERR_PLUGIN_API, $severity = E_USER_ERROR)
		
		protected function ErrorAttachLogs($request, $response = null)
		
	}	
?> 

OnlinePaymentInterface::method_* method constants are used to specify the output for the payment plug-in class methods. This output will be further used by 4PSA VoipNow Automation store in order to log the transaction or to display the details to the client.

OnlinePaymentInterface::LoadLanguagePack function is used to load the language pack for the plug-in.

OnlinePaymentInterface::GetPaymentPluginRoot function will return the plug-in root folder needed for loading auxiliary files. E.g.: the required fields file.

OnlinePaymentInterface::GetPluginParams function should be used in the plug-in class constructor for loading the gateway credentials stored in the database.

OnlinePaymentInterface::Translate function is used for translating the language keys from the language packs. E.g.:

<?php 
	self::Translate('language_key');
?> 

OnlinePaymentInterface::RaiseError function is used for returning an error. The function's parameters will offer a code for each error, a message explaining what was wrong, a location for where the error occurred (inside the plug-in - ERR_PLUGIN_API, inside the handler - ERROR_PLUGIN_HANLDER or it ca be a custom location - ERROR_PLUGIN_CUSTOM) and a severity level (the severity level is defined with the help of the PHP error predefined constants).

OnlinePaymentInterface::ErrorAttachLogs function will help the developper to attach the function's inputs and outputs of each method (it is mandatory for the two parameters to have the same form that they are sent and received to and from the gateway) to the error message. This method will always be used before OnlinePaymentInterface::RaiseError in order for the message to be built correctly. Otherwise, the error message will not contain the two elements.

Plug-in XML Configure Syntax

Both the setup and the requirements files use the same XML syntax, the differences between these two files being minimal. Both files contain the XML header:

	<?xml version="1.0" encoding="UTF-8"?>

The root tag for both files has the pimmodule name and the following attributes:

  • name - the default name of the plug-in. This can be changed from the setup interface, but the changed name can only be stored in the database and not in the XML file.
  • uid - the plug-in's unique identifier. The identifier is the same as the plug-in class name.
  • version - the plug-in's version.
  • type - the plug-in's type:
    • payment - for payment plug-ins.
    • fraud - for fraud plug-ins.
  • subtype - The plug-in's subtype:
    • gateway - for payment processors that use credit card payment.
    • virtual - for virtual processors that use virtual payment accounts or agreements.
    • offline - for offline payment methods, like check or wire transfers.
	<pimmodule name="MODULE_NAME" uid="UID" version="VERSION" type="TYPE" subtype="SUBTYPE">
		<!-- config content -->
	</pimmodule>

Setup File

Here is an example of a configured field:

	<fieldset langname="LANGUAGE_KEY" collapse="1">
		<field langname="pe_endpoint" param="endpoint" type="text" required="1" size="large" validate="/^(ht|f)tp(s?)\:\/\/[0-9a-z]([-.\w]*[0-9a-z])*(:(0-9)*)*(\/?)([\~a-z0-9\-\.\?\=\,\'\/\\\+&%\$#_]*)?$/i" alert="regexp=pe_error_incorect_pe_endpoint,notempty=pe_error_invalid_pe_endpoint" default="https://pluginexample.foo.ext" />
	</fieldset>
  • title - it will change the VoipNow Automation page title under the setup page. The title is specified as a language key within the langname attribute. E.g.:
    	<title name="LANGUAGE_KEY" />
    
  • fieldset - it will group the fields into field sets for easier comprehension. The field list title is set using the same attribute as for the title tag. The field set can be configured in three states, defined by the collapse attribute with the following values:
    • 0 - the field set cannot be collapsed.
    • 1 - the field set can be collapsed, but it is not collapsed by default.
    • 2 - the field set can be and is by default collapsed.
    Example of a field set structure:
    	<fieldset langname="LANGUAGE_KEY" collapse="1">
    		<!-- config content -->
    	</fieldset>
    
    Within each field set, the tag field will be used to define the input fields needed for the setup section. The field tags can be formated through the following attributes:
    • langname - it will set the input's label and it is defined as a language pack key.
    • tip - it will set a text placed after the input.
    • param - it will define the input's name as it will be used in the setup section and stored in the database.
    • type - it will define the input's type:
      • text - HTML text input.
      • textarea - HTML textarea input.
      • select - HTML select input.
      • checkbox - HTML checkbox input.
      • selection_lists - it will display selection lists.
    • size - it will define the input's size. The size can be selected between small, medium, large and x-large.
    • required - it will specify if the input is required.
    • validate - it will define a regular expression for the inputs's validation.
    • alert - it will define the errors language keys for the two validation elements, required and validate. The error messages will have the following content: validation_key=error_language_key, e.g.
      	alert="regexp=pe_error_incorect_pe_endpoint,notempty=pe_error_invalid_pe_endpoint"
      
      A validation key can be:
      • notempty related to the required attribute.
      • array_notempty related to the required attribute of the selection_lists field that is described below and to the regexp attribute that defines the error for the validate regular expression.
    • default - it will define the field's default value.
    A field type that is slightly different from the others is selection_lists. This field offers the possibility of displaying two lists: one with the elements that can be selected and one with the elements that have already been selected. E.g.:
    	<field langname="pe_curency_title" param="currency" type="selection_lists" size="medium" required="1"  alert="array_notempty=pe_error_invalid_pe_curency_title">		
    		<lefttitle langname="pe_currency_left_title"/>	
    		<leftvalue value="USD"/>
    		<leftvalue value="CAD"/>
    		<righttitle langname="pe_currency_right_title"/>
    	</field>
    
    The difference between the selection_lists field and the other ones is that it contains specific tags:
    • lefttitle - it will define the title of the left list (the list the user selects from). The title will be defined through the attribute langname.
    • leftvalue - it will define items for the left list. The values of this list will be defined through the value attribute that will be used both as value and as language key.
    • righttitle - it will define the title of the right list (that shows the already selected items).
    • rightvalue - it will define items from the right list. These items will act as the default selected items, but only at the first load of the setup page. If the list is modified or saved, the setup page will then load the saved information.
    If the selection_lists field is used for selecting the currency and you need all the currencies 4PSA VoipNow Automation supports, in the lefttile tag, the attribute type can be used with the value all. E.g.:
    	<lefttitle langname="pe_currency_left_title" type="all">
    
    As well as the selection_lists element, the select element has it's own inner tag, fieldvalue that allows adding items to the select field. The value from this field will act the same way as the ones from leftvalue or rightvalue. E.g.:
    	<field langname="pe_select" param="selectex" type="select" required="1" size="medium" alert="notempty=pe_error_invalid_pe_endpoint" default="VALUE_01" />
    		<fieldvalue value="VALUE_01">
    		<fieldvalue value="VALUE_02">
    		<fieldvalue value="VALUE_03">
    	</field>
    

Requirements File

Unlike the setup file, the requirements file does not use the fieldset or the title tags. The requirements file will use a new tag named operation that has only one attribute, named, in order to specify the operation's name. This file has to inclued all the operations included in the OnlinePayment interface.

The content of the operation tag is 100% similar to the content of the fieldset tag from the setup file (see the section called “Requirements File”) with the mention that the tag selection_lists si not used.

Further more there will be little modification in the field tag attributes as well. Thus, the default one will be replaced with the position one. Its function is to establish the order of the inputs in the store form.

Plug-in Example

For this example a fake gateway named Plug-in Example, having the endpoint https://pluginexample.foo.ext and implementing a SOAP engine, will be used.

Setup File

<?xml version="1.0" encoding="UTF-8"?>
<pimmodule name="PluginExample" uid="pluginexample" version="1.0.0" type="onlinepayment" subtype="gateway">
    <title langname="pe_title_page"/>
	<fieldset langname="pe_lg_connection">
		<field langname="pe_endpoint" param="endpoint" type="text" required="1" size="xlarge" validate="/^(ht|f)tp(s?)\:\/\/[0-9a-z]([-.\w]*[0-9a-z])*(:(0-9)*)*(\/?)([\~a-z0-9\-\.\?\=\,\'\/\\\+&%\$#_]*)?$/i" alert="regexp=pe_error_incorect_pe_endpoint,notempty=pe_error_invalid_pe_endpoint" default="https://plugin.foo.ext" />
		<field langname="pe_username" param="username" type="text" required="1"  alert="notempty=pe_error_invalid_pe_username"/>
		<field langname="pe_password" param="password" type="text" size="large" required="1"  alert="notempty=pe_error_invalid_pe_password"/>
		<field langname="pe_curency_title" param="currency" type="selection_lists" size="medium" required="1"  alert="array_notempty=pe_error_invalid_pe_curency_title">		
			<lefttitle langname="pe_currency_left_title"/>	
			<leftvalue value="USD"/>
			
			<righttitle langname="pe_currency_right_title"/>
						
		</field>			
	</fieldset>
</pimmodule>

The setup page will display three input fields for the gateway's endpoint and merchant's username and password along with a selection list containing the available currency supported by gateway.

Requirements File

	<?xml version="1.0" encoding="UTF-8"?>
	<pimmodule name="Authorize" uid="authorize" version="1.0.0" type="onlinepayment" subtype="gateway">
		<operation id="PreAuthorisePayment"/>
		<operation id="ProcessPreAuthorisePayment"/>
		<operation id="AuthorisePayment">
			<field langname="pe_cc_number" param="CreditCardNumber" type="text" size="large" required="1" validate="/^[0-9]{6,25}$/" validate_error="pe_invalid_cc_number" position="2" />
			<block langname="pe_cc_expire" position="3" >
				<field  param="CardExpYear" type="select" size="small" required="1" validate_error="pe_invalid_cc_date_year" tip="/">
					<fieldvalue langname="2009" value="2009"/>
					<fieldvalue langname="2010" value="2010"/>
					<fieldvalue langname="2011" value="2011"/>
					<fieldvalue langname="2012" value="2012"/>
					<fieldvalue langname="2013" value="2013"/>
					<fieldvalue langname="2014" value="2014"/>
					<fieldvalue langname="2015" value="2015"/>
				</field>
				<field  param="CardExpMonth" type="select" size="small" required="1" validate_error="pe_invalid_cc_date_month" tip="pe_cc_expire_tip">
					<fieldvalue langname="01" value="01"/>
					<fieldvalue langname="02" value="02"/>
					<fieldvalue langname="03" value="03"/>
					<fieldvalue langname="04" value="04"/>
					<fieldvalue langname="05" value="05"/>
					<fieldvalue langname="06" value="06"/>
					<fieldvalue langname="07" value="07"/>
					<fieldvalue langname="08" value="08"/>
					<fieldvalue langname="09" value="09"/>
					<fieldvalue langname="10" value="10"/>
					<fieldvalue langname="11" value="11"/>
					<fieldvalue langname="12" value="12"/>
				</field>
			</block>
			<field langname="pe_cc_cvv2" param="CardSecurityCode" type="text" size="small" required="1" validate="/^[0-9]{3,4}$/" validate_error="pe_invalid_cc_ccv2" position="3" />
		</operation>
		<operation id="CapturePayment"/>
		<operation id="GetTransactionDetails"/>
		<operation id="RefundTransaction">
			<field langname="pe_refund_amount" param="Amount" type="text" size="medium" validate="/^(\-?)[0-9]+(\.[0-9]+){0,1}$/i" validate_error="pe_invalid_cc_refund_amount" position="1" />
		</operation>
		<operation id="RecurringPayment"/>
	</pimmodule>

Requirements file has specified that for the Authorize method there are 4 fields required for CreditCardNumber, CardExpMonth, CardExpYear and CardSecurityCode parameters; and for the Refund method a field for the Amount needed to be refunded. Please notice that expiration date is grouped and that all existing methods are present in the xml file even though only two have configured fields. The presence of all methods in the xml file is mandatory.

Input Requirements

<?php 
	$_required_params = array(
		'AuthorisePayment' => array(
			'CreditCardNumber',
			'CardExpMonth',
			'CardExpYear',
			'CardSecurityValue',
			
			'OrderTotal',
			'InvoiceID'
		),
		'CapturePayment' => array(
			'TransactionID',
		),
		'GetTransactionDetails' => array(
			'TransactionID',
		),
		'RefundTransaction' => array(
			'TransactionID',
			'OrderTotal',
		),
		'RecurringPayment' => array(
			'SubscriptionID',
			'OrderTotal',
			'InvoiceID'
		),
	);
	
  ?> 
  

Language File

<?php 
	$plugin_msg_arr = array();
	
	$plugin_msg_arr['pe_api_10002'] = 'Security Data : MerchantToken authentication failed.';

	$plugin_msg_arr['pe_api_connect'] = 'Unable to connect to Plug-in Example endpoint.';
	$plugin_msg_arr['pe_api_notset_param'] = 'This transaction cannot be processed. Missing parameter {param}.';
	
	/* Plug-in custom errors */	
	$plugin_msg_arr['pe_invalid_cc_type'] = 'Credit card type must not be empty.';
	$plugin_msg_arr['pe_invalid_cc_number'] = 'The credit card number must be a number between 6 and 25 digits.';
	$plugin_msg_arr['pe_invalid_cc_date_month'] = 'The credit card expire date month must be a number between 1 and 12.';
	$plugin_msg_arr['pe_invalid_cc_date_year'] = 'The credit card expire year must be a 4 digit number greater or equal to the current year.';
	$plugin_msg_arr['pe_invalid_cc_refund_amount'] = 'The refund amount must be a number greater than 0.';
	$plugin_msg_arr['pe_invalid_cc_ccv2'] = 'The Card Validation Code must be a 4 digit number for American Express and 3 digit number for all other cards. You can find this number on the back of the credit card.';

	$plugin_msg_arr['pe_error_incorect_pe_endpoint'] = 'The url for Plug-in Example endpoint is invalid. Please fill in a valid url.';
	$plugin_msg_arr['pe_error_invalid_pe_endpoint'] = 'Please fill in the Plug-in Example endpoint url.';
	$plugin_msg_arr['pe_error_incorect_pe_username'] = 'The API version is incorrect. Please fill in a valid version.';
	$plugin_msg_arr['pe_error_invalid_pe_password'] = 'Please fill in the password.';
	$plugin_msg_arr['pe_error_invalid_pe_username'] = 'Please fill in the Merchant ID.';
	$plugin_msg_arr['pe_error_invalid_pe_curency_title'] = 'Please select at least one currency.';

	/* Page titles */
	$plugin_msg_arr['pe_title_page'] = 'Example Plug-in';

	/* Fieldset title definitions */
	$plugin_msg_arr['pe_lg_connection'] = 'Example Plug-in configuration';

	/* Plug-in configuration parameters */
	$plugin_msg_arr['pe_endpoint'] = 'Plug-in Example endpoint';
	$plugin_msg_arr['pe_username'] = 'Merchant';
	$plugin_msg_arr['pe_password'] = 'Password';

	/* Plug-in required parameters */
	$plugin_msg_arr['pe_cc_number'] = 'Credit card number';
	$plugin_msg_arr['pe_cc_expire'] = 'Credit card expiration';
	$plugin_msg_arr['pe_transaction_cost'] = 'Charge amount';
	$plugin_msg_arr['pe_cc_expire_tip'] = '(yyyy/mm)';
	$plugin_msg_arr['pe_cc_cvv2'] = 'Card verification value';

	$plugin_msg_arr['pe_amount'] = 'Capture amount';

	$plugin_msg_arr['pe_refund_amount'] = 'Refund amount';

	$plugin_msg_arr['pe_recurring_cost'] = 'Charge amount';

	$plugin_msg_arr['pe_curency_title'] = 'Currencies';
	$plugin_msg_arr['pe_currency_left_title'] = 'Available currencies';
	$plugin_msg_arr['pe_currency_right_title'] = 'Currencies in use';

	$plugin_msg_arr['USD']='US Dollar';
	
?> 

Plug-in Class

<?php 
class pluginexample extends OnlinePaymentAbstract {

/**
* Operation constants
*/
const OPERATION_AUTHORIZE = 'AUTHORIZE';
const OPERATION_CAPTURE = 'CAPTURE';
const OPERATION_REFUND = 'REFUND';
const OPERATION_GET_DETAILS = 'DETAILS';
const OPERATION_REFERENCED = 'REFERENCED';
const OPERATION_VOID = 'VOID';
const OPERATION_CREATE_SUBSCRIPTION = 'CREATE_SUBSCRIPTION';
const OPERATION_VERIFY_SUBSCRIPTION = 'VERIFY_SUBSCRIPTION';

	/**
	 * @var {string} username
	 *
	 * @see pluginexample::__construct()
	 */
	private $_username = '';
	
	/**
	 * @var {string} password
	 *
	 * @see pluginexample::__construct()
	 */
	private $_password = '';

	/**
	 * @var {string} endpoint
	 *
	 * @see pluginexample::__construct()
	 * @see pluginexample::__call()
	 */
	static private $_endpoint = 'http://pluginexample.foo.ext';
	
	/**
	 * @var {object} client
	 *
	 * @see pluginexample::__construct()
	 * @see pluginexample::__call()
	 */
	private $client = null;
	
	/**
	 * Class constructor: implements connection to endpoint
	 *
	 * @return boolean
	 */
	public function __construct() {
		
		/* query plug-in setup data */
		$payment_plugin_data = self::GetPluginParams();
		
		/* setup protocol endpoint */
		if  (isset($payment_plugin_data['endpoint'])) {
			$endpoint = $payment_plugin_data['endpoint'];
		} else {
			$endpoint = $this->_endpoint;
		}
		
		/* Set authentication: headers */
		if (!isset($payment_plugin_data['username']) || 
			!isset($payment_plugin_data['password'])) {
			// push error: code taken from reference guide
			return self::RaiseError('10002', self::Translate('pp_api_10002'), ERR_PLUGIN_CUSTOM);
		}
		
		/* Create SOAP client based on WSDL, with trace for debugging */
		$this->client = new SoapClient($endpoint);
								
		/* Authentication */
		$credentials = array(
			'credentials' => array(
				'Username'  => $payment_plugin_data['username'],
				'Password'  => $payment_plugin_data['password'],
			)
		);
		$headers = new SoapHeader('credentials', 'credentials', $credentials);
		
		$this->client->__setSoapHeader($headers);
	
		return;
	}
	
	
	/**
	 * Call method
	 *
	 * @return {array}
	 *
	 */
	private function __call($method, $package, $log_request = false) {
		/* call request method */
		$result = $this->client->__soapCall($method, array($package), null);	
		
		/* enqueue protocol errors */
		if (is_soap_fault($result)) {
			if ($log_request) {
				self::ErrorAttachLogs($this->client->__getLastRequest(), $this->client->__getLastResponse());
			}
			return self::RaiseError($result->faultcode, $result->faultstring, ERR_PLUGIN_API);
		}
		
		/* check PluginExample custom errors */
		// here you should check if the returned answer does not have other type of errors
		
		/* transform all results to array structures */
		// here you should convert the answer into an array based structure

		// We will assume further that $result indexes are the indexes returned by this gateway
		$result['ACK'] = (isset($result['Ack'])  $result['Ack'] != 'Success') ? 'failure' : 'success';
		$result['Date'] = strtotime($result['Timestamp']);
		$result['Currency'] = $result['currencyID'];
		$result['BankRef'] = $result['CorrelationID'];
		$result['MerchantID'] = '';
		if ($log_request) {
			$result['APIRequest'] = $this->client->__getLastRequest();
			$result['APIResponse'] = $this->client->__getLastResponse();
		}
		switch ($method) {
			case 'Authorize': {
				// We assumed that TransactionID already exists from the soap response
				$result['SubscriptionID'] = $result['TransactionID'];
				$result['Amount'] = $result['AuthoriseAmount'];
				break;
			}
			case 'Capture': {
				$result['Amount'] = $result['CaptureAmount'];
				break;
			}
			case 'Refund': {
				$result['Amount'] = $result['RefundAmount'];
				break;
			}
			case 'ReferencedAuthorize': {
				$result['SubscriptionID'] = $result['TransactionID'];
				$result['Amount'] = $result['Amount'];
				break;
			}
			case 'GetTransactionDetails': {
				break;
			}
		}
		if (isset($result['Ack'])) {
			unset($result['Ack']);
		}		
		return $result;
	}
	
	/**
	* Checks required params in package for a specific method
	*
	* @param params array with input parameters
	* @return array with required fields empty
	*
	*/
	final private function __check($params) {
		/* include requirements file */
		require_once self::GetPaymentPluginRoot() . '/required_inc.php';
		
		// here you check if the parameters given to the function are according to the 
		// ones from the required_inc.php file.
		// if any of the parameters from required_inc.php for the called function is 
		// missing, e suggest the return of the following error
		//
		//	$msg = self::Translate('pe_api_notset_param');
		//	$msg = str_replace('{param_name}', $param_name, $msg)
		//	return self::RaiseError('PARAM_MISSING', $msg, ERR_PLUGIN_CUSTOM);
		//
		return true;
	}
	/* 
	 * pluginexample::PreauthorisePayment
	 * 
	 * We will assume this function is not supported by this plug-in
	 * (which in most credit card gateways it true)
	 */
	public function PreAuthorisePayment($params) {
		$msg = self::Translate('pe_api_missing_method');
		$msg = str_replace('{method}', 'PreAuthorisePayment', $sg);		
		return self::RaiseError('METHOD_MISSING', $msg, ERR_PLUGIN_CUSTOM);
	}
	
	/*
	 * Process a credit card payment
	 *
	 * @return SOAP method response
	 */
	final public function AuthorisePayment($params) {
		
		
		/* check SOAP  client resource and package headers */
		if (!$this->client) {
			self::__construct();
		}
		if (!$this->client) {
			self::RaiseError(0100, self::Translate('pe_client_could_not_connect'), ERR_PLUGIN_API);
		}
		
		// RaiseError function can also be used like this
		// the OnlinePaymentInterface function will keep the error from 
		// __check function and pass it to the new empty call
		if (!$this->__check($params)) {
			return self::RaiseError();
		}
		
		// We will assume that the all the soap variables are the same with the ones
		// in the documentation. This will shorten the example of a very long 
		// unnecessary index mapping
		
		/* call request method */
		$result = array();
		$result[parent::method_auth] = self::__call('Authorize', $params, true);
		
		/* credit card info */
		if ($result[parent::method_auth]['ACK'] == 'success') {
			$result[parent::method_auth]['CardNumberEnding'] = substr($params['CreditCardNumber'], -4);
			$result[parent::method_auth]['CardExpMonth'] = $params['CardExpMonth'];
			$result[parent::method_auth]['CardExpYear'] = $params['CardExpYear'];
		}
		
		return $result;
	}	
	
	// further implementations of the other methods
	
}
?>