diff --git a/config/services.xml b/config/services.xml
index cf9d38c9..742f048f 100644
--- a/config/services.xml
+++ b/config/services.xml
@@ -39,10 +39,6 @@
-
-
-
-
%sylius_paypal.sandbox%
@@ -68,9 +64,6 @@
-
-
-
%sylius_paypal.sandbox%
@@ -370,5 +363,13 @@
%sylius_paypal.supported_locales%
+
+
+
+
+
diff --git a/config/validation/PaymentMethod.xml b/config/validation/PaymentMethod.xml
new file mode 100644
index 00000000..b4102997
--- /dev/null
+++ b/config/validation/PaymentMethod.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/features/managing_multiple_paypal_payment_methods.feature b/features/managing_multiple_paypal_payment_methods.feature
new file mode 100644
index 00000000..f334a2de
--- /dev/null
+++ b/features/managing_multiple_paypal_payment_methods.feature
@@ -0,0 +1,30 @@
+@managing_payment_methods
+Feature: Managing multiple PayPal payment methods
+ In order to switch between PayPal configurations
+ As an Administrator
+ I want to have multiple PayPal methods but only one enabled at a time
+
+ Background:
+ Given the store operates on a single channel in "United States"
+ And I am logged in as an administrator
+
+ @ui
+ Scenario: Cannot create and enable a new PayPal method when another is already enabled
+ Given the store allows paying with "PayPal Sandbox" with "PayPal" factory name
+ When I create a new PayPal payment method "PayPal Production" and try to save it as enabled
+ Then I should see a validation error that only one PayPal method can be enabled
+ And the PayPal payment method "PayPal Production" should not exist
+
+ @ui
+ Scenario: Cannot enable an existing PayPal method when another is already enabled
+ Given the store allows paying with "PayPal Sandbox" with "PayPal" factory name
+ And the store has a disabled "PayPal Production" payment method with "PayPal" gateway factory
+ When I try to enable the PayPal payment method "PayPal Production"
+ Then I should see a validation error that only one PayPal method can be enabled
+ And the PayPal payment method "PayPal Production" should still be disabled
+
+ @ui
+ Scenario: Can create and enable a new PayPal method when no other is enabled
+ Given the store has a disabled "PayPal Sandbox" payment method with "PayPal" gateway factory
+ When I create a new PayPal payment method "PayPal Production" and save it as enabled
+ Then the new PayPal payment method should be in the list and enabled
diff --git a/features/trying_to_onboard_more_than_one_pay_pal_seller.feature b/features/trying_to_onboard_more_than_one_pay_pal_seller.feature
deleted file mode 100644
index a7fe86a2..00000000
--- a/features/trying_to_onboard_more_than_one_pay_pal_seller.feature
+++ /dev/null
@@ -1,20 +0,0 @@
-@managing_payment_methods
-Feature: Trying to onboard more than one PayPal seller
- In order to handle PayPal integration properly
- As an Administrator
- I want to be prevented from onboarding more than one PayPal seller
-
- Background:
- Given the store operates on a single channel in "United States"
- And the store allows paying with "PayPal" with "PayPal" factory name
- And I am logged in as an administrator
-
- @ui
- Scenario: Trying to onboard second PayPal seller
- When I try to create a new payment method with "PayPal" gateway factory
- Then I should be notified that I cannot onboard more than one PayPal seller
-
- @ui
- Scenario: Being able to create different payment methods
- When I want to create a new offline payment method
- Then I should not be notified that I cannot onboard more than one PayPal seller
diff --git a/src/Form/Extension/PaymentMethodTypeExtension.php b/src/Form/Extension/PaymentMethodTypeExtension.php
deleted file mode 100644
index 65ce647f..00000000
--- a/src/Form/Extension/PaymentMethodTypeExtension.php
+++ /dev/null
@@ -1,51 +0,0 @@
-addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event): void {
- /** @var PaymentMethodInterface $data */
- $data = $event->getData();
- $form = $event->getForm();
-
- /** @var GatewayConfigInterface $gatewayConfig */
- $gatewayConfig = $data->getGatewayConfig();
- if ($gatewayConfig->getFactoryName() === SyliusPayPalExtension::PAYPAL_FACTORY_NAME) {
- $form->add('enabled', HiddenType::class, [
- 'required' => false,
- 'label' => 'sylius.form.payment_method.enabled',
- 'data' => $data->isEnabled(),
- ]);
- }
- });
- }
-
- public static function getExtendedTypes(): iterable
- {
- return [PaymentMethodType::class];
- }
-}
diff --git a/src/Listener/PayPalPaymentMethodListener.php b/src/Listener/PayPalPaymentMethodListener.php
index 061ebc69..3dbe9389 100644
--- a/src/Listener/PayPalPaymentMethodListener.php
+++ b/src/Listener/PayPalPaymentMethodListener.php
@@ -16,29 +16,21 @@
use Sylius\Bundle\ResourceBundle\Event\ResourceControllerEvent;
use Sylius\Component\Core\Model\PaymentMethodInterface;
use Sylius\PayPalPlugin\DependencyInjection\SyliusPayPalExtension;
-use Sylius\PayPalPlugin\Exception\PayPalPaymentMethodNotFoundException;
use Sylius\PayPalPlugin\Onboarding\Initiator\OnboardingInitiatorInterface;
-use Sylius\PayPalPlugin\Provider\FlashBagProvider;
-use Sylius\PayPalPlugin\Provider\PayPalPaymentMethodProviderInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
-use Symfony\Component\HttpFoundation\RequestStack;
-use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Webmozart\Assert\Assert;
final readonly class PayPalPaymentMethodListener
{
public function __construct(
private OnboardingInitiatorInterface $onboardingInitiator,
- private UrlGeneratorInterface $urlGenerator,
- private RequestStack $flashBagOrRequestStack,
- private PayPalPaymentMethodProviderInterface $payPalPaymentMethodProvider,
private bool $isSandbox = false,
) {
}
public function initializeCreate(ResourceControllerEvent $event): void
{
- /** @var object $paymentMethod */
+ /** @var PaymentMethodInterface|mixed $paymentMethod */
$paymentMethod = $event->getSubject();
Assert::isInstanceOf($paymentMethod, PaymentMethodInterface::class);
@@ -46,16 +38,6 @@ public function initializeCreate(ResourceControllerEvent $event): void
return;
}
- if ($this->isTherePayPalPaymentMethod()) {
- FlashBagProvider::getFlashBag($this->flashBagOrRequestStack)
- ->add('error', 'sylius_paypal.more_than_one_seller_not_allowed')
- ;
-
- $event->setResponse(new RedirectResponse($this->urlGenerator->generate('sylius_admin_payment_method_index')));
-
- return;
- }
-
if ($this->isSandbox || !$this->onboardingInitiator->supports($paymentMethod)) {
return;
}
@@ -69,15 +51,4 @@ private function isNewPaymentMethodPayPal(PaymentMethodInterface $paymentMethod)
return $gatewayConfig->getFactoryName() === SyliusPayPalExtension::PAYPAL_FACTORY_NAME;
}
-
- private function isTherePayPalPaymentMethod(): bool
- {
- try {
- $this->payPalPaymentMethodProvider->provide();
- } catch (PayPalPaymentMethodNotFoundException $exception) {
- return false;
- }
-
- return true;
- }
}
diff --git a/src/Provider/PayPalPaymentMethodProvider.php b/src/Provider/PayPalPaymentMethodProvider.php
index 01a1d301..3ca6f94f 100644
--- a/src/Provider/PayPalPaymentMethodProvider.php
+++ b/src/Provider/PayPalPaymentMethodProvider.php
@@ -28,7 +28,7 @@ public function __construct(private PaymentMethodRepositoryInterface $paymentMet
public function provide(): PaymentMethodInterface
{
- $paymentMethods = $this->paymentMethodRepository->findAll();
+ $paymentMethods = $this->paymentMethodRepository->findBy(['enabled' => true]);
/** @var PaymentMethodInterface $paymentMethod */
foreach ($paymentMethods as $paymentMethod) {
diff --git a/src/Validator/Constraints/OnlyOneEnabledPayPalPaymentMethod.php b/src/Validator/Constraints/OnlyOneEnabledPayPalPaymentMethod.php
new file mode 100644
index 00000000..9e6ddcaf
--- /dev/null
+++ b/src/Validator/Constraints/OnlyOneEnabledPayPalPaymentMethod.php
@@ -0,0 +1,31 @@
+ $paymentMethodRepository */
+ public function __construct(
+ private readonly PaymentMethodRepositoryInterface $paymentMethodRepository,
+ ) {
+ }
+
+ public function validate(mixed $value, Constraint $constraint): void
+ {
+ if (!$constraint instanceof OnlyOneEnabledPayPalPaymentMethod) {
+ throw new UnexpectedTypeException($constraint, OnlyOneEnabledPayPalPaymentMethod::class);
+ }
+
+ if (!$value instanceof PaymentMethodInterface) {
+ throw new UnexpectedValueException($value, PaymentMethodInterface::class);
+ }
+
+ if (!$this->isPayPalMethod($value) || !$value->isEnabled()) {
+ return;
+ }
+
+ $allMethods = $this->paymentMethodRepository->findBy(['enabled' => true]);
+ foreach ($allMethods as $method) {
+ if ($method->getId() === $value->getId()) {
+ continue;
+ }
+ if (!$this->isPayPalMethod($method)) {
+ continue;
+ }
+
+ $this->context
+ ->buildViolation($constraint->message)
+ ->atPath('enabled')
+ ->addViolation()
+ ;
+
+ return;
+ }
+ }
+
+ private function isPayPalMethod(PaymentMethodInterface $paymentMethod): bool
+ {
+ $gatewayConfig = $paymentMethod->getGatewayConfig();
+
+ return $gatewayConfig?->getFactoryName() === SyliusPayPalExtension::PAYPAL_FACTORY_NAME;
+ }
+}
diff --git a/tests/Behat/Context/Admin/ManagingPaymentMethodsContext.php b/tests/Behat/Context/Admin/ManagingPaymentMethodsContext.php
index caae1349..bfe86d2f 100644
--- a/tests/Behat/Context/Admin/ManagingPaymentMethodsContext.php
+++ b/tests/Behat/Context/Admin/ManagingPaymentMethodsContext.php
@@ -15,10 +15,13 @@
use Behat\Behat\Context\Context;
use Behat\Mink\Exception\ElementNotFoundException;
-use Sylius\Behat\Exception\NotificationExpectationMismatchException;
use Sylius\Behat\NotificationType;
+use Sylius\Behat\Page\Admin\Crud\IndexPageInterface;
use Sylius\Behat\Page\Admin\PaymentMethod\CreatePageInterface;
+use Sylius\Behat\Page\Admin\PaymentMethod\UpdatePageInterface;
use Sylius\Behat\Service\NotificationCheckerInterface;
+use Sylius\Behat\Service\SharedStorageInterface;
+use Sylius\Component\Payment\Repository\PaymentMethodRepositoryInterface;
use Sylius\PayPalPlugin\DependencyInjection\SyliusPayPalExtension;
use Tests\Sylius\PayPalPlugin\Behat\Element\DownloadPayPalReportElementInterface;
use Webmozart\Assert\Assert;
@@ -29,6 +32,10 @@ public function __construct(
private DownloadPayPalReportElementInterface $downloadPayPalReportElement,
private NotificationCheckerInterface $notificationChecker,
private CreatePageInterface $createPage,
+ private UpdatePageInterface $updatePage,
+ private IndexPageInterface $indexPage,
+ private PaymentMethodRepositoryInterface $paymentMethodRepository,
+ private SharedStorageInterface $sharedStorage,
) {
}
@@ -49,11 +56,52 @@ public function yesterdayReportCsvFileShouldBeSuccessfullyDownloaded(): void
}
/**
- * @When I try to create a new payment method with "PayPal" gateway factory
+ * @When I create a new PayPal payment method :name and try to save it as enabled
*/
- public function iTryToCreateANewPaymentMethodWithGatewayFactory(): void
+ public function iCreateANewPayPalPaymentMethodAndTryToSaveItAsEnabled(string $name): void
{
- $this->createPage->tryToOpen(['factory' => SyliusPayPalExtension::PAYPAL_FACTORY_NAME]);
+ $code = $this->normalizeCode($name);
+
+ $this->createPage->open(['factory' => SyliusPayPalExtension::PAYPAL_FACTORY_NAME]);
+ $this->createPage->nameIt($name, 'en_US');
+ $this->createPage->specifyCode($code);
+ $this->createPage->create();
+
+ $this->sharedStorage->set('payment_method_name', $name);
+ $this->sharedStorage->set('payment_method_code', $code);
+ }
+
+ /**
+ * @When I try to enable the PayPal payment method :name
+ */
+ public function iTryToEnableThePayPalPaymentMethod(string $name): void
+ {
+ $code = $this->normalizeCode($name);
+ $paymentMethod = $this->paymentMethodRepository->findOneBy(['code' => $code]);
+ Assert::notNull($paymentMethod, sprintf('Payment method "%s" not found', $name));
+
+ $this->updatePage->open(['id' => $paymentMethod->getId()]);
+ $this->updatePage->enable();
+ $this->updatePage->saveChanges();
+
+ $this->sharedStorage->set('payment_method_name', $name);
+ $this->sharedStorage->set('payment_method_code', $code);
+ }
+
+ /**
+ * @When I create a new PayPal payment method :name and save it as enabled
+ */
+ public function iCreateANewPayPalPaymentMethodAndSaveItAsEnabled(string $name): void
+ {
+ $code = $this->normalizeCode($name);
+
+ $this->createPage->open(['factory' => SyliusPayPalExtension::PAYPAL_FACTORY_NAME]);
+ $this->createPage->nameIt($name, 'en_US');
+ $this->createPage->specifyCode($code);
+ $this->createPage->create();
+
+ $this->sharedStorage->set('payment_method_name', $name);
+ $this->sharedStorage->set('payment_method_code', $code);
}
/**
@@ -68,19 +116,79 @@ public function iShouldBeNotifiedThatICannotOnboardMoreThanOnePayPalSeller(): vo
}
/**
- * @Then I should not be notified that I cannot onboard more than one PayPal seller
+ * @Then I should see a validation error that only one PayPal method can be enabled
*/
- public function iShouldNotBeNotifiedThatICannotOnboardMoreThanOnePayPalSeller(): void
+ public function iShouldSeeAValidationErrorThatOnlyOnePayPalMethodCanBeEnabled(): void
{
try {
- $this->notificationChecker->checkNotification(
- 'You cannot onboard more than one PayPal seller!',
- NotificationType::failure(),
- );
- } catch (NotificationExpectationMismatchException|ElementNotFoundException $exception) {
- return;
+ $message = $this->updatePage->getValidationMessage('enabled');
+ } catch (ElementNotFoundException) {
+ $message = $this->createPage->getValidationMessage('enabled');
}
- throw new \DomainException('Step should fail');
+ Assert::contains(
+ $message,
+ 'Only one PayPal payment method can be enabled at a time',
+ );
+ }
+
+ /**
+ * @Then the PayPal payment method :name should not exist
+ */
+ public function thePayPalPaymentMethodShouldNotExist(string $name): void
+ {
+ $this->indexPage->open();
+
+ Assert::false(
+ $this->indexPage->isSingleResourceOnPage(['name' => $name]),
+ sprintf('Payment method "%s" should not exist', $name),
+ );
+ }
+
+ /**
+ * @Then the PayPal payment method :name should still be disabled
+ */
+ public function thePayPalPaymentMethodShouldStillBeDisabled(string $name): void
+ {
+ $paymentMethod = $this->paymentMethodRepository->findOneBy(['code' => $this->normalizeCode($name)]);
+ Assert::notNull($paymentMethod, sprintf('Payment method "%s" not found', $name));
+
+ $this->updatePage->open(['id' => $paymentMethod->getId()]);
+
+ Assert::false(
+ $this->updatePage->isPaymentMethodEnabled(),
+ sprintf('Payment method "%s" should be disabled', $name),
+ );
+ }
+
+ /**
+ * @Then the new PayPal payment method should be in the list and enabled
+ */
+ public function theNewPayPalPaymentMethodShouldBeInTheListAndEnabled(): void
+ {
+ $name = $this->sharedStorage->get('payment_method_name');
+ $code = $this->sharedStorage->get('payment_method_code');
+
+ $this->indexPage->open();
+
+ Assert::true(
+ $this->indexPage->isSingleResourceOnPage(['name' => $name]),
+ sprintf('Payment method "%s" should exist in the list', $name),
+ );
+
+ $paymentMethod = $this->paymentMethodRepository->findOneBy(['code' => $code]);
+ Assert::notNull($paymentMethod, sprintf('Payment method "%s" not found', $name));
+
+ $this->updatePage->open(['id' => $paymentMethod->getId()]);
+
+ Assert::true(
+ $this->updatePage->isPaymentMethodEnabled(),
+ sprintf('Payment method "%s" should be enabled', $name),
+ );
+ }
+
+ private function normalizeCode(string $name): string
+ {
+ return 'PM_' . str_replace(' ', '_', strtoupper($name));
}
}
diff --git a/tests/Behat/Context/Setup/PaymentPayPalContext.php b/tests/Behat/Context/Setup/PaymentPayPalContext.php
index 1b72c584..9b4f2c0a 100644
--- a/tests/Behat/Context/Setup/PaymentPayPalContext.php
+++ b/tests/Behat/Context/Setup/PaymentPayPalContext.php
@@ -39,9 +39,17 @@ public function __construct(
* @Given /^the store allows paying with "([^"]*)" with "([^"]*)" factory name at position (\d+)$/
* @Given /^the store allows paying with "([^"]*)" with "([^"]*)" factory name$/
*/
- public function theStoreAllowsPayingWithWithFactoryNameAtPosition(string $paymentMethodName, string $gatewayFactory, ?int $position = 0)
+ public function theStoreAllowsPayingWithWithFactoryNameAtPosition(string $paymentMethodName, string $gatewayFactory, ?int $position = 0): void
{
- $this->createPaymentMethod($paymentMethodName, 'PM_' . $paymentMethodName, $gatewayFactory, 'Payment method', $position);
+ $this->createPaymentMethod($paymentMethodName, 'PM_' . str_replace(' ', '_', strtoupper($paymentMethodName)), $gatewayFactory, 'Payment method', $position, true);
+ }
+
+ /**
+ * @Given /^the store has a disabled "([^"]*)" payment method with "([^"]*)" gateway factory$/
+ */
+ public function theStoreHasADisabledPaymentMethodWithGatewayFactory(string $paymentMethodName, string $gatewayFactory): void
+ {
+ $this->createPaymentMethod($paymentMethodName, 'PM_' . str_replace(' ', '_', strtoupper($paymentMethodName)), $gatewayFactory, 'Payment method', 0, false);
}
/**
@@ -58,6 +66,7 @@ private function createPaymentMethod(
string $gatewayFactory,
string $description,
int $position,
+ bool $enabled = true,
): void {
$gatewayFactory = $this->findGatewayNameByTranslation($gatewayFactory, $this->gatewayFactories);
@@ -68,7 +77,7 @@ private function createPaymentMethod(
'description' => $description,
'gatewayName' => $gatewayFactory,
'gatewayFactory' => $gatewayFactory,
- 'enabled' => true,
+ 'enabled' => $enabled,
'channels' => ($this->sharedStorage->has('channel')) ? [$this->sharedStorage->get('channel')] : [],
]);
diff --git a/tests/Behat/Resources/services.xml b/tests/Behat/Resources/services.xml
index 036e71e5..8b0c98ac 100644
--- a/tests/Behat/Resources/services.xml
+++ b/tests/Behat/Resources/services.xml
@@ -24,6 +24,10 @@
+
+
+
+
diff --git a/tests/DependencyInjection/SyliusPayPalExtensionTest.php b/tests/DependencyInjection/SyliusPayPalExtensionTest.php
index 29ded92f..e2590075 100644
--- a/tests/DependencyInjection/SyliusPayPalExtensionTest.php
+++ b/tests/DependencyInjection/SyliusPayPalExtensionTest.php
@@ -85,54 +85,6 @@ public static function sandboxModeParametersProvider(): iterable
'sylius_paypal.facilitator_url',
'https://paypal.sylius.com',
];
-
- yield 'production mode sftp host' => [
- false,
- 'sylius.pay_pal.reports_sftp_host',
- 'reports.paypal.com',
- ];
-
- yield 'sandbox mode sftp host' => [
- true,
- 'sylius.pay_pal.reports_sftp_host',
- 'reports.sandbox.paypal.com',
- ];
-
- yield 'production mode aliased facilitator url' => [
- false,
- 'sylius.pay_pal.facilitator_url',
- 'https://prod.paypal.sylius.com',
- ];
-
- yield 'sandbox mode aliased facilitator url' => [
- true,
- 'sylius.pay_pal.facilitator_url',
- 'https://paypal.sylius.com',
- ];
-
- yield 'production mode aliased api base url' => [
- false,
- 'sylius.pay_pal.api_base_url',
- 'https://api.paypal.com/',
- ];
-
- yield 'sandbox mode aliased api base url' => [
- true,
- 'sylius.pay_pal.api_base_url',
- 'https://api.sandbox.paypal.com/',
- ];
-
- yield 'production mode aliased sftp host' => [
- false,
- 'sylius.pay_pal.reports_sftp_host',
- 'reports.paypal.com',
- ];
-
- yield 'sandbox mode aliased sftp host' => [
- true,
- 'sylius.pay_pal.reports_sftp_host',
- 'reports.sandbox.paypal.com',
- ];
}
/**
diff --git a/tests/Unit/Listener/PayPalPaymentMethodListenerTest.php b/tests/Unit/Listener/PayPalPaymentMethodListenerTest.php
index fb4e1936..b856a859 100644
--- a/tests/Unit/Listener/PayPalPaymentMethodListenerTest.php
+++ b/tests/Unit/Listener/PayPalPaymentMethodListenerTest.php
@@ -19,41 +19,23 @@
use Sylius\Bundle\ResourceBundle\Event\ResourceControllerEvent;
use Sylius\Component\Core\Model\PaymentMethodInterface;
use Sylius\Component\Payment\Model\GatewayConfigInterface;
-use Sylius\PayPalPlugin\Exception\PayPalPaymentMethodNotFoundException;
use Sylius\PayPalPlugin\Listener\PayPalPaymentMethodListener;
use Sylius\PayPalPlugin\Onboarding\Initiator\OnboardingInitiatorInterface;
-use Sylius\PayPalPlugin\Provider\PayPalPaymentMethodProviderInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
-use Symfony\Component\HttpFoundation\RequestStack;
-use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface;
-use Symfony\Component\HttpFoundation\Session\SessionInterface;
-use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
final class PayPalPaymentMethodListenerTest extends TestCase
{
private OnboardingInitiatorInterface&MockObject $onboardingInitiator;
- private UrlGeneratorInterface&MockObject $urlGenerator;
-
- private RequestStack&MockObject $requestStack;
-
- private PayPalPaymentMethodProviderInterface&MockObject $payPalPaymentMethodProvider;
-
private PayPalPaymentMethodListener $payPalPaymentMethodListener;
protected function setUp(): void
{
parent::setUp();
$this->onboardingInitiator = $this->createMock(OnboardingInitiatorInterface::class);
- $this->urlGenerator = $this->createMock(UrlGeneratorInterface::class);
- $this->requestStack = $this->createMock(RequestStack::class);
- $this->payPalPaymentMethodProvider = $this->createMock(PayPalPaymentMethodProviderInterface::class);
$this->payPalPaymentMethodListener = new PayPalPaymentMethodListener(
$this->onboardingInitiator,
- $this->urlGenerator,
- $this->requestStack,
- $this->payPalPaymentMethodProvider,
);
}
@@ -79,11 +61,6 @@ public function it_initiates_onboarding_when_creating_a_supported_payment_method
->method('getFactoryName')
->willReturn('sylius_paypal');
- $this->payPalPaymentMethodProvider
- ->expects(self::once())
- ->method('provide')
- ->willThrowException(new PayPalPaymentMethodNotFoundException());
-
$this->onboardingInitiator
->expects(self::once())
->method('supports')
@@ -122,24 +99,17 @@ public function it_throws_an_exception_if_subject_is_not_a_payment_method(): voi
}
#[Test]
- public function it_redirects_with_error_if_the_paypal_payment_method_already_exists(): void
+ public function it_does_nothing_when_creating_an_unsupported_payment_method(): void
{
$event = $this->createMock(ResourceControllerEvent::class);
$paymentMethod = $this->createMock(PaymentMethodInterface::class);
$gatewayConfig = $this->createMock(GatewayConfigInterface::class);
- $session = $this->createMock(SessionInterface::class);
- $flashBag = $this->createMock(FlashBagInterface::class);
$event
->expects(self::once())
->method('getSubject')
->willReturn($paymentMethod);
- $this->payPalPaymentMethodProvider
- ->expects(self::once())
- ->method('provide')
- ->willReturn($paymentMethod);
-
$paymentMethod
->expects(self::once())
->method('getGatewayConfig')
@@ -150,44 +120,21 @@ public function it_redirects_with_error_if_the_paypal_payment_method_already_exi
->method('getFactoryName')
->willReturn('sylius_paypal');
- $flashBag
- ->expects(self::once())
- ->method('add')
- ->with('error', 'sylius_paypal.more_than_one_seller_not_allowed');
-
- $session
- ->expects(self::once())
- ->method('getBag')
- ->with('flashes')
- ->willReturn($flashBag);
-
- $this->requestStack
- ->expects(self::once())
- ->method('getSession')
- ->willReturn($session);
-
- $this->urlGenerator
+ $this->onboardingInitiator
->expects(self::once())
- ->method('generate')
- ->with('sylius_admin_payment_method_index')
- ->willReturn('http://redirect-url.com');
+ ->method('supports')
+ ->with($paymentMethod)
+ ->willReturn(false);
$event
- ->expects(self::once())
- ->method('setResponse')
- ->with($this->callback(function (RedirectResponse $response): bool {
- return $response->getTargetUrl() === 'http://redirect-url.com';
- }));
-
- $this->onboardingInitiator
->expects($this->never())
- ->method('initiate');
+ ->method('setResponse');
$this->payPalPaymentMethodListener->initializeCreate($event);
}
#[Test]
- public function it_does_nothing_when_creating_an_unsupported_payment_method(): void
+ public function it_does_nothing_if_payment_method_is_not_paypal(): void
{
$event = $this->createMock(ResourceControllerEvent::class);
$paymentMethod = $this->createMock(PaymentMethodInterface::class);
@@ -206,18 +153,7 @@ public function it_does_nothing_when_creating_an_unsupported_payment_method(): v
$gatewayConfig
->expects(self::once())
->method('getFactoryName')
- ->willReturn('sylius_paypal');
-
- $this->payPalPaymentMethodProvider
- ->expects(self::once())
- ->method('provide')
- ->willThrowException(new PayPalPaymentMethodNotFoundException());
-
- $this->onboardingInitiator
- ->expects(self::once())
- ->method('supports')
- ->with($paymentMethod)
- ->willReturn(false);
+ ->willReturn('offline');
$event
->expects($this->never())
@@ -227,8 +163,13 @@ public function it_does_nothing_when_creating_an_unsupported_payment_method(): v
}
#[Test]
- public function it_does_nothing_if_payment_method_is_not_paypal(): void
+ public function it_does_nothing_when_in_sandbox_mode(): void
{
+ $sandboxListener = new PayPalPaymentMethodListener(
+ $this->onboardingInitiator,
+ true, // isSandbox = true
+ );
+
$event = $this->createMock(ResourceControllerEvent::class);
$paymentMethod = $this->createMock(PaymentMethodInterface::class);
$gatewayConfig = $this->createMock(GatewayConfigInterface::class);
@@ -246,12 +187,20 @@ public function it_does_nothing_if_payment_method_is_not_paypal(): void
$gatewayConfig
->expects(self::once())
->method('getFactoryName')
- ->willReturn('offline');
+ ->willReturn('sylius_paypal');
+
+ $this->onboardingInitiator
+ ->expects($this->never())
+ ->method('supports');
+
+ $this->onboardingInitiator
+ ->expects($this->never())
+ ->method('initiate');
$event
->expects($this->never())
->method('setResponse');
- $this->payPalPaymentMethodListener->initializeCreate($event);
+ $sandboxListener->initializeCreate($event);
}
}
diff --git a/tests/Unit/Provider/PayPalPaymentMethodProviderTest.php b/tests/Unit/Provider/PayPalPaymentMethodProviderTest.php
new file mode 100644
index 00000000..a53c51e2
--- /dev/null
+++ b/tests/Unit/Provider/PayPalPaymentMethodProviderTest.php
@@ -0,0 +1,185 @@
+&MockObject */
+ private PaymentMethodRepositoryInterface&MockObject $paymentMethodRepository;
+
+ private PayPalPaymentMethodProvider $payPalPaymentMethodProvider;
+
+ protected function setUp(): void
+ {
+ parent::setUp();
+
+ $this->paymentMethodRepository = $this->createMock(PaymentMethodRepositoryInterface::class);
+ $this->payPalPaymentMethodProvider = new PayPalPaymentMethodProvider($this->paymentMethodRepository);
+ }
+
+ #[Test]
+ public function it_provides_pay_pal_payment_method(): void
+ {
+ $payPalPaymentMethod = $this->createMock(PaymentMethodInterface::class);
+ $payPalGatewayConfig = $this->createMock(GatewayConfigInterface::class);
+
+ $this->paymentMethodRepository
+ ->method('findBy')
+ ->with(['enabled' => true])
+ ->willReturn([$payPalPaymentMethod])
+ ;
+
+ $payPalPaymentMethod
+ ->method('getGatewayConfig')
+ ->willReturn($payPalGatewayConfig)
+ ;
+
+ $payPalGatewayConfig
+ ->method('getFactoryName')
+ ->willReturn('sylius_paypal')
+ ;
+
+ $result = $this->payPalPaymentMethodProvider->provide();
+
+ self::assertSame($payPalPaymentMethod, $result);
+ }
+
+ #[Test]
+ public function it_provides_first_pay_pal_payment_method_when_multiple_exist(): void
+ {
+ $firstPayPalPaymentMethod = $this->createMock(PaymentMethodInterface::class);
+ $secondPayPalPaymentMethod = $this->createMock(PaymentMethodInterface::class);
+ $firstPayPalGatewayConfig = $this->createMock(GatewayConfigInterface::class);
+ $secondPayPalGatewayConfig = $this->createMock(GatewayConfigInterface::class);
+
+ $this->paymentMethodRepository
+ ->method('findBy')
+ ->with(['enabled' => true])
+ ->willReturn([$firstPayPalPaymentMethod, $secondPayPalPaymentMethod])
+ ;
+
+ $firstPayPalPaymentMethod
+ ->method('getGatewayConfig')
+ ->willReturn($firstPayPalGatewayConfig)
+ ;
+
+ $firstPayPalGatewayConfig
+ ->method('getFactoryName')
+ ->willReturn('sylius_paypal')
+ ;
+
+ $secondPayPalPaymentMethod
+ ->method('getGatewayConfig')
+ ->willReturn($secondPayPalGatewayConfig)
+ ;
+
+ $secondPayPalGatewayConfig
+ ->method('getFactoryName')
+ ->willReturn('sylius_paypal')
+ ;
+
+ $result = $this->payPalPaymentMethodProvider->provide();
+
+ self::assertSame($firstPayPalPaymentMethod, $result);
+ }
+
+ #[Test]
+ public function it_provides_pay_pal_payment_method_when_other_payment_methods_exist(): void
+ {
+ $otherPaymentMethod = $this->createMock(PaymentMethodInterface::class);
+ $payPalPaymentMethod = $this->createMock(PaymentMethodInterface::class);
+ $otherGatewayConfig = $this->createMock(GatewayConfigInterface::class);
+ $payPalGatewayConfig = $this->createMock(GatewayConfigInterface::class);
+
+ $this->paymentMethodRepository
+ ->method('findBy')
+ ->with(['enabled' => true])
+ ->willReturn([$otherPaymentMethod, $payPalPaymentMethod])
+ ;
+
+ $otherPaymentMethod
+ ->method('getGatewayConfig')
+ ->willReturn($otherGatewayConfig)
+ ;
+
+ $otherGatewayConfig
+ ->method('getFactoryName')
+ ->willReturn('other')
+ ;
+
+ $payPalPaymentMethod
+ ->method('getGatewayConfig')
+ ->willReturn($payPalGatewayConfig)
+ ;
+
+ $payPalGatewayConfig
+ ->method('getFactoryName')
+ ->willReturn('sylius_paypal')
+ ;
+
+ $result = $this->payPalPaymentMethodProvider->provide();
+
+ self::assertSame($payPalPaymentMethod, $result);
+ }
+
+ #[Test]
+ public function it_throws_exception_when_no_enabled_payment_methods_exist(): void
+ {
+ $this->paymentMethodRepository
+ ->method('findBy')
+ ->with(['enabled' => true])
+ ->willReturn([])
+ ;
+
+ $this->expectException(PayPalPaymentMethodNotFoundException::class);
+
+ $this->payPalPaymentMethodProvider->provide();
+ }
+
+ #[Test]
+ public function it_throws_exception_when_no_pay_pal_payment_method_exists(): void
+ {
+ $otherPaymentMethod = $this->createMock(PaymentMethodInterface::class);
+ $otherGatewayConfig = $this->createMock(GatewayConfigInterface::class);
+
+ $this->paymentMethodRepository
+ ->method('findBy')
+ ->with(['enabled' => true])
+ ->willReturn([$otherPaymentMethod])
+ ;
+
+ $otherPaymentMethod
+ ->method('getGatewayConfig')
+ ->willReturn($otherGatewayConfig)
+ ;
+
+ $otherGatewayConfig
+ ->method('getFactoryName')
+ ->willReturn('other')
+ ;
+
+ $this->expectException(PayPalPaymentMethodNotFoundException::class);
+
+ $this->payPalPaymentMethodProvider->provide();
+ }
+}
diff --git a/tests/Unit/Validator/Constraints/OnlyOneEnabledPayPalPaymentMethodValidatorTest.php b/tests/Unit/Validator/Constraints/OnlyOneEnabledPayPalPaymentMethodValidatorTest.php
new file mode 100644
index 00000000..e7b73732
--- /dev/null
+++ b/tests/Unit/Validator/Constraints/OnlyOneEnabledPayPalPaymentMethodValidatorTest.php
@@ -0,0 +1,237 @@
+paymentMethodRepository = $this->createMock(PaymentMethodRepositoryInterface::class);
+ $this->context = $this->createMock(ExecutionContextInterface::class);
+
+ $this->validator = new OnlyOneEnabledPayPalPaymentMethodValidator(
+ $this->paymentMethodRepository,
+ );
+ $this->validator->initialize($this->context);
+ }
+
+ #[Test]
+ public function it_throws_exception_for_wrong_constraint(): void
+ {
+ $wrongConstraint = $this->createMock(Constraint::class);
+ $paymentMethod = $this->createMock(PaymentMethodInterface::class);
+
+ $this->expectException(UnexpectedTypeException::class);
+
+ $this->validator->validate($paymentMethod, $wrongConstraint);
+ }
+
+ #[Test]
+ public function it_throws_exception_for_wrong_value(): void
+ {
+ $constraint = new OnlyOneEnabledPayPalPaymentMethod();
+
+ $this->expectException(UnexpectedValueException::class);
+
+ $this->validator->validate(new \stdClass(), $constraint);
+ }
+
+ #[Test]
+ public function it_does_nothing_for_non_paypal_methods(): void
+ {
+ $constraint = new OnlyOneEnabledPayPalPaymentMethod();
+ $paymentMethod = $this->createMock(PaymentMethodInterface::class);
+ $gatewayConfig = $this->createMock(GatewayConfigInterface::class);
+
+ $paymentMethod
+ ->expects(self::once())
+ ->method('getGatewayConfig')
+ ->willReturn($gatewayConfig)
+ ;
+
+ $gatewayConfig
+ ->expects(self::once())
+ ->method('getFactoryName')
+ ->willReturn('offline')
+ ;
+
+ $this->context
+ ->expects(self::never())
+ ->method('buildViolation')
+ ;
+
+ $this->validator->validate($paymentMethod, $constraint);
+ }
+
+ #[Test]
+ public function it_does_nothing_when_paypal_method_is_disabled(): void
+ {
+ $constraint = new OnlyOneEnabledPayPalPaymentMethod();
+ $paymentMethod = $this->createMock(PaymentMethodInterface::class);
+ $gatewayConfig = $this->createMock(GatewayConfigInterface::class);
+
+ $paymentMethod
+ ->expects(self::once())
+ ->method('getGatewayConfig')
+ ->willReturn($gatewayConfig)
+ ;
+
+ $gatewayConfig
+ ->expects(self::once())
+ ->method('getFactoryName')
+ ->willReturn('sylius_paypal')
+ ;
+
+ $paymentMethod
+ ->expects(self::once())
+ ->method('isEnabled')
+ ->willReturn(false)
+ ;
+
+ $this->context
+ ->expects(self::never())
+ ->method('buildViolation')
+ ;
+
+ $this->validator->validate($paymentMethod, $constraint);
+ }
+
+ #[Test]
+ public function it_passes_when_no_other_paypal_method_is_enabled(): void
+ {
+ $constraint = new OnlyOneEnabledPayPalPaymentMethod();
+ $paymentMethod = $this->createEnabledPayPalPaymentMethod(1);
+ $offlinePaymentMethod = $this->createOfflinePaymentMethod(2);
+
+ $this->paymentMethodRepository
+ ->expects(self::once())
+ ->method('findBy')
+ ->with(['enabled' => true])
+ ->willReturn([$paymentMethod, $offlinePaymentMethod])
+ ;
+
+ $this->context
+ ->expects(self::never())
+ ->method('buildViolation')
+ ;
+
+ $this->validator->validate($paymentMethod, $constraint);
+ }
+
+ #[Test]
+ public function it_adds_violation_when_another_paypal_method_is_enabled(): void
+ {
+ $constraint = new OnlyOneEnabledPayPalPaymentMethod();
+ $paymentMethod = $this->createEnabledPayPalPaymentMethod(1);
+ $otherPayPalMethod = $this->createEnabledPayPalPaymentMethod(2);
+
+ $this->paymentMethodRepository
+ ->expects(self::once())
+ ->method('findBy')
+ ->with(['enabled' => true])
+ ->willReturn([$paymentMethod, $otherPayPalMethod])
+ ;
+
+ $violationBuilder = $this->createMock(ConstraintViolationBuilderInterface::class);
+
+ $this->context
+ ->expects(self::once())
+ ->method('buildViolation')
+ ->with($constraint->message)
+ ->willReturn($violationBuilder)
+ ;
+
+ $violationBuilder
+ ->expects(self::once())
+ ->method('atPath')
+ ->with('enabled')
+ ->willReturn($violationBuilder)
+ ;
+
+ $violationBuilder
+ ->expects(self::once())
+ ->method('addViolation')
+ ;
+
+ $this->validator->validate($paymentMethod, $constraint);
+ }
+
+ #[Test]
+ public function it_skips_the_same_payment_method_when_checking(): void
+ {
+ $constraint = new OnlyOneEnabledPayPalPaymentMethod();
+ $paymentMethod = $this->createEnabledPayPalPaymentMethod(1, true);
+
+ $this->paymentMethodRepository
+ ->expects(self::once())
+ ->method('findBy')
+ ->with(['enabled' => true])
+ ->willReturn([$paymentMethod])
+ ;
+
+ $this->context
+ ->expects(self::never())
+ ->method('buildViolation');
+
+ $this->validator->validate($paymentMethod, $constraint);
+ }
+
+ private function createEnabledPayPalPaymentMethod(int $id): PaymentMethodInterface&MockObject
+ {
+ $paymentMethod = $this->createMock(PaymentMethodInterface::class);
+ $gatewayConfig = $this->createMock(GatewayConfigInterface::class);
+
+ $paymentMethod->method('getId')->willReturn($id);
+ $paymentMethod->method('isEnabled')->willReturn(true);
+ $paymentMethod->method('getGatewayConfig')->willReturn($gatewayConfig);
+ $gatewayConfig->method('getFactoryName')->willReturn('sylius_paypal');
+
+ return $paymentMethod;
+ }
+
+ private function createOfflinePaymentMethod(int $id): PaymentMethodInterface&MockObject
+ {
+ $paymentMethod = $this->createMock(PaymentMethodInterface::class);
+ $gatewayConfig = $this->createMock(GatewayConfigInterface::class);
+
+ $paymentMethod->method('getId')->willReturn($id);
+ $paymentMethod->method('isEnabled')->willReturn(true);
+ $paymentMethod->method('getGatewayConfig')->willReturn($gatewayConfig);
+ $gatewayConfig->method('getFactoryName')->willReturn('offline');
+
+ return $paymentMethod;
+ }
+}
diff --git a/translations/validators.de.yml b/translations/validators.de.yml
new file mode 100644
index 00000000..f49c1903
--- /dev/null
+++ b/translations/validators.de.yml
@@ -0,0 +1,2 @@
+sylius_paypal:
+ only_one_paypal_enabled: 'Es kann nur eine PayPal-Zahlungsmethode gleichzeitig aktiviert sein. Bitte deaktivieren Sie zuerst die andere PayPal-Methode.'
diff --git a/translations/validators.en.yml b/translations/validators.en.yml
new file mode 100644
index 00000000..cc1948c6
--- /dev/null
+++ b/translations/validators.en.yml
@@ -0,0 +1,2 @@
+sylius_paypal:
+ only_one_paypal_enabled: 'Only one PayPal payment method can be enabled at a time. Please disable the other PayPal method first.'
diff --git a/translations/validators.fr.yml b/translations/validators.fr.yml
new file mode 100644
index 00000000..8630c8af
--- /dev/null
+++ b/translations/validators.fr.yml
@@ -0,0 +1,2 @@
+sylius_paypal:
+ only_one_paypal_enabled: "Une seule methode de paiement PayPal peut etre activee a la fois. Veuillez d'abord desactiver l'autre methode PayPal."
diff --git a/translations/validators.nl.yml b/translations/validators.nl.yml
new file mode 100644
index 00000000..3b564420
--- /dev/null
+++ b/translations/validators.nl.yml
@@ -0,0 +1,2 @@
+sylius_paypal:
+ only_one_paypal_enabled: 'Er kan slechts een PayPal-betaalmethode tegelijk actief zijn. Schakel eerst de andere PayPal-methode uit.'