diff --git a/src/Constants/AdditionalTransactionDataKeys.php b/src/Constants/AdditionalTransactionDataKeys.php index 3cf31f3b2..338a6e2c1 100644 --- a/src/Constants/AdditionalTransactionDataKeys.php +++ b/src/Constants/AdditionalTransactionDataKeys.php @@ -18,4 +18,5 @@ class AdditionalTransactionDataKeys public const CHECKOUTTYPE = 'checkoutType'; public const CARD = 'card'; + public const WERO = 'wero'; } diff --git a/src/Constants/IdStrings.php b/src/Constants/IdStrings.php index ee363228b..662c9ea96 100755 --- a/src/Constants/IdStrings.php +++ b/src/Constants/IdStrings.php @@ -52,6 +52,7 @@ class IdStrings public const SOFORT = 'sft'; public const TWINT = 'twt'; public const WECHATPAY = 'wcp'; + public const WERO = 'wro'; public const OPEN_BANKING = 'obp'; @@ -95,6 +96,7 @@ class IdStrings self::SOFORT, self::TWINT, self::WECHATPAY, + self::WERO, self::OPEN_BANKING, ]; } diff --git a/src/Constants/WeroAmountPaymentTypes.php b/src/Constants/WeroAmountPaymentTypes.php new file mode 100644 index 000000000..aeb1ae4d4 --- /dev/null +++ b/src/Constants/WeroAmountPaymentTypes.php @@ -0,0 +1,12 @@ +eventDependentPayment; + } + + public function setEventDependentPayment(?WeroEventDependentPayment $eventDependentPayment): PaymentMethodConfig + { + $this->eventDependentPayment = $eventDependentPayment; + return $this; + } + /** * @param bool|null $enabled * @param int|null $order @@ -80,4 +94,4 @@ public function setExemption(?string $exemption): PaymentMethodConfig $this->exemption = $exemption; return $this; } -} \ No newline at end of file +} diff --git a/src/Resources/EmbeddedResources/WeroEventDependentPayment.php b/src/Resources/EmbeddedResources/WeroEventDependentPayment.php new file mode 100644 index 000000000..e210e4956 --- /dev/null +++ b/src/Resources/EmbeddedResources/WeroEventDependentPayment.php @@ -0,0 +1,76 @@ +captureTrigger; + } + + /** + * @see WeroCaptureTriggers for allowed values + */ + public function setCaptureTrigger(?string $captureTrigger): WeroEventDependentPayment + { + $this->captureTrigger = $captureTrigger; + return $this; + } + + public function getAmountPaymentType(): ?string + { + return $this->amountPaymentType; + } + + /** + * @see WeroAmountPaymentTypes for allowed values + */ + public function setAmountPaymentType(?string $amountPaymentType): WeroEventDependentPayment + { + $this->amountPaymentType = $amountPaymentType; + return $this; + } + + /** + * @return string|int|null + */ + public function getMaxAuthToCaptureTime() + { + return $this->maxAuthToCaptureTime; + } + + public function setMaxAuthToCaptureTime(?int $maxAuthToCaptureTime): WeroEventDependentPayment + { + $this->maxAuthToCaptureTime = $maxAuthToCaptureTime; + return $this; + } + + /** + * @return bool|string|null + */ + public function getMultiCapturesAllowed() + { + return $this->multiCapturesAllowed; + } + + public function setMultiCapturesAllowed(?bool $multiCapturesAllowed): WeroEventDependentPayment + { + $this->multiCapturesAllowed = $multiCapturesAllowed; + return $this; + } +} diff --git a/src/Resources/EmbeddedResources/WeroTransactionData.php b/src/Resources/EmbeddedResources/WeroTransactionData.php new file mode 100644 index 000000000..e172e4e3a --- /dev/null +++ b/src/Resources/EmbeddedResources/WeroTransactionData.php @@ -0,0 +1,48 @@ +eventDependentPayment; + } + + /** + * @param WeroEventDependentPayment|null $eventDependentPayment + * @return WeroTransactionData + */ + public function setEventDependentPayment(?WeroEventDependentPayment $eventDependentPayment): WeroTransactionData + { + $this->eventDependentPayment = $eventDependentPayment; + return $this; + } + + /** + * @inheritDoc + */ + public function handleResponse($response, string $method = HttpAdapterInterface::REQUEST_GET): void + { + parent::handleResponse($response, $method); + if ($response instanceof stdClass && isset($response->eventDependentPayment)) { + $edp = $this->getEventDependentPayment() ?? new WeroEventDependentPayment(); + $edp->handleResponse($response->eventDependentPayment); + $this->setEventDependentPayment($edp); + } + } +} diff --git a/src/Resources/PaymentTypes/Wero.php b/src/Resources/PaymentTypes/Wero.php new file mode 100644 index 000000000..93a3f0469 --- /dev/null +++ b/src/Resources/PaymentTypes/Wero.php @@ -0,0 +1,7 @@ +handleRiskData($additionalTransactionData); $this->handleShipping($additionalTransactionData); $this->handleCardTransactionData($additionalTransactionData); + $this->handleWeroTransactionData($additionalTransactionData); } } @@ -223,4 +225,21 @@ protected function handleCardTransactionData(stdClass $additionalTransactionData $this->setCardTransactionData($cardTransactionData); } } + + /** + * Handle WeroTransactionData object contained in additional transaction data from API response. + * + * @param stdClass $additionalTransactionData + * + * @return void + */ + protected function handleWeroTransactionData(stdClass $additionalTransactionData): void + { + $wero = $additionalTransactionData->wero ?? null; + if ($wero !== null) { + $weroTransactionData = $this->getWeroTransactionData() ?? new WeroTransactionData(); + $weroTransactionData->handleResponse($wero); + $this->setWeroTransactionData($weroTransactionData); + } + } } diff --git a/src/Services/ResourceService.php b/src/Services/ResourceService.php index 2b2d1c922..b4bedfe1f 100755 --- a/src/Services/ResourceService.php +++ b/src/Services/ResourceService.php @@ -52,6 +52,7 @@ use UnzerSDK\Resources\PaymentTypes\Sofort; use UnzerSDK\Resources\PaymentTypes\Twint; use UnzerSDK\Resources\PaymentTypes\Wechatpay; +use UnzerSDK\Resources\PaymentTypes\Wero; use UnzerSDK\Resources\Recurring; use UnzerSDK\Resources\TransactionTypes\Authorization; use UnzerSDK\Resources\TransactionTypes\Cancellation; @@ -957,6 +958,9 @@ public static function getTypeInstanceFromIdString($typeId): BasePaymentType case IdStrings::WECHATPAY: $paymentType = new Wechatpay(); break; + case IdStrings::WERO: + $paymentType = new Wero(); + break; case IdStrings::OPEN_BANKING: $paymentType = new OpenbankingPis(); break; diff --git a/src/Traits/HasAdditionalTransactionData.php b/src/Traits/HasAdditionalTransactionData.php index 3dcd4b2cd..ff8ced736 100644 --- a/src/Traits/HasAdditionalTransactionData.php +++ b/src/Traits/HasAdditionalTransactionData.php @@ -14,6 +14,7 @@ use UnzerSDK\Resources\EmbeddedResources\CardTransactionData; use UnzerSDK\Resources\EmbeddedResources\RiskData; use UnzerSDK\Resources\EmbeddedResources\ShippingData; +use UnzerSDK\Resources\EmbeddedResources\WeroTransactionData; use UnzerSDK\Resources\PaymentTypes\BasePaymentType; use UnzerSDK\Resources\TransactionTypes\AbstractTransactionType; use UnzerSDK\Services\ResourceService; @@ -221,4 +222,29 @@ public function setCardTransactionData(?CardTransactionData $cardTransactionData $this->addAdditionalTransactionData(AdditionalTransactionDataKeys::CARD, $cardTransactionData); return $this; } + + /** + * Get the wero field from additional transaction Data. + * + * @return WeroTransactionData|null "wero" object of additionalTransaction data. + */ + public function getWeroTransactionData(): ?WeroTransactionData + { + $key = AdditionalTransactionDataKeys::WERO; + $wero = $this->getAdditionalTransactionData()->$key ?? null; + return $wero instanceof WeroTransactionData ? $wero : null; + } + + /** + * Sets WeroTransactionData object as "wero" field of additionalTransactionData. + * + * @param WeroTransactionData|null $weroTransactionData + * + * @return self + */ + public function setWeroTransactionData(?WeroTransactionData $weroTransactionData): self + { + $this->addAdditionalTransactionData(AdditionalTransactionDataKeys::WERO, $weroTransactionData); + return $this; + } } diff --git a/test/BasePaymentTest.php b/test/BasePaymentTest.php index 54d6bf0d1..dae503180 100755 --- a/test/BasePaymentTest.php +++ b/test/BasePaymentTest.php @@ -292,7 +292,7 @@ public function createCharge($amount = 100.0): Charge */ public static function generateRandomId(): string { - return str_replace('.', '', microtime(true)); + return str_replace('.', '', random_int(0, 99999999)); } /** diff --git a/test/integration/PaymentTypes/PaylaterInstallmentTest.php b/test/integration/PaymentTypes/PaylaterInstallmentTest.php index 3770a9ab6..bb98d74ab 100644 --- a/test/integration/PaymentTypes/PaylaterInstallmentTest.php +++ b/test/integration/PaymentTypes/PaylaterInstallmentTest.php @@ -13,18 +13,18 @@ namespace UnzerSDK\test\integration\PaymentTypes; use UnzerSDK\Constants\CustomerTypes; +use UnzerSDK\Constants\ShippingTypes; use UnzerSDK\Exceptions\UnzerApiException; use UnzerSDK\Resources\Customer; use UnzerSDK\Resources\CustomerFactory; use UnzerSDK\Resources\EmbeddedResources\Address; -use UnzerSDK\Resources\EmbeddedResources\Paylater\InstallmentPlansQuery; use UnzerSDK\Resources\EmbeddedResources\Paylater\InstallmentPlan; +use UnzerSDK\Resources\EmbeddedResources\Paylater\InstallmentPlansQuery; use UnzerSDK\Resources\PaymentTypes\PaylaterInstallment; use UnzerSDK\Resources\TransactionTypes\Authorization; use UnzerSDK\Resources\TransactionTypes\Cancellation; use UnzerSDK\Resources\TransactionTypes\Charge; use UnzerSDK\test\BaseIntegrationTest; - use function count; class PaylaterInstallmentTest extends BaseIntegrationTest @@ -232,7 +232,7 @@ protected function createAuthorizeTransaction(): Authorization $ins = new PaylaterInstallment($plans->getId(), $selectedPlan->getNumberOfRates(), 'DE89370400440532013000', 'DE', 'Peter Mustermann'); $this->unzer->createPaymentType($ins); - $customer = $this->getCustomer()->setFirstname('Peter')->setLastname('Mustermann'); + $customer = $this->getCustomer(); $basket = $this->createBasket(); $authorization = new Authorization(99.99, 'EUR', self::RETURN_URL); @@ -244,17 +244,22 @@ protected function createAuthorizeTransaction(): Authorization */ public function getCustomer(): Customer { - $customer = CustomerFactory::createCustomer('Manuel', 'Weißmann'); + $customer = CustomerFactory::createCustomer('Maximilian', 'Mustermann'); $address = (new Address()) - ->setStreet('Hugo-Junckers-Straße 3') + ->setName('Maximilian Mustermann') + ->setStreet('Hugo-Junkers-Str. 3') ->setState('DE-BO') ->setZip('60386') ->setCity('Frankfurt am Main') ->setCountry('DE'); $customer + ->setSalutation('mr') ->setBillingAddress($address) - ->setBirthDate('2000-12-12') - ->setEmail('manuel-weissmann@unzer.com'); + ->setCustomerId('c' . substr(self::generateRandomId(), 0, 7)) + ->setShippingAddress((clone $address)->setShippingType(ShippingTypes::EQUALS_BILLING)) + ->setLanguage('de') + ->setBirthDate('1974-10-02') + ->setEmail('accept@unzer.com'); return $customer; } diff --git a/test/integration/PaymentTypes/WeroTest.php b/test/integration/PaymentTypes/WeroTest.php new file mode 100644 index 000000000..a7c08e822 --- /dev/null +++ b/test/integration/PaymentTypes/WeroTest.php @@ -0,0 +1,105 @@ +unzer->createPaymentType(new Wero()); + $this->assertInstanceOf(Wero::class, $wero); + $this->assertNotEmpty($wero->getId()); + + $fetched = $this->unzer->fetchPaymentType($wero->getId()); + $this->assertInstanceOf(Wero::class, $fetched); + $this->assertNotSame($wero, $fetched); + $this->assertEquals($wero->expose(), $fetched->expose()); + + return $fetched; + } + + /** + * Verify Wero can authorize. + * + * @test + * + * @depends weroShouldBeCreatableAndFetchable + */ + public function weroShouldBeAuthorizable(Wero $wero): void + { + $authorization = new Authorization(100.0, 'EUR', self::RETURN_URL); + + // Add Wero additional transaction data + $weroData = (new WeroTransactionData()) + ->setEventDependentPayment( + (new WeroEventDependentPayment()) + ->setCaptureTrigger(WeroCaptureTriggers::SERVICEFULFILMENT) + ->setAmountPaymentType(WeroAmountPaymentTypes::PAY) + ->setMaxAuthToCaptureTime(300) + ->setMultiCapturesAllowed(false) + ); + $authorization->setWeroTransactionData($weroData); + + $authorization = $this->unzer->performAuthorization($authorization, $wero); + $this->assertNotNull($authorization); + $this->assertNotEmpty($authorization->getId()); + $this->assertNotEmpty($authorization->getRedirectUrl()); + + $payment = $authorization->getPayment(); + $this->assertNotNull($payment); + $this->assertTrue($payment->isPending()); + } + + /** + * Verify Wero can charge. + * + * @test + * + * @depends weroShouldBeCreatableAndFetchable + */ + public function weroShouldBeChargeable(Wero $wero): void + { + $charge = new Charge(100.0, 'EUR', self::RETURN_URL); + + // Add Wero additional transaction data + $weroData = (new WeroTransactionData()) + ->setEventDependentPayment( + (new WeroEventDependentPayment()) + ->setCaptureTrigger(WeroCaptureTriggers::SERVICEFULFILMENT) + ->setAmountPaymentType(WeroAmountPaymentTypes::PAY) + ->setMaxAuthToCaptureTime(300) + ->setMultiCapturesAllowed(false) + ); + $charge->setWeroTransactionData($weroData); + + $charge = $this->unzer->performCharge($charge, $wero); + $this->assertNotNull($charge); + $this->assertNotEmpty($charge->getId()); + $this->assertNotEmpty($charge->getRedirectUrl()); + + $fetched = $this->unzer->fetchChargeById($charge->getPayment()->getId(), $charge->getId()); + + $this->assertEquals(($charge->getPaymentId()), $fetched->getPaymentId()); + $this->assertEquals(($charge->getAmount()), $fetched->getAmount()); + $this->assertEquals(($charge->getId()), $fetched->getId()); + } +} diff --git a/test/integration/Resources/PaypageV2Test.php b/test/integration/Resources/PaypageV2Test.php index 2b6b82389..18ca4ac67 100644 --- a/test/integration/Resources/PaypageV2Test.php +++ b/test/integration/Resources/PaypageV2Test.php @@ -5,12 +5,15 @@ use UnzerSDK\Constants\CustomerGroups; use UnzerSDK\Constants\ExemptionType; use UnzerSDK\Constants\PaypageCheckoutTypes; +use UnzerSDK\Constants\WeroAmountPaymentTypes; +use UnzerSDK\Constants\WeroCaptureTriggers; use UnzerSDK\Resources\EmbeddedResources\Paypage\PaymentMethodConfig; use UnzerSDK\Resources\EmbeddedResources\Paypage\PaymentMethodsConfigs; use UnzerSDK\Resources\EmbeddedResources\Paypage\Resources; use UnzerSDK\Resources\EmbeddedResources\Paypage\Style; use UnzerSDK\Resources\EmbeddedResources\Paypage\Urls; use UnzerSDK\Resources\EmbeddedResources\Risk; +use UnzerSDK\Resources\EmbeddedResources\WeroEventDependentPayment; use UnzerSDK\Resources\Metadata; use UnzerSDK\Resources\PaymentTypes\Alipay; use UnzerSDK\Resources\PaymentTypes\Applepay; @@ -294,6 +297,18 @@ public function paymentMethodsConfigsDataProvider() 'paypal' => $enabledConfig ]); + $withWeroConfig = new PaymentMethodsConfigs(); + $withWeroConfig->setMethodConfigs([ + 'wero' => (new PaymentMethodConfig()) + ->setEnabled(true) + ->setEventDependentPayment($edp = (new WeroEventDependentPayment()) + ->setCaptureTrigger(WeroCaptureTriggers::SERVICEFULFILMENT) + ->setAmountPaymentType(WeroAmountPaymentTypes::PAY) + ->setMaxAuthToCaptureTime(300) + ->setMultiCapturesAllowed(false) + ) + ]); + $withCardSpecificConfig = (new PaymentMethodsConfigs())->addMethodConfig(Card::class, $cardConfig); @@ -331,7 +346,8 @@ public function paymentMethodsConfigsDataProvider() 'Method Configs' => [$withMethodConfigs], 'CardSpecificConfig' => [$withCardSpecificConfig], 'ClassNames' => [$withClassNames], - 'PaylaterConfig' => [$withPaylaterConfig] + 'PaylaterConfig' => [$withPaylaterConfig], + 'WeroConfig' => [$withWeroConfig] ]; } } diff --git a/test/unit/Resources/EmbeddedResources/WeroTransactionDataTest.php b/test/unit/Resources/EmbeddedResources/WeroTransactionDataTest.php new file mode 100644 index 000000000..feca25e21 --- /dev/null +++ b/test/unit/Resources/EmbeddedResources/WeroTransactionDataTest.php @@ -0,0 +1,40 @@ +setCaptureTrigger(WeroCaptureTriggers::SERVICEFULFILMENT) + ->setAmountPaymentType(WeroAmountPaymentTypes::PAY) + ->setMaxAuthToCaptureTime(300) + ->setMultiCapturesAllowed(false); + + $wtd = (new WeroTransactionData()) + ->setEventDependentPayment($edp); + + // Getters + $this->assertInstanceOf(WeroEventDependentPayment::class, $wtd->getEventDependentPayment()); + $this->assertEquals(WeroCaptureTriggers::SERVICEFULFILMENT, $wtd->getEventDependentPayment()->getCaptureTrigger()); + + // Expose + $exposed = $wtd->expose(); + $this->assertArrayHasKey('eventDependentPayment', $exposed); + $this->assertEquals(WeroCaptureTriggers::SERVICEFULFILMENT, $exposed['eventDependentPayment']['captureTrigger']); + $this->assertEquals(WeroAmountPaymentTypes::PAY, $exposed['eventDependentPayment']['amountPaymentType']); + $this->assertSame(300, $exposed['eventDependentPayment']['maxAuthToCaptureTime']); + $this->assertSame(false, $exposed['eventDependentPayment']['multiCapturesAllowed']); + $this->assertArrayNotHasKey('enabled', $exposed); + } +} diff --git a/test/unit/Services/ResourceServiceTest.php b/test/unit/Services/ResourceServiceTest.php index 8ba3c4c74..f87d57c43 100755 --- a/test/unit/Services/ResourceServiceTest.php +++ b/test/unit/Services/ResourceServiceTest.php @@ -1426,7 +1426,8 @@ public function fetchResourceByUrlForAPaymentTypeShouldCallFetchPaymentTypeDP(): 'SEPA_DIRECT_DEBIT' => ['s-sdd-xen2ybcovn56', 'https://api.unzer.com/v1/types/direct-debit/s-sdd-xen2ybcovn56/'], 'SEPA_DIRECT_DEBIT_GUARANTEED' => ['s-ddg-xen2ybcovn56', 'https://api.unzer.com/v1/types/direct-debit-guaranteed/s-ddg-xen2ybcovn56/'], 'SOFORT' => ['s-sft-xen2ybcovn56', 'https://api.unzer.com/v1/types/sofort/s-sft-xen2ybcovn56/'], - 'WECHATPAY' => ['s-wcp-xen2ybcovn56', 'https://api.unzer.com/v1/types/wechatpay/s-wcp-xen2ybcovn56/'] + 'WECHATPAY' => ['s-wcp-xen2ybcovn56', 'https://api.unzer.com/v1/types/wechatpay/s-wcp-xen2ybcovn56/'], + 'WERO' => ['s-wro-xen2ybcovn56', 'https://api.unzer.com/v1/types/wechatpay/s-wro-xen2ybcovn56/'] ]; } diff --git a/test/unit/Traits/HasAdditionalTransactionDataTest.php b/test/unit/Traits/HasAdditionalTransactionDataTest.php index 2a926e85c..93707196e 100644 --- a/test/unit/Traits/HasAdditionalTransactionDataTest.php +++ b/test/unit/Traits/HasAdditionalTransactionDataTest.php @@ -11,9 +11,13 @@ namespace UnzerSDK\test\unit\Traits; +use UnzerSDK\Constants\WeroAmountPaymentTypes; +use UnzerSDK\Constants\WeroCaptureTriggers; use UnzerSDK\Resources\EmbeddedResources\CardTransactionData; use UnzerSDK\Resources\EmbeddedResources\RiskData; use UnzerSDK\Resources\EmbeddedResources\ShippingData; +use UnzerSDK\Resources\EmbeddedResources\WeroEventDependentPayment; +use UnzerSDK\Resources\EmbeddedResources\WeroTransactionData; use UnzerSDK\Resources\PaymentTypes\Paypal; use UnzerSDK\test\BasePaymentTest; @@ -110,6 +114,69 @@ public function exposeCardDataAsExpected(): void $this->assertEquals('exemptionType', $additionalTransactionData->card['exemptionType']); } + /** + * WeroData setters/getters should work as expected. + * + * @test + */ + public function setAndGetWeroData(): void + { + $dummy = new TraitDummyHasAdditionalTransactionData(); + $this->assertNull($dummy->getWeroTransactionData()); + + $edp = (new WeroEventDependentPayment()) + ->setCaptureTrigger(WeroCaptureTriggers::SERVICEFULFILMENT) + ->setAmountPaymentType(WeroAmountPaymentTypes::PAY) + ->setMaxAuthToCaptureTime(300) + ->setMultiCapturesAllowed(false); + + $weroTransactionData = (new WeroTransactionData()) + ->setEventDependentPayment($edp); + + $dummy->setWeroTransactionData($weroTransactionData); + + $wero = $dummy->getWeroTransactionData(); + $this->assertNotNull($wero); + $this->assertInstanceOf(WeroTransactionData::class, $wero); + $this->assertInstanceOf(WeroEventDependentPayment::class, $wero->getEventDependentPayment()); + $this->assertEquals(WeroCaptureTriggers::SERVICEFULFILMENT, $wero->getEventDependentPayment()->getCaptureTrigger()); + $this->assertEquals(WeroAmountPaymentTypes::PAY, $wero->getEventDependentPayment()->getAmountPaymentType()); + $this->assertSame(300, $wero->getEventDependentPayment()->getMaxAuthToCaptureTime()); + $this->assertSame(false, $wero->getEventDependentPayment()->getMultiCapturesAllowed()); + } + + /** + * WeroData should be exposed correctly. + * + * @test + */ + public function exposeWeroDataAsExpected(): void + { + $dummy = new TraitDummyHasAdditionalTransactionData(); + $this->assertNull($dummy->getWeroTransactionData()); + + $edp = (new WeroEventDependentPayment()) + ->setCaptureTrigger(WeroCaptureTriggers::SERVICEFULFILMENT) + ->setAmountPaymentType(WeroAmountPaymentTypes::PAY) + ->setMaxAuthToCaptureTime(300) + ->setMultiCapturesAllowed(false); + + $weroTransactionData = (new WeroTransactionData()) + ->setEventDependentPayment($edp); + + $dummy->setWeroTransactionData($weroTransactionData); + + $exposedResource = $dummy->expose(); + $this->assertNotNull($exposedResource['additionalTransactionData']); + $additionalTransactionData = $exposedResource['additionalTransactionData']; + + $this->assertArrayHasKey('eventDependentPayment', $additionalTransactionData->wero); + $this->assertEquals(WeroCaptureTriggers::SERVICEFULFILMENT, $additionalTransactionData->wero['eventDependentPayment']['captureTrigger']); + $this->assertEquals(WeroAmountPaymentTypes::PAY, $additionalTransactionData->wero['eventDependentPayment']['amountPaymentType']); + $this->assertSame(300, $additionalTransactionData->wero['eventDependentPayment']['maxAuthToCaptureTime']); + $this->assertSame(false, $additionalTransactionData->wero['eventDependentPayment']['multiCapturesAllowed']); + } + /** * Verify checkoutType can be set via typeId correctly. *