From ebb2db8a3fb5f20adb5f6b018b9668534c03c5e9 Mon Sep 17 00:00:00 2001 From: Ben James Date: Wed, 23 Jun 2021 10:13:26 +0100 Subject: [PATCH 01/15] Payment Integration Signed-off-by: Ben James --- src/ConstantsInterface.php | 4 + src/Message/AbstractRestRequest.php | 260 ++++++++++++++ src/Message/DirectAuthorizeRequest.php | 2 +- src/Message/RestResponse.php | 138 ++++++++ .../ServerRestCompletePurchaseRequest.php | 89 +++++ src/Message/ServerRestCompleteResponse.php | 18 + src/Message/ServerRestInstructionResponse.php | 34 ++ .../ServerRestMerchantSessionKeyRequest.php | 37 ++ .../ServerRestMerchantSessionKeyResponse.php | 28 ++ src/Message/ServerRestPurchaseRequest.php | 90 +++++ src/Message/ServerRestPurchaseResponse.php | 11 + src/Message/ServerRestRefundRequest.php | 62 ++++ src/Message/ServerRestRefundResponse.php | 11 + src/Message/ServerRestRepeatRequest.php | 62 ++++ src/Message/ServerRestRepeatResponse.php | 11 + .../ServerRestRetrieveTransactionRequest.php | 47 +++ .../ServerRestRetrieveTransactionResponse.php | 11 + src/Message/ServerRestVoidRequest.php | 57 +++ src/RestServerGateway.php | 98 ++++++ src/Traits/ResponseRestFieldsTrait.php | 333 ++++++++++++++++++ 20 files changed, 1402 insertions(+), 1 deletion(-) create mode 100644 src/Message/AbstractRestRequest.php create mode 100644 src/Message/RestResponse.php create mode 100644 src/Message/ServerRestCompletePurchaseRequest.php create mode 100644 src/Message/ServerRestCompleteResponse.php create mode 100644 src/Message/ServerRestInstructionResponse.php create mode 100644 src/Message/ServerRestMerchantSessionKeyRequest.php create mode 100644 src/Message/ServerRestMerchantSessionKeyResponse.php create mode 100644 src/Message/ServerRestPurchaseRequest.php create mode 100644 src/Message/ServerRestPurchaseResponse.php create mode 100644 src/Message/ServerRestRefundRequest.php create mode 100644 src/Message/ServerRestRefundResponse.php create mode 100644 src/Message/ServerRestRepeatRequest.php create mode 100644 src/Message/ServerRestRepeatResponse.php create mode 100644 src/Message/ServerRestRetrieveTransactionRequest.php create mode 100644 src/Message/ServerRestRetrieveTransactionResponse.php create mode 100644 src/Message/ServerRestVoidRequest.php create mode 100644 src/RestServerGateway.php create mode 100644 src/Traits/ResponseRestFieldsTrait.php diff --git a/src/ConstantsInterface.php b/src/ConstantsInterface.php index e4efa7a0..4421167d 100644 --- a/src/ConstantsInterface.php +++ b/src/ConstantsInterface.php @@ -115,6 +115,10 @@ 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'; /** * 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..5d25fe0c --- /dev/null +++ b/src/Message/AbstractRestRequest.php @@ -0,0 +1,260 @@ +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 setMerchantSessionKey($value) + { + return $this->setParameter('merchantSessionKey', $value); + } + + public function setCardIdentifier($value) + { + return $this->setParameter('cardIdentifier', $value); + } + + /** + * @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..4a83c03a --- /dev/null +++ b/src/Message/RestResponse.php @@ -0,0 +1,138 @@ +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()) { + 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..b4ed3e65 --- /dev/null +++ b/src/Message/ServerRestCompletePurchaseRequest.php @@ -0,0 +1,89 @@ +getParameter('transactionId'); + } + + public function getData() + { + + $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..3dfeae0d --- /dev/null +++ b/src/Message/ServerRestCompleteResponse.php @@ -0,0 +1,18 @@ +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..5b3312bf --- /dev/null +++ b/src/Message/ServerRestPurchaseRequest.php @@ -0,0 +1,90 @@ +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 = $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(); + return $data; + } +} 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(); + + 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..0f42e2f2 --- /dev/null +++ b/src/RestServerGateway.php @@ -0,0 +1,98 @@ +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 = array()) + { + return $this->createRequest(ServerRestMerchantSessionKeyRequest::class, $parameters); + } + + /** + * Purchase and handling of return from 3D Secure redirection. + */ + public function purchase(array $parameters = array()) + { + return $this->createRequest(ServerRestPurchaseRequest::class, $parameters); + } + + /** + * Handle purchase notifcation callback. + */ + public function complete(array $parameters = array()) + { + return $this->createRequest(ServerRestCompletePurchaseRequest::class, $parameters); + } + + /** + * Get transaction information from Sage. + */ + public function getTransaction(array $parameters = array()) + { + return $this->createRequest(ServerRestRetrieveTransactionRequest::class, $parameters); + } + + /** + * Refund request. + */ + public function refund(array $parameters = array()) + { + return $this->createRequest(ServerRestRefundRequest::class, $parameters); + } + + /** + * Repeat request. + */ + public function repeat(array $parameters = array()) + { + return $this->createRequest(ServerRestRepeatRequest::class, $parameters); + } + + /** + * Void request. + */ + public function void(array $parameters = array()) + { + return $this->createRequest(ServerRestVoidRequest::class, $parameters); + } +} diff --git a/src/Traits/ResponseRestFieldsTrait.php b/src/Traits/ResponseRestFieldsTrait.php new file mode 100644 index 00000000..cf4f9d39 --- /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 (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(); + } +} From 3e02ac0058a97f61e737e12da11f195f95b662a9 Mon Sep 17 00:00:00 2001 From: Ben James Date: Thu, 1 Jul 2021 11:39:16 +0100 Subject: [PATCH 02/15] wip Signed-off-by: Ben James --- src/ConstantsInterface.php | 1 + .../ServerRestCompletePurchaseRequest.php | 26 ++++++++++++++----- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/ConstantsInterface.php b/src/ConstantsInterface.php index 4421167d..9809e0af 100644 --- a/src/ConstantsInterface.php +++ b/src/ConstantsInterface.php @@ -119,6 +119,7 @@ interface ConstantsInterface 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/ServerRestCompletePurchaseRequest.php b/src/Message/ServerRestCompletePurchaseRequest.php index b4ed3e65..9a2210f0 100644 --- a/src/Message/ServerRestCompletePurchaseRequest.php +++ b/src/Message/ServerRestCompletePurchaseRequest.php @@ -12,6 +12,10 @@ class ServerRestCompletePurchaseRequest extends AbstractRestRequest { public function getService() { + if($this->httpRequest->request->has('cres')){ + return static::SERVICE_REST_3D_CHALLENGE; + } + return static::SERVICE_REST_3D; } @@ -27,14 +31,24 @@ public function getParentServiceReference() public function getData() { + if($this->httpRequest->request->has('cres')){ + $data = array( + 'CRes' => $this->httpRequest->request->get('cres'), // inconsistent caps are intentional + 'VPSTxId' => $this->httpRequest->request->get('threeDSSessionData'), + ); - $data = array( - 'MD' => $this->getMd() ?: $this->httpRequest->request->get('MD'), - 'paRes' => $this->getPaRes() ?: $this->httpRequest->request->get('PaRes'), - ); + if (empty($data['CRes']) || empty($data['VPSTxId'])) { + 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; + if (empty($data['MD']) || empty($data['paRes'])) { + throw new InvalidResponseException; + } } return $data; From 24c5d94a6242d6e424869143b91b2f9b4f63c729 Mon Sep 17 00:00:00 2001 From: Ben James Date: Fri, 2 Jul 2021 13:58:14 +0100 Subject: [PATCH 03/15] wip Signed-off-by: Ben James --- src/Message/AbstractRestRequest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Message/AbstractRestRequest.php b/src/Message/AbstractRestRequest.php index 5d25fe0c..641f60fe 100644 --- a/src/Message/AbstractRestRequest.php +++ b/src/Message/AbstractRestRequest.php @@ -14,6 +14,7 @@ abstract class AbstractRestRequest extends AbstractRequest implements ConstantsInterface { + protected $VPSProtocol = '4.00'; /** * @var string The service name, used in the endpoint URL. @@ -95,7 +96,7 @@ public function getApiVersion() return $this->apiVersion; } - + public function getUsername() { return $this->getParameter('username'); From 635ec754800ae8d73f10d2c27ee850df51ee3ee7 Mon Sep 17 00:00:00 2001 From: Ben James Date: Fri, 2 Jul 2021 22:10:31 +0100 Subject: [PATCH 04/15] wip Signed-off-by: Ben James --- src/Message/RestResponse.php | 17 +++++++++---- .../ServerRestCompletePurchaseRequest.php | 6 ++--- src/Message/ServerRestPurchaseRequest.php | 24 +++++++++++++++++-- 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/src/Message/RestResponse.php b/src/Message/RestResponse.php index 4a83c03a..cc5405db 100644 --- a/src/Message/RestResponse.php +++ b/src/Message/RestResponse.php @@ -65,11 +65,18 @@ public function getRedirectMethod() public function getRedirectData() { if ($this->isRedirect()) { - return array( - 'PaReq' => $this->getDataItem('paReq'), - 'TermUrl' => $this->getRequest()->getReturnUrl(), - 'MD' => $this->getRequest()->getMd(), - ); + 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(), + ); + } } } diff --git a/src/Message/ServerRestCompletePurchaseRequest.php b/src/Message/ServerRestCompletePurchaseRequest.php index 9a2210f0..cb86a27a 100644 --- a/src/Message/ServerRestCompletePurchaseRequest.php +++ b/src/Message/ServerRestCompletePurchaseRequest.php @@ -33,11 +33,11 @@ public function getData() { if($this->httpRequest->request->has('cres')){ $data = array( - 'CRes' => $this->httpRequest->request->get('cres'), // inconsistent caps are intentional - 'VPSTxId' => $this->httpRequest->request->get('threeDSSessionData'), + 'cRes' => $this->httpRequest->request->get('cres'), // inconsistent caps are intentional + 'MD' => $this->httpRequest->request->get('threeDSSessionData'), ); - if (empty($data['CRes']) || empty($data['VPSTxId'])) { + if (empty($data['cres']) || empty($data['MD'])) { throw new InvalidResponseException; } }else{ diff --git a/src/Message/ServerRestPurchaseRequest.php b/src/Message/ServerRestPurchaseRequest.php index 5b3312bf..3c35af79 100644 --- a/src/Message/ServerRestPurchaseRequest.php +++ b/src/Message/ServerRestPurchaseRequest.php @@ -11,7 +11,7 @@ public function getService() { return static::SERVICE_REST_TRANSACTIONS; } - + /** * @return string the transaction type */ @@ -54,6 +54,7 @@ protected function getBasePurchaseData() $data['currency'] = $this->getCurrency(); $data['NotificationURL'] = $this->getNotifyUrl() ?: $this->getReturnUrl(); $data['MD'] = $this->getMd(); + $data['strongCustomerAuthentication'] = $this->getStrongCustomerAuthentication(); $data = $this->getBillingAddressData($data); $data = $this->getShippingDetailsData($data); @@ -63,7 +64,7 @@ protected function getBasePurchaseData() } // $data['ApplyAVSCV2'] = $this->getApplyAVSCV2() ?: static::APPLY_AVSCV2_DEFAULT; - // $data['apply3DSecure'] = $this->getApply3DSecure() ?: static::APPLY_3DSECURE_APPLY; + $data['apply3DSecure'] = $this->getApply3DSecure() ?: static::APPLY_3DSECURE_APPLY; // user parent data here and the abstract can provide txtype vendor etc return $data; } @@ -87,4 +88,23 @@ public function getPaymentMethodData($data = []) $data['paymentMethod']['card']['cardIdentifier'] = $this->getCardIdentifier(); 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'); + } } From 0e37062591e44c0e046fe9bf9ca635836ed6818d Mon Sep 17 00:00:00 2001 From: Ben James Date: Thu, 8 Jul 2021 14:31:58 +0100 Subject: [PATCH 05/15] wip Signed-off-by: Ben James --- src/Message/ServerRestCompletePurchaseRequest.php | 2 +- src/Message/ServerRestCompleteResponse.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Message/ServerRestCompletePurchaseRequest.php b/src/Message/ServerRestCompletePurchaseRequest.php index cb86a27a..9b4ecc8d 100644 --- a/src/Message/ServerRestCompletePurchaseRequest.php +++ b/src/Message/ServerRestCompletePurchaseRequest.php @@ -37,7 +37,7 @@ public function getData() 'MD' => $this->httpRequest->request->get('threeDSSessionData'), ); - if (empty($data['cres']) || empty($data['MD'])) { + if (empty($data['cRes']) || empty($data['MD'])) { throw new InvalidResponseException; } }else{ diff --git a/src/Message/ServerRestCompleteResponse.php b/src/Message/ServerRestCompleteResponse.php index 3dfeae0d..8b6bc13b 100644 --- a/src/Message/ServerRestCompleteResponse.php +++ b/src/Message/ServerRestCompleteResponse.php @@ -13,6 +13,6 @@ class ServerRestCompleteResponse extends RestResponse */ public function isSuccessful() { - return strtoupper($this->getStatus()) === static::SAGEPAY_STATUS_AUTHENTICATED; + return strtoupper($this->get3DSecureStatus() ?? $this->getStatus()) === static::SAGEPAY_STATUS_AUTHENTICATED; } } From a7bf9c8712b333870f94c53cf8b5ca955634e640 Mon Sep 17 00:00:00 2001 From: Ben James Date: Fri, 9 Jul 2021 13:28:45 +0100 Subject: [PATCH 06/15] wip Signed-off-by: Ben James --- src/Traits/ResponseRestFieldsTrait.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Traits/ResponseRestFieldsTrait.php b/src/Traits/ResponseRestFieldsTrait.php index cf4f9d39..38198079 100644 --- a/src/Traits/ResponseRestFieldsTrait.php +++ b/src/Traits/ResponseRestFieldsTrait.php @@ -142,7 +142,7 @@ public function getAvsCvcCheck($wholeStatus = true) } return (object) $avsCvcCheck; } - + /** * This is the response from AVS and CV2 checks. * Provided for Vendor info and backward compatibility with the @@ -205,7 +205,7 @@ public function get3DSecureStatus() { $secure3DResponse = $this->getDataItem('3DSecure'); - if (array_key_exists('status', $secure3DResponse)) { + if (is_array($secure3DResponse) && array_key_exists('status', $secure3DResponse)) { return strtoupper($secure3DResponse['status']); } From 5170d8c06f9b4f35277586119bf54013f1eb9271 Mon Sep 17 00:00:00 2001 From: Ben James Date: Fri, 20 Aug 2021 10:38:36 +0100 Subject: [PATCH 07/15] wip Signed-off-by: Ben James --- src/Message/ServerRestPurchaseRequest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Message/ServerRestPurchaseRequest.php b/src/Message/ServerRestPurchaseRequest.php index 3c35af79..ad6d4fb7 100644 --- a/src/Message/ServerRestPurchaseRequest.php +++ b/src/Message/ServerRestPurchaseRequest.php @@ -47,7 +47,7 @@ protected function getBasePurchaseData() $data = $this->getBaseData(); - $data['transactionType'] = $this->getTxType(); + $data['transactionType'] = ucfirst(strtolower($this->getTxType())); $data['vendorTxCode'] = $this->getTransactionId(); $data['description'] = $this->getDescription(); $data['amount'] = (int) $this->getAmount(); From c11fed6e3cf14dd2705839342594c2fc3e68fa8d Mon Sep 17 00:00:00 2001 From: Ben James Date: Tue, 24 Aug 2021 20:31:38 +0100 Subject: [PATCH 08/15] transaction type case issue Signed-off-by: Ben James --- src/Message/ServerRestPurchaseRequest.php | 4 ++-- src/Message/ServerRestRefundRequest.php | 4 ++-- src/Message/ServerRestRepeatRequest.php | 4 ++-- src/Message/ServerRestVoidRequest.php | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Message/ServerRestPurchaseRequest.php b/src/Message/ServerRestPurchaseRequest.php index ad6d4fb7..f682dcf3 100644 --- a/src/Message/ServerRestPurchaseRequest.php +++ b/src/Message/ServerRestPurchaseRequest.php @@ -17,7 +17,7 @@ public function getService() */ public function getTxType() { - return static::TXTYPE_PAYMENT; + return ucfirst(strtolower(static::TXTYPE_PAYMENT)); } /** @@ -47,7 +47,7 @@ protected function getBasePurchaseData() $data = $this->getBaseData(); - $data['transactionType'] = ucfirst(strtolower($this->getTxType())); + $data['transactionType'] = $this->getTxType(); $data['vendorTxCode'] = $this->getTransactionId(); $data['description'] = $this->getDescription(); $data['amount'] = (int) $this->getAmount(); diff --git a/src/Message/ServerRestRefundRequest.php b/src/Message/ServerRestRefundRequest.php index 1be506c1..815a3439 100644 --- a/src/Message/ServerRestRefundRequest.php +++ b/src/Message/ServerRestRefundRequest.php @@ -13,13 +13,13 @@ public function getService() { return static::SERVICE_REST_TRANSACTIONS; } - + /** * @return string the transaction type */ public function getTxType() { - return static::TXTYPE_REFUND; + return ucfirst(strtolower(static::TXTYPE_REFUND)); } /** diff --git a/src/Message/ServerRestRepeatRequest.php b/src/Message/ServerRestRepeatRequest.php index 463b0fc1..2a33242a 100644 --- a/src/Message/ServerRestRepeatRequest.php +++ b/src/Message/ServerRestRepeatRequest.php @@ -13,13 +13,13 @@ public function getService() { return static::SERVICE_REST_TRANSACTIONS; } - + /** * @return string the transaction type */ public function getTxType() { - return static::TXTYPE_REPEAT; + return ucfirst(strtolower(static::TXTYPE_REPEAT); } /** diff --git a/src/Message/ServerRestVoidRequest.php b/src/Message/ServerRestVoidRequest.php index f76419c9..acf5e769 100644 --- a/src/Message/ServerRestVoidRequest.php +++ b/src/Message/ServerRestVoidRequest.php @@ -19,13 +19,13 @@ public function getParentService() { return static::SERVICE_REST_TRANSACTIONS; } - + /** * @return string the transaction type */ public function getTxType() { - return static::TXTYPE_VOID; + return ucfirst(strtolower(static::TXTYPE_VOID); } /** @@ -45,7 +45,7 @@ public function getParentServiceReference() { return $this->getParameter('transactionId'); } - + /** * @param array $data * @return ServerRestInstructionResponse From 2268a9c2cc10c0a1b1c6246ee76c70ccd5cd3857 Mon Sep 17 00:00:00 2001 From: Ben James Date: Tue, 24 Aug 2021 20:48:23 +0100 Subject: [PATCH 09/15] getter setter credentialType Signed-off-by: Ben James --- src/Message/ServerRestPurchaseRequest.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/Message/ServerRestPurchaseRequest.php b/src/Message/ServerRestPurchaseRequest.php index f682dcf3..e85295b2 100644 --- a/src/Message/ServerRestPurchaseRequest.php +++ b/src/Message/ServerRestPurchaseRequest.php @@ -89,6 +89,25 @@ public function getPaymentMethodData($data = []) return $data; } + /** + * 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'); + } + /** * Set the strongCustomerAuthentication field(s). * From 71d64d8776a7910232ca91c4f9b9caf22775711b Mon Sep 17 00:00:00 2001 From: Ben James Date: Tue, 24 Aug 2021 21:11:55 +0100 Subject: [PATCH 10/15] getter setter credentialType Signed-off-by: Ben James --- src/Message/ServerRestPurchaseRequest.php | 1 + src/Message/ServerRestRepeatRequest.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Message/ServerRestPurchaseRequest.php b/src/Message/ServerRestPurchaseRequest.php index e85295b2..bb3b64c9 100644 --- a/src/Message/ServerRestPurchaseRequest.php +++ b/src/Message/ServerRestPurchaseRequest.php @@ -55,6 +55,7 @@ protected function getBasePurchaseData() $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); diff --git a/src/Message/ServerRestRepeatRequest.php b/src/Message/ServerRestRepeatRequest.php index 2a33242a..23456878 100644 --- a/src/Message/ServerRestRepeatRequest.php +++ b/src/Message/ServerRestRepeatRequest.php @@ -19,7 +19,7 @@ public function getService() */ public function getTxType() { - return ucfirst(strtolower(static::TXTYPE_REPEAT); + return ucfirst(strtolower(static::TXTYPE_REPEAT)); } /** From ba48e2934f7be23b751f978f13df8d03b69b989e Mon Sep 17 00:00:00 2001 From: Ben James Date: Wed, 25 Aug 2021 09:23:54 +0100 Subject: [PATCH 11/15] token save and reuse Signed-off-by: Ben James --- src/Message/AbstractRestRequest.php | 20 ++++++++++++++++++++ src/Message/ServerRestPurchaseRequest.php | 6 ++++++ 2 files changed, 26 insertions(+) diff --git a/src/Message/AbstractRestRequest.php b/src/Message/AbstractRestRequest.php index 641f60fe..f4ab57ef 100644 --- a/src/Message/AbstractRestRequest.php +++ b/src/Message/AbstractRestRequest.php @@ -127,6 +127,26 @@ public function getCardIdentifier() return $this->getParameter('cardIdentifier'); } + public function getTokenReusable() + { + return $this->getParameter('tokenReusable'); + } + + public function getTokenSave() + { + return $this->getParameter('tokenSave'); + } + + public function setTokenReusable() + { + return $this->setParameter('tokenReusable'); + } + + public function setTokenSave($value) + { + return $this->setParameter('tokenSave', $value); + } + public function setMerchantSessionKey($value) { return $this->setParameter('merchantSessionKey', $value); diff --git a/src/Message/ServerRestPurchaseRequest.php b/src/Message/ServerRestPurchaseRequest.php index bb3b64c9..54713e28 100644 --- a/src/Message/ServerRestPurchaseRequest.php +++ b/src/Message/ServerRestPurchaseRequest.php @@ -87,6 +87,12 @@ 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; } From 50b14311c903266d981bdd87c5082dec822e2705 Mon Sep 17 00:00:00 2001 From: Ben James Date: Wed, 25 Aug 2021 09:44:40 +0100 Subject: [PATCH 12/15] bug fix and move Credential type to abstract Signed-off-by: Ben James --- src/Message/AbstractRestRequest.php | 21 ++++++++++++++++++++- src/Message/ServerRestPurchaseRequest.php | 19 ------------------- src/Message/ServerRestRepeatRequest.php | 1 + 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/Message/AbstractRestRequest.php b/src/Message/AbstractRestRequest.php index f4ab57ef..e079c5e3 100644 --- a/src/Message/AbstractRestRequest.php +++ b/src/Message/AbstractRestRequest.php @@ -139,7 +139,7 @@ public function getTokenSave() public function setTokenReusable() { - return $this->setParameter('tokenReusable'); + return $this->setParameter('tokenReusable', $value); } public function setTokenSave($value) @@ -157,6 +157,25 @@ 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 */ diff --git a/src/Message/ServerRestPurchaseRequest.php b/src/Message/ServerRestPurchaseRequest.php index 54713e28..fbe7ca94 100644 --- a/src/Message/ServerRestPurchaseRequest.php +++ b/src/Message/ServerRestPurchaseRequest.php @@ -96,25 +96,6 @@ public function getPaymentMethodData($data = []) return $data; } - /** - * 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'); - } - /** * Set the strongCustomerAuthentication field(s). * diff --git a/src/Message/ServerRestRepeatRequest.php b/src/Message/ServerRestRepeatRequest.php index 23456878..8559fdbd 100644 --- a/src/Message/ServerRestRepeatRequest.php +++ b/src/Message/ServerRestRepeatRequest.php @@ -37,6 +37,7 @@ public function getData() $data['amount'] = (int) $this->getAmount(); $data['currency'] = $this->getCurrency(); $data['referenceTransactionId'] = $this->getReferenceTransactionId(); + $data['credentialType'] = $this->getCredentialType(); return $data; } From 462a376a3b2488006c5d6e4f1e7fc3ff0a55fdc0 Mon Sep 17 00:00:00 2001 From: Ben James Date: Wed, 25 Aug 2021 09:49:05 +0100 Subject: [PATCH 13/15] wip Signed-off-by: Ben James --- src/Message/AbstractRestRequest.php | 2 +- src/Message/ServerRestVoidRequest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Message/AbstractRestRequest.php b/src/Message/AbstractRestRequest.php index e079c5e3..8f83b875 100644 --- a/src/Message/AbstractRestRequest.php +++ b/src/Message/AbstractRestRequest.php @@ -137,7 +137,7 @@ public function getTokenSave() return $this->getParameter('tokenSave'); } - public function setTokenReusable() + public function setTokenReusable($value) { return $this->setParameter('tokenReusable', $value); } diff --git a/src/Message/ServerRestVoidRequest.php b/src/Message/ServerRestVoidRequest.php index acf5e769..0a2c77dc 100644 --- a/src/Message/ServerRestVoidRequest.php +++ b/src/Message/ServerRestVoidRequest.php @@ -25,7 +25,7 @@ public function getParentService() */ public function getTxType() { - return ucfirst(strtolower(static::TXTYPE_VOID); + return ucfirst(strtolower(static::TXTYPE_VOID)); } /** From c5d6d30e969f3223f09e7df9529be3e63354f5c4 Mon Sep 17 00:00:00 2001 From: Ben James Date: Mon, 6 Dec 2021 15:59:59 +0000 Subject: [PATCH 14/15] PHP 8 allowed Signed-off-by: Ben James --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 40ebdeb5..4814af7a 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,7 @@ } }, "require": { - "php": "^5.6|^7", + "php": "^7.2|^8", "omnipay/common": "~3.0" }, "require-dev": { From 3ac925e0ff2d736730cfced4d2dd922886341b81 Mon Sep 17 00:00:00 2001 From: Ben James Date: Mon, 6 Dec 2021 16:07:46 +0000 Subject: [PATCH 15/15] add fetchTransaction as the method, with getTransaction as an alias. Run style fix Signed-off-by: Ben James --- src/Message/RestResponse.php | 4 +-- .../ServerRestCompletePurchaseRequest.php | 6 ++-- src/RestServerGateway.php | 32 ++++++++++++------- 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/Message/RestResponse.php b/src/Message/RestResponse.php index cc5405db..a8bbb4e8 100644 --- a/src/Message/RestResponse.php +++ b/src/Message/RestResponse.php @@ -65,12 +65,12 @@ public function getRedirectMethod() public function getRedirectData() { if ($this->isRedirect()) { - if($this->getDataItem('cReq')){ + if ($this->getDataItem('cReq')) { return array( 'creq' => $this->getDataItem('cReq'), 'threeDSSessionData' => $this->getRequest()->getMd(), ); - }else{ + } else { return array( 'PaReq' => $this->getDataItem('paReq'), 'TermUrl' => $this->getRequest()->getReturnUrl(), diff --git a/src/Message/ServerRestCompletePurchaseRequest.php b/src/Message/ServerRestCompletePurchaseRequest.php index 9b4ecc8d..906a1fd4 100644 --- a/src/Message/ServerRestCompletePurchaseRequest.php +++ b/src/Message/ServerRestCompletePurchaseRequest.php @@ -12,7 +12,7 @@ class ServerRestCompletePurchaseRequest extends AbstractRestRequest { public function getService() { - if($this->httpRequest->request->has('cres')){ + if ($this->httpRequest->request->has('cres')) { return static::SERVICE_REST_3D_CHALLENGE; } @@ -31,7 +31,7 @@ public function getParentServiceReference() public function getData() { - if($this->httpRequest->request->has('cres')){ + if ($this->httpRequest->request->has('cres')) { $data = array( 'cRes' => $this->httpRequest->request->get('cres'), // inconsistent caps are intentional 'MD' => $this->httpRequest->request->get('threeDSSessionData'), @@ -40,7 +40,7 @@ public function getData() if (empty($data['cRes']) || empty($data['MD'])) { throw new InvalidResponseException; } - }else{ + } else { $data = array( 'MD' => $this->getMd() ?: $this->httpRequest->request->get('MD'), 'paRes' => $this->getPaRes() ?: $this->httpRequest->request->get('PaRes'), diff --git a/src/RestServerGateway.php b/src/RestServerGateway.php index 0f42e2f2..ec4b2337 100644 --- a/src/RestServerGateway.php +++ b/src/RestServerGateway.php @@ -2,13 +2,13 @@ namespace Omnipay\SagePay; -use Omnipay\SagePay\Message\ServerRestCompletePurchaseRequest; -use Omnipay\SagePay\Message\ServerRestMerchantSessionKeyRequest; -use Omnipay\SagePay\Message\ServerRestPurchaseRequest; +use Omnipay\SagePay\Message\ServerRestVoidRequest; use Omnipay\SagePay\Message\ServerRestRefundRequest; use Omnipay\SagePay\Message\ServerRestRepeatRequest; +use Omnipay\SagePay\Message\ServerRestPurchaseRequest; +use Omnipay\SagePay\Message\ServerRestCompletePurchaseRequest; +use Omnipay\SagePay\Message\ServerRestMerchantSessionKeyRequest; use Omnipay\SagePay\Message\ServerRestRetrieveTransactionRequest; -use Omnipay\SagePay\Message\ServerRestVoidRequest; /** * Sage Pay Rest Server Gateway @@ -19,7 +19,7 @@ public function getName() { return 'Sage Pay REST Server'; } - + public function getUsername() { return $this->getParameter('username'); @@ -43,7 +43,7 @@ public function setPassword($value) /** * Create merchant session key (MSK). */ - public function createMerchantSessionKey(array $parameters = array()) + public function createMerchantSessionKey(array $parameters = []) { return $this->createRequest(ServerRestMerchantSessionKeyRequest::class, $parameters); } @@ -51,7 +51,7 @@ public function createMerchantSessionKey(array $parameters = array()) /** * Purchase and handling of return from 3D Secure redirection. */ - public function purchase(array $parameters = array()) + public function purchase(array $parameters = []) { return $this->createRequest(ServerRestPurchaseRequest::class, $parameters); } @@ -59,7 +59,7 @@ public function purchase(array $parameters = array()) /** * Handle purchase notifcation callback. */ - public function complete(array $parameters = array()) + public function complete(array $parameters = []) { return $this->createRequest(ServerRestCompletePurchaseRequest::class, $parameters); } @@ -67,7 +67,15 @@ public function complete(array $parameters = array()) /** * Get transaction information from Sage. */ - public function getTransaction(array $parameters = array()) + 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); } @@ -75,7 +83,7 @@ public function getTransaction(array $parameters = array()) /** * Refund request. */ - public function refund(array $parameters = array()) + public function refund(array $parameters = []) { return $this->createRequest(ServerRestRefundRequest::class, $parameters); } @@ -83,7 +91,7 @@ public function refund(array $parameters = array()) /** * Repeat request. */ - public function repeat(array $parameters = array()) + public function repeat(array $parameters = []) { return $this->createRequest(ServerRestRepeatRequest::class, $parameters); } @@ -91,7 +99,7 @@ public function repeat(array $parameters = array()) /** * Void request. */ - public function void(array $parameters = array()) + public function void(array $parameters = []) { return $this->createRequest(ServerRestVoidRequest::class, $parameters); }