This page describes a plug-in example, where a fake gateway named Plug-in Example, having the endpoint https://pluginexample.foo.ext and implementing a SOAP engine, will be used.

Setup file

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.

<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>

Requirements file

The Requirements file has specified that:

Please note 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.

	<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>

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
	
}
?>