diff --git a/src/Processor.php b/src/Processor.php index 4b7ba44..77e87eb 100644 --- a/src/Processor.php +++ b/src/Processor.php @@ -4,6 +4,43 @@ class Processor { + /** + * API configuration. + * + * @var array + */ + protected static $config = [ + 'protocol' => 'https', + 'host' => 'esqa.moneris.com', + 'port' => '443', + 'url' => '/gateway2/servlet/MpgRequest', + 'api_version' => 'PHP - 2.5.1', + 'timeout' => '60', + ]; + + /** + * Global error response to maintain consistency. + * + * @var string + */ + protected static $error = "Global Error Receiptnullnullnull nullnullnullnullfalsenullnullnullnullnull"; + + /** + * Retrieve the API configuration. + * + * @param string $environment + * + * @return array + */ + public static function config(string $environment) + { + if ($environment === Moneris::ENV_LIVE) { + self::$config['host'] = 'www3.moneris.com'; + } + + return self::$config; + } + /** * Determine if the request is valid. If so, process the * transaction via the Moneris API. @@ -22,6 +59,77 @@ public static function process(Transaction $transaction) return $response; } - return new Response($transaction); + $response = self::submit($transaction); + + return $transaction->validate($response); + } + + /** + * Parse the global error response stub. + * + * @return \SimpleXMLElement + */ + protected static function error() + { + return simplexml_load_string(self::$error); + } + + /** + * Set up and send the request to the Moneris API. + * + * @param array $config + * @param string $url + * @param string $xml + * + * @return string + */ + protected static function send(array $config, string $url, string $xml) + { + $curl = curl_init(); + + curl_setopt($curl, CURLOPT_URL, $url); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($curl, CURLOPT_HEADER, 0); + curl_setopt($curl, CURLOPT_POST, 1); + curl_setopt($curl, CURLOPT_POSTFIELDS, $xml); + curl_setopt($curl, CURLOPT_TIMEOUT, $config['timeout']); + curl_setopt($curl, CURLOPT_USERAGENT, $config['api_version']); + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true); + + $response = curl_exec($curl); + + curl_close($curl); + + return $response; + } + + /** + * Submit the transaction to the Moneris API. + * + * @param \CraigPaul\Moneris\Transaction $transaction + * + * @return \SimpleXMLElement|string + */ + protected static function submit(Transaction $transaction) + { + $config = self::config($transaction->gateway->environment); + + $url = $config['protocol'].'://'.$config['host'].':'.$config['port'].$config['url']; + + $xml = str_replace(' toXml()); + + $response = self::send($config, $url, $xml); + + if (!$response) { + return self::error(); + } + + $response = @simplexml_load_string($response); + + if ($response === false) { + return self::error(); + } + + return $response; } } \ No newline at end of file diff --git a/src/Response.php b/src/Response.php index a240c80..f5a3fd5 100644 --- a/src/Response.php +++ b/src/Response.php @@ -2,6 +2,8 @@ namespace CraigPaul\Moneris; +use SimpleXMLElement; + /** * CraigPaul\Moneris\Response * @@ -13,8 +15,22 @@ class Response { use Gettable, Settable; + const ERROR = -23; const INVALID_TRANSACTION_DATA = 0; + const GLOBAL_ERROR_RECEIPT = -3; + + const SYSTEM_UNAVAILABLE = -14; + const CARD_EXPIRED = -15; + const INVALID_CARD = -16; + const INSUFFICIENT_FUNDS = -17; + const PREAUTH_FULL = -18; + const DUPLICATE_TRANSACTION = -19; + const DECLINED = -20; + const NOT_AUTHORIZED = -21; + + const CVD = -4; + /** * The status code. * @@ -43,4 +59,98 @@ public function __construct(Transaction $transaction) { $this->transaction = $transaction; } + + /** + * Create a new Response instance. + * + * @param \CraigPaul\Moneris\Transaction $transaction + * + * @return $this + */ + public static function create(Transaction $transaction) + { + return new static($transaction); + } + + /** + * @param SimpleXMLElement $receipt + */ + protected function handle(SimpleXMLElement $receipt) + { + switch ($receipt->ResponseCode) { + case '050': + case '074': + case 'null': + $this->status = Response::SYSTEM_UNAVAILABLE; + break; + case '051': + case '482': + case '484': + $this->status = Response::CARD_EXPIRED; + break; + case '075': + $this->status = Response::INVALID_CARD; + break; + case '076': + case '079': + case '080': + case '081': + case '082': + case '083': + $this->status = Response::INSUFFICIENT_FUNDS; + break; + case '077': + $this->status = Response::PREAUTH_FULL; + break; + case '078': + $this->status = Response::DUPLICATE_TRANSACTION; + break; + case '481': + case '483': + $this->status = Response::DECLINED; + break; + case '485': + $this->status = Response::NOT_AUTHORIZED; + break; + case '486': + case '487': + case '489': + case '490': + $this->status = Response::CVD; + break; + default: + $this->status = Response::ERROR; + } + } + + /** + * Validate the response. + * + * @return $this + */ + public function validate() + { + /** @var \SimpleXMLElement $receipt */ + $receipt = $this->transaction->response->receipt; + + if ($receipt->ReceiptId === 'Global Error Receipt') { + $this->status = Response::GLOBAL_ERROR_RECEIPT; + $this->successful = false; + + return $this; + } + + $code = (int)$receipt->ResponseCode; + + if ($code >= 50 || $code === 0) { + $this->handle($receipt); + $this->successful = false; + + return $this; + } + + $this->successful = true; + + return $this; + } } \ No newline at end of file diff --git a/src/Transaction.php b/src/Transaction.php index 4f0aa40..a150d30 100644 --- a/src/Transaction.php +++ b/src/Transaction.php @@ -2,15 +2,19 @@ namespace CraigPaul\Moneris; +use SimpleXMLElement; + /** * CraigPaul\Moneris\Gateway * + * @property-read array $errors * @property-read \CraigPaul\Moneris\Gateway $gateway * @property-read array $params + * @property SimpleXMLElement $response */ class Transaction { - use Gettable; + use Gettable, Settable; /** * The errors for the transaction. @@ -33,6 +37,11 @@ class Transaction */ protected $params; + /** + * @var SimpleXMLElement + */ + protected $response; + /** * Create a new Transaction instance. * @@ -82,6 +91,32 @@ protected function prepare(array $params) return $params; } + /** + * Convert the transaction parameters into an XML structure. + * + * @return string|bool + */ + public function toXml() + { + $gateway = $this->gateway; + $params = $this->params; + + $type = in_array($params['type'], ['txn', 'acs']) ? 'MpiRequest' : 'request'; + + $xml = new SimpleXMLElement("<$type/>"); + $xml->addChild('store_id', $gateway->id); + $xml->addChild('api_token', $gateway->token); + + $type = $xml->addChild($params['type']); + unset($params['type']); + + foreach ($params as $key => $value) { + $type->addChild($key, $value); + } + + return $xml->asXML(); + } + /** * Check that the required parameters have been provided to the transaction. * @@ -115,4 +150,21 @@ public function valid() return empty($errors); } + + /** + * Validate the result of the Moneris API call. + * + * @param \SimpleXMLElement $result + * + * @return \CraigPaul\Moneris\Response + */ + public function validate(SimpleXMLElement $result) + { + $this->response = $result; + + $response = Response::create($this); + $response->validate(); + + return $response; + } } \ No newline at end of file