diff --git a/composer.json b/composer.json index 40ebdeb5..f71e437f 100644 --- a/composer.json +++ b/composer.json @@ -12,6 +12,7 @@ "sage pay", "sagepay" ], + "homepage": "https://github.com/thephpleague/omnipay-sagepay", "license": "MIT", "authors": [ @@ -34,7 +35,7 @@ } }, "require": { - "php": "^5.6|^7", + "php": "^5.6|^7|^8", "omnipay/common": "~3.0" }, "require-dev": { diff --git a/src/Message/AbstractRequest.php b/src/Message/AbstractRequest.php index 31f02334..a7c8106f 100644 --- a/src/Message/AbstractRequest.php +++ b/src/Message/AbstractRequest.php @@ -17,6 +17,34 @@ abstract class AbstractRequest extends OmnipayAbstractRequest implements Constan { use GatewayParamsTrait; + /** + * Flag whether customer's browser can run javascript. + */ + const BROWSER_JAVASCRIPT_YES = 1; + const BROWSER_JAVASCRIPT_NO = 0; + + /** + * Fallback browser language + */ + const BROWSER_LANGUAGE = 'en-GB'; + + /** + * Dimensions of the challenge window to be displayed to the cardholder. + * + * 01 = 250 x 400 + * 02 = 390 x 400 + * 03 = 500 x 600 + * 04 = 600 x 400 + * 05 = Full screen + * + * @var string + */ + const CHALLENGE_WINDOW_SIZE_01 = '01'; + const CHALLENGE_WINDOW_SIZE_02 = '02'; + const CHALLENGE_WINDOW_SIZE_03 = '03'; + const CHALLENGE_WINDOW_SIZE_04 = '04'; + const CHALLENGE_WINDOW_SIZE_05 = '05'; + /** * @var string The service name, used in the endpoint URL. */ @@ -70,6 +98,17 @@ public function getTxType() throw new InvalidRequestException('Transaction type not defined.'); } + + public function getProtocol() + { + return $this->getParameter('protocol'); + } + + public function setProtocol($value) + { + return $this->setParameter('protocol', $value); + } + /** * Basic authorisation, transaction type and protocol version. * @@ -79,7 +118,7 @@ protected function getBaseData() { $data = array(); - $data['VPSProtocol'] = $this->VPSProtocol; + $data['VPSProtocol'] = $this->getProtocol() ?: $this->VPSProtocol; $data['TxType'] = $this->getTxType(); $data['Vendor'] = $this->getVendor(); $data['AccountType'] = $this->getAccountType() ?: static::ACCOUNT_TYPE_E; diff --git a/src/Message/DirectAuthorizeRequest.php b/src/Message/DirectAuthorizeRequest.php index 19325f24..4e6179ed 100644 --- a/src/Message/DirectAuthorizeRequest.php +++ b/src/Message/DirectAuthorizeRequest.php @@ -56,6 +56,32 @@ protected function getBaseAuthorizeData() $data['VendorTxCode'] = $this->getTransactionId(); $data['ClientIPAddress'] = $this->getClientIp(); + $data['BrowserJavascriptEnabled'] = $this->getBrowserJavascriptEnabled() ?: static::BROWSER_JAVASCRIPT_NO; + $data['BrowserLanguage'] = $this->getBrowserLanguage() ?: static::BROWSER_LANGUAGE; + $data['ThreeDSNotificationURL'] = $this->getThreeDSNotificationURL(); + $data['BrowserAcceptHeader'] = $_SERVER['HTTP_ACCEPT']; + $data['BrowserUserAgent'] = $_SERVER['HTTP_USER_AGENT']; + $data['ChallengeWindowSize'] = $this->getChallengeWindowSize() ?: static::CHALLENGE_WINDOW_SIZE_05; + // ---- + // ---- "4.00" - required if BrowserJavascriptEnabled == "1" + $data['BrowserJavaEnabled'] = $this->getBrowserJavaEnabled(); + $data['BrowserColorDepth'] = $this->getBrowserColorDepth(); + $data['BrowserScreenHeight'] = $this->getBrowserScreenHeight(); + $data['BrowserScreenWidth'] = $this->getBrowserScreenWidth(); + $data['BrowserTZ'] = $this->getBrowserTZ(); + // ---- + + // repeat payments required fields + $data['MITType'] = $this->getMITType(); + $data['COFUsage'] = $this->getCOFUsage(); + $data['InitiatedType'] = $this->getInitiatedType(); + $data['SchemeTraceID'] = $this->getSchemeTraceID(); + $data['RecurringExpiry'] = $this->getRecurringExpiry(); + $data['RecurringFrequency'] = $this->getRecurringFrequency(); + $data['ACSTransID'] = $this->getACSTransID(); + $data['DSTransID'] = $this->getDSTransID(); + + $data['ApplyAVSCV2'] = $this->getApplyAVSCV2() ?: static::APPLY_AVSCV2_DEFAULT; $data['Apply3DSecure'] = $this->getApply3DSecure() ?: static::APPLY_3DSECURE_APPLY; diff --git a/src/Message/DirectCompleteAuthorizeRequest.php b/src/Message/DirectCompleteAuthorizeRequest.php index 973ae88e..863475ca 100644 --- a/src/Message/DirectCompleteAuthorizeRequest.php +++ b/src/Message/DirectCompleteAuthorizeRequest.php @@ -16,19 +16,27 @@ public function getService() public function getData() { - // Inconsistent letter case is intentional. - // The issuing bank will return PaRes, but the merchant - // site must send this result as PARes to Sage Pay. + 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->httpRequest->request->get('MD'), + 'PARes' => $this->httpRequest->request->get('PaRes'), // inconsistent caps are intentional + ); - if (empty($data['MD']) || empty($data['PARes'])) { - throw new InvalidResponseException; + if (empty($data['MD']) || empty($data['PARes'])) { + throw new InvalidResponseException; + } } + return $data; } diff --git a/src/Message/Response.php b/src/Message/Response.php index 34d54d34..76fefb25 100644 --- a/src/Message/Response.php +++ b/src/Message/Response.php @@ -96,11 +96,21 @@ public function getRedirectMethod() public function getRedirectData() { if ($this->isRedirect()) { - return array( - 'PaReq' => $this->getDataItem('PAReq'), - 'TermUrl' => $this->getRequest()->getReturnUrl(), - 'MD' => $this->getDataItem('MD'), - ); + if (isset($this->data['CReq'])) { + // 3DSv2 + return [ + 'creq' => $this->data['CReq'], + 'threeDSSessionData' => $this->data['VPSTxId'], + ]; + } else { + // fallback from 3DSv2 to 3DSv1 + return [ + 'PaReq' => $this->data['PAReq'], + 'TermUrl' => $this->getRequest()->getReturnUrl(), + 'MD' => $this->data['MD'], + ]; + } + } } diff --git a/src/Message/SharedRepeatAuthorizeRequest.php b/src/Message/SharedRepeatAuthorizeRequest.php index 23331684..a65a72be 100644 --- a/src/Message/SharedRepeatAuthorizeRequest.php +++ b/src/Message/SharedRepeatAuthorizeRequest.php @@ -91,6 +91,19 @@ public function getData() $data['BasketXML'] = $basketXML; } + // add v4 request fields + if ($this->getProtocol() == '4.00'){ + + $data['MITType'] = $this->getMITType(); + $data['COFUsage'] = $this->getCOFUsage(); + $data['InitiatedType'] = $this->getInitiatedType(); + $data['SchemeTraceID'] = $this->getSchemeTraceID(); + $data['RecurringExpiry'] = $this->getRecurringExpiry(); + $data['RecurringFrequency'] = $this->getRecurringFrequency(); + $data['ACSTransID'] = $this->getACSTransID(); + $data['DSTransID'] = $this->getDSTransID(); + } + return $data; } diff --git a/src/Traits/GatewayParamsTrait.php b/src/Traits/GatewayParamsTrait.php index 83ccdd26..15e56c14 100644 --- a/src/Traits/GatewayParamsTrait.php +++ b/src/Traits/GatewayParamsTrait.php @@ -257,4 +257,175 @@ public function setDisableUtf8Decode($value) { return $this->setParameter('disableUtf8Decode', $value); } + + public function setThreeDSNotificationURL($value) + { + return $this->setParameter('ThreeDSNotificationURL', $value); + } + + public function getThreeDSNotificationURL() + { + return $this->getParameter('ThreeDSNotificationURL'); + } + + public function setBrowserJavascriptEnabled($value) + { + return $this->setParameter('BrowserJavascriptEnabled', $value); + } + + public function getBrowserJavascriptEnabled() + { + return $this->getParameter('BrowserJavascriptEnabled'); + } + + public function setBrowserLanguage($value) + { + return $this->setParameter('BrowserLanguage', $value); + } + + public function getBrowserLanguage() + { + return $this->getParameter('BrowserLanguage'); + } + + public function setChallengeWindowSize($value) + { + return $this->setParameter('ChallengeWindowSize', $value); + } + + public function getChallengeWindowSize() + { + return $this->getParameter('ChallengeWindowSize'); + } + + public function setBrowserJavaEnabled($value) + { + return $this->setParameter('BrowserJavaEnabled', $value); + } + + public function getBrowserJavaEnabled() + { + return $this->getParameter('BrowserJavaEnabled'); + } + + + public function setBrowserColorDepth($value) + { + return $this->setParameter('BrowserColorDepth', $value); + } + + public function getBrowserColorDepth() + { + return $this->getParameter('BrowserColorDepth'); + } + + public function setBrowserScreenHeight($value) + { + return $this->setParameter('BrowserScreenHeight', $value); + } + + public function getBrowserScreenHeight() + { + return $this->getParameter('BrowserScreenHeight'); + } + + public function setBrowserScreenWidth($value) + { + return $this->setParameter('BrowserScreenWidth', $value); + } + + public function getBrowserScreenWidth() + { + return $this->getParameter('BrowserScreenWidth'); + } + + public function setBrowserTZ($value) + { + return $this->setParameter('BrowserTZ', $value); + } + + public function getBrowserTZ() + { + return $this->getParameter('BrowserTZ'); + } + + public function setInitiatedType($value) + { + return $this->setParameter('InitiatedType', $value); + } + + public function getInitiatedType() + { + return $this->getParameter('InitiatedType'); + } + + public function setCOFUsage($value) + { + return $this->setParameter('COFUsage', $value); + } + + public function getCOFUsage() + { + return $this->getParameter('COFUsage'); + } + + public function setMITType($value) + { + return $this->setParameter('MITType', $value); + } + + public function getMITType() + { + return $this->getParameter('MITType'); + } + + public function setSchemeTraceID($value) + { + return $this->setParameter('SchemeTraceID', $value); + } + + public function getSchemeTraceID() + { + return $this->getParameter('SchemeTraceID'); + } + + public function setRecurringExpiry($value) + { + return $this->setParameter('RecurringExpiry', $value); + } + + public function getRecurringExpiry() + { + return $this->getParameter('RecurringExpiry'); + } + + public function setRecurringFrequency($value) + { + return $this->setParameter('RecurringFrequency', $value); + } + + public function getRecurringFrequency() + { + return $this->getParameter('RecurringFrequency'); + } + + public function setACSTransID($value) + { + return $this->setParameter('ACSTransID', $value); + } + + public function getACSTransID() + { + return $this->getParameter('ACSTransID'); + } + + public function setDSTransID($value) + { + return $this->setParameter('DSTransID', $value); + } + + public function getDSTransID() + { + return $this->getParameter('DSTransID'); + } } diff --git a/src/Traits/ServerNotifyTrait.php b/src/Traits/ServerNotifyTrait.php index 0d54111b..defc2203 100644 --- a/src/Traits/ServerNotifyTrait.php +++ b/src/Traits/ServerNotifyTrait.php @@ -91,6 +91,18 @@ public function buildSignature() $this->getBankAuthCode(), ) ); + // new in v4 + if ( $this->getDataItem('VPSProtocol') =='4.00'){ + $signatureData = array_merge( + $signatureData, + array( + $this->getDataItem('ACSTransID'), + $this->getDataItem('DSTransID'), + $this->getDataItem('SchemeTraceID'), + ) + ); + } + } return md5(implode('', $signatureData));