Manual Version 63748.4 at 2010/03/03 14:17:56
For suggestions regarding this manual contact:
docs@4psa.com
Copyright © 2002-2010 Rack-Soft, Inc.
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
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.
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.
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.
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.
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:
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 } ?>
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:
Create a folder named /language in the plug-in root.
Create one file for each language defined in the 4PSA VoipNow control panel and place it inside the /language folder.
You must name the file with the language code in order to have effect. (E.g.: en.php)
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.'; ?>
In order to be functional, you must keep the array name.
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); } // ... } } ?>
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”.
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.
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”.
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/.
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:
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 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.
The interactions between 4PSA VoipNow Automation interface and the payment plug-ins are being recognized at two levels:
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.
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.
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.
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; } } ?>
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:
Requested method output parameters are:
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:
The requested output parameters for this method are:
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:
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:
The requested output parameters for this method are:
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:
The requested output parameters for this method are:
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:
The requested output parameters for this method are:
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.
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.
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:
<pimmodule name="MODULE_NAME" uid="UID" version="VERSION" type="TYPE" subtype="SUBTYPE"> <!-- config content --> </pimmodule>
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 name="LANGUAGE_KEY" />
<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:
alert="regexp=pe_error_incorect_pe_endpoint,notempty=pe_error_invalid_pe_endpoint"A validation key can be:
<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 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>
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.
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.
<?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.
<?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.
<?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' ), ); ?>
<?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'; ?>
<?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 } ?>