Location: PHPKode > scripts > XPay System > xpay-system/XPaySystem.php
<?php
/**
 * Class for payment system.
 * @version 0.2
 * @author Shaymardanov Maxim
 */
class XPaySystem {
	
	/**
	 * Constructor for XPaySystem class
	 * 
	 * @access public
	 * @param String $UrlXPay internet address
	 * @param Integer $portXPay internet port
	 * @param String $siteReference affiliate / partner
	 * @param String $uriCertificateFile path to certificate file
	 * @param Array $httpCustomerInfo http customer info.  Is format: 
	   array(
	      'accept' => 'https://merchantSite.net/3dauth.cgi'
	     ,'user_agent' => '25 character field'
	   );
	 * @param Array $customerInfo customer info. Not required. Is format: 
	   array(
	     'name_prefix' => 'Mr.'
	     ,'first_name' => 'Joe'
	     ,'middle_name' => 'A.'
	     ,'last_name' => 'Bloggs'
	     ,'name_suffix' => 'CEng.'
	     ,'company' => 'A COMPANY'
	     ,'street' => 'A STREET'
	     ,'city' => 'A CITY'
	     ,'state_prov' => 'A STATE'
	     ,'postal_code' => 'TE2 3ST'
	     ,'country' => 'Russia'
	     ,'phone' => '0000 111111'
	     ,'email' => 'hide@address.com'
	   );
	 * @param Boolean $liveStatus status of certificate test. Default is 'false' and not required.
	 * @return XPaySystem
	 * @throws InvalidArgumentException
	 * @throws XPayException
	 */
	public function XPaySystem($urlXPay, $portXPay, $siteReference, $uriCertificateFile, array $httpCustomerInfo, array $customerInfo = null, $testStatus = false) {
		/* Validation */
		if ($urlXPay == null || $urlXPay == '') {
			throw new InvalidArgumentException('Net address to XPay server is empty');
		}
		if ($portXPay == null || $portXPay == '') {
			throw new InvalidArgumentException('Net port to XPay server is empty');
		}
		if ($siteReference == null || $siteReference == '') {
			throw new InvalidArgumentException('Affiliate / Partner ID is empty');
		}
		if ($uriCertificateFile == null || $uriCertificateFile == '') {
			throw new InvalidArgumentException('Certificate File (*.pem file) is empty');
		}
		if (is_file($uriCertificateFile) == false) {
			throw new InvalidArgumentException("URI certificate File: '$uriCertificateFile' is not URI file");
		}
		
		/* Initializations */
		$lastReqest = null;
		$lastResponse = null;
		$this->urlXPay = $urlXPay;
		$this->portXPay = (int) $portXPay;
		$this->siteReference = $siteReference;
		$this->certificate = file_get_contents($uriCertificateFile);
		$this->setHttpCustomerInfo($httpCustomerInfo);
		$this->setCustomerInfo($customerInfo);
		if ($testStatus == false) {
			$this->liveStatus = 'TRUE';
		} else {
			$this->liveStatus = 'FALSE';
		}
		$this->socket = null;
	}
	
	/**
	 * A secureCardQuery request is the process used to determine if a credit card 
	 * is in the 3-D Secure scheme.
	 * 
	 * @access public
	 * @param Array $operationInfo operation info:<br/>
	 * <pre>array(
	 *   'price' => 4500
	 *  ,'currency'=> 'USD'
	 * );</pre>
	 * @param Array $cardInfo card info
	   array(
		    'card_type' => 'Visa'                //Type card
		   ,'card_number' => '1111222233334444'  //Number card
		   ,'security_code' => '123'             //Security Code (CVC)
		   ,'expiry_date' => '09/08'             //Date of expiry card.
		   ,'start_date' => '12/07'              //Date of start card.
		   ,'issue' => '01'                      //Number of issue card
	   );
	 * @param $termUrl 
	 * @param $merchantName
	 * @param String $orderReference order reference can contain any combination  
	 * of alphanumeric characters. Not required.
	 * @param String $orderInformation order information can contain any 
	 * combination of alphanumeric characters. Not required.
	 * @return SimpleXMLElement
	 * @throws XPayException
	 * @throws InvalidArgumentException
	 * @throws Exception (network)
	 */
	public function secureCardQuery(array $operationInfo, array $cardInfo, $termUrl, $merchantName, $orderReference = null, $orderInformation = null) {
		/* Validation */
		if ($termUrl == null || $termUrl == '') {
			throw new InvalidArgumentException('Term URL is empty');
		}
		if ($merchantName == null || $merchantName == '') {
			throw new InvalidArgumentException('Merchant name is empty');
		}
		else if (strlen($merchantName) > self::AMOUNT_CHARS_IN_MERCHANT_NAME) {
			throw new InvalidArgumentException(
		    'Merchant name=\'' . $merchantName . '\' is not valid. ' . 
		    'String length \'merchant name\' is ' . strlen($merchantName) . '. ' .
		    'Maximum number of characters is ' . self::AMOUNT_CHARS_IN_MERCHANT_NAME);
		}
		//Get socket connetcion
		$this->socket = $this->getSocketConnect();
		
		//Create customer info xml
		$customerInfoXml = $this->getCustomerInfoXml();
		$orderXml = $this->getOrderXml($orderReference, $orderInformation);
		$request = 
			'<RequestBlock Version="' . self::XPAY_VERSION_PROTOCOL . '">' .
			'<Request Type="ST3DCARDQUERY">' .
				'<Operation>' . 
					'<Amount>' . $operationInfo['price'] . '</Amount>' .                    //required
					'<Currency>' . $operationInfo['currency'] . '</Currency>' . 
					'<SiteReference>' . $this->siteReference . '</SiteReference>' .         //required
					'<TermUrl>' . $termUrl . '</TermUrl>' .                                 //required
					'<MerchantName>' . $merchantName . '</MerchantName>' .                  //required
				'</Operation>' . 
					$customerInfoXml .
				'<PaymentMethod>' . 
					'<CreditCard>' . 
						'<Type>' . $cardInfo['card_type'] . '</Type>' .                       //required
						'<Number>' . $cardInfo['card_number'] . '</Number>' .                 //required
						'<Issue>' . $cardInfo['issue'] . '</Issue>' . 
						'<StartDate>' . $cardInfo['start_date'] . '</StartDate>' .  
						'<ExpiryDate>' . $cardInfo['expiry_date'] . '</ExpiryDate>' .         //required
					'</CreditCard>' .
				'</PaymentMethod>' . 
				$orderXml . 
			'</Request>' .
			'<Certificate>' . $this->certificate . '</Certificate>' .
			'</RequestBlock>';
			
		$this->setLastRequest($request);
		$result === socket_write($this->socket, $request);
		$XmlResponse = null;
		while ($out = socket_read($this->socket, 2048)) {
		   $XmlResponse .= $out;
		}
		$this->setLastResponse($XmlResponse);
		socket_close($this->socket);
		$this->socket = null;
		//Dell 1 string
		$XmlResponse = substr_replace($XmlResponse, '', 0, 16);
		
		//Process response
		$objXml = simplexml_load_string($XmlResponse);
		$data = $objXml->Response->OperationResponse;
		if ($data->Result == 0) {
			//Error
			throw new XPayException($data->Message, $data->TransactionReference, 0);
		}
		//Payment is passed
		if ($data->Result == 1 || $data->Result == 2) {
			return $data;
		}
	}	
	
	/**
	 * An authorisation request involves the sending of a credit or debit card 
	 * authorisation to an acquiring bank. 
	 * 
	 * @param Array $operationInfo operation info:<br/>
	 * <pre>array(
	 *   'price' => 4500
	 *  ,'currency'=> 'USD'
	 * );</pre>
	 * @param Array $cardInfo card info
	   array(
		    'card_type' => 'Visa'                //Type card
		   ,'card_number' => '1111222233334444'  //Number card
		   ,'security_code' => '123'             //Security Code (CVC)
		   ,'expiry_date' => '09/08'             //Date of expiry card.
		   ,'start_date' => '12/07'              //Date of start card.
		   ,'issue' => '01'                      //Number of issue card
	   );
	 * @param String $orderReference order reference can contain any combination 
	 * of alphanumeric characters. Not required.
	 * @param String $orderInformation order information can contain any 
	 * combination of alphanumeric characters. Not required.
	 * @return Object(SimpleXMLElement)
			 public 'TransactionReference' => string '21-5-1490593'
			 public 'TransactionCompletedTimestamp' => string '2008-08-04 03:33:38'
			 public 'AuthCode' => string 'AUTH CODE:013840'
			 public 'TransactionVerifier' => string 'A/erdfhket624n9gvAmrrghdeftreth'
			 public 'Result' => string '1'
			 public 'SettleStatus' => string '0' (length=1)
			 public 'SecurityResponseSecurityCode' => string '2'
			 public 'SecurityResponsePostCode' => string '1'
			 public 'SecurityResponseAddress' => string '1'
	 * @access public
	 * @throws XPayException
	 * @throws Exception
	 */
	public function authorisation(array $operationInfo, array $cardInfo, $orderReference = null, $orderInformation = null) {
		//Get socket connetcion
		$this->socket = $this->getSocketConnect();
		
		//Create customer info xml
		$customerInfoXml = $this->getCustomerInfoXml();
		$orderXml = $this->getOrderXml($orderReference, $orderInformation);
		$request = 
	    '<RequestBlock Live="' . $this->liveStatus . '" Version="' . self::XPAY_VERSION_PROTOCOL . '">' .
	      '<Request Type="AUTH">' . 
	        '<Operation>' .                                                        //required
	          '<Amount>' . $operationInfo['price'] . '</Amount>' .                 //required
	          '<Currency>' . $operationInfo['currency'] . '</Currency>' . 
	          '<SiteReference>' . $this->siteReference . '</SiteReference>' .      //required
	          '<SettlementDay>' . 1 . '</SettlementDay>' . 
	        '</Operation>' . 
	        $customerInfoXml .
	        '<PaymentMethod>' .                                                    //required
	          '<CreditCard>' . 
	            '<Type>' . $cardInfo['card_type'] . '</Type>' .                    //required
	            '<Number>' . $cardInfo['card_number'] . '</Number>' .              //required
	            '<SecurityCode>' . $cardInfo['security_code'] . '</SecurityCode>' . 
	            '<Issue>' . $cardInfo['issue'] . '</Issue>' . 
	            '<StartDate>' . $cardInfo['start_date'] . '</StartDate>' .  
	            '<ExpiryDate>' . $cardInfo['expiry_date'] . '</ExpiryDate>' .      //required
	          '</CreditCard>' . 
	        '</PaymentMethod>' . 
	        $orderXml .
	      '</Request>' . 
	      '<Certificate>' . $this->certificate . '</Certificate>' . 
	    '</RequestBlock>';

		$this->setLastRequest($request);
		$result === socket_write($this->socket, $request);
		$XmlResponse = null;
		while ($out = socket_read($this->socket, 2048)) {
		   $XmlResponse .= $out;
		}
		$this->setLastResponse($XmlResponse);
		socket_close($this->socket);
		$this->socket = null;
		//Dell 1 string
		$XmlResponse = substr_replace($XmlResponse, '', 0, 16);
		//Process response
		$objXml = simplexml_load_string($XmlResponse);
		$data = $objXml->Response->OperationResponse;
		if ($data->Result == 0) {
			//Error
			throw new XPayException($data->Message, $data->TransactionReference, 0);
		}
		if ($data->Result == 2) {
			//Payment is not passed
			throw new XPayException($data->AuthCode, $data->TransactionReference, 2);
		}
		//Payment is passed
		if ($data->Result == 1) {
			return $data;
		}
	}
	
	/**
	 * An secure authorisation request involves the sending of a credit or debit card 
	 * secure authorisation to an acquiring bank.
	 * 
	 * @access public
	 * @param Array $operationInfo operation info:<br/>
	   array(
	     'price' => 4500
	    ,'currency'=> 'USD'
	   );
	 * @param Array $cardInfo card info. Is format:
	   array(
		    'card_type' => 'Visa'                //Type card
		   ,'card_number' => '1111222233334444'  //Number card
		   ,'security_code' => '123'             //Security Code (CVC)
		   ,'expiry_date' => '09/08'             //Date of expiry card.
		   ,'start_date' => '12/07'              //Date of start card.
		   ,'issue' => '01'                      //Number of issue card
	   );
	 * @param $secureInfo ThreeDSecure. Is format:
	   array(
		    'enrolled' => 'Y'
		   ,'pa_res' => 'ABJASDKA+SDKAJ/SGDSAD'
		   ,'md' => 'LFKH/DSPLFIHPO+SEHFIKEHFEFDES'
	   );
	 * @param String $orderReference order reference can contain any combination  
	 * of alphanumeric characters. Not required.
	 * @param String $orderInformation order information can contain any 
	 * combination of alphanumeric characters. Not required.

	 * @throws XPayException
	 * @throws InvalidArgumentException
	 * @throws Exception (network)
	 */
	public function secureAuthorisation(array $operationInfo, array $cardInfo, $secureInfo, $parentTransactionReference, $orderReference = null, $orderInformation = null) {
 		/* Validation */
		if ($parentTransactionReference == null || $parentTransactionReference == '') {
			throw new InvalidArgumentException('Parent transaction reference is empty');
		}
		//Get socket connetcion
		$this->socket = $this->getSocketConnect();
		
		//Create customer info xml
		$customerInfoXml = $this->getCustomerInfoXml();
		$secureInfoXml = $this->getSecureInfoXml($secureInfo);
		$orderXml = $this->getOrderXml($orderReference, $orderInformation);
		$request = 
			'<RequestBlock Version="' . self::XPAY_VERSION_PROTOCOL . '">' .
			  '<Request Type="ST3DAUTH">' .
	        '<Operation>' .                                                       //required
	          '<Amount>' . $operationInfo['price'] . '</Amount>' .                //required
	          '<Currency>' . $operationInfo['currency'] . '</Currency>' . 
	          '<SiteReference>' . $this->siteReference . '</SiteReference>' .     //required
	          '<SettlementDay>' . 1 . '</SettlementDay>' . 
	        '</Operation>' . 
						$customerInfoXml .
			    '<PaymentMethod>' .
			      '<CreditCard>' .
	            '<Type>' . $cardInfo['card_type'] . '</Type>' .                    //required
	            '<Number>' . $cardInfo['card_number'] . '</Number>' .              //required
	            '<SecurityCode>' . $cardInfo['security_code'] . '</SecurityCode>' . 
	            '<Issue>' . $cardInfo['issue'] . '</Issue>' . 
	            '<StartDate>' . $cardInfo['start_date'] . '</StartDate>' .  
	            '<ExpiryDate>' . $cardInfo['expiry_date'] . '</ExpiryDate>' .      //required
			        '<ParentTransactionReference>' . $parentTransactionReference . '</ParentTransactionReference>' . 
			      '</CreditCard>' . 
						$secureInfoXml .
			    '</PaymentMethod>' .
			    $orderXml .
			  '</Request>' .
			  '<Certificate>' . $this->certificate . '</Certificate>' . 
			'</RequestBlock>';

		$this->setLastRequest($request);
		$result === socket_write($this->socket, $request);
		$XmlResponse = null;
		while ($out = socket_read($this->socket, 2048)) {
		   $XmlResponse .= $out;
		}
		$this->setLastResponse($XmlResponse);
		socket_close($this->socket);
		$this->socket = null;
		//Dell 1 string
		$XmlResponse = substr_replace($XmlResponse, '', 0, 16);
		//Process response
		$objXml = simplexml_load_string($XmlResponse);
		$data = $objXml->Response->OperationResponse;
		if ($data->Result == 0) {
			//Error
			throw new XPayException($data->Message, $data->TransactionReference, 0);
		}
		if ($data->Result == 2) {
			//Payment is not passed
			throw new XPayException($data->AuthCode, $data->TransactionReference, 2);
		}
		//Payment is passed
		if ($data->Result == 1) {
			return $data;
		}
	}
	
	/**
	 * An authorisation reversal request involves the cancellation of an 
	 * unsettled credit card transaction. 
	 * Note: After an authorisation reversal has taken place the transaction 
	 * cannot be settled. 
	 * @param String $parentTransactionReference the unique SecureTrading 
	 * transaction reference for that transaction.
	 * @param String $transactionVerifier
	 * @param String $orderReference order reference can contain any combination 
	 * of alphanumeric characters. Not required.
	 * @param String $orderInformation order information can contain any 
	 * combination of alphanumeric characters. Not required.
	 * @return Object(SimpleXMLElement)
			 public 'TransactionReference' => string '23-5-1451082'
			 public 'TransactionCompletedTimestamp' => string '2008-08-04 04:50:11'
			 public 'AuthCode' => string 'REVERSAL OK'
			 public 'Result' => string '1'
	 * @access public
	 * @throws XPayException
	 * @throws Exception
	 */
	public function authorisationReversal($parentTransactionReference, $transactionVerifier, $orderReference = null, $orderInformation = null) {		
		//Get socket connetcion
		$this->socket = $this->getSocketConnect();
		
		//Create customer info xml
		$customerInfoXml = $this->getCustomerInfoXml();
		$orderXml = $this->getOrderXml($orderReference, $orderInformation);
		$request = 
		    '<RequestBlock Live="' . $this->liveStatus . '" Version="' . self::XPAY_VERSION_PROTOCOL . '">' .
		      '<Request Type="AUTHREVERSAL">' . 
		        '<Operation>' .                                           //required
		          "<SiteReference>$this->siteReference</SiteReference>" . //required
		        '</Operation>' . 
		        $customerInfoXml .
		        '<PaymentMethod>' .                                       //required
		          '<CreditCard>' . 
		            '<TransactionVerifier>' . $transactionVerifier . '</TransactionVerifier>' .
		            '<ParentTransactionReference>' . $parentTransactionReference . '</ParentTransactionReference>' .  
		          '</CreditCard>' . 
		        '</PaymentMethod>' . 
		        $orderXml .
		      '</Request>' . 
		      "<Certificate>$this->certificate</Certificate>" . 
		    '</RequestBlock>';
		
		$this->setLastRequest($request);
		$result === socket_write($this->socket, $request);
		$XmlResponse = null;
		while ($out = socket_read($this->socket, 2048)) {
		   $XmlResponse .= $out;
		}
		$this->setLastResponse($XmlResponse);
		socket_close($this->socket);
		$this->socket = null;
		//Dell 1 string
		$XmlResponse = substr_replace($XmlResponse, '', 0, 16);
		//Process response
		$objXml = simplexml_load_string($XmlResponse);
		$data = $objXml->Response->OperationResponse;
			if ($data->Result == 0) {
			//Error
			throw new XPayException($data->Message, $data->TransactionReference, 0);
		}
		//Payment is passed
		if ($data->Result == 1) {
			return $data;
		}
	}
	
	/**
	 * 
	 * (Public, Method no tested) A refund request involves the crediting of a credit card after the 
	 * settlement of a successful
	 * @param Integer $amount - money is formats: 9,45$ = 945 cent = (9 $, 45 cent)
	 * @param String $parentTransactionReference
	 * @param String $transactionVerifier
	 * @throws InvalidArgumentException
	 * @throws Exception
	 */
	private function refund($amount, $parentTransactionReference, $transactionVerifier) {
		//Validation amount
		//$amount = DataValidation::validAmount($amount);
		
		//Get socket connetcion
		$this->socket = $this->getSocketConnect();
		
		//Create customer info xml
		$customerInfoXml = $this->getCustomerInfoXml();
		$request = 
		    '<RequestBlock Live="' . $this->liveStatus . '" Version="3.51">' .
		      '<Request Type="REFUND">' . 
		        '<Operation>' .
		          '<Amount>' . $amount . '</Amount>' .                    //required
		          "<SiteReference>$this->siteReference</SiteReference>" . //required
		        '</Operation>' . 
		        $customerInfoXml .
		        '<PaymentMethod>' .
		          '<CreditCard>' . 
		            '<ParentTransactionReference>' . $parentTransactionReference . '</ParentTransactionReference>' . 
		            '<TransactionVerifier>' . $transactionVerifier . '</TransactionVerifier>' .
		          '</CreditCard>' . 
		        '</PaymentMethod>' . 
		      '</Request>' . 
		      "<Certificate>$this->certificate</Certificate>" . 
		    '</RequestBlock>';
   
		$result === socket_write($this->socket, $request);
		$response = null;
		while ($out = socket_read($this->socket, 2048)) {
		   $response .= $out;
		}
		socket_close($this->socket);
		$this->socket = null;
		return $response;
		//$objectXml = simplexml_load_string($response);
	}
	
	/**
	 * (Public, Method no tested) A refund reversal involves reversing a refund on an unsettled refund request.
	 * @param String $parentTransactionReference the unique SecureTrading 
	 * transaction reference for that transaction.
	 * @param String $transactionVerifier
	 * @param String $orderReference order reference can contain any combination 
	 * of alphanumeric characters. Not required.
	 * @param String $orderInformation order information can contain any 
	 * combination of alphanumeric characters. Not required.
	 * @throws InvalidArgumentException
	 * @throws Exception
	 */
	private function refundReversal($parentTransactionReference, $transactionVerifier, $orderReference = null, $orderInformation = null) {
		//Get socket connetcion
		$this->socket = $this->getSocketConnect();
		
		//Create customer info xml
		$customerInfoXml = $this->getCustomerInfoXml();
		$orderXml = $this->getOrderXml($orderReference, $orderInformation);
		$request = 
		    '<RequestBlock Live="' . $this->liveStatus . '" Version="3.51">' .
		      '<Request Type="REFUNDREVERSAL">' . 
		        '<Operation>' .                                           //required
		          "<SiteReference>$this->siteReference</SiteReference>" . //required
		        '</Operation>' . 
		        $customerInfoXml .
		        '<PaymentMethod>' .                                       //required
		          '<CreditCard>' . 
		           '<ParentTransactionReference>' . $parentTransactionReference . '</ParentTransactionReference>' . 
		           '<TransactionVerifier>' . $transactionVerifier . '</TransactionVerifier>' . 
		          '</CreditCard>' . 
		        '</PaymentMethod>' . 
		        $orderXml .
		      '</Request>' . 
		      "<Certificate>$this->certificate</Certificate>" . 
		    '</RequestBlock>';
		
		echo $request;
		$result === socket_write($this->socket, $request);
		$response = null;
		while ($out = socket_read($this->socket, 2048)) {
		   $response .= $out;
		}
		socket_close($this->socket);
		$this->socket = null;
		return $response;
	}
	
	/**
	 * (Public, Method no tested) Settlement involves the transfer of authorised funds to the merchant’s 
	 * bank account.
	 * @param Integer $settleAmount
	 * @param String $settleDate is format: "YYYY-MM-DD"
	 * @param String $transactionReference
	 * @param Integer $settleStatus is default = 0. 
	 * 0: The transaction is pending settlement and will be included in the 
	 * fraud checking system (This is the default value for all transactions) 
	 * 1: The transaction is pending settlement and won’t be included in the 
	 * fraud checking system
	 * 2: The transaction is suspended
	 * @return void
	 * @throws InvalidArgumentException
	 * @throws Exception
	 */
	private function settlement($settleAmount, $settleDate, $transactionReference, $settleStatus = 0) {
		//Validation amount
		//$settleAmount = DataValidation::validAmount($settleAmount);
		
		//Validation settleDate
		//$settleDate = DataValidation::validSettleDate($settleDate);
		
		//Validation $settleStatus
		if ($settleStatus < 0 || $settleStatus > 2) {
		  throw new InvalidArgumentException(
		    'Type settle status=\'' . $settleStatus . '\' is not valid. ' . 
		    'Valid is 0, 1, 2.' . "\n" .
		    '0: The transaction is pending settlement and will ' . "\n" .
		    'be included in the fraud checking system (This is the default value for all transactions).' .
			  '1: The transaction is pending settlement and won’t be included in the ' . "\n" .
			  'fraud checking system.' .
			  '2: The transaction is suspended.');
		}
		
		//Get socket connetcion
		$this->socket = $this->getSocketConnect();
		
		//Create customer info xml
		$customerInfoXml = $this->getCustomerInfoXml();
		$request = 
		    '<RequestBlock Live="' . $this->liveStatus . '" Version="3.51">' .
		      '<Request Type="SETTLEMENT">' . 
		        '<Operation>' .
		          "<SiteReference>$this->siteReference</SiteReference>" . //required
		          '<TransactionReference>' . $transactionReference . '</TransactionReference>' . 
		          '<SettleDate>' . $settleDate . '</SettleDate>' . 
		          '<SettleStatus>' . $settleStatus . '</SettleStatus>' .
		          '<SettleAmount>' . $settleAmount . '</SettleAmount>' .
		        '</Operation>' . 
		      '</Request>' . 
		      "<Certificate>$this->certificate</Certificate>" . 
		    '</RequestBlock>';
   
		$result === socket_write($this->socket, $request);
		$response = null;
		while ($out = socket_read($this->socket, 2048)) {
		   $response .= $out;
		}
		socket_close($this->socket);
		$this->socket = null;
		return $response;
		//$objectXml = simplexml_load_string($response);
	}
	
	/**
	 * Get last request
	 * @return String last request
	 */
	public function getLastRequest() {
		return $this->lastReqest;
	}
	
	/**
	 * Set last request
	 */
	private function setLastRequest($lastRequest) {
		$this->lastReqest = $lastRequest;
	}
	
	/**
	 * Get last response
	 * @return String last response
	 */
	public function getLastResponse() {
		return $this->lastResponse;
	}
	
	/**
	 * Set last response
	 */
	private function setLastResponse($lastResponse) {
		$this->lastResponse = $lastResponse;
	}
	
	/**
	 * Convert object to string
	 * @return String status object
	 */
	public function __toString() {
		return __FILE__ . ':' . __CLASS__ . 
		'[Fundamental:' .
		'$urlXPay=' . $this->urlXPay .
		',$portXPay=' . $this->portXPay .
		',$siteReference=' . $this->siteReference .
		',$socket=' . $this->socket .
		']' .
		'[CustomerNameInfo:' .
		'$prefixName=' . $this->customerInfo['name_prefix'] .
		',$firstName=' . $this->customerInfo['first_name'] .
		',$middleName=' . $this->customerInfo['middle_name'] .
		',$lastName=' . $this->customerInfo['last_name'] .
		',$suffixName=' . $this->customerInfo['name_suffix'] .
		']' .
		'[CustomerInfo:' .
		'$company=' . $this->customerInfo['company'] .
		',$street=' . $this->customerInfo['street'] .
		',$city=' . $this->customerInfo['city'] .
		',$stateProv=' . $this->customerInfo['state_prov'] .
		',$postalCode=' . $this->customerInfo['postal_code'] .
		',$countryCode=' . $this->customerInfo['country_code'] .
		',$phone=' . $this->customerInfo['phone'] .
		',$email=' . $this->customerInfo['email'] .
		']';
	}

	/**
	 * &#1059;&#1089;&#1090;&#1072;&#1085;&#1072;&#1074;&#1083;&#1080;&#1074;&#1072;&#1077;&#1090; &#1089;&#1086;&#1077;&#1085;&#1076;&#1080;&#1085;&#1077;&#1085;&#1080;&#1077; &#1089; Xpay &#1089;&#1077;&#1088;&#1074;&#1077;&#1088;&#1086;&#1084; &#1080; &#1074;&#1086;&#1079;&#1074;&#1088;&#1072;&#1097;&#1103;&#1077;&#1090; &#1101;&#1090;&#1086; &#1089;&#1077;&#1085;&#1076;&#1080;&#1085;&#1077;&#1085;&#1080;&#1077; &#1074; 
	 * &#1074;&#1080;&#1076;&#1077; socket_connect.
	 * 
	 * @throws Exception
	 * @return resource socket connect
	 */
	private function getSocketConnect() {
		//Create socket
		if (false == ($socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP))) {
			throw new Exception(
			    'socket_create() failed. Reason: ' . socket_strerror(socket_last_error()));
		}
		//Create socket connection 
		if (false == ($result = socket_connect($socket, $this->urlXPay, $this->portXPay))) {
			throw new Exception(
			    "socket_connect() failed. Reason: ($result) " . socket_strerror(socket_last_error($this->socket)) .
			    'Most likely not running XPay server.');
		}
		return $socket;
	}
	
	/**
	 * Set customer info
	 * @param Array $customerInfo is format:
	   array(
	     'name_prefix' => 'Mr.'
	     ,'first_name' => 'Joe'
	     ,'middle_name' => 'A.'
	     ,'last_name' => 'Bloggs'
	     ,'name_suffix' => 'CEng.'
	     ,'company' => 'A COMPANY'
	     ,'street' => 'A STREET'
	     ,'city' => 'A CITY'
	     ,'state_prov' => 'A STATE'
	     ,'postal_code' => 'TE2 3ST'
	     ,'country' => 'Russia'
	     ,'phone' => '0000 111111'
	     ,'email' => 'hide@address.com'
	   );
	 * @return void
	 */
	private function setCustomerInfo($customerInfo) {
		if ($customerInfo == null) {
			 $this->customerInfo = null;
		} else {
			if ($customerInfo['country'] != null) {
				$customerInfo['country_code'] = Convert::nameToCountryCode($customerInfo['country']);
			}
			$this->customerInfo = $customerInfo;
		}
	}
	
	/**
	 * Set customer info
	 * @param Array $httpCustomerInfo is format:
	   array(
	      'accept' => 'https://merchantSite.net/3dauth.cgi'
	     ,'user_agent' => '25 character field'
	   );
	 * @return void
	 * @throws InvalidArgumentException
	 */
	private function setHttpCustomerInfo(array $httpCustomerInfo) {
		if ($httpCustomerInfo == null) {
			throw new InvalidArgumentException(
			    'Http customer info (Accept and UserAgent) is empty');
		}
		else {
			$this->httpCustomerInfo = $httpCustomerInfo;
		}
	}
	
	/**
	 * Get customer info in XML format
	 * @return String "<CustomerInfo> ... </CustomerInfo>"
	 */
	private function getCustomerInfoXml() {
		if ($this->customerInfo == null) {
			$customerInfoXml = 
					'<CustomerInfo>' .
						'<Accept>' . $this->httpCustomerInfo['accept'] . '</Accept>' . 
						'<UserAgent>' . $this->httpCustomerInfo['user_agent'] . '</UserAgent>' .
					'</CustomerInfo>';
		}
		else {
			$customerInfoXml = 
			    '<CustomerInfo>' . 
	          '<Postal>' . 
	            '<Name>' . 
			         '<NamePrefix>' . $this->customerInfo['name_prefix'] . '</NamePrefix>' . 
	              '<FirstName>' . $this->customerInfo['first_name'] . '</FirstName>' . 
	              '<MiddleName>' . $this->customerInfo['middle_name'] . '</MiddleName>' . 
	              '<LastName>' . $this->customerInfo['last_name'] . '</LastName>' . 
	              '<NameSuffix>' . $this->customerInfo['name_suffix'] . '</NameSuffix>' . 
	            '</Name>' . 
	            '<Company>' . $this->customerInfo['company'] . '</Company>' . 
	            '<Street>' . $this->customerInfo['street'] . '</Street>' . 
	            '<City>' . $this->customerInfo['city'] . '</City>' . 
	            '<StateProv>' . $this->customerInfo['state_prov'] . '</StateProv>' . 
	            '<PostalCode>' . $this->customerInfo['postal_code'] . '</PostalCode>' . 
	            '<CountryCode>' . $this->customerInfo['country_code'] . '</CountryCode>' . 
	          '</Postal>' . 
	            '<Telecom>' .  
	            '<Phone>' . $this->customerInfo['phone'] . '</Phone>' . 
	          '</Telecom>' .  
	          '<Online>' . 
	            '<Email>' . $this->customerInfo['email'] . '</Email>' .   
	          '</Online>' .
						'<Accept>' . $this->httpCustomerInfo['accept'] . '</Accept>' . 
						'<UserAgent>' . $this->httpCustomerInfo['user_agent'] . '</UserAgent>' . 
	        '</CustomerInfo>';
			return $customerInfoXml;
		}
	}
	
	/**
	 * Get secure info xml
	 * 
	 * @param $secureInfo is format: 
	   array(
		    'enrolled' => 'Y'
		   ,'pa_res' => 'ABJASDKA+SDKAJ/SGDSAD'
		   ,'md' => 'LFKH/DSPLFIHPO+SEHFIKEHFEFDES'
	   );
	 */
	private function getSecureInfoXml($secureInfo) {
		/* Validation */
		
		if ($secureInfo['enrolled'] == null || $secureInfo['enrolled'] == '') {
			throw new InvalidArgumentException('Secure Info: Enrolled is empty');
		}
		/*
		if ($secureInfo['pa_res'] == null || $secureInfo['pa_res'] == '') {
			throw new InvalidArgumentException('Secure Info: PaRes is empty');
		}
		if ($secureInfo['md'] == null || $secureInfo['md'] == '') {
			throw new InvalidArgumentException('Secure Info: MD is empty');
		}
		*/
		$secureInfoXml = null;
		if ($secureInfo['enrolled'] == 'Y') {
			$secureInfoXml = 
				'<ThreeDSecure>' . 
					'<Enrolled>' . $secureInfo['enrolled'] . '</Enrolled>' .
					'<PaRes>' . $secureInfo['pa_res'] . '</PaRes>' . 
					'<MD>' . $secureInfo['md'] . '</MD>' . 
				'</ThreeDSecure>';
		}
		else {
			$secureInfoXml = 
				'<ThreeDSecure>' . 
					'<Enrolled>' . $secureInfo['enrolled'] . '</Enrolled>' .
					'<PaRes></PaRes>' . 
					'<MD>' . $secureInfo['md'] . '</MD>' . 
				'</ThreeDSecure>';
		}
		return $secureInfoXml;
	}

	/**
	 * Get order in XML format
	 * It should be noted that the maximum number of characters a tag can store is 255.
	 * @param String $reference order reference can contain any combination of 
	 * alphanumeric characters
	 * @param String $information order information can contain any combination of 
	 * alphanumeric characters
	 * @return String "<Order> ... </Order>"
	 * @throws InvalidArgumentException
	 */
	private function getOrderXml($reference, $information = null) {
		if ($reference == null && $information == null) {
			return null;
		}
		if (strlen($reference) > self::MAX_AMOUNT_CHARS_TEGS_OF_ORDER) {
			throw new InvalidArgumentException(
		    'Order reference=\'' . $reference . '\' is not valid. ' . 
		    'String length \'order reference\' is ' . strlen($reference) . '. ' .
		    'Maximum number of characters is ' . self::MAX_AMOUNT_CHARS_TEGS_OF_ORDER);
		}
		$order = '<Order>' . 
		          '<OrderReference>' . $reference . '</OrderReference>';
		if ($information != null) {
			if (strlen($information) > self::MAX_AMOUNT_CHARS_TEGS_OF_ORDER) {
				throw new InvalidArgumentException(
			    'Order reference=\'' . $information . '\' is not valid. ' . 
			    'String length \'order reference\' is ' . strlen($information) . '. ' .
			    'Maximum number of characters is ' . self::MAX_AMOUNT_CHARS_TEGS_OF_ORDER);
			}
			$order .= '<OrderInformation>' . $information . '</OrderInformation>';
		}         
		$order .= '</Order>';
	}

	/**
	 * For testing class
	 */
	private $liveStatus;
	
	/**
	 * Net address to XPay server
	 */
	private $urlXPay;
	
	/**
	 * Net port to XPay server
	 */
	private $portXPay;

	/**
	 * Affiliate / Partner ID
	 */
	private $siteReference;	
	
	/**
	 * Certificate
	 */
	private $certificate;
	
	/**
	 * Socket for work with server
	 */
	private $socket;
	
	/**
	 * Customer info is format:
	   array(
	     'name_prefix' => 'Mr.'
	     'first_name' => 'Joe'
	     'middle_name' => 'A.'
	     'last_name' => 'Bloggs'
	     'name_suffix' => 'CEng.'
	     'company' => 'A COMPANY'
	     'city' => 'A CITY'
	     'state_prov' => 'A STATE'
	     'postal_code' => 'TE2 3ST'
	     'country_code' => 'RU'
	     'phone' => '0000 111111'
	     'email' => 'hide@address.com'
	   );
	 * @var Array
	 */
	private $customerInfo;
	
	/**
	 * Array customer info is format: 
	   array(
	      'accept' => 'https://merchantSite.net/3dauth.cgi'
	     ,'user_agent' => '25 character field'
	   );
	 * @var Array
	 */
	private $httpCustomerInfo;
	
	/**
	 * Last reqest
	 * @var string
	 */
	private $lastReqest;
	
	/**
	 * Last response
	 * @var string
	 */
	private $lastResponse;
	
	/**
	 * Amount chars in nomer card
	 */
	const AMOUNT_CHARS_IN_NOMER_CARD = 16;
	
	/**
	 * Amount chars in expiry date
	 */
	const AMOUNT_CHARS_IN_EXPIRY_DATE = 5;
	
	/**
	 * Amount chars in settle date
	 */
	const AMOUNT_CHARS_IN_SETTLE_DATE = 10;
	
	/**
	 *  Amount chars in merchant name
	 */
	const AMOUNT_CHARS_IN_MERCHANT_NAME = 25;
	
	/**
	 * Maximum amount chars in tegs of order
	 */
	const MAX_AMOUNT_CHARS_TEGS_OF_ORDER = 255;
	
	/**
	 * XPay version protcol
	 */
	const XPAY_VERSION_PROTOCOL = 3.51;
	
}

/**
 * XPay Exception
 */
class XPayException extends RuntimeException {
	
	public function XPayException($message, $transactionId, $code) {
		parent::__construct($message, $code);
		$this->transactionId = $transactionId;
	}
	
	/**
	 * Get Transaction reference
	 * @return Transaction reference, is format: '64-2-736166'
	 */
	public function getTransactionId() {
		return $this->transactionId;
	}
	
	/**
	 * Transaction reference
	 */
	private $transactionId;
	
}
?>
Return current item: XPay System