<?php

/*/
	2018 modified from CyberSource sample code
	File:  CyberSourceSOAP.phi
	Purpose:  run credit card transactions against the CyberSource SOAP gateway
	Notes:  CyberSourceClientEx{} is an exact copy of CybsClient{} class - used to encapsulate this in one file
	Modification History:
		� 03/30/2018 - Nathan - initial port to remove .ini loading and support overrides
		� mm/dd/yyyy - [name] - [comment]
/*/


/**
 * CybsSoapClient
 *
 * An implementation of PHP's SOAPClient class for making CyberSource requests.
 */
class CyberSourceSoapClientEx extends CyberSourceClientEx
{

	/**
	 * @param array overrides = hash allows overrides, such as for the merchant_id and transaction_key values
	 * @param array options = hash is passed down to the parent's base 'SoapClient' level
	 * @return stdClass A request with the data from the SimpleXMLElement.
	 */
	function __construct($overrides=array(),$options=array())
	{
		//$properties = arrayparse_ini_file('cybs.ini');
		$properties = array(
			'wsdl' => "https://ics2ws.ic3.com/commerce/1.x/transactionProcessor/CyberSourceTransaction_1.120.wsdl",
			'nvp_wsdl'  => "https://ics2ws.ic3.com/commerce/1.x/transactionProcessor/CyberSourceTransaction_NVP_1.120.wsdl",
			'merchant_id' => "v6554402",
			'transaction_key' => "YBQQP0MTnVzfXak4fiqQVjC5maO/K5CmNO4xzYMt7lterCI4Ig/KsIpT2AW3mIAmbTLH9mMYOLrusTXh4gqMsl6yxW4tOCoJUjj2l1z5brbrh6K84QZIamvaALKU5PS0eqXSGIpOEoQjVxKHd4rXfgLccjJtf0uzLThQpWYaUiFDvrXRCHGOXGb7U5dBE0RgTJ9oll0DfW+Zr4dQ5UZxkYHYCMdwUEUjM+3LZRx169BQLj80CIt+fHuALPctXXBjiEtBSBQYQ9/EIt3tW6ibrLW/ZhEmYwBcCnMKf4BWnLKrhjC1k/JBV4XPywvkK3Hgvl2x1G0pxFO070W3VTzegQ==",
		);
		foreach( $overrides as $k => $v ) {
			$properties[$k] = $v;
		}
		parent::__construct($options, $properties);
	}

	/**
	 * Returns a properly formatted request object from a SimpleXMLElement. 
	 *
	 * @param SimpleXMLElement $simpleXml Representation of an XML structure
	 * @return stdClass A request with the data from the SimpleXMLElement.
	 */
	public function simpleXmlToCybsRequest($simpleXml)
	{
		$vars = get_object_vars($simpleXml);
		$request = new stdClass();

		foreach(array_keys($vars) as $key) {
			$element = $vars[$key];
			if ($key == 'comment') {
				continue;
			}
			if (is_string($element)) {
				$request->$key = $element;
			} else if (is_array($element)) {
				$array = $element;
				if ($key == "@attributes") {
					// Each attribute in the '@attributes' array should
					// instead be a property of the parent element.
					foreach($array as $k => $value) {
						$request->$k = $value;
					}
				} else {
					$newArray = array();
					foreach($array as $k => $value) {
						$newArray[$k] = $this->simpleXmlToCybsRequest($value);
					}
					$request->$key = $newArray; 
				}
			} else if ($element instanceof SimpleXMLElement) {
				$request->$key = $this->simpleXmlToCybsRequest($element);
			}
		}
		return $request;
	}

	/**
	 * Returns an object initialized with basic client information.
	 *
	 * @param string $merchantReferenceCode Desired reference code for the request
	 * @return stdClass An object initialized with the basic client info.
	 */
	public function createRequest($merchantReferenceCode)
	{
		$request = new stdClass();
		$request->merchantID = $this->getMerchantId();
		$request->merchantReferenceCode = $merchantReferenceCode;
		$request->clientLibrary = self::CLIENT_LIBRARY_VERSION;
		$request->clientLibraryVersion = phpversion();
		$request->clientEnvironment = php_uname();
		return $request;
	}

	/**
	 * Runs a transaction from an XML string
	 *
	 * @param string $filePath The path to the XML file
	 * @param string $merchantReferenceCode Desired reference code for the request
	 * @return stdClass An object representation of the transaction response.	 
	 */
	public function runTransactionFromXml($xml, $merchantReferenceCode)
	{
		$request = $this->createRequest($merchantReferenceCode);
		$simpleXml = simplexml_load_string($xml);
		$xmlRequest = $this->simpleXmlToCybsRequest($simpleXml);
		$mergedRequest = (object) array_merge((array) $request, (array) $xmlRequest);
		return $this->runTransaction($mergedRequest);
	}

	/**
	 * Runs a transaction from an XML file.
	 *
	 * @param string $filePath The path to the XML file
	 * @param string $merchantReferenceCode Desired reference code for the request
	 * @return stdClass An object representation of the transaction response.	 
	 */
	public function runTransactionFromFile($filePath, $merchantReferenceCode)
	{
		$request = $this->createRequest($merchantReferenceCode);
		$xml = file_get_contents($filePath);
		return $this->runTransactionFromXml($xml, $merchantReferenceCode);
	}
}


/**
 * CyberSourceClientEx = exact copy of CybsClient
 *
 * An implementation of PHP's SOAPClient class for making either name-value pair
 * or XML CyberSource requests.
 */
class CyberSourceClientEx extends SoapClient
{
	const CLIENT_LIBRARY_VERSION = "CyberSource PHP 1.0.0";

	private $merchantId;
	private $transactionKey;

	function __construct($options=array(), $properties, $nvp=false)
	{
		$required = array('merchant_id', 'transaction_key');

		if (!$properties) {
			throw new Exception('Unable to read cybs.ini.');
		}

		if ($nvp === true) {
			array_push($required, 'nvp_wsdl');
			$wsdl = $properties['nvp_wsdl'];
		} else {
			array_push($required, 'wsdl');
			$wsdl = $properties['wsdl'];
		}

		foreach ($required as $req) {
			if (empty($properties[$req])) {
				throw new Exception($req . ' not found in cybs.ini.');
			}
		}

		parent::__construct($wsdl, $options);
		$this->merchantId = $properties['merchant_id'];
		$this->transactionKey = $properties['transaction_key'];

		$nameSpace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";

		$soapUsername = new SoapVar(
			$this->merchantId,
			XSD_STRING,
			NULL,
			$nameSpace,
			NULL,
			$nameSpace
		);

		$soapPassword = new SoapVar(
			$this->transactionKey,
			XSD_STRING,
			NULL,
			$nameSpace,
			NULL,
			$nameSpace
		);

		$auth = new stdClass();
		$auth->Username = $soapUsername;
		$auth->Password = $soapPassword; 

		$soapAuth = new SoapVar(
			$auth,
			SOAP_ENC_OBJECT,
			NULL, $nameSpace,
			'UsernameToken',
			$nameSpace
		); 

		$token = new stdClass();
		$token->UsernameToken = $soapAuth; 

		$soapToken = new SoapVar(
			$token,
			SOAP_ENC_OBJECT,
			NULL,
			$nameSpace,
			'UsernameToken',
			$nameSpace
		);

		$security =new SoapVar(
			$soapToken,
			SOAP_ENC_OBJECT,
			NULL,
			$nameSpace,
			'Security',
			$nameSpace
		);

		$header = new SoapHeader($nameSpace, 'Security', $security, true); 
		$this->__setSoapHeaders(array($header)); 
	}

	/**
	 * @return string The client's merchant ID.
	 */
	public function getMerchantId()
	{
		return $this->merchantId;
	}

	/**
	 * @return string The client's transaction key.
	 */
	public function getTransactionKey()
	{
		return $this->transactionKey;
	}
}

