From 6a5b54b547fa163bc11386a659f6a597cf7b0839 Mon Sep 17 00:00:00 2001 From: Craig Paul Date: Wed, 19 Oct 2016 20:07:04 -0600 Subject: [PATCH] Adds CVD and AVS support --- src/Gateway.php | 30 +++++++++++++++++- src/Moneris.php | 12 ++++++- src/Response.php | 77 +++++++++++++++++++++++++++++++++++++++++++-- src/Transaction.php | 34 ++++++++++++++++++++ 4 files changed, 149 insertions(+), 4 deletions(-) diff --git a/src/Gateway.php b/src/Gateway.php index eea3778..baef696 100644 --- a/src/Gateway.php +++ b/src/Gateway.php @@ -5,14 +5,42 @@ /** * CraigPaul\Moneris\Gateway * + * @property bool $avs + * @property-read array $avsCodes + * @property bool $cvd + * @property-read array $cvdCodes + * @property-read string $environment * @property-read string $id * @property-read string $token - * @property-read string $environment */ class Gateway { use Gettable; + /** + * Determine if we will use the Address Verification Service. + * + * @var bool + */ + protected $avs = false; + + /** + * @var array + */ + protected $avsCodes = ['A', 'B', 'D', 'M', 'P', 'W', 'X', 'Y', 'Z']; + + /** + * Determine if we will use the Card Validation Digits. + * + * @var bool + */ + protected $cvd = false; + + /** + * @var array + */ + protected $cvdCodes = ['M', 'Y', 'P', 'S', 'U']; + /** * The environment used for connecting to the Moneris API. * diff --git a/src/Moneris.php b/src/Moneris.php index 14c4a72..0964e27 100644 --- a/src/Moneris.php +++ b/src/Moneris.php @@ -84,6 +84,16 @@ public static function create(string $id, string $token, array $params = []) */ public function connect() { - return new Gateway($this->id, $this->token, $this->environment); + $gateway = new Gateway($this->id, $this->token, $this->environment); + + if (isset($this->params['avs'])) { + $gateway->avs = boolval($this->params['avs']); + } + + if (isset($this->params['cvd'])) { + $gateway->cvd = boolval($this->params['cvd']); + } + + return $gateway; } } \ No newline at end of file diff --git a/src/Response.php b/src/Response.php index f5a3fd5..89ba3d5 100644 --- a/src/Response.php +++ b/src/Response.php @@ -7,6 +7,8 @@ /** * CraigPaul\Moneris\Response * + * @property bool $failedAvs + * @property bool $failedCvd * @property null|int $status * @property bool $successful * @property \CraigPaul\Moneris\Transaction $transaction @@ -18,7 +20,9 @@ class Response const ERROR = -23; const INVALID_TRANSACTION_DATA = 0; - const GLOBAL_ERROR_RECEIPT = -3; + const FAILED_ATTEMPT = -1; + const CREATE_TRANSACTION_RECORD = -2; + const GLOBAL_ERROR_RECEIPT = -3; const SYSTEM_UNAVAILABLE = -14; const CARD_EXPIRED = -15; @@ -29,7 +33,33 @@ class Response const DECLINED = -20; const NOT_AUTHORIZED = -21; - const CVD = -4; + const CVD = -4; + const CVD_NO_MATCH = -5; + const CVD_NOT_PROCESSED = -6; + const CVD_MISSING = -7; + const CVD_NOT_SUPPORTED = -8; + + const AVS = -9; + const AVS_POSTAL_CODE = -10; + const AVS_ADDRESS = -11; + const AVS_NO_MATCH = -12; + const AVS_TIMEOUT = -13; + + const POST_FRAUD = -22; + + /** + * Determine if we have failed Address Verification Service verification. + * + * @var bool + */ + protected $failedAvs = false; + + /** + * Determine if we have failed Card Validation Digits verification. + * + * @var bool + */ + protected $failedCvd = false; /** * The status code. @@ -132,6 +162,7 @@ public function validate() { /** @var \SimpleXMLElement $receipt */ $receipt = $this->transaction->response->receipt; + $gateway = $this->transaction->gateway; if ($receipt->ReceiptId === 'Global Error Receipt') { $this->status = Response::GLOBAL_ERROR_RECEIPT; @@ -149,6 +180,48 @@ public function validate() return $this; } + $code = isset($receipt->AvsResultCode) ? (string)$receipt->AvsResultCode : false; + + if ($gateway->avs && $code && $code !== 'null' && !in_array($code, $gateway->avsCodes)) { + switch ($code) { + case 'B': + case 'C': + $this->status = Response::AVS_POSTAL_CODE; + break; + case 'G': + case 'I': + case 'P': + case 'S': + case 'U': + case 'Z': + $this->status = Response::AVS_ADDRESS; + break; + case 'N': + $this->status = Response::AVS_NO_MATCH; + break; + case 'R': + $this->status = Response::AVS_TIMEOUT; + break; + default: + $this->status = Response::AVS; + } + + $this->failedAvs = true; + $this->successful = false; + + return $this; + } + + $code = isset($receipt->CvdResultCode) ? (string)$receipt->CvdResultCode : null; + + if ($gateway->avs && !is_null($code) && $code !== 'null' && !in_array($code{1}, $gateway->cvdCodes)) { + $this->status = Response::CVD; + $this->failedCvd = true; + $this->successful = false; + + return $this; + } + $this->successful = true; return $this; diff --git a/src/Transaction.php b/src/Transaction.php index a766d2f..46d7b8a 100644 --- a/src/Transaction.php +++ b/src/Transaction.php @@ -118,8 +118,32 @@ public function toXml() $xml->addChild('api_token', $gateway->token); $type = $xml->addChild($params['type']); + $efraud = in_array( + $params['type'], + ['purchase', 'preauth', 'card_verification', 'cavv_purchase', 'cavv_preauth'] + ); unset($params['type']); + if ($gateway->cvd && $efraud) { + $cvd = $type->addChild('cvd_info'); + $cvd->addChild('cvd_indicator', '1'); + $cvd->addChild('cvd_value', $params['cvd']); + unset($params['cvd']); + } + + if ($gateway->avs && $efraud) { + $avs = $type->addChild('avs_info'); + + foreach ($params as $key => $value) { + if (substr($key, 0, 4) !== 'avs_') { + continue; + } + + $avs->addChild($key, $value); + unset($params[$key]); + } + } + foreach ($params as $key => $value) { $type->addChild($key, $value); } @@ -147,6 +171,16 @@ public function valid() $errors[] = Validator::set($params, 'amount') ? null : 'Amount not provided.'; $errors[] = Validator::set($params, 'expdate') ? null : 'Expiry date not provided.'; + if ($this->gateway->avs) { + $errors[] = Validator::set($params, 'avs_street_number') ? null : 'Street number not provided.'; + $errors[] = Validator::set($params, 'avs_street_name') ? null : 'Street name not provided.'; + $errors[] = Validator::set($params, 'avs_zipcode') ? null : 'Postal/Zip code not provided.'; + } + + if ($this->gateway->cvd) { + $errors[] = Validator::set($params, 'cvd') ? null : 'CVD not provided.'; + } + break; default: $errors[] = $params['type'].' is not a supported transaction type.';