From 675b355b209b02110457fea4cfe76711cfba07f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20M=C3=BCller?= Date: Sun, 14 Jan 2024 16:26:16 +0100 Subject: [PATCH 1/3] PAYOSWXP-113: Add configuration to allow different shipping address for BNPL payments --- .../PayoneBNPLPaymentMethodFilter.php | 30 +++++++++++++++++++ .../payment_method_filter.xml | 3 ++ src/Resources/config/settings.xml | 18 +++++++++++ 3 files changed, 51 insertions(+) diff --git a/src/Components/PaymentFilter/PayoneBNPLPaymentMethodFilter.php b/src/Components/PaymentFilter/PayoneBNPLPaymentMethodFilter.php index 21f9a60ca..285acd5e6 100644 --- a/src/Components/PaymentFilter/PayoneBNPLPaymentMethodFilter.php +++ b/src/Components/PaymentFilter/PayoneBNPLPaymentMethodFilter.php @@ -4,16 +4,46 @@ namespace PayonePayment\Components\PaymentFilter; +use PayonePayment\Components\ConfigReader\ConfigReader; use PayonePayment\Components\PaymentFilter\Exception\PaymentMethodNotAllowedException; use PayonePayment\Core\Utils\AddressCompare; use Shopware\Core\Checkout\Customer\Aggregate\CustomerAddress\CustomerAddressEntity; use Shopware\Core\Checkout\Order\Aggregate\OrderAddress\OrderAddressEntity; use Shopware\Core\Checkout\Payment\PaymentMethodCollection; +use Shopware\Core\System\SystemConfig\SystemConfigService; class PayoneBNPLPaymentMethodFilter extends DefaultPaymentFilterService { + public function __construct( + private readonly SystemConfigService $systemConfigService, + private readonly string $paymentHandlerClass, + ?array $allowedCountries = null, + ?array $allowedB2bCountries = null, + ?array $allowedCurrencies = null, + float $allowedMinValue = 0.0, + ?float $allowedMaxValue = null, + ) { + parent::__construct( + $paymentHandlerClass, + $allowedCountries, + $allowedB2bCountries, + $allowedCurrencies, + $allowedMinValue, + $allowedMaxValue + ); + } + protected function additionalChecks(PaymentMethodCollection $methodCollection, PaymentFilterContext $filterContext): void { + $differentShippingAddressAllowed = (bool) $this->systemConfigService->get( + ConfigReader::getConfigKeyByPaymentHandler($this->paymentHandlerClass, 'AllowDifferentShippingAddress'), + $filterContext->getSalesChannelContext()->getSalesChannelId() + ); + + if ($differentShippingAddressAllowed) { + return; + } + $billingAddress = $filterContext->getBillingAddress(); $shippingAddress = $filterContext->getShippingAddress(); diff --git a/src/DependencyInjection/payment_method_filter.xml b/src/DependencyInjection/payment_method_filter.xml index 2ff1b60f6..3262a75df 100644 --- a/src/DependencyInjection/payment_method_filter.xml +++ b/src/DependencyInjection/payment_method_filter.xml @@ -90,6 +90,7 @@ + PayonePayment\PaymentHandler\PayoneSecuredInvoicePaymentHandler DE @@ -107,6 +108,7 @@ + PayonePayment\PaymentHandler\PayoneSecuredInstallmentPaymentHandler DE @@ -124,6 +126,7 @@ + PayonePayment\PaymentHandler\PayoneSecuredDirectDebitPaymentHandler DE diff --git a/src/Resources/config/settings.xml b/src/Resources/config/settings.xml index ff30c4b33..a0ec6338a 100644 --- a/src/Resources/config/settings.xml +++ b/src/Resources/config/settings.xml @@ -4322,6 +4322,12 @@ Gibt an, ob bei Zahlungen die Bestellnummer im Verwendungszweck der Zahlart übertragen werden soll. Ist die Option deaktiviert, greift die Einstellung für den Verwendungszweck aus dem Zahlungsportal im PMI + + securedInvoiceAllowDifferentShippingAddress + + + + securedInvoicePaymentStatusAppointed @@ -4475,6 +4481,12 @@ Gibt an, ob bei Zahlungen die Bestellnummer im Verwendungszweck der Zahlart übertragen werden soll. Ist die Option deaktiviert, greift die Einstellung für den Verwendungszweck aus dem Zahlungsportal im PMI + + securedInstallmentAllowDifferentShippingAddress + + + + securedInstallmentPaymentStatusAppointed @@ -4628,6 +4640,12 @@ Gibt an, ob bei Zahlungen die Bestellnummer im Verwendungszweck der Zahlart übertragen werden soll. Ist die Option deaktiviert, greift die Einstellung für den Verwendungszweck aus dem Zahlungsportal im PMI + + securedDirectDebitAllowDifferentShippingAddress + + + + securedDirectDebitPaymentStatusAppointed From a13de27581b871cf76a681fa8bdb511d9a1e47bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20M=C3=BCller?= Date: Tue, 30 Jan 2024 19:57:30 +0100 Subject: [PATCH 2/3] PAYOSWXP-113: Move validation of different shipping address to default filter service --- phpunit.xml.dist | 1 + src/Components/ConfigReader/ConfigReader.php | 5 + .../ConfigurationPrefixMissingException.php | 9 ++ .../DefaultPaymentFilterService.php | 59 ++++++++- .../PaymentMethodNotAllowedException.php | 13 ++ .../PaymentFilter/PaymentFilterContext.php | 30 +++++ .../PayoneBNPLPaymentMethodFilter.php | 60 --------- .../payment_method_filter.xml | 9 +- .../AbstractPaymentFilterTest.php | 4 +- .../DefaultPaymentFilterServiceTest.php | 117 +++++++++++++++--- .../PaymentFilter/KlarnaPaymentFilterTest.php | 3 +- .../PostfinancePaymentFilterTest.php | 3 +- .../Przelewy24PaymentFilterTest.php | 3 +- .../SecuredDirectDebitPaymentFilterTest.php | 67 +--------- .../SecuredInstallmentPaymentFilterTest.php | 67 +--------- .../SecuredInvoicePaymentFilterTest.php | 67 +--------- 16 files changed, 230 insertions(+), 287 deletions(-) create mode 100644 src/Components/ConfigReader/Exception/ConfigurationPrefixMissingException.php delete mode 100644 src/Components/PaymentFilter/PayoneBNPLPaymentMethodFilter.php diff --git a/phpunit.xml.dist b/phpunit.xml.dist index b038fc4aa..ecb61176f 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -6,6 +6,7 @@ colors="true"> + diff --git a/src/Components/ConfigReader/ConfigReader.php b/src/Components/ConfigReader/ConfigReader.php index 2bf777bfe..6cde6838a 100644 --- a/src/Components/ConfigReader/ConfigReader.php +++ b/src/Components/ConfigReader/ConfigReader.php @@ -4,6 +4,7 @@ namespace PayonePayment\Components\ConfigReader; +use PayonePayment\Components\ConfigReader\Exception\ConfigurationPrefixMissingException; use PayonePayment\Configuration\ConfigurationPrefixes; use PayonePayment\Struct\Configuration; use Shopware\Core\System\SystemConfig\SystemConfigService; @@ -18,6 +19,10 @@ public function __construct(private readonly SystemConfigService $systemConfigSe public static function getConfigKeyByPaymentHandler(string $paymentHandler, string $configuration): string { + if (!isset(ConfigurationPrefixes::CONFIGURATION_PREFIXES[$paymentHandler])) { + throw new ConfigurationPrefixMissingException(sprintf('No configuration prefix for payment handler "%s" found!', $paymentHandler)); + } + return self::SYSTEM_CONFIG_DOMAIN . ConfigurationPrefixes::CONFIGURATION_PREFIXES[$paymentHandler] . $configuration; } diff --git a/src/Components/ConfigReader/Exception/ConfigurationPrefixMissingException.php b/src/Components/ConfigReader/Exception/ConfigurationPrefixMissingException.php new file mode 100644 index 000000000..f95a858bb --- /dev/null +++ b/src/Components/ConfigReader/Exception/ConfigurationPrefixMissingException.php @@ -0,0 +1,9 @@ + $paymentHandlerClass */ public function __construct( + private readonly SystemConfigService $systemConfigService, private readonly string $paymentHandlerClass, private readonly ?array $allowedCountries = null, private readonly ?array $allowedB2bCountries = null, @@ -31,7 +35,8 @@ final public function filterPaymentMethods( PaymentMethodCollection $methodCollection, PaymentFilterContext $filterContext ): PaymentMethodCollection { - if ($this->getSupportedPaymentMethods($methodCollection)->getElements() === []) { + $supportedPaymentMethods = $this->getSupportedPaymentMethods($methodCollection); + if ($supportedPaymentMethods->getElements() === []) { return $methodCollection; } @@ -45,6 +50,7 @@ final public function filterPaymentMethods( $currentValue = $filterContext->getOrder()->getPrice()->getTotalPrice(); } + // Validate and remove all supported payment methods if necessary try { $this->validateCurrency($currency); $this->validateAddress($billingAddress); @@ -53,9 +59,13 @@ final public function filterPaymentMethods( $this->validateMinValue($currentValue); $this->validateMaxValue($currentValue); } + + // Validate and remove a specific payment method if necessary + $this->validateDifferentShippingAddress($supportedPaymentMethods, $filterContext); + $this->additionalChecks($methodCollection, $filterContext); - } catch (PaymentMethodNotAllowedException) { - $methodCollection = $this->removePaymentMethods($methodCollection); + } catch (PaymentMethodNotAllowedException $paymentMethodNotAllowedException) { + $methodCollection = $this->removePaymentMethods($methodCollection, $paymentMethodNotAllowedException->getDisallowedPaymentMethodCollection()); } return $methodCollection; @@ -76,9 +86,9 @@ private function getSupportedPaymentMethods(PaymentMethodCollection $paymentMeth : $paymentMethod->getHandlerIdentifier() === $this->paymentHandlerClass); } - private function removePaymentMethods(PaymentMethodCollection $paymentMethodCollection): PaymentMethodCollection + private function removePaymentMethods(PaymentMethodCollection $paymentMethodCollection, ?PaymentMethodCollection $itemsToRemove = null): PaymentMethodCollection { - $itemsToRemove = $this->getSupportedPaymentMethods($paymentMethodCollection); + $itemsToRemove = $itemsToRemove ?: $this->getSupportedPaymentMethods($paymentMethodCollection); return $paymentMethodCollection->filter(static fn (PaymentMethodEntity $entity) => !$itemsToRemove->has($entity->getUniqueIdentifier())); } @@ -123,4 +133,43 @@ private function validateMaxValue(float $currentValue): void throw new PaymentMethodNotAllowedException('The current cart/order value is higher than the allowed max value'); } } + + private function validateDifferentShippingAddress(PaymentMethodCollection $paymentMethods, PaymentFilterContext $filterContext): void + { + // addresses are already identical - it is not required to check if it is allowed if they are identical or not + if ($filterContext->areAddressesIdentical()) { + return; + } + + $disallowedPaymentMethods = []; + + foreach ($paymentMethods as $paymentMethod) { + try { + $configKey = ConfigReader::getConfigKeyByPaymentHandler( + $paymentMethod->getHandlerIdentifier(), + 'AllowDifferentShippingAddress' + ); + } catch (ConfigurationPrefixMissingException) { + continue; + } + + /** @var boolean|null $differentShippingAddressAllowed */ + $differentShippingAddressAllowed = $this->systemConfigService->get( + $configKey, + $filterContext->getSalesChannelContext()->getSalesChannelId() + ); + + // if configuration value is null, the payment method should be removed. + if ($differentShippingAddressAllowed === false) { + $disallowedPaymentMethods[] = $paymentMethod; + } + } + + if ($disallowedPaymentMethods !== []) { + throw new PaymentMethodNotAllowedException( + 'It is not permitted to use a different shipping address', + new PaymentMethodCollection($disallowedPaymentMethods) + ); + } + } } diff --git a/src/Components/PaymentFilter/Exception/PaymentMethodNotAllowedException.php b/src/Components/PaymentFilter/Exception/PaymentMethodNotAllowedException.php index 0bcd04f40..63e65ae29 100644 --- a/src/Components/PaymentFilter/Exception/PaymentMethodNotAllowedException.php +++ b/src/Components/PaymentFilter/Exception/PaymentMethodNotAllowedException.php @@ -4,6 +4,19 @@ namespace PayonePayment\Components\PaymentFilter\Exception; +use Shopware\Core\Checkout\Payment\PaymentMethodCollection; + class PaymentMethodNotAllowedException extends \Exception { + public function __construct( + string $message, + private readonly ?PaymentMethodCollection $disallowedPaymentMethodCollection = null + ) { + parent::__construct($message); + } + + public function getDisallowedPaymentMethodCollection(): ?PaymentMethodCollection + { + return $this->disallowedPaymentMethodCollection; + } } diff --git a/src/Components/PaymentFilter/PaymentFilterContext.php b/src/Components/PaymentFilter/PaymentFilterContext.php index 7c63f4259..2cc329a16 100644 --- a/src/Components/PaymentFilter/PaymentFilterContext.php +++ b/src/Components/PaymentFilter/PaymentFilterContext.php @@ -4,6 +4,7 @@ namespace PayonePayment\Components\PaymentFilter; +use PayonePayment\Core\Utils\AddressCompare; use Shopware\Core\Checkout\Cart\Cart; use Shopware\Core\Checkout\Customer\Aggregate\CustomerAddress\CustomerAddressEntity; use Shopware\Core\Checkout\Order\Aggregate\OrderAddress\OrderAddressEntity; @@ -14,6 +15,8 @@ class PaymentFilterContext extends Struct { + private bool $_areAddressesIdentical; + public function __construct( private readonly SalesChannelContext $salesChannelContext, private readonly CustomerAddressEntity|OrderAddressEntity|null $billingAddress = null, @@ -53,4 +56,31 @@ public function getCart(): ?Cart { return $this->cart; } + + public function areAddressesIdentical(): bool + { + if (isset($this->_areAddressesIdentical)) { + return $this->_areAddressesIdentical; + } + + $billingAddress = $this->getBillingAddress(); + $shippingAddress = $this->getShippingAddress(); + + if ($billingAddress instanceof OrderAddressEntity + && $shippingAddress instanceof OrderAddressEntity + && $billingAddress->getId() !== $shippingAddress->getId() + && !AddressCompare::areOrderAddressesIdentical($billingAddress, $shippingAddress) + ) { + return $this->_areAddressesIdentical = false; + } + + if ($billingAddress instanceof CustomerAddressEntity + && $shippingAddress instanceof CustomerAddressEntity + && $billingAddress->getId() !== $shippingAddress->getId() + && !AddressCompare::areCustomerAddressesIdentical($billingAddress, $shippingAddress)) { + return $this->_areAddressesIdentical = false; + } + + return $this->_areAddressesIdentical = true; + } } diff --git a/src/Components/PaymentFilter/PayoneBNPLPaymentMethodFilter.php b/src/Components/PaymentFilter/PayoneBNPLPaymentMethodFilter.php deleted file mode 100644 index 285acd5e6..000000000 --- a/src/Components/PaymentFilter/PayoneBNPLPaymentMethodFilter.php +++ /dev/null @@ -1,60 +0,0 @@ -systemConfigService->get( - ConfigReader::getConfigKeyByPaymentHandler($this->paymentHandlerClass, 'AllowDifferentShippingAddress'), - $filterContext->getSalesChannelContext()->getSalesChannelId() - ); - - if ($differentShippingAddressAllowed) { - return; - } - - $billingAddress = $filterContext->getBillingAddress(); - $shippingAddress = $filterContext->getShippingAddress(); - - if ($billingAddress instanceof OrderAddressEntity - && $shippingAddress instanceof OrderAddressEntity - && !AddressCompare::areOrderAddressesIdentical($billingAddress, $shippingAddress)) { - throw new PaymentMethodNotAllowedException('Different billing and shipping addresses are not allowed for secured invoice'); - } elseif ($billingAddress instanceof CustomerAddressEntity - && $shippingAddress instanceof CustomerAddressEntity - && !AddressCompare::areCustomerAddressesIdentical($billingAddress, $shippingAddress)) { - throw new PaymentMethodNotAllowedException('Different billing and shipping addresses are not allowed for secured invoice'); - } - } -} diff --git a/src/DependencyInjection/payment_method_filter.xml b/src/DependencyInjection/payment_method_filter.xml index 3262a75df..413a22f55 100644 --- a/src/DependencyInjection/payment_method_filter.xml +++ b/src/DependencyInjection/payment_method_filter.xml @@ -33,6 +33,7 @@ + PayonePayment\PaymentHandler\AbstractKlarnaPaymentHandler AT @@ -61,6 +62,7 @@ + PayonePayment\PaymentHandler\AbstractPostfinancePaymentHandler CH @@ -75,6 +77,7 @@ + PayonePayment\PaymentHandler\PayonePrzelewy24PaymentHandler PL @@ -89,7 +92,7 @@ + class="PayonePayment\Components\PaymentFilter\DefaultPaymentFilterService"> PayonePayment\PaymentHandler\PayoneSecuredInvoicePaymentHandler @@ -107,7 +110,7 @@ + class="PayonePayment\Components\PaymentFilter\DefaultPaymentFilterService"> PayonePayment\PaymentHandler\PayoneSecuredInstallmentPaymentHandler @@ -125,7 +128,7 @@ + class="PayonePayment\Components\PaymentFilter\DefaultPaymentFilterService"> PayonePayment\PaymentHandler\PayoneSecuredDirectDebitPaymentHandler diff --git a/tests/Components/PaymentFilter/AbstractPaymentFilterTest.php b/tests/Components/PaymentFilter/AbstractPaymentFilterTest.php index 4b2ff4475..eb4943f45 100644 --- a/tests/Components/PaymentFilter/AbstractPaymentFilterTest.php +++ b/tests/Components/PaymentFilter/AbstractPaymentFilterTest.php @@ -2,10 +2,8 @@ declare(strict_types=1); -namespace PayonePayment\EventListener; +namespace PayonePayment\Components\PaymentFilter; -use PayonePayment\Components\PaymentFilter\PaymentFilterContext; -use PayonePayment\Components\PaymentFilter\PaymentFilterServiceInterface; use PayonePayment\TestCaseBase\ConfigurationHelper; use PayonePayment\TestCaseBase\Mock\PaymentHandler\PaymentHandlerMock; use PayonePayment\TestCaseBase\PayoneTestBehavior; diff --git a/tests/Components/PaymentFilter/DefaultPaymentFilterServiceTest.php b/tests/Components/PaymentFilter/DefaultPaymentFilterServiceTest.php index d73d43721..55e1a9d32 100644 --- a/tests/Components/PaymentFilter/DefaultPaymentFilterServiceTest.php +++ b/tests/Components/PaymentFilter/DefaultPaymentFilterServiceTest.php @@ -4,13 +4,17 @@ namespace PayonePayment\Components\PaymentFilter; +use PayonePayment\Components\ConfigReader\ConfigReader; +use PayonePayment\PaymentHandler\PayoneSecuredInvoicePaymentHandler; use PayonePayment\TestCaseBase\Mock\PaymentHandler\PaymentHandlerMock; use PayonePayment\TestCaseBase\PayoneTestBehavior; use PHPUnit\Framework\TestCase; +use Shopware\Core\Checkout\Customer\Aggregate\CustomerAddress\CustomerAddressEntity; use Shopware\Core\Checkout\Payment\PaymentMethodCollection; use Shopware\Core\Checkout\Payment\PaymentMethodEntity; use Shopware\Core\Framework\Uuid\Uuid; use Shopware\Core\System\Currency\CurrencyEntity; +use Shopware\Core\System\SystemConfig\SystemConfigService; /** * @covers \PayonePayment\Components\PaymentFilter\DefaultPaymentFilterService @@ -50,23 +54,25 @@ public function testCurrency(): void $euroCurrency ); - $filterService = new DefaultPaymentFilterService(PaymentHandlerMock::class, ['DE'], null, ['EUR']); + $systemConfigService = $this->createMock(SystemConfigService::class); + + $filterService = new DefaultPaymentFilterService($systemConfigService, PaymentHandlerMock::class, ['DE'], null, ['EUR']); $result = $filterService->filterPaymentMethods($methodCollection, $chfFilterContext); static::assertCount(2, $result->getElements(), 'first payment method should be removed, cause service should only process first payment method, and the currency is not allowed'); - $filterService = new DefaultPaymentFilterService(\stdClass::class, ['DE'], null, ['EUR']); + $filterService = new DefaultPaymentFilterService($systemConfigService, \stdClass::class, ['DE'], null, ['EUR']); $result = $filterService->filterPaymentMethods($methodCollection, $chfFilterContext); static::assertCount(1, $result->getElements(), 'second and third payment method should be removed, cause service should only process second/third payment method, and the currency is not allowed'); - $filterService = new DefaultPaymentFilterService(PaymentHandlerMock::class, ['DE'], null, ['EUR']); + $filterService = new DefaultPaymentFilterService($systemConfigService, PaymentHandlerMock::class, ['DE'], null, ['EUR']); $result = $filterService->filterPaymentMethods($methodCollection, $euroFilterContext); static::assertCount(3, $result->getElements(), 'no payment method should be removed, cause the currency is allowed for the first method'); - $filterService = new DefaultPaymentFilterService(\stdClass::class, ['DE'], null, ['EUR']); + $filterService = new DefaultPaymentFilterService($systemConfigService, \stdClass::class, ['DE'], null, ['EUR']); $result = $filterService->filterPaymentMethods($methodCollection, $euroFilterContext); static::assertCount(3, $result->getElements(), 'no payment method should be removed, cause the currency is allowed for the second and the method'); - $filterService = new DefaultPaymentFilterService(\stdClass::class, ['DE'], null, null); + $filterService = new DefaultPaymentFilterService($systemConfigService, \stdClass::class, ['DE'], null, null); $result = $filterService->filterPaymentMethods($methodCollection, $euroFilterContext); static::assertCount(3, $result->getElements(), 'no payment method should be removed, cause no currency filter is provided'); } @@ -90,27 +96,29 @@ public function testB2C(): void $currency ); + $systemConfigService = $this->createMock(SystemConfigService::class); + $billingAddress->getCountry()->setIso('DE'); - $filterService = new DefaultPaymentFilterService(PaymentHandlerMock::class, ['FR'], null, null); + $filterService = new DefaultPaymentFilterService($systemConfigService, PaymentHandlerMock::class, ['FR'], null, null); $result = $filterService->filterPaymentMethods($methodCollection, $filterContext); static::assertCount(2, $result->getElements(), 'first payment method should be removed, cause service should only process first payment method, and the country is not allowed'); $billingAddress->getCountry()->setIso('DE'); - $filterService = new DefaultPaymentFilterService(\stdClass::class, ['FR'], null, null); + $filterService = new DefaultPaymentFilterService($systemConfigService, \stdClass::class, ['FR'], null, null); $result = $filterService->filterPaymentMethods($methodCollection, $filterContext); static::assertCount(1, $result->getElements(), 'second and third payment method should be removed, cause service should only process second/third payment method, and the country is not allowed'); $billingAddress->getCountry()->setIso('FR'); - $filterService = new DefaultPaymentFilterService(PaymentHandlerMock::class, ['FR'], null, null); + $filterService = new DefaultPaymentFilterService($systemConfigService, PaymentHandlerMock::class, ['FR'], null, null); $result = $filterService->filterPaymentMethods($methodCollection, $filterContext); static::assertCount(3, $result->getElements(), 'no payment method should be removed, cause the country is allowed for the first method'); $billingAddress->getCountry()->setIso('FR'); - $filterService = new DefaultPaymentFilterService(\stdClass::class, ['FR'], null, null); + $filterService = new DefaultPaymentFilterService($systemConfigService, \stdClass::class, ['FR'], null, null); $result = $filterService->filterPaymentMethods($methodCollection, $filterContext); static::assertCount(3, $result->getElements(), 'no payment method should be removed, cause the country is allowed for the second and the method'); - $filterService = new DefaultPaymentFilterService(\stdClass::class, null, null, null); + $filterService = new DefaultPaymentFilterService($systemConfigService, \stdClass::class, null, null, null); $result = $filterService->filterPaymentMethods($methodCollection, $filterContext); static::assertCount(3, $result->getElements(), 'no payment method should be removed, cause no country filter is provided'); } @@ -135,31 +143,110 @@ public function testB2B(): void $currency ); + $systemConfigService = $this->createMock(SystemConfigService::class); + $billingAddress->getCountry()->setIso('DE'); - $filterService = new DefaultPaymentFilterService(PaymentHandlerMock::class, null, ['FR'], null); + $filterService = new DefaultPaymentFilterService($systemConfigService, PaymentHandlerMock::class, null, ['FR'], null); $result = $filterService->filterPaymentMethods($methodCollection, $filterContext); static::assertCount(2, $result->getElements(), 'first payment method should be removed, cause service should only process first payment method, and the country is not allowed'); $billingAddress->getCountry()->setIso('DE'); - $filterService = new DefaultPaymentFilterService(\stdClass::class, null, ['FR'], null); + $filterService = new DefaultPaymentFilterService($systemConfigService, \stdClass::class, null, ['FR'], null); $result = $filterService->filterPaymentMethods($methodCollection, $filterContext); static::assertCount(1, $result->getElements(), 'second and third payment method should be removed, cause service should only process second/third payment method, and the country is not allowed'); $billingAddress->getCountry()->setIso('FR'); - $filterService = new DefaultPaymentFilterService(PaymentHandlerMock::class, null, ['FR'], null); + $filterService = new DefaultPaymentFilterService($systemConfigService, PaymentHandlerMock::class, null, ['FR'], null); $result = $filterService->filterPaymentMethods($methodCollection, $filterContext); static::assertCount(3, $result->getElements(), 'no payment method should be removed, cause the country is allowed for the first method'); $billingAddress->getCountry()->setIso('FR'); - $filterService = new DefaultPaymentFilterService(\stdClass::class, null, ['FR'], null); + $filterService = new DefaultPaymentFilterService($systemConfigService, \stdClass::class, null, ['FR'], null); $result = $filterService->filterPaymentMethods($methodCollection, $filterContext); static::assertCount(3, $result->getElements(), 'no payment method should be removed, cause the country is allowed for the second and th method'); - $filterService = new DefaultPaymentFilterService(\stdClass::class, null, null, null); + $filterService = new DefaultPaymentFilterService($systemConfigService, \stdClass::class, null, null, null); $result = $filterService->filterPaymentMethods($methodCollection, $filterContext); static::assertCount(3, $result->getElements(), 'no payment method should be removed, cause no country filter is provided'); } + public function testDifferentShippingAddress(): void + { + $method = new PaymentMethodEntity(); + $method->setUniqueIdentifier(Uuid::randomHex()); + $method->setHandlerIdentifier(PayoneSecuredInvoicePaymentHandler::class); + + $methodCollection = $this->getMethodCollection(); + $methodCollection->add($method); + + $salesChannelContext = $this->createSalesChannelContextWithLoggedInCustomerAndWithNavigation(); + + $differentShippingAddress = new CustomerAddressEntity(); + $differentShippingAddress->setId(Uuid::randomHex()); + $differentShippingAddress->setFirstName('Different Firstname'); + $salesChannelContext->getCustomer()->setActiveShippingAddress($differentShippingAddress); + + $currency = $this->createMock(CurrencyEntity::class); + $currency->method('getIsoCode')->willReturn('EUR'); + + $filterContext = new PaymentFilterContext( + $salesChannelContext, + $salesChannelContext->getCustomer()->getActiveBillingAddress(), + $salesChannelContext->getCustomer()->getActiveShippingAddress(), + $currency + ); + + $configKey = ConfigReader::getConfigKeyByPaymentHandler( + PayoneSecuredInvoicePaymentHandler::class, + 'AllowDifferentShippingAddress' + ); + + $systemConfigService = $this->createMock(SystemConfigService::class); + $systemConfigService + ->expects(static::once()) + ->method('get') + ->with( + static::equalTo($configKey), + static::equalTo($salesChannelContext->getSalesChannelId()) + ) + ->willReturn(null) + ; + + $filterService = new DefaultPaymentFilterService($systemConfigService, PayoneSecuredInvoicePaymentHandler::class, null, null, null); + $result = $filterService->filterPaymentMethods($methodCollection, $filterContext); + static::assertCount(4, $result->getElements(), 'no payment method should be removed, because no configuration exists'); + + $systemConfigService = $this->createMock(SystemConfigService::class); + $systemConfigService + ->expects(static::once()) + ->method('get') + ->with( + static::equalTo($configKey), + static::equalTo($salesChannelContext->getSalesChannelId()) + ) + ->willReturn(true) + ; + + $filterService = new DefaultPaymentFilterService($systemConfigService, PayoneSecuredInvoicePaymentHandler::class, null, null, null); + $result = $filterService->filterPaymentMethods($methodCollection, $filterContext); + static::assertCount(4, $result->getElements(), 'no payment method should be removed, because a different shipping address is allowed'); + + $systemConfigService = $this->createMock(SystemConfigService::class); + $systemConfigService + ->expects(static::once()) + ->method('get') + ->with( + static::equalTo($configKey), + static::equalTo($salesChannelContext->getSalesChannelId()) + ) + ->willReturn(false) + ; + + $filterService = new DefaultPaymentFilterService($systemConfigService, PayoneSecuredInvoicePaymentHandler::class, null, null, null); + $result = $filterService->filterPaymentMethods($methodCollection, $filterContext); + static::assertCount(3, $result->getElements(), 'payment method should be removed, because a different shipping address is not allowed'); + } + private function getMethodCollection(): PaymentMethodCollection { $method1 = new PaymentMethodEntity(); diff --git a/tests/Components/PaymentFilter/KlarnaPaymentFilterTest.php b/tests/Components/PaymentFilter/KlarnaPaymentFilterTest.php index 9759503d4..08bf825b7 100644 --- a/tests/Components/PaymentFilter/KlarnaPaymentFilterTest.php +++ b/tests/Components/PaymentFilter/KlarnaPaymentFilterTest.php @@ -2,9 +2,8 @@ declare(strict_types=1); -namespace PayonePayment\EventListener; +namespace PayonePayment\Components\PaymentFilter; -use PayonePayment\Components\PaymentFilter\PaymentFilterServiceInterface; use PayonePayment\PaymentHandler\PayoneKlarnaInvoicePaymentHandler; use Shopware\Core\System\Currency\CurrencyEntity; diff --git a/tests/Components/PaymentFilter/PostfinancePaymentFilterTest.php b/tests/Components/PaymentFilter/PostfinancePaymentFilterTest.php index 3e8b8c25a..9565784ef 100644 --- a/tests/Components/PaymentFilter/PostfinancePaymentFilterTest.php +++ b/tests/Components/PaymentFilter/PostfinancePaymentFilterTest.php @@ -2,9 +2,8 @@ declare(strict_types=1); -namespace PayonePayment\EventListener; +namespace PayonePayment\Components\PaymentFilter; -use PayonePayment\Components\PaymentFilter\PaymentFilterServiceInterface; use PayonePayment\PaymentHandler\PayonePostfinanceCardPaymentHandler; use Shopware\Core\System\Currency\CurrencyEntity; diff --git a/tests/Components/PaymentFilter/Przelewy24PaymentFilterTest.php b/tests/Components/PaymentFilter/Przelewy24PaymentFilterTest.php index 160ef197d..ef559bda2 100644 --- a/tests/Components/PaymentFilter/Przelewy24PaymentFilterTest.php +++ b/tests/Components/PaymentFilter/Przelewy24PaymentFilterTest.php @@ -2,9 +2,8 @@ declare(strict_types=1); -namespace PayonePayment\EventListener; +namespace PayonePayment\Components\PaymentFilter; -use PayonePayment\Components\PaymentFilter\PaymentFilterServiceInterface; use PayonePayment\PaymentHandler\PayonePrzelewy24PaymentHandler; use Shopware\Core\System\Currency\CurrencyEntity; diff --git a/tests/Components/PaymentFilter/SecuredDirectDebitPaymentFilterTest.php b/tests/Components/PaymentFilter/SecuredDirectDebitPaymentFilterTest.php index e7a116538..5db421a76 100644 --- a/tests/Components/PaymentFilter/SecuredDirectDebitPaymentFilterTest.php +++ b/tests/Components/PaymentFilter/SecuredDirectDebitPaymentFilterTest.php @@ -2,79 +2,16 @@ declare(strict_types=1); -namespace PayonePayment\EventListener; +namespace PayonePayment\Components\PaymentFilter; -use PayonePayment\Components\PaymentFilter\PaymentFilterContext; -use PayonePayment\Components\PaymentFilter\PaymentFilterServiceInterface; use PayonePayment\PaymentHandler\PayoneSecuredDirectDebitPaymentHandler; -use PayonePayment\TestCaseBase\Mock\PaymentHandler\PaymentHandlerMock; -use Shopware\Core\Checkout\Customer\Aggregate\CustomerAddress\CustomerAddressEntity; -use Shopware\Core\Checkout\Order\Aggregate\OrderAddress\OrderAddressEntity; -use Shopware\Core\System\Country\CountryEntity; use Shopware\Core\System\Currency\CurrencyEntity; /** - * @covers \PayonePayment\Components\PaymentFilter\PayoneBNPLPaymentMethodFilter + * @covers \PayonePayment\Components\PaymentFilter\DefaultPaymentFilterService */ class SecuredDirectDebitPaymentFilterTest extends AbstractPaymentFilterTest { - public function testItHidesPaymentMethodForDifferentShippingAddressOnCheckout(): void - { - $methods = $this->getPaymentMethods(); - - $country = new CountryEntity(); - $country->setIso($this->getAllowedBillingCountry()); - - $salesChannelContext = $this->createSalesChannelContextWithLoggedInCustomerAndWithNavigation(); - $salesChannelContext->getCustomer()->getActiveBillingAddress()->setCountry($country); - - $differentShippingAddress = new CustomerAddressEntity(); - $differentShippingAddress->setFirstName('Different Firstname'); - $salesChannelContext->getCustomer()->setActiveShippingAddress($differentShippingAddress); - - $filterContext = new PaymentFilterContext( - $salesChannelContext, - $salesChannelContext->getCustomer()->getActiveBillingAddress(), - $salesChannelContext->getCustomer()->getActiveShippingAddress(), - $this->getAllowedCurrency() - ); - - $result = $this->getFilterService()->filterPaymentMethods($methods, $filterContext); - - static::assertNotInPaymentCollection($this->getPaymentHandlerClass(), $result); - static::assertInPaymentCollection(PaymentHandlerMock::class, $result); - } - - public function testItHidesPaymentMethodForDifferentShippingAddressOnEditOrderPage(): void - { - $methods = $this->getPaymentMethods(); - - $country = new CountryEntity(); - $country->setIso($this->getAllowedBillingCountry()); - - $salesChannelContext = $this->createSalesChannelContextWithLoggedInCustomerAndWithNavigation(); - - $billingAddress = new OrderAddressEntity(); - $billingAddress->setCountry($country); - $billingAddress->setFirstName('Foo'); - - $shippingAddress = new OrderAddressEntity(); - $shippingAddress->setCountry($country); - $shippingAddress->setFirstName('Bar'); - - $filterContext = new PaymentFilterContext( - $salesChannelContext, - $billingAddress, - $shippingAddress, - $this->getAllowedCurrency() - ); - - $result = $this->getFilterService()->filterPaymentMethods($methods, $filterContext); - - static::assertNotInPaymentCollection($this->getPaymentHandlerClass(), $result); - static::assertInPaymentCollection(PaymentHandlerMock::class, $result); - } - protected function getFilterService(): PaymentFilterServiceInterface { return $this->getContainer()->get('payone.payment_filter_method.secured_direct_debit'); diff --git a/tests/Components/PaymentFilter/SecuredInstallmentPaymentFilterTest.php b/tests/Components/PaymentFilter/SecuredInstallmentPaymentFilterTest.php index 93803b540..c45802382 100644 --- a/tests/Components/PaymentFilter/SecuredInstallmentPaymentFilterTest.php +++ b/tests/Components/PaymentFilter/SecuredInstallmentPaymentFilterTest.php @@ -2,79 +2,16 @@ declare(strict_types=1); -namespace PayonePayment\EventListener; +namespace PayonePayment\Components\PaymentFilter; -use PayonePayment\Components\PaymentFilter\PaymentFilterContext; -use PayonePayment\Components\PaymentFilter\PaymentFilterServiceInterface; use PayonePayment\PaymentHandler\PayoneSecuredInstallmentPaymentHandler; -use PayonePayment\TestCaseBase\Mock\PaymentHandler\PaymentHandlerMock; -use Shopware\Core\Checkout\Customer\Aggregate\CustomerAddress\CustomerAddressEntity; -use Shopware\Core\Checkout\Order\Aggregate\OrderAddress\OrderAddressEntity; -use Shopware\Core\System\Country\CountryEntity; use Shopware\Core\System\Currency\CurrencyEntity; /** - * @covers \PayonePayment\Components\PaymentFilter\PayoneBNPLPaymentMethodFilter + * @covers \PayonePayment\Components\PaymentFilter\DefaultPaymentFilterService */ class SecuredInstallmentPaymentFilterTest extends AbstractPaymentFilterTest { - public function testItHidesPaymentMethodForDifferentShippingAddressOnCheckout(): void - { - $methods = $this->getPaymentMethods(); - - $country = new CountryEntity(); - $country->setIso($this->getAllowedBillingCountry()); - - $salesChannelContext = $this->createSalesChannelContextWithLoggedInCustomerAndWithNavigation(); - $salesChannelContext->getCustomer()->getActiveBillingAddress()->setCountry($country); - - $differentShippingAddress = new CustomerAddressEntity(); - $differentShippingAddress->setFirstName('Different Firstname'); - $salesChannelContext->getCustomer()->setActiveShippingAddress($differentShippingAddress); - - $filterContext = new PaymentFilterContext( - $salesChannelContext, - $salesChannelContext->getCustomer()->getActiveBillingAddress(), - $salesChannelContext->getCustomer()->getActiveShippingAddress(), - $this->getAllowedCurrency() - ); - - $result = $this->getFilterService()->filterPaymentMethods($methods, $filterContext); - - static::assertNotInPaymentCollection($this->getPaymentHandlerClass(), $result); - static::assertInPaymentCollection(PaymentHandlerMock::class, $result); - } - - public function testItHidesPaymentMethodForDifferentShippingAddressOnEditOrderPage(): void - { - $methods = $this->getPaymentMethods(); - - $country = new CountryEntity(); - $country->setIso($this->getAllowedBillingCountry()); - - $salesChannelContext = $this->createSalesChannelContextWithLoggedInCustomerAndWithNavigation(); - - $billingAddress = new OrderAddressEntity(); - $billingAddress->setCountry($country); - $billingAddress->setFirstName('Foo'); - - $shippingAddress = new OrderAddressEntity(); - $shippingAddress->setCountry($country); - $shippingAddress->setFirstName('Bar'); - - $filterContext = new PaymentFilterContext( - $salesChannelContext, - $billingAddress, - $shippingAddress, - $this->getAllowedCurrency() - ); - - $result = $this->getFilterService()->filterPaymentMethods($methods, $filterContext); - - static::assertNotInPaymentCollection($this->getPaymentHandlerClass(), $result); - static::assertInPaymentCollection(PaymentHandlerMock::class, $result); - } - protected function getFilterService(): PaymentFilterServiceInterface { return $this->getContainer()->get('payone.payment_filter_method.secured_installment'); diff --git a/tests/Components/PaymentFilter/SecuredInvoicePaymentFilterTest.php b/tests/Components/PaymentFilter/SecuredInvoicePaymentFilterTest.php index de03ff61e..d73e2dcab 100644 --- a/tests/Components/PaymentFilter/SecuredInvoicePaymentFilterTest.php +++ b/tests/Components/PaymentFilter/SecuredInvoicePaymentFilterTest.php @@ -2,79 +2,16 @@ declare(strict_types=1); -namespace PayonePayment\EventListener; +namespace PayonePayment\Components\PaymentFilter; -use PayonePayment\Components\PaymentFilter\PaymentFilterContext; -use PayonePayment\Components\PaymentFilter\PaymentFilterServiceInterface; use PayonePayment\PaymentHandler\PayoneSecuredInvoicePaymentHandler; -use PayonePayment\TestCaseBase\Mock\PaymentHandler\PaymentHandlerMock; -use Shopware\Core\Checkout\Customer\Aggregate\CustomerAddress\CustomerAddressEntity; -use Shopware\Core\Checkout\Order\Aggregate\OrderAddress\OrderAddressEntity; -use Shopware\Core\System\Country\CountryEntity; use Shopware\Core\System\Currency\CurrencyEntity; /** - * @covers \PayonePayment\Components\PaymentFilter\PayoneBNPLPaymentMethodFilter + * @covers \PayonePayment\Components\PaymentFilter\DefaultPaymentFilterService */ class SecuredInvoicePaymentFilterTest extends AbstractPaymentFilterTest { - public function testItHidesPaymentMethodForDifferentShippingAddressOnCheckout(): void - { - $methods = $this->getPaymentMethods(); - - $country = new CountryEntity(); - $country->setIso($this->getAllowedBillingCountry()); - - $salesChannelContext = $this->createSalesChannelContextWithLoggedInCustomerAndWithNavigation(); - $salesChannelContext->getCustomer()->getActiveBillingAddress()->setCountry($country); - - $differentShippingAddress = new CustomerAddressEntity(); - $differentShippingAddress->setFirstName('Different Firstname'); - $salesChannelContext->getCustomer()->setActiveShippingAddress($differentShippingAddress); - - $filterContext = new PaymentFilterContext( - $salesChannelContext, - $salesChannelContext->getCustomer()->getActiveBillingAddress(), - $salesChannelContext->getCustomer()->getActiveShippingAddress(), - $this->getAllowedCurrency() - ); - - $result = $this->getFilterService()->filterPaymentMethods($methods, $filterContext); - - static::assertNotInPaymentCollection($this->getPaymentHandlerClass(), $result); - static::assertInPaymentCollection(PaymentHandlerMock::class, $result); - } - - public function testItHidesPaymentMethodForDifferentShippingAddressOnEditOrderPage(): void - { - $methods = $this->getPaymentMethods(); - - $country = new CountryEntity(); - $country->setIso($this->getAllowedBillingCountry()); - - $salesChannelContext = $this->createSalesChannelContextWithLoggedInCustomerAndWithNavigation(); - - $billingAddress = new OrderAddressEntity(); - $billingAddress->setCountry($country); - $billingAddress->setFirstName('Foo'); - - $shippingAddress = new OrderAddressEntity(); - $shippingAddress->setCountry($country); - $shippingAddress->setFirstName('Bar'); - - $filterContext = new PaymentFilterContext( - $salesChannelContext, - $billingAddress, - $shippingAddress, - $this->getAllowedCurrency() - ); - - $result = $this->getFilterService()->filterPaymentMethods($methods, $filterContext); - - static::assertNotInPaymentCollection($this->getPaymentHandlerClass(), $result); - static::assertInPaymentCollection(PaymentHandlerMock::class, $result); - } - protected function getFilterService(): PaymentFilterServiceInterface { return $this->getContainer()->get('payone.payment_filter_method.secured_invoice'); From b51f16c9b5005217191ab0cad0ba948bfdd0f329 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20M=C3=BCller?= Date: Fri, 19 Jan 2024 19:43:55 +0100 Subject: [PATCH 3/3] [PAYOSWXP-113] Add B2B handling for secured invoice --- .../payment_method_filter.xml | 2 + .../AuthorizeRequestParameterBuilder.php | 30 ++++++++++++ .../AuthorizeRequestParameterBuilderTest.php | 47 +++++++++++++++++++ 3 files changed, 79 insertions(+) diff --git a/src/DependencyInjection/payment_method_filter.xml b/src/DependencyInjection/payment_method_filter.xml index 413a22f55..e804c9377 100644 --- a/src/DependencyInjection/payment_method_filter.xml +++ b/src/DependencyInjection/payment_method_filter.xml @@ -100,6 +100,8 @@ AT + DE + AT EUR diff --git a/src/Payone/RequestParameter/Builder/SecuredInvoice/AuthorizeRequestParameterBuilder.php b/src/Payone/RequestParameter/Builder/SecuredInvoice/AuthorizeRequestParameterBuilder.php index a8e954d1c..5e3b2bd51 100644 --- a/src/Payone/RequestParameter/Builder/SecuredInvoice/AuthorizeRequestParameterBuilder.php +++ b/src/Payone/RequestParameter/Builder/SecuredInvoice/AuthorizeRequestParameterBuilder.php @@ -13,6 +13,8 @@ use PayonePayment\Payone\RequestParameter\Builder\AbstractRequestParameterBuilder; use PayonePayment\Payone\RequestParameter\Struct\AbstractRequestParameterStruct; use PayonePayment\Payone\RequestParameter\Struct\PaymentTransactionStruct; +use Shopware\Core\Checkout\Order\Aggregate\OrderAddress\OrderAddressCollection; +use Shopware\Core\Checkout\Order\Aggregate\OrderAddress\OrderAddressEntity; use Shopware\Core\Checkout\Order\OrderEntity; use Shopware\Core\Framework\Context; use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository; @@ -55,6 +57,7 @@ public function getRequestParameter(AbstractRequestParameterStruct $arguments): $this->applyPhoneParameter($order, $parameters, $dataBag->get('securedInvoicePhone') ?? '', $context); $this->applyBirthdayParameter($order, $parameters, $dataBag->get('payoneInvoiceBirthday') ?? '', $context); + $this->applyB2bParameters($order, $parameters); return $parameters; } @@ -82,4 +85,31 @@ protected function getOrder(string $orderId, Context $context): OrderEntity return $order; } + + protected function applyB2bParameters(OrderEntity $order, array &$parameters): void + { + $billingAddress = $order->getAddresses()?->get($order->getBillingAddressId()); + + if ($billingAddress === null) { + return; + } + + $company = $billingAddress->getCompany(); + if ($company === null) { + return; + } + + $parameters['businessrelation'] = 'b2b'; + $parameters['company'] = $company; + + if ($billingAddress->getVatId()) { + $parameters['vatid'] = $billingAddress->getVatId(); + } else { + $vatIds = $order->getOrderCustomer()?->getVatIds(); + + if (\is_array($vatIds) && isset($vatIds[0])) { + $parameters['vatid'] = $vatIds[0]; + } + } + } } diff --git a/tests/Payone/RequestParameter/Builder/SecuredInvoice/AuthorizeRequestParameterBuilderTest.php b/tests/Payone/RequestParameter/Builder/SecuredInvoice/AuthorizeRequestParameterBuilderTest.php index c98dd989d..13fd61376 100644 --- a/tests/Payone/RequestParameter/Builder/SecuredInvoice/AuthorizeRequestParameterBuilderTest.php +++ b/tests/Payone/RequestParameter/Builder/SecuredInvoice/AuthorizeRequestParameterBuilderTest.php @@ -199,6 +199,53 @@ public function testItAddsCorrectAuthorizeParametersWithSavedBirthday(): void ); } + public function testItAddsCorrectAuthorizeParametersForB2b(): void + { + $request = $this->getRequestWithSession([ + PayoneBNPLDeviceFingerprintService::SESSION_VAR_NAME => 'the-device-ident-token', + ]); + $this->getContainer()->get(RequestStack::class)->push($request); + + $dataBag = new RequestDataBag([ + 'securedInvoicePhone' => '0123456789', + 'payoneInvoiceBirthday' => '2000-01-01', + ]); + + $struct = $this->getPaymentTransactionStruct( + $dataBag, + $this->getValidPaymentHandler(), + $this->getValidRequestAction() + ); + + $builder = $this->getContainer()->get($this->getParameterBuilder()); + + // Save company and vat id on customer billing address + $this->getContainer()->get('order_address.repository')->update([ + [ + 'id' => $struct->getPaymentTransaction()->getOrder()->getBillingAddressId(), + 'company' => 'The Company', + 'vatId' => 'DE123456789', + ], + ], $struct->getSalesChannelContext()->getContext()); + + $parameters = $builder->getRequestParameter($struct); + + Assert::assertArraySubset( + [ + 'request' => $this->getValidRequestAction(), + 'clearingtype' => AbstractRequestParameterBuilder::CLEARING_TYPE_FINANCING, + 'financingtype' => AbstractPayonePaymentHandler::PAYONE_FINANCING_PIV, + 'telephonenumber' => '0123456789', + 'birthday' => '20000101', + 'it[1]' => LineItemHydrator::TYPE_GOODS, + 'businessrelation' => 'b2b', + 'company' => 'The Company', + 'vatid' => 'DE123456789', + ], + $parameters + ); + } + protected function getParameterBuilder(): string { return AuthorizeRequestParameterBuilder::class;