diff --git a/src/Provider/SupportedMethodsProvider.php b/src/Provider/SupportedMethodsProvider.php index dcdcf268..352665db 100644 --- a/src/Provider/SupportedMethodsProvider.php +++ b/src/Provider/SupportedMethodsProvider.php @@ -22,9 +22,11 @@ public function provide( array $supportedMethods, string $factoryName, int $paymentAmount, + ?string $billingCountryCode = null, ): array { $activeCurrencyCode = $this->currencyContext->getCurrencyCode(); $authorizedCurrencies = null; + $allowedCountries = null; foreach ($supportedMethods as $key => $paymentMethod) { Assert::isInstanceOf($paymentMethod, PaymentMethodInterface::class); @@ -37,6 +39,13 @@ public function provide( } $authorizedCurrencies ??= $this->resolveAuthorizedCurrencies($factoryName); + $allowedCountries ??= $this->resolveAllowedCountries($factoryName); + + if ($billingCountryCode !== null && $allowedCountries !== [] && !\in_array($billingCountryCode, $allowedCountries, true)) { + unset($supportedMethods[$key]); + + continue; + } if (!\array_key_exists($activeCurrencyCode, $authorizedCurrencies)) { unset($supportedMethods[$key]); @@ -93,4 +102,28 @@ private function resolveAuthorizedCurrencies(string $factoryName): array return $currencies; } + + private function resolveAllowedCountries(string $factoryName): array + { + $underscorePos = strpos($factoryName, '_'); + if ($underscorePos === false) { + return []; + } + + $account = $this->clientFactory->create($factoryName)->getAccount(); + $pmKey = substr($factoryName, $underscorePos + 1); + $paymentMethods = $account['payment_methods'] ?? []; + Assert::isArray($paymentMethods); + $pmData = $paymentMethods[$pmKey] ?? []; + Assert::isArray($pmData); + + $allowedCountries = $pmData['allowed_countries'] ?? []; + Assert::isArray($allowedCountries); + + if (\in_array('ALL', $allowedCountries, true)) { + return []; + } + + return $allowedCountries; + } } diff --git a/src/Resolver/AmericanExpressPaymentMethodsResolverDecorator.php b/src/Resolver/AmericanExpressPaymentMethodsResolverDecorator.php index eb5a8a05..d525bf61 100644 --- a/src/Resolver/AmericanExpressPaymentMethodsResolverDecorator.php +++ b/src/Resolver/AmericanExpressPaymentMethodsResolverDecorator.php @@ -6,6 +6,7 @@ use PayPlug\SyliusPayPlugPlugin\Gateway\AmericanExpressGatewayFactory; use PayPlug\SyliusPayPlugPlugin\Provider\SupportedMethodsProvider; +use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\Payment; use Sylius\Component\Payment\Model\PaymentInterface as BasePaymentInterface; use Sylius\Component\Payment\Resolver\PaymentMethodsResolverInterface; @@ -28,10 +29,15 @@ public function getSupportedMethods(BasePaymentInterface $subject): array Assert::isInstanceOf($subject, Payment::class); $supportedMethods = $this->decorated->getSupportedMethods($subject); + /** @var OrderInterface $order */ + $order = $subject->getOrder(); + $billingCountryCode = $order->getBillingAddress()?->getCountryCode(); + return $this->supportedMethodsProvider->provide( $supportedMethods, AmericanExpressGatewayFactory::FACTORY_NAME, $subject->getAmount() ?? 0, + $billingCountryCode, ); } diff --git a/src/Resolver/ApplePayPaymentMethodsResolverDecorator.php b/src/Resolver/ApplePayPaymentMethodsResolverDecorator.php index 2309867c..5205eaf7 100644 --- a/src/Resolver/ApplePayPaymentMethodsResolverDecorator.php +++ b/src/Resolver/ApplePayPaymentMethodsResolverDecorator.php @@ -6,6 +6,7 @@ use PayPlug\SyliusPayPlugPlugin\Gateway\ApplePayGatewayFactory; use PayPlug\SyliusPayPlugPlugin\Provider\SupportedMethodsProvider; +use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\Payment; use Sylius\Component\Payment\Model\PaymentInterface as BasePaymentInterface; use Sylius\Component\Payment\Resolver\PaymentMethodsResolverInterface; @@ -28,10 +29,15 @@ public function getSupportedMethods(BasePaymentInterface $subject): array Assert::isInstanceOf($subject, Payment::class); $supportedMethods = $this->decorated->getSupportedMethods($subject); + /** @var OrderInterface $order */ + $order = $subject->getOrder(); + $billingCountryCode = $order->getBillingAddress()?->getCountryCode(); + return $this->supportedMethodsProvider->provide( $supportedMethods, ApplePayGatewayFactory::FACTORY_NAME, $subject->getAmount() ?? 0, + $billingCountryCode, ); } diff --git a/src/Resolver/BancontactPaymentMethodsResolverDecorator.php b/src/Resolver/BancontactPaymentMethodsResolverDecorator.php index 444ebbf3..c003b444 100644 --- a/src/Resolver/BancontactPaymentMethodsResolverDecorator.php +++ b/src/Resolver/BancontactPaymentMethodsResolverDecorator.php @@ -6,6 +6,7 @@ use PayPlug\SyliusPayPlugPlugin\Gateway\BancontactGatewayFactory; use PayPlug\SyliusPayPlugPlugin\Provider\SupportedMethodsProvider; +use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\Payment; use Sylius\Component\Payment\Model\PaymentInterface as BasePaymentInterface; use Sylius\Component\Payment\Resolver\PaymentMethodsResolverInterface; @@ -28,10 +29,15 @@ public function getSupportedMethods(BasePaymentInterface $subject): array Assert::isInstanceOf($subject, Payment::class); $supportedMethods = $this->decorated->getSupportedMethods($subject); + /** @var OrderInterface $order */ + $order = $subject->getOrder(); + $billingCountryCode = $order->getBillingAddress()?->getCountryCode(); + return $this->supportedMethodsProvider->provide( $supportedMethods, BancontactGatewayFactory::FACTORY_NAME, $subject->getAmount() ?? 0, + $billingCountryCode, ); } diff --git a/src/Resolver/PayPlugPaymentMethodsResolverDecorator.php b/src/Resolver/PayPlugPaymentMethodsResolverDecorator.php index 190a9b91..39412fa1 100644 --- a/src/Resolver/PayPlugPaymentMethodsResolverDecorator.php +++ b/src/Resolver/PayPlugPaymentMethodsResolverDecorator.php @@ -6,6 +6,7 @@ use PayPlug\SyliusPayPlugPlugin\Gateway\PayPlugGatewayFactory; use PayPlug\SyliusPayPlugPlugin\Provider\SupportedMethodsProvider; +use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\Payment; use Sylius\Component\Payment\Model\PaymentInterface as BasePaymentInterface; use Sylius\Component\Payment\Resolver\PaymentMethodsResolverInterface; @@ -28,10 +29,15 @@ public function getSupportedMethods(BasePaymentInterface $subject): array Assert::isInstanceOf($subject, Payment::class); $supportedMethods = $this->decorated->getSupportedMethods($subject); + /** @var OrderInterface $order */ + $order = $subject->getOrder(); + $billingCountryCode = $order->getBillingAddress()?->getCountryCode(); + return $this->supportedMethodsProvider->provide( $supportedMethods, PayPlugGatewayFactory::FACTORY_NAME, $subject->getAmount() ?? 0, + $billingCountryCode, ); } diff --git a/src/Resolver/ScalapayPaymentMethodsResolverDecorator.php b/src/Resolver/ScalapayPaymentMethodsResolverDecorator.php index 51e4d967..05664c39 100644 --- a/src/Resolver/ScalapayPaymentMethodsResolverDecorator.php +++ b/src/Resolver/ScalapayPaymentMethodsResolverDecorator.php @@ -6,6 +6,7 @@ use PayPlug\SyliusPayPlugPlugin\Gateway\ScalapayGatewayFactory; use PayPlug\SyliusPayPlugPlugin\Provider\SupportedMethodsProvider; +use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\Payment; use Sylius\Component\Payment\Model\PaymentInterface as BasePaymentInterface; use Sylius\Component\Payment\Resolver\PaymentMethodsResolverInterface; @@ -28,10 +29,15 @@ public function getSupportedMethods(BasePaymentInterface $subject): array Assert::isInstanceOf($subject, Payment::class); $supportedMethods = $this->decorated->getSupportedMethods($subject); + /** @var OrderInterface $order */ + $order = $subject->getOrder(); + $billingCountryCode = $order->getBillingAddress()?->getCountryCode(); + return $this->supportedMethodsProvider->provide( $supportedMethods, ScalapayGatewayFactory::FACTORY_NAME, $subject->getAmount() ?? 0, + $billingCountryCode, ); } diff --git a/tests/PHPUnit/Provider/SupportedMethodsProviderTest.php b/tests/PHPUnit/Provider/SupportedMethodsProviderTest.php index f1ee5732..6e015ea5 100644 --- a/tests/PHPUnit/Provider/SupportedMethodsProviderTest.php +++ b/tests/PHPUnit/Provider/SupportedMethodsProviderTest.php @@ -47,7 +47,9 @@ protected function setUp(): void public function testProvide_withDifferentFactory_doesNotFilter(): void { $this->currencyContext->method('getCurrencyCode')->willReturn('EUR'); - $this->apiClient->method('getAccount')->willReturn($this->buildAccount(30, 2000000)); + // getAccount() must never be called: the PayPlug method never matches the Bancontact factory, + // so the loop always hits `continue` before reaching the lazy-loading lines. + $this->apiClient->expects(self::never())->method('getAccount'); // PaymentMethod is PayPlug, but we're querying for Bancontact — so it's passed through as-is $method = $this->buildPaymentMethod(PayPlugGatewayFactory::FACTORY_NAME); @@ -206,6 +208,82 @@ public function testProvide_withMixedList_filtersCorrectly(): void self::assertCount(2, $result); // Bancontact not removed (no filter applied for different factory) } + // ------------------------------------------------------------------------- + // provide() — allowed_countries filter + // ------------------------------------------------------------------------- + + /** + * Billing country is in the allowed_countries list → method kept. + */ + public function testProvide_withAllowedCountry_keepsMethod(): void + { + $this->currencyContext->method('getCurrencyCode')->willReturn('EUR'); + $account = [ + 'configuration' => ['min_amounts' => ['EUR' => 30], 'max_amounts' => ['EUR' => 2000000]], + 'payment_methods' => ['scalapay' => ['min_amounts' => ['EUR' => 500], 'max_amounts' => ['EUR' => 200000], 'allowed_countries' => ['FR', 'DE']]], + ]; + $this->apiClient->method('getAccount')->willReturn($account); + + $method = $this->buildPaymentMethod('payplug_scalapay'); + $result = $this->provider->provide([$method], 'payplug_scalapay', 1000, 'FR'); + + self::assertCount(1, $result); + } + + /** + * Billing country is NOT in the allowed_countries list → method removed. + */ + public function testProvide_withDisallowedCountry_removesMethod(): void + { + $this->currencyContext->method('getCurrencyCode')->willReturn('EUR'); + $account = [ + 'configuration' => ['min_amounts' => ['EUR' => 30], 'max_amounts' => ['EUR' => 2000000]], + 'payment_methods' => ['scalapay' => ['min_amounts' => ['EUR' => 500], 'max_amounts' => ['EUR' => 200000], 'allowed_countries' => ['FR', 'DE']]], + ]; + $this->apiClient->method('getAccount')->willReturn($account); + + $method = $this->buildPaymentMethod('payplug_scalapay'); + $result = $this->provider->provide([$method], 'payplug_scalapay', 1000, 'US'); + + self::assertEmpty($result); + } + + /** + * allowed_countries = ["ALL"] → country check skipped, method kept. + */ + public function testProvide_withAllowedCountriesAll_keepsMethod(): void + { + $this->currencyContext->method('getCurrencyCode')->willReturn('EUR'); + $account = [ + 'configuration' => ['min_amounts' => ['EUR' => 30], 'max_amounts' => ['EUR' => 2000000]], + 'payment_methods' => ['bancontact' => ['min_amounts' => ['EUR' => 30], 'max_amounts' => ['EUR' => 2000000], 'allowed_countries' => ['ALL']]], + ]; + $this->apiClient->method('getAccount')->willReturn($account); + + $method = $this->buildPaymentMethod('payplug_bancontact'); + $result = $this->provider->provide([$method], 'payplug_bancontact', 1000, 'US'); + + self::assertCount(1, $result); + } + + /** + * Billing country is null (no billing address yet) → country check skipped, method kept. + */ + public function testProvide_withNullBillingCountry_keepsMethod(): void + { + $this->currencyContext->method('getCurrencyCode')->willReturn('EUR'); + $account = [ + 'configuration' => ['min_amounts' => ['EUR' => 30], 'max_amounts' => ['EUR' => 2000000]], + 'payment_methods' => ['scalapay' => ['min_amounts' => ['EUR' => 500], 'max_amounts' => ['EUR' => 200000], 'allowed_countries' => ['FR', 'DE']]], + ]; + $this->apiClient->method('getAccount')->willReturn($account); + + $method = $this->buildPaymentMethod('payplug_scalapay'); + $result = $this->provider->provide([$method], 'payplug_scalapay', 1000, null); + + self::assertCount(1, $result); + } + // ------------------------------------------------------------------------- // provide() — payment method specific amounts used over configuration defaults // -------------------------------------------------------------------------