diff --git a/src/ConstantsInterface.php b/src/ConstantsInterface.php index e4efa7a0..9809e0af 100644 --- a/src/ConstantsInterface.php +++ b/src/ConstantsInterface.php @@ -115,6 +115,11 @@ interface ConstantsInterface const SERVICE_REPEAT = 'repeat'; const SERVICE_TOKEN = 'directtoken'; const SERVICE_DIRECT3D = 'direct3dcallback'; + const SERVICE_REST_MSK = 'merchant-session-keys'; + const SERVICE_REST_TRANSACTIONS = 'transactions'; + const SERVICE_REST_INSTRUCTIONS = 'instructions'; + const SERVICE_REST_3D = '3d-secure'; + const SERVICE_REST_3D_CHALLENGE = '3d-secure-challenge'; /** * 0 = Do not send either customer or vendor emails diff --git a/src/Message/AbstractRestRequest.php b/src/Message/AbstractRestRequest.php new file mode 100644 index 00000000..8f83b875 --- /dev/null +++ b/src/Message/AbstractRestRequest.php @@ -0,0 +1,300 @@ +method; + } + + /** + * @return string URL for the test or live gateway, as appropriate. + */ + public function getEndpoint() + { + return sprintf( + '%s/%s/%s', + $this->getTestMode() ? $this->testEndpoint : $this->liveEndpoint, + $this->getApiVersion(), + $this->isSubservice() ? $this->getSubService() : $this->getService() + ); + } + + public function isSubservice() + { + return !empty($this->getParentService()); + } + + /** + * The name of the service used in the endpoint to send the message. + * With override for services used on specific parent services + * + * @return string Sage Pay endpoint service name. + */ + public function getSubService() + { + if ($this->isSubservice()) { + return sprintf( + '%s/%s/%s', + $this->getParentService(), + $this->getParentServiceReference(), + $this->getService() + ); + } + return $this->getService(); + } + + public function getParentService() + { + return false; + } + + public function getParentServiceReference() + { + return false; + } + + /** + * Gets the api version for the end point. + * + * @return string + */ + public function getApiVersion() + { + return $this->apiVersion; + } + + + public function getUsername() + { + return $this->getParameter('username'); + } + + public function setUsername($value) + { + return $this->setParameter('username', $value); + } + + public function getPassword() + { + return $this->getParameter('password'); + } + + public function setPassword($value) + { + return $this->setParameter('password', $value); + } + + public function getMerchantSessionKey() + { + return $this->getParameter('merchantSessionKey'); + } + + public function getCardIdentifier() + { + return $this->getParameter('cardIdentifier'); + } + + public function getTokenReusable() + { + return $this->getParameter('tokenReusable'); + } + + public function getTokenSave() + { + return $this->getParameter('tokenSave'); + } + + public function setTokenReusable($value) + { + return $this->setParameter('tokenReusable', $value); + } + + public function setTokenSave($value) + { + return $this->setParameter('tokenSave', $value); + } + + public function setMerchantSessionKey($value) + { + return $this->setParameter('merchantSessionKey', $value); + } + + public function setCardIdentifier($value) + { + return $this->setParameter('cardIdentifier', $value); + } + + /** + * Set the credentialType field(s). + * + * @param json $credentialType The credentialType for sagepay. + * @return $this + */ + public function setCredentialType($credentialType) + { + return $this->setParameter('credentialType', $credentialType); + } + + /** + * @return string The credentialType data as set. + */ + public function getCredentialType() + { + return $this->getParameter('credentialType'); + } + + /** + * @return string + */ + public function getMd() + { + return $this->getParameter('MD'); + } + + /** + * Override the MD passed into the current request. + * + * @param string $value + * @return $this + */ + public function setMd($value) + { + return $this->setParameter('MD', $value); + } + + /** + * Send data to the remote gateway, parse the result into an array, + * then use that to instantiate the response object. + * + * @param array + * @return Response The reponse object initialised with the data returned from the gateway. + */ + public function sendData($data) + { + // Issue #20 no data values should be null. + + array_walk($data, function (&$value) { + if (! isset($value)) { + $value = ''; + } + }); + + $httpResponse = $this + ->httpClient + ->request( + $this->getMethod(), + $this->getEndpoint(), + [ + 'Content-Type' => 'application/json', + 'Authorization' => 'Basic '.base64_encode($this->getUsername() . ':' . $this->getPassword()), + ], + json_encode($data) + ); + + // We might want to check $httpResponse->getStatusCode() + + $responseData = static::parseBodyData($httpResponse); + + return $this->createResponse($responseData); + } + + /** + * Add the billing address details to the data. + * + * @param array $data + * @return array $data + */ + protected function getBillingAddressData(array $data = []) + { + $card = $this->getCard(); + + $data['customerFirstName'] = $card->getFirstName(); + $data['customerLastName'] = $card->getLastName(); + $data['billingAddress']['address1'] = $card->getBillingAddress1(); + $data['billingAddress']['address2'] = $card->getBillingAddress2(); + $data['billingAddress']['city'] = $card->getBillingCity(); + $data['billingAddress']['postalCode'] = $card->getBillingPostcode(); + $data['billingAddress']['state'] = $card->getBillingState(); + $data['billingAddress']['country'] = $card->getBillingCountry(); + + if ($data['billingAddress']['country'] !== 'US') { + $data['billingAddress']['state'] = ''; + } + + return $data; + } + + /** + * Add the shipping details to the data. + * + * @param array $data + * @return array $data + */ + protected function getShippingDetailsData(array $data = []) + { + $card = $this->getCard(); + + $data['shippingDetails']['recipientFirstName'] = $card->getShippingFirstName(); + $data['shippingDetails']['recipientLastName'] = $card->getShippingLastName(); + $data['shippingDetails']['shippingAddress1'] = $card->getShippingAddress1(); + $data['shippingDetails']['shippingAddress2'] = $card->getShippingAddress2(); + $data['shippingDetails']['shippingCity'] = $card->getShippingCity(); + $data['shippingDetails']['shippingPostalCode'] = $card->getShippingPostcode(); + $data['shippingDetails']['shippingState'] = $card->getShippingState(); + $data['shippingDetails']['shippingCountry'] = $card->getShippingCountry(); + + if ($data['shippingDetails']['shippingCountry'] !== 'US') { + $data['shippingDetails']['shippingState'] = ''; + } + + return $data; + } + + /** + * The payload consists of json. + * + * @param ResponseInterface $httpResponse + * @return array + */ + public static function parseBodyData(ResponseInterface $httpResponse) + { + $bodyText = (string)$httpResponse->getBody(); + + $responseData = json_decode($bodyText, true); + + return $responseData; + } +} diff --git a/src/Message/DirectAuthorizeRequest.php b/src/Message/DirectAuthorizeRequest.php index 19325f24..23b67891 100644 --- a/src/Message/DirectAuthorizeRequest.php +++ b/src/Message/DirectAuthorizeRequest.php @@ -9,7 +9,7 @@ class DirectAuthorizeRequest extends AbstractRequest { /** - * @var array Some mapping from Omnipay card brand codes to Sage Pay card branc codes. + * @var array Some mapping from Omnipay card brand codes to Sage Pay card brand codes. */ protected $cardBrandMap = array( 'mastercard' => 'MC', diff --git a/src/Message/RestResponse.php b/src/Message/RestResponse.php new file mode 100644 index 00000000..a8bbb4e8 --- /dev/null +++ b/src/Message/RestResponse.php @@ -0,0 +1,145 @@ +getDataItem('transactionId'); + } + + /** + * The only reason supported for a redirect from a Server transaction + * will be 3D Secure. PayPal may come into this at some point. + * + * @return bool True if a 3DSecure Redirect needs to be performed. + */ + public function isRedirect() + { + return $this->getStatus() === static::SAGEPAY_STATUS_3DAUTH; + } + + /** + * @return string URL to 3D Secure endpoint. + */ + public function getRedirectUrl() + { + if ($this->isRedirect()) { + return $this->getDataItem('acsUrl'); + } + } + + /** + * @return string The redirect method. + */ + public function getRedirectMethod() + { + return 'POST'; + } + + /** + * The usual reason for a redirect is for a 3D Secure check. + * Note: when PayPal is supported, a different set of data will be returned. + * + * @return array Collected 3D Secure POST data. + */ + public function getRedirectData() + { + if ($this->isRedirect()) { + if ($this->getDataItem('cReq')) { + return array( + 'creq' => $this->getDataItem('cReq'), + 'threeDSSessionData' => $this->getRequest()->getMd(), + ); + } else { + return array( + 'PaReq' => $this->getDataItem('paReq'), + 'TermUrl' => $this->getRequest()->getReturnUrl(), + 'MD' => $this->getRequest()->getMd(), + ); + } + } + } + + /** + * The Sage Pay ID to uniquely identify the transaction on their system. + * Only present if Status is OK or OK REPEATED. + * + * @return string + */ + public function getVPSTxId() + { + return $this->getDataItem('VPSTxId'); + } + + /** + * A secret used to sign the notification request sent direct to your + * application. + */ + public function getSecurityKey() + { + return $this->getDataItem('SecurityKey'); + } + + /** + * A secret used to sign the notification request sent direct to your + * application. + */ + public function getErrors() + { + return $this->getDataItem('errors'); + } + + public function getError() + { + if (!empty($this->getErrors())) { + return array_values($this->getErrors())[0]; + } + return null; + } + + /** + * A secret used to sign the notification request sent direct to your + * application. + */ + public function getErrorCode() + { + if ($this->getError()) { + $error = $this->getError(); + return $error['code'] ?? null; + } + return $this->getCode(); + } + + /** + * A secret used to sign the notification request sent direct to your + * application. + */ + public function getErrorDescription() + { + if ($this->getError()) { + $error = $this->getError(); + return $error['description'] ?? null; + } + return $this->getMessage(); + } +} diff --git a/src/Message/ServerRestCompletePurchaseRequest.php b/src/Message/ServerRestCompletePurchaseRequest.php new file mode 100644 index 00000000..906a1fd4 --- /dev/null +++ b/src/Message/ServerRestCompletePurchaseRequest.php @@ -0,0 +1,103 @@ +httpRequest->request->has('cres')) { + return static::SERVICE_REST_3D_CHALLENGE; + } + + return static::SERVICE_REST_3D; + } + + public function getParentService() + { + return static::SERVICE_REST_TRANSACTIONS; + } + + public function getParentServiceReference() + { + return $this->getParameter('transactionId'); + } + + public function getData() + { + if ($this->httpRequest->request->has('cres')) { + $data = array( + 'cRes' => $this->httpRequest->request->get('cres'), // inconsistent caps are intentional + 'MD' => $this->httpRequest->request->get('threeDSSessionData'), + ); + + if (empty($data['cRes']) || empty($data['MD'])) { + throw new InvalidResponseException; + } + } else { + $data = array( + 'MD' => $this->getMd() ?: $this->httpRequest->request->get('MD'), + 'paRes' => $this->getPaRes() ?: $this->httpRequest->request->get('PaRes'), + ); + + if (empty($data['MD']) || empty($data['paRes'])) { + throw new InvalidResponseException; + } + } + + return $data; + } + + /** + * @return string + */ + public function getMd() + { + return $this->getParameter('MD'); + } + + /** + * Override the MD passed into the current request. + * + * @param string $value + * @return $this + */ + public function setMd($value) + { + return $this->setParameter('MD', $value); + } + + /** + * @return string + */ + public function getPaRes() + { + return $this->getParameter('paRes'); + } + + /** + * Override the PaRes passed into the current request. + * + * @param string $value + * @return $this + */ + public function setPaRes($value) + { + return $this->setParameter('paRes', $value); + } + + /** + * @param array $data + * @return ServerRestCompleteResponse + */ + protected function createResponse($data) + { + return $this->response = new ServerRestCompleteResponse($this, $data); + } +} diff --git a/src/Message/ServerRestCompleteResponse.php b/src/Message/ServerRestCompleteResponse.php new file mode 100644 index 00000000..8b6bc13b --- /dev/null +++ b/src/Message/ServerRestCompleteResponse.php @@ -0,0 +1,18 @@ +get3DSecureStatus() ?? $this->getStatus()) === static::SAGEPAY_STATUS_AUTHENTICATED; + } +} diff --git a/src/Message/ServerRestInstructionResponse.php b/src/Message/ServerRestInstructionResponse.php new file mode 100644 index 00000000..b9cbfad2 --- /dev/null +++ b/src/Message/ServerRestInstructionResponse.php @@ -0,0 +1,34 @@ +getInstructionType() ?? false; + } + + /** + * @return string|null instructionType if present + */ + public function getInstructionType() + { + return $this->getDataItem('instructionType'); + } + + /** + * @return string|null date if present + */ + public function getInstructionDate() + { + return $this->getDataItem('date'); + } +} diff --git a/src/Message/ServerRestMerchantSessionKeyRequest.php b/src/Message/ServerRestMerchantSessionKeyRequest.php new file mode 100644 index 00000000..984c2063 --- /dev/null +++ b/src/Message/ServerRestMerchantSessionKeyRequest.php @@ -0,0 +1,37 @@ +getVendor(); + $data['username'] = $this->getUsername(); + $data['password'] = $this->getPassword(); + + return $data; + } + + /** + * @param array $data + * @return ServerRestMerchantSessionKeyResponse + */ + protected function createResponse($data) + { + return $this->response = new ServerRestMerchantSessionKeyResponse($this, $data); + } +} diff --git a/src/Message/ServerRestMerchantSessionKeyResponse.php b/src/Message/ServerRestMerchantSessionKeyResponse.php new file mode 100644 index 00000000..539c3c3b --- /dev/null +++ b/src/Message/ServerRestMerchantSessionKeyResponse.php @@ -0,0 +1,28 @@ +getMerchantSessionKey() ?? false; + } + + /** + * @return string|null MSK if present + */ + public function getMerchantSessionKey() + { + return $this->getDataItem('merchantSessionKey'); + } +} diff --git a/src/Message/ServerRestPurchaseRequest.php b/src/Message/ServerRestPurchaseRequest.php new file mode 100644 index 00000000..fbe7ca94 --- /dev/null +++ b/src/Message/ServerRestPurchaseRequest.php @@ -0,0 +1,117 @@ +getBasePurchaseData(); + + if ($this->getCardIdentifier() && $this->getMerchantSessionKey()) { + $data = $this->getPaymentMethodData($data); + } + + return $data; + } + + /** + * The required fields concerning the purchase + * + * @return array + */ + protected function getBasePurchaseData() + { + $card = $this->getCard(); + + $data = $this->getBaseData(); + + $data['transactionType'] = $this->getTxType(); + $data['vendorTxCode'] = $this->getTransactionId(); + $data['description'] = $this->getDescription(); + $data['amount'] = (int) $this->getAmount(); + $data['currency'] = $this->getCurrency(); + $data['NotificationURL'] = $this->getNotifyUrl() ?: $this->getReturnUrl(); + $data['MD'] = $this->getMd(); + $data['strongCustomerAuthentication'] = $this->getStrongCustomerAuthentication(); + $data['credentialType'] = $this->getCredentialType(); + + $data = $this->getBillingAddressData($data); + $data = $this->getShippingDetailsData($data); + + if ($card->getEmail()) { + $data['customerEmail'] = $card->getEmail(); + } + + // $data['ApplyAVSCV2'] = $this->getApplyAVSCV2() ?: static::APPLY_AVSCV2_DEFAULT; + $data['apply3DSecure'] = $this->getApply3DSecure() ?: static::APPLY_3DSECURE_APPLY; + // user parent data here and the abstract can provide txtype vendor etc + return $data; + } + + /** + * @param array $data + * @return ServerRestPurchaseKeyResponse + */ + protected function createResponse($data) + { + return $this->response = new ServerRestPurchaseResponse($this, $data); + } + + /** + * @param array $data + * @return array $data. + */ + public function getPaymentMethodData($data = []) + { + $data['paymentMethod']['card']['merchantSessionKey'] = $this->getMerchantSessionKey(); + $data['paymentMethod']['card']['cardIdentifier'] = $this->getCardIdentifier(); + if (!is_null($this->getTokenReusable())) { + $data['paymentMethod']['card']['reusable'] = $this->getTokenReusable(); + } + if (!is_null($this->getTokenSave())) { + $data['paymentMethod']['card']['save'] = $this->getTokenSave(); + } + return $data; + } + + /** + * Set the strongCustomerAuthentication field(s). + * + * @param json $strongCustomerAuthentication The strongCustomerAuthentication for sagepay. + * @return $this + */ + public function setStrongCustomerAuthentication($strongCustomerAuthentication) + { + return $this->setParameter('strongCustomerAuthentication', $strongCustomerAuthentication); + } + + /** + * @return string The strongCustomerAuthentication data as set. + */ + public function getStrongCustomerAuthentication() + { + return $this->getParameter('strongCustomerAuthentication'); + } +} diff --git a/src/Message/ServerRestPurchaseResponse.php b/src/Message/ServerRestPurchaseResponse.php new file mode 100644 index 00000000..dc61368e --- /dev/null +++ b/src/Message/ServerRestPurchaseResponse.php @@ -0,0 +1,11 @@ +getBaseData(); + + $data['transactionType'] = $this->getTxType(); + $data['vendorTxCode'] = $this->getTransactionId(); + $data['description'] = $this->getDescription(); + $data['amount'] = (int) $this->getAmount(); + // $data['currency'] = $this->getCurrency(); + $data['referenceTransactionId'] = $this->getReferenceTransactionId(); + + return $data; + } + + /** + * @param array $data + * @return ServerRestPurchaseKeyResponse + */ + protected function createResponse($data) + { + return $this->response = new ServerRestRefundResponse($this, $data); + } + + public function getReferenceTransactionId() + { + return $this->getParameter('referenceTransactionId'); + } + + public function setReferenceTransactionId($value) + { + return $this->setParameter('referenceTransactionId', $value); + } +} diff --git a/src/Message/ServerRestRefundResponse.php b/src/Message/ServerRestRefundResponse.php new file mode 100644 index 00000000..fe57fb1f --- /dev/null +++ b/src/Message/ServerRestRefundResponse.php @@ -0,0 +1,11 @@ +getBaseData(); + + $data['transactionType'] = $this->getTxType(); + $data['vendorTxCode'] = $this->getTransactionId(); + $data['description'] = $this->getDescription(); + $data['amount'] = (int) $this->getAmount(); + $data['currency'] = $this->getCurrency(); + $data['referenceTransactionId'] = $this->getReferenceTransactionId(); + $data['credentialType'] = $this->getCredentialType(); + + return $data; + } + + /** + * @param array $data + * @return ServerRestRepeatResponse + */ + protected function createResponse($data) + { + return $this->response = new ServerRestRepeatResponse($this, $data); + } + + public function getReferenceTransactionId() + { + return $this->getParameter('referenceTransactionId'); + } + + public function setReferenceTransactionId($value) + { + return $this->setParameter('referenceTransactionId', $value); + } +} diff --git a/src/Message/ServerRestRepeatResponse.php b/src/Message/ServerRestRepeatResponse.php new file mode 100644 index 00000000..8c1d5e44 --- /dev/null +++ b/src/Message/ServerRestRepeatResponse.php @@ -0,0 +1,11 @@ +getParameter('transactionId'); // temporary + } + + /** + * Add the optional token details to the base data. + * + * @return array + */ + public function getData() + { + return []; + } + + /** + * @param array $data + * @return ServerRestPurchaseKeyResponse + */ + protected function createResponse($data) + { + return $this->response = new ServerRestRetrieveTransactionResponse($this, $data); + } + + /** + * A card token is returned if one has been requested. + * + * @return string Currently an md5 format token. + */ + public function getPaymentMethodData($data = []) + { + $data['paymentMethod']['card']['merchantSessionKey'] = $this->getMerchantSessionKey(); + $data['paymentMethod']['card']['cardIdentifier'] = $this->getCardIdentifier(); + return $data; + } +} diff --git a/src/Message/ServerRestRetrieveTransactionResponse.php b/src/Message/ServerRestRetrieveTransactionResponse.php new file mode 100644 index 00000000..1ede91aa --- /dev/null +++ b/src/Message/ServerRestRetrieveTransactionResponse.php @@ -0,0 +1,11 @@ +getBaseData(); + + $data['instructionType'] = $this->getTxType(); + return $data; + } + + public function getParentServiceReference() + { + return $this->getParameter('transactionId'); + } + + /** + * @param array $data + * @return ServerRestInstructionResponse + */ + protected function createResponse($data) + { + return $this->response = new ServerRestInstructionResponse($this, $data); + } +} diff --git a/src/RestServerGateway.php b/src/RestServerGateway.php new file mode 100644 index 00000000..ec4b2337 --- /dev/null +++ b/src/RestServerGateway.php @@ -0,0 +1,106 @@ +getParameter('username'); + } + + public function setUsername($value) + { + return $this->setParameter('username', $value); + } + + public function getPassword() + { + return $this->getParameter('password'); + } + + public function setPassword($value) + { + return $this->setParameter('password', $value); + } + + /** + * Create merchant session key (MSK). + */ + public function createMerchantSessionKey(array $parameters = []) + { + return $this->createRequest(ServerRestMerchantSessionKeyRequest::class, $parameters); + } + + /** + * Purchase and handling of return from 3D Secure redirection. + */ + public function purchase(array $parameters = []) + { + return $this->createRequest(ServerRestPurchaseRequest::class, $parameters); + } + + /** + * Handle purchase notifcation callback. + */ + public function complete(array $parameters = []) + { + return $this->createRequest(ServerRestCompletePurchaseRequest::class, $parameters); + } + + /** + * Get transaction information from Sage. + */ + public function getTransaction(array $parameters = []) + { + return $this->fetchTransaction($parameters); + } + + /** + * Fetch transaction information from Sage. + */ + public function fetchTransaction(array $parameters = []) + { + return $this->createRequest(ServerRestRetrieveTransactionRequest::class, $parameters); + } + + /** + * Refund request. + */ + public function refund(array $parameters = []) + { + return $this->createRequest(ServerRestRefundRequest::class, $parameters); + } + + /** + * Repeat request. + */ + public function repeat(array $parameters = []) + { + return $this->createRequest(ServerRestRepeatRequest::class, $parameters); + } + + /** + * Void request. + */ + public function void(array $parameters = []) + { + return $this->createRequest(ServerRestVoidRequest::class, $parameters); + } +} diff --git a/src/Traits/ResponseRestFieldsTrait.php b/src/Traits/ResponseRestFieldsTrait.php new file mode 100644 index 00000000..38198079 --- /dev/null +++ b/src/Traits/ResponseRestFieldsTrait.php @@ -0,0 +1,333 @@ +getData(); + + return isset($this->data[$name]) ? $this->data[$name] : $default; + } + + /** + * @return bool True if the transaction is successful and complete. + */ + public function isSuccessful() + { + return $this->getStatus() === static::SAGEPAY_STATUS_OK + || $this->getStatus() === static::SAGEPAY_STATUS_OK_REPEATED + || $this->getStatus() === static::SAGEPAY_STATUS_REGISTERED + || $this->getStatus() === static::SAGEPAY_STATUS_AUTHENTICATED; + } + + /** + * Get the cardReference generated when creating a card reference + * during an authorisation or payment, or as an explicit request. + * + * @return string Currently an md5 format token. + */ + public function getCardReference() + { + return $this->getToken(); + } + + /** + * A card token is returned if one has been requested. + * + * @return string Currently an md5 format token. + */ + public function getToken() + { + return $this->getDataItem('Token'); + } + + /** + * The raw status code. + * + * @return string One of static::SAGEPAY_STATUS_* + */ + public function getStatus() + { + return strtoupper($this->getDataItem('status')); + } + + /** + * The raw status code. + * + * @return string One of static::SAGEPAY_STATUS_* + */ + public function getCode() + { + return $this->getStatusCode() ?? $this->getDataItem('code') ?? 'No Code'; + } + + /** + * The raw status code. + * + * @return string One of static::SAGEPAY_STATUS_* + */ + public function getStatusCode() + { + return $this->getDataItem('statusCode'); + } + + /** + * Response Textual Message + * + * @return string A response message from the payment gateway + */ + public function getMessage() + { + return $this->getStatusDetail() ?? $this->getDataItem('description'); + } + + /** + * Response Textual Message + * + * @return string A response message from the payment gateway + */ + public function getStatusDetail() + { + return $this->getDataItem('statusDetail'); + } + + /** + * Sage Pay unique Authorisation Code for a successfully authorised transaction. + * Only present if Status is OK + * + * @return string + */ + public function getTxAuthNo() + { + return $this->getRetrievalReference(); + } + + /** + * Sage Pay unique Authorisation Code for a successfully authorised transaction. + * Only present if Status is OK + * + * @return string + */ + public function getRetrievalReference() + { + return $this->getDataItem('retrievalReference'); + } + + /** + * Sage Pay unique Authorisation Code for a successfully authorised transaction. + * Only present if Status is OK + * + * @return array + */ + public function getAvsCvcCheck($wholeStatus = true) + { + $avsCvcCheck = $this->getDataItem('avsCvcCheck'); + + if (array_key_exists('status', $avsCvcCheck) && $wholeStatus) { + return $avsCvcCheck['status']; + } + return (object) $avsCvcCheck; + } + + /** + * This is the response from AVS and CV2 checks. + * Provided for Vendor info and backward compatibility with the + * banks. Rules set up in MySagePay will accept or reject + * the transaction based on these values. + * + * More detailed results are split out in the next three fields: + * AddressResult, PostCodeResult and CV2Result. + * + * Not present if the Status is: + * 3DAUTH, AUTHENTICATED, PPREDIRECT or REGISTERED. + * + * @return string One of static::AVSCV2_RESULT_* + */ + public function getAVSCV2() + { + return strtoupper($this->getAvsCvcCheck()); + } + + /** + * The specific result of the checks on the cardholder’s + * address numeric from the AVS/CV2 checks. + * + * @return string Once of static::ADDRESS_RESULT_* + */ + public function getAddressResult() + { + return strtoupper($this->getAvsCvcCheck(false)->address); + } + + /** + * The specific result of the checks on the cardholder’s + * Postcode from the AVS/CV2 checks. + * + * @return string Once of static::POSTCODE_RESULT_* + */ + public function getPostCodeResult() + { + return strtoupper($this->getAvsCvcCheck(false)->postalCode); + } + + /** + * The specific result of the checks on the cardholder’s CV2 + * code from the AVS/CV2 checks. + * + * @return string One of static::CV2_RESULT_* + */ + public function getCV2Result() + { + return strtoupper($this->getAvsCvcCheck(false)->securityCode); + } + + /** + * This field details the results of the 3D-Secure checks + * where appropriate. + * + * @return string One of static::SECURE3D_STATUS_* + */ + public function get3DSecureStatus() + { + $secure3DResponse = $this->getDataItem('3DSecure'); + + if (is_array($secure3DResponse) && array_key_exists('status', $secure3DResponse)) { + return strtoupper($secure3DResponse['status']); + } + + return $secure3DResponse; + } + + /** + * The encoded result code from the 3D-Secure checks (CAVV or UCAF). + * Only present if the 3DSecureStatus field is OK or ATTEMPTONLY. + * + * @return string Up to 32 characters long. + */ + public function getCAVV() + { + return $this->getDataItem('CAVV'); + } + + /** + * The raw frawd response from the gateway. + * + * @return string One of static::FRAUD_RESPONSE_* + */ + public function getFraudResponse() + { + return $this->getDataItem('FraudResponse'); + } + + /** + * The authorisation code returned from the bank. e.g. T99777 + * @return string + */ + public function getBankAuthCode() + { + return $this->getDataItem('BankAuthCode'); + } + + /** + * The decline code from the bank. These codes are + * specific to the bank. Please contact them for a description + * of each code. e.g. 00 + * @return string Two digit code, specific to the bacnk. + */ + public function getDeclineCode() + { + return $this->getDataItem('DeclineCode'); + } + + /** + * Returns the surcharge amount charged and is only + * present if a surcharge was applied to the transaction. + * The surcharge should be added to the original requested amount + * to give the total amount authorised for payment. + * + * @return string|null Contains a floating point number. + */ + public function getSurcharge() + { + return $this->getDataItem('Surcharge'); + } + + /** + * Raw expiry date for the card, "MMYY" format by default. + * The expiry date is available for Sage Pay Direct responses, even if the + * remaining card details are not. + * Also supports custom formats. + * + * @param string|null $format Format using the PHP date() format string. + * @return string + */ + public function getExpiryDate($format = null) + { + $expiryDate = $this->getDataItem('ExpiryDate'); + + if ($format === null || $expiryDate === null) { + return $expiryDate; + } else { + return gmdate( + $format, + gmmktime(0, 0, 0, $this->getExpiryMonth(), 1, $this->getExpiryYear()) + ); + } + } + + /** + * Get the card expiry month. + * + * @return int The month number, 1 to 12. + */ + public function getExpiryMonth() + { + $expiryDate = $this->getDataItem('ExpiryDate'); + + if (! empty($expiryDate)) { + return (int)substr($expiryDate, 0, 2); + } + } + + /** + * Get the card expiry year. + * + * @return int The full four-digit year. + */ + public function getExpiryYear() + { + $expiryDate = $this->getDataItem('ExpiryDate'); + + if (! empty($expiryDate)) { + // COnvert 2-digit year to 4-dogot year, in 1970-2069 range. + $dateTime = \DateTime::createFromFormat('y', substr($expiryDate, 2, 2)); + return (int)$dateTime->format('Y'); + } + } + + /** + * The transaction ID will be returned in the data for the Form API, or + * we will have to refer to the request for the Server and Direct APIs. + * + * @return @inherit + */ + public function getTransactionId() + { + return $this->getDataItem('VendorTxCode') + ?: $this->getRequest()->getTransactionId(); + } +}