From 4bb0f8b5eedad11600d022e243522af5131f8e23 Mon Sep 17 00:00:00 2001 From: "david.owusu" Date: Tue, 16 Apr 2024 14:21:54 +0200 Subject: [PATCH 1/3] Revert "[CC-730] add ctp back in" This reverts commit 73b2eb90dc6dca061e0a6f490b1b65aad143b3fa. --- CHANGELOG.md | 3 +- src/Constants/IdStrings.php | 1 + src/Resources/PaymentTypes/Clicktopay.php | 88 +++++ src/Services/ResourceService.php | 4 + .../jsonData/clicktopay/createRequest.json | 6 + .../jsonData/clicktopay/fetchResponse.json | 14 + .../PaymentTypes/ClickToPayTest.php | 307 ++++++++++++++++++ .../Resources/PaymentTypes/ClickToPayTest.php | 69 ++++ 8 files changed, 491 insertions(+), 1 deletion(-) create mode 100644 src/Resources/PaymentTypes/Clicktopay.php create mode 100644 test/Fixtures/jsonData/clicktopay/createRequest.json create mode 100644 test/Fixtures/jsonData/clicktopay/fetchResponse.json create mode 100644 test/integration/PaymentTypes/ClickToPayTest.php create mode 100644 test/unit/Resources/PaymentTypes/ClickToPayTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index f7006278..994b6dea 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,10 +7,11 @@ to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ## UNRELEASED -Twint payment method is added to SDK. +Click To Pay payment method is added to Java SDK. ### Added +* Added `\UnzerSDK\Resources\PaymentTypes\ClickToPay` payment method. * Added `\UnzerSDK\Resources\PaymentTypes\Twint` payment method. ## [3.5.0](https://github.com/unzerdev/php-sdk/compare/3.4.1..3.5.0) diff --git a/src/Constants/IdStrings.php b/src/Constants/IdStrings.php index e670e1c9..f299f7d0 100755 --- a/src/Constants/IdStrings.php +++ b/src/Constants/IdStrings.php @@ -25,6 +25,7 @@ class IdStrings public const EPS = 'eps'; public const GIROPAY = 'gro'; public const GOOGLE_PAY = 'gop'; + public const CLICK_TO_PAY = 'ctp'; public const HIRE_PURCHASE_DIRECT_DEBIT = 'hdd'; public const IDEAL = 'idl'; public const INSTALLMENT_SECURED = 'ins'; diff --git a/src/Resources/PaymentTypes/Clicktopay.php b/src/Resources/PaymentTypes/Clicktopay.php new file mode 100644 index 00000000..7f697859 --- /dev/null +++ b/src/Resources/PaymentTypes/Clicktopay.php @@ -0,0 +1,88 @@ +mcCorrelationId = $mcCorrelationId; + $this->mcCxFlowId = $mcCxFlowId; + $this->mcMerchantTransactionId = $mcMerchantTransactionId; + $this->brand = $brand; + } + + + public function getMcCorrelationId() : ?string + { + return $this->mcCorrelationId; + } + + + public function getMcCxFlowId(): ?string + { + return $this->mcCxFlowId; + } + + + public function getBrand(): ?string + { + return $this->brand; + } + + + public function getMcMerchantTransactionId(): ?string + { + return $this->mcMerchantTransactionId; + } + + + public function setMcCxFlowId($mcCxFlowId): self + { + $this->mcCxFlowId = $mcCxFlowId; + return $this; + } + + + public function setMcCorrelationId($mcCorrelationId): self + { + $this->mcCorrelationId = $mcCorrelationId; + return $this; + } + + + public function setMcMerchantTransactionId($mcMerchantTransactionId): self + { + $this->mcMerchantTransactionId = $mcMerchantTransactionId; + return $this; + } + + + public function setBrand($brand): self + { + $this->brand = $brand; + return $this; + } + + +} \ No newline at end of file diff --git a/src/Services/ResourceService.php b/src/Services/ResourceService.php index 819ef101..0b439872 100755 --- a/src/Services/ResourceService.php +++ b/src/Services/ResourceService.php @@ -10,6 +10,7 @@ use UnzerSDK\Exceptions\UnzerApiException; use UnzerSDK\Resources\Config; use UnzerSDK\Resources\PaymentTypes\Applepay; +use UnzerSDK\Resources\PaymentTypes\Clicktopay; use UnzerSDK\Resources\PaymentTypes\Googlepay; use UnzerSDK\Resources\PaymentTypes\Klarna; use UnzerSDK\Resources\PaymentTypes\PaylaterDirectDebit; @@ -825,6 +826,9 @@ public static function getTypeInstanceFromIdString($typeId): BasePaymentType case IdStrings::GOOGLE_PAY: $paymentType = new Googlepay(); break; + case IdStrings::CLICK_TO_PAY: + $paymentType = new Clicktopay(); + break; case IdStrings::HIRE_PURCHASE_DIRECT_DEBIT: case IdStrings::INSTALLMENT_SECURED: $paymentType = new InstallmentSecured(); diff --git a/test/Fixtures/jsonData/clicktopay/createRequest.json b/test/Fixtures/jsonData/clicktopay/createRequest.json new file mode 100644 index 00000000..85268646 --- /dev/null +++ b/test/Fixtures/jsonData/clicktopay/createRequest.json @@ -0,0 +1,6 @@ +{ + "mcCorrelationId": "corr12345", + "mcCxFlowId": "34f4a04b.5ab95e32-30f7-483f-846f-a08230a6d2ed.1618397078", + "mcMerchantTransactionId": "0a4e0d3.34f4a04b.894125b16ddd1f1b3a58273d63a0894179ac3535", + "brand": "mastercard" +} \ No newline at end of file diff --git a/test/Fixtures/jsonData/clicktopay/fetchResponse.json b/test/Fixtures/jsonData/clicktopay/fetchResponse.json new file mode 100644 index 00000000..772741fd --- /dev/null +++ b/test/Fixtures/jsonData/clicktopay/fetchResponse.json @@ -0,0 +1,14 @@ +{ + "id": "s-ctp-q0nucec6itwe", + "method": "card", + "recurring": false, + "geoLocation": { + "clientIp": "0:0:0:0:0:0:0:1", + "countryIsoA2": "" + }, + "processing": { + "uniqueId": "31HA07BC8127DC45EE6946C3B070FF71", + "shortId": "5550.0369.6888", + "traceId": "24c0a2ecbe3d54a838c76444d4bdcd1f" + } +} \ No newline at end of file diff --git a/test/integration/PaymentTypes/ClickToPayTest.php b/test/integration/PaymentTypes/ClickToPayTest.php new file mode 100644 index 00000000..5b43cade --- /dev/null +++ b/test/integration/PaymentTypes/ClickToPayTest.php @@ -0,0 +1,307 @@ +markTestSkipped('Skipped by default as setup is missing for integration tests.'); + } + + /** + * Verify that clickToPay payment type resource can be created. + * + * @test + * + * @return BasePaymentType + */ + public function clickToPayShouldBeCreatable(): BasePaymentType + { + $clickToPay = $this->createClickToPayObject(); + + $this->unzer->createPaymentType($clickToPay); + + $this->assertInstanceOf(Clicktopay::class, $clickToPay); + $this->assertNotNull($clickToPay->getId()); + $this->assertSame($this->unzer, $clickToPay->getUnzerObject()); + + $geoLocation = $clickToPay->getGeoLocation(); + $this->assertNotEmpty($geoLocation->getClientIp()); + $this->assertNotEmpty($geoLocation->getCountryCode()); + + return $clickToPay; + } + + /** + * Verify that a clickToPay resource can be fetched from the api using its id. + * + * @test + * + * @depends clickToPayShouldBeCreatable + * + * @param mixed $type + */ + public function googlepayCanBeFetched($type): void + { + $this->assertNotNull($type->getId()); + + /** @var Clicktopay $fetchedClickToPay */ + $fetchedClickToPay = $this->unzer->fetchPaymentType($type->getId()); + $this->assertNotNull($fetchedClickToPay->getId()); + } + + /** + * Verify that authorization can be performed with ClickToPay. + * + * @test + * + * @depends clickToPayShouldBeCreatable + * + * @param mixed $type + */ + public function clickToPayCanPerformAuthorization($type): Authorization + { + $authorizationRequest = $this->getLvpAuthorizationObject(); + $authorization = $this->getUnzerObject() + ->performAuthorization( + $authorizationRequest, + $type + ); + + // verify authorization has been created + $this->assertNotNull($authorization->getId()); + + // verify payment object has been created + $payment = $authorization->getPayment(); + $this->assertNotNull($payment); + $this->assertNotNull($payment->getId()); + + // verify resources are linked properly + $this->assertSame($authorization, $payment->getAuthorization()); + $this->assertSame($type, $payment->getPaymentType()); + + // verify the payment object has been updated properly + $this->assertAmounts($payment, 2.99, 0.0, 2.99, 0.0); + $this->assertTrue($payment->isPending()); + $this->assertTrue($authorization->isSuccess()); + + return $authorization; + } + + /** + * Verify that authorization can be charged with ClickToPay. + * + * @test + * + * @depends clickToPayCanPerformAuthorization + * + * @param Authorization $authorization + */ + public function authorizationCanBeCharged(Authorization $authorization): Payment + { + $charge = $this->getUnzerObject() + ->performChargeOnPayment( + $authorization->getPayment(), + new Charge() + ); + + // verify charge has been created + $this->assertNotNull($charge->getId()); + + // verify payment object has been created + $payment = $charge->getPayment(); + $this->assertNotNull($payment); + $this->assertNotNull($payment->getId()); + + // verify resources are linked properly + $this->assertSame($charge, $payment->getCharge('s-chg-1')); + $this->assertSame($authorization->getPayment()->getPaymentType(), $payment->getPaymentType()); + + // verify the payment object has been updated properly + $this->assertAmounts($payment, 0, 2.99, 2.99, 0.0); + $this->assertTrue($payment->isCompleted()); + $this->assertTrue($charge->isSuccess()); + + return $payment; + } + + /** + * Verify the clickToPay can perform charges and creates a payment object doing so. + * + * @test + * + * @depends clickToPayShouldBeCreatable + * + * @param mixed $type + */ + public function canPerformCharge($type): void + { + $charge = $this->getUnzerObject() + ->performCharge( + $this->getLvpChargeObject(), + $type + ); + + $fetchedType = $this->unzer->fetchPaymentType($type->getId()); + + // verify charge has been created + $this->assertNotNull($charge->getId()); + + // verify payment object has been created + $payment = $charge->getPayment(); + $this->assertNotNull($payment); + $this->assertNotNull($payment->getId()); + + // verify resources are linked properly + $this->assertEquals($charge->expose(), $payment->getCharge($charge->getId())->expose()); + + // verify the payment object has been updated properly + $this->assertAmounts($payment, 0.0, 2.99, 2.99, 0.0); + $this->assertTrue($payment->isCompleted()); + } + + /** + * Verify the clickToPay can charge part of the authorized amount and the payment state is updated accordingly. + * + * @test + */ + public function partialChargeAfterAuthorization(): void + { + $clickToPay = $this->createClickToPayObject(); + /** @var Clicktopay $clickToPay */ + $clickToPay = $this->unzer->createPaymentType($clickToPay); + $authorization = $this->getUnzerObject() + ->performAuthorization( + $this->getLvpAuthorizationObject(), + $clickToPay + ); + + $payment = $authorization->getPayment(); + $this->assertAmounts($payment, 2.99, 0.0, 2.99, 0.0); + $this->assertTrue($payment->isPending()); + + $charge = $this->unzer->performChargeOnPayment($payment->getId(), new Charge(1)); + $payment1 = $charge->getPayment(); + $this->assertAmounts($payment1, 1.99, 1, 2.99, 0.0); + $this->assertTrue($payment1->isPartlyPaid()); + + $charge = $this->unzer->performChargeOnPayment($payment->getId(), new Charge(1)); + $payment2 = $charge->getPayment(); + $this->assertAmounts($payment2, 0.99, 2, 2.99, 0.0); + $this->assertTrue($payment2->isPartlyPaid()); + + $charge = $this->unzer->performChargeOnPayment($payment->getId(), new Charge(0.99)); + $payment3 = $charge->getPayment(); + $this->assertAmounts($payment3, 00.0, 2.99, 2.99, 0.0); + $this->assertTrue($payment3->isCompleted()); + } + + /** + * Verify that an exception is thrown when trying to charge more than authorized. + * + * @test + */ + public function exceptionShouldBeThrownWhenChargingMoreThenAuthorized(): void + { + $clickToPay = $this->createClickToPayObject(); + /** @var Clicktopay $clickToPay */ + $clickToPay = $this->unzer->createPaymentType($clickToPay); + $authorization = $this->getUnzerObject() + ->performAuthorization( + $this->getLvpAuthorizationObject(), + $clickToPay + ); + $payment = $authorization->getPayment(); + $this->assertAmounts($payment, 2.99, 0.0, 2.99, 0.0); + $this->assertTrue($payment->isPending()); + + $charge = $this->unzer->performChargeOnPayment($payment->getId(), new Charge(1.99)); + $payment1 = $charge->getPayment(); + $this->assertAmounts($payment1, 1, 1.99, 2.99, 0.0); + $this->assertTrue($payment1->isPartlyPaid()); + + $this->expectException(UnzerApiException::class); + $this->expectExceptionCode(ApiResponseCodes::API_ERROR_CHARGED_AMOUNT_HIGHER_THAN_EXPECTED); + $this->unzer->performChargeOnPayment($payment->getId(), new Charge(2)); + } + + /** + * @test + */ + public function fullCancelAfterCharge(): void + { + $clickToPay = $this->createClickToPayObject(); + /** @var Clicktopay $clickToPay */ + $clickToPay = $this->unzer->createPaymentType($clickToPay); + $charge = $this->getUnzerObject() + ->performCharge( + $this->getLvpChargeObject(), + $clickToPay + ); + $payment = $charge->getPayment(); + + $this->assertAmounts($payment, 0.0, 2.99, 2.99, 0.0); + $this->assertTrue($payment->isCompleted()); + + $this->unzer->cancelChargedPayment($payment); + $this->assertAmounts($payment, 0.0, 0.0, 2.99, 2.99); + $this->assertTrue($payment->isCanceled()); + } + + /** Creates an authorization request object with low value payment(lvp) set to avoid 3ds challenge. + * + * @return Authorization + */ + protected function getLvpAuthorizationObject() + { + return (new Authorization(2.99, 'EUR', self::RETURN_URL)) + ->setCardTransactionData( + (new CardTransactionData())->setExemptionType(ExemptionType::LOW_VALUE_PAYMENT) + ); + } + + /** Creates an charge request object with low value payment(lvp) set to avoid 3ds challenge. + * + * @return Charge + */ + protected function getLvpChargeObject() + { + return (new Charge(2.99, 'EUR', self::RETURN_URL)) + ->setCardTransactionData( + (new CardTransactionData())->setExemptionType(ExemptionType::LOW_VALUE_PAYMENT) + ); + } + + + protected function createClickToPayObject(): Clicktopay + { + $clickToPay = (new Clicktopay()); + $this->assertNull($clickToPay->getId()); + + $geoLocation = $clickToPay->getGeoLocation(); + $this->assertNull($geoLocation->getClientIp()); + $this->assertNull($geoLocation->getCountryCode()); + + + $clickToPay->setBrand("mastercard") + ->setMcCorrelationId("corr12345") + ->setMcMerchantTransactionId("0a4e0d3.34f4a04b.894125b16ddd1f1b3a58273d63a0894179ac3535") + ->setMcCxFlowId("34f4a04b.5ab95e32-30f7-483f-846f-a08230a6d2ed.1618397078"); + + return $clickToPay; + } + +} \ No newline at end of file diff --git a/test/unit/Resources/PaymentTypes/ClickToPayTest.php b/test/unit/Resources/PaymentTypes/ClickToPayTest.php new file mode 100644 index 00000000..ddd5a704 --- /dev/null +++ b/test/unit/Resources/PaymentTypes/ClickToPayTest.php @@ -0,0 +1,69 @@ +assertEquals($correlationId, $clickToPay->getMcCorrelationId()); + $this->assertEquals($brand, $clickToPay->getBrand()); + $this->assertEquals($mcCxFlowId, $clickToPay->getMcCxFlowId()); + $this->assertEquals($mcMerchantTransactionId, $clickToPay->getMcMerchantTransactionId()); + } + + /** + * Test ClickToPay json serialization. + * + * @test + */ + public function jsonSerialization(): void + { + $clickToPayObject = new Clicktopay("corr12345", + "34f4a04b.5ab95e32-30f7-483f-846f-a08230a6d2ed.1618397078", + "0a4e0d3.34f4a04b.894125b16ddd1f1b3a58273d63a0894179ac3535", + "mastercard" + ); + + $expectedJson = JsonProvider::getJsonFromFile('clicktopay/createRequest.json'); + $this->assertJsonStringEqualsJsonString($expectedJson, $clickToPayObject->jsonSerialize()); + } + + /** + * Test Click To Pay json response handling. + * + * @test + */ + public function clickToPayAuthorizationShouldBeMappedCorrectly(): void + { + $clickToPay = new Clicktopay(null, null, null, null); + + $jsonResponse = JsonProvider::getJsonFromFile('clicktopay/fetchResponse.json'); + + $jsonObject = json_decode($jsonResponse, false, 512, JSON_THROW_ON_ERROR); + $clickToPay->handleResponse($jsonObject); + + $this->assertEquals('s-ctp-q0nucec6itwe', $clickToPay->getId()); + } + + +} \ No newline at end of file From 263ccca4a4a8386f10924c4b6fb2e77895061d9e Mon Sep 17 00:00:00 2001 From: "david.owusu" Date: Fri, 26 Apr 2024 16:37:42 +0200 Subject: [PATCH 2/3] [CC-730] update changelog --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 994b6dea..c82965d9 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,8 +12,16 @@ Click To Pay payment method is added to Java SDK. ### Added * Added `\UnzerSDK\Resources\PaymentTypes\ClickToPay` payment method. + +## [3.6.0](https://github.com/unzerdev/php-sdk/compare/3.5.0..3.6.0) + +Twint payment method is added to SDK. + +### Added + * Added `\UnzerSDK\Resources\PaymentTypes\Twint` payment method. + ## [3.5.0](https://github.com/unzerdev/php-sdk/compare/3.4.1..3.5.0) This version adds support for Google Pay. From 46b5cd026db608e4f24ab3fd2869de8658a1194b Mon Sep 17 00:00:00 2001 From: "david.owusu" Date: Fri, 26 Apr 2024 16:47:57 +0200 Subject: [PATCH 3/3] [CC-730] update changelog --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9483c407..6a35ee29 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,9 +5,9 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). -## [3.6.0](https://github.com/unzerdev/php-sdk/compare/3.5.0..3.6.0) +## UNRELEASED -Click To Pay payment method is added to Java SDK. +Click To Pay payment method is added to SDK. ### Added