diff --git a/composer.json b/composer.json index 287d744..b7abb22 100644 --- a/composer.json +++ b/composer.json @@ -43,7 +43,7 @@ }, "extra": { "branch-alias": { - "dev-main": "3.1.x-dev" + "dev-main": "3.2.x-dev" } }, "prefer-stable": true, diff --git a/src/Message/AbstractRequest.php b/src/Message/AbstractRequest.php index 7cb2d4d..27a121a 100644 --- a/src/Message/AbstractRequest.php +++ b/src/Message/AbstractRequest.php @@ -54,6 +54,16 @@ public function setMerchantId($value) return $this->setParameter('merchantId', $value); } + public function getCreateToken() + { + return $this->getParameter('createToken'); + } + + public function setCreateToken($value) + { + return $this->setParameter('createToken', $value); + } + public function getEndpoint() { return ($this->getTestMode() ? $this->testEndpoint : $this->liveEndpoint).$this->getAction(); diff --git a/src/Message/CompletePurchaseResponse.php b/src/Message/CompletePurchaseResponse.php index a3f495b..c282f7a 100644 --- a/src/Message/CompletePurchaseResponse.php +++ b/src/Message/CompletePurchaseResponse.php @@ -38,4 +38,14 @@ public function getMessage() { return $this->data->createdPaymentOutput->payment->status; } + + /** + * Get the card reference (payment token) if available + * + * @return null|string + */ + public function getCardReference() + { + return $this->data->createdPaymentOutput->payment->paymentOutput->cardPaymentMethodSpecificOutput->token ?? null; + } } diff --git a/src/Message/PurchaseRequest.php b/src/Message/PurchaseRequest.php index 5beaaf3..019abbb 100644 --- a/src/Message/PurchaseRequest.php +++ b/src/Message/PurchaseRequest.php @@ -114,6 +114,7 @@ public function getData() $data = [ 'cardPaymentMethodSpecificInput' => [ 'authorizationMode' => $this->authorizationMode ?? 'SALE', + 'tokenize' => (bool) $this->getCreateToken(), 'transactionChannel' => $this->getTransactionChannel() ?? 'ECOMMERCE', ], 'hostedCheckoutSpecificInput' => [ @@ -163,6 +164,10 @@ public function getData() $data['hostedCheckoutSpecificInput']['sessionTimeout'] = (int) $this->getSessionTimeout(); } + if ($this->getToken() !== null || $this->getCardReference() !== null) { + $data['cardPaymentMethodSpecificInput']['token'] = $this->getToken() ?? $this->getCardReference(); + } + if ($this->getNotifyUrl() !== null) { $data['feedbacks'] = [ 'webhookUrl' => $this->getNotifyUrl(), diff --git a/tests/GatewayTest.php b/tests/GatewayTest.php index 03229b6..18244a9 100644 --- a/tests/GatewayTest.php +++ b/tests/GatewayTest.php @@ -57,6 +57,7 @@ public function testCompletePurchaseSuccess() $this->assertFalse($response->isRedirect()); $this->assertSame('1234567890_0', $response->getTransactionReference()); $this->assertSame('PENDING_CAPTURE', $response->getMessage()); + $this->assertSame('12345678-90ab-cdef-1234-567890abcdef', $response->getCardReference()); } public function testCompletePurchaseFailure() @@ -71,6 +72,7 @@ public function testCompletePurchaseFailure() $this->assertFalse($response->isRedirect()); $this->assertSame('1234567890_0', $response->getTransactionReference()); $this->assertSame('CANCELLED', $response->getMessage()); + $this->assertNull($response->getCardReference()); } public function testRefundSuccess() diff --git a/tests/Message/PurchaseRequestTest.php b/tests/Message/PurchaseRequestTest.php index d43152b..52035be 100644 --- a/tests/Message/PurchaseRequestTest.php +++ b/tests/Message/PurchaseRequestTest.php @@ -106,6 +106,8 @@ public function testGetData() $this->assertArrayHasKey("cardPaymentMethodSpecificInput", $data); $this->assertArrayHasKey("authorizationMode", $data["cardPaymentMethodSpecificInput"]); $this->assertSame("SALE", $data["cardPaymentMethodSpecificInput"]['authorizationMode']); + $this->assertArrayHasKey("tokenize", $data["cardPaymentMethodSpecificInput"]); + $this->assertFalse($data["cardPaymentMethodSpecificInput"]['tokenize']); $this->assertArrayHasKey("transactionChannel", $data["cardPaymentMethodSpecificInput"]); $this->assertSame("ECOMMERCE", $data["cardPaymentMethodSpecificInput"]['transactionChannel']); $this->assertArrayHasKey("hostedCheckoutSpecificInput", $data); @@ -157,6 +159,48 @@ public function testGetItemPriceIntegerWithBadItemPrice() $data = $request->getData(); } + public function testCardReference() + { + $params = $this->allParams; + $params['cardReference'] = '12345678-90ab-cdef-1234-567890abcdef'; + $request = new PurchaseRequest($this->getHttpClient(), $this->getHttpRequest()); + $request->initialize($params); + + $data = $request->getData(); + + $this->assertArrayHasKey("cardPaymentMethodSpecificInput", $data); + $this->assertArrayHasKey("token", $data["cardPaymentMethodSpecificInput"]); + $this->assertSame('12345678-90ab-cdef-1234-567890abcdef', $data["cardPaymentMethodSpecificInput"]['token']); + } + + public function testToken() + { + $params = $this->allParams; + $params['token'] = '12345678-90ab-cdef-1234-567890abcdef'; + $request = new PurchaseRequest($this->getHttpClient(), $this->getHttpRequest()); + $request->initialize($params); + + $data = $request->getData(); + + $this->assertArrayHasKey("cardPaymentMethodSpecificInput", $data); + $this->assertArrayHasKey("token", $data["cardPaymentMethodSpecificInput"]); + $this->assertSame('12345678-90ab-cdef-1234-567890abcdef', $data["cardPaymentMethodSpecificInput"]['token']); + } + + public function testCreateToken() + { + $params = $this->allParams; + $params['createToken'] = true; + $request = new PurchaseRequest($this->getHttpClient(), $this->getHttpRequest()); + $request->initialize($params); + + $data = $request->getData(); + + $this->assertArrayHasKey("cardPaymentMethodSpecificInput", $data); + $this->assertArrayHasKey("tokenize", $data["cardPaymentMethodSpecificInput"]); + $this->assertTrue($data["cardPaymentMethodSpecificInput"]['tokenize']); + } + public function testMotoTransactionChannel() { $params = $this->allParams; diff --git a/tests/Mock/HostedCompletePurchaseSuccess.txt b/tests/Mock/HostedCompletePurchaseSuccess.txt index 684e54d..0b32569 100644 --- a/tests/Mock/HostedCompletePurchaseSuccess.txt +++ b/tests/Mock/HostedCompletePurchaseSuccess.txt @@ -6,4 +6,4 @@ Correlation-Id: 11111111-2222-3333-4444-abcdefabcdef Api_Auth: true Strict-Transport-Security: max-age=16000000; includeSubDomains; preload; -{"createdPaymentOutput":{"payment":{"hostedCheckoutSpecificOutput":{"hostedCheckoutId":"0000000001"},"paymentOutput":{"amountOfMoney":{"amount":145,"currencyCode":"EUR"},"references":{"merchantReference":"123abc"},"acquiredAmount":{"amount":145,"currencyCode":"EUR"},"customer":{"device":{"ipAddressCountryCode":"99"}},"cardPaymentMethodSpecificOutput":{"paymentProductId":1,"authorisationCode":"123456","card":{"cardNumber":"************1111","expiryDate":"1230","bin":"411111","countryCode":"99"},"fraudResults":{"fraudServiceResult":"no-advice","avsResult":"U","cvvResult":"M"},"schemeReferenceData":"123456789012","paymentAccountReference":"0123456789ABCDEFGHIJKLMNOPQRS","threeDSecureResults":{"version":"2.2.0","flow":"frictionless","cavv":"AAABBBCCCDDDEEEFFFZZAAAAAAA=","eci":"5","schemeEci":"05","authenticationStatus":"Y","acsTransactionId":"ABCDEF01-2345-6789-0ABC-DEF123456789","dsTransactionId":"ABCDEF01-2345-6789-0ABC-DEF012345678","xid":"ZYXWVUTSRQP000==","liability":"issuer"},"acquirerInformation":{"name":"ACQUIRER"}},"paymentMethod":"card"},"status":"PENDING_CAPTURE","statusOutput":{"isCancellable":true,"statusCategory":"PENDING_MERCHANT","statusCode":5,"isAuthorized":true,"isRefundable":false},"id":"1234567890_0"},"paymentStatusCategory":"SUCCESSFUL"},"status":"PAYMENT_CREATED"} \ No newline at end of file +{"createdPaymentOutput":{"payment":{"hostedCheckoutSpecificOutput":{"hostedCheckoutId":"0000000001"},"paymentOutput":{"amountOfMoney":{"amount":145,"currencyCode":"EUR"},"references":{"merchantReference":"123abc"},"acquiredAmount":{"amount":145,"currencyCode":"EUR"},"customer":{"device":{"ipAddressCountryCode":"99"}},"cardPaymentMethodSpecificOutput":{"paymentProductId":1,"authorisationCode":"123456","card":{"cardNumber":"************1111","expiryDate":"1230","bin":"411111","countryCode":"99"},"fraudResults":{"fraudServiceResult":"no-advice","avsResult":"U","cvvResult":"M"},"schemeReferenceData":"123456789012","paymentAccountReference":"0123456789ABCDEFGHIJKLMNOPQRS","threeDSecureResults":{"version":"2.2.0","flow":"frictionless","cavv":"AAABBBCCCDDDEEEFFFZZAAAAAAA=","eci":"5","schemeEci":"05","authenticationStatus":"Y","acsTransactionId":"ABCDEF01-2345-6789-0ABC-DEF123456789","dsTransactionId":"ABCDEF01-2345-6789-0ABC-DEF012345678","xid":"ZYXWVUTSRQP000==","liability":"issuer"},"token":"12345678-90ab-cdef-1234-567890abcdef","acquirerInformation":{"name":"ACQUIRER"}},"paymentMethod":"card"},"status":"PENDING_CAPTURE","statusOutput":{"isCancellable":true,"statusCategory":"PENDING_MERCHANT","statusCode":5,"isAuthorized":true,"isRefundable":false},"id":"1234567890_0"},"paymentStatusCategory":"SUCCESSFUL"},"status":"PAYMENT_CREATED"} \ No newline at end of file