Skip to content

Commit c1c5d45

Browse files
authored
bug #136 Allow only one seller to be onboarded (Zales0123)
This PR was merged into the 1.0-dev branch. Discussion ---------- Fixes #126 and #115 Even though ultimately we would like to allow one PayPal payment method for each channel, right now multiple PayPal PMs can lead to some unexpected behaviour (as in linked issues). I propose to allow only one seller until the stable release and fix the problems in the later plugin versions 💃 Commits ------- 0e3e1d3 Redirect with error during onboarding if there is alredy a PayPal payment method d78b318 Hide "Enable PayPal" button
2 parents ead716b + d78b318 commit c1c5d45

File tree

12 files changed

+206
-17
lines changed

12 files changed

+206
-17
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
@managing_payment_methods
2+
Feature: Trying to onboard more than one PayPal seller
3+
In order to handle PayPal integration properly
4+
As an Administrator
5+
I want to be prevented from onboarding more than one PayPal seller
6+
7+
Background:
8+
Given the store operates on a single channel in "United States"
9+
And the store allows paying with "PayPal" with "PayPal" factory name
10+
And I am logged in as an administrator
11+
12+
@ui
13+
Scenario: Trying to onboard second PayPal seller
14+
When I try to create a new payment method with "PayPal" gateway factory
15+
Then I should be notified that I cannot onboard more than one PayPal seller

spec/Listener/PayPalPaymentMethodListenerSpec.php

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,37 @@
88
use Prophecy\Argument;
99
use Sylius\Bundle\ResourceBundle\Event\ResourceControllerEvent;
1010
use Sylius\Component\Core\Model\PaymentMethodInterface;
11+
use Sylius\PayPalPlugin\Exception\PayPalPaymentMethodNotFoundException;
1112
use Sylius\PayPalPlugin\Onboarding\Initiator\OnboardingInitiatorInterface;
13+
use Sylius\PayPalPlugin\Provider\PayPalPaymentMethodProviderInterface;
1214
use Symfony\Component\HttpFoundation\RedirectResponse;
15+
use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface;
16+
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
1317

1418
final class PayPalPaymentMethodListenerSpec extends ObjectBehavior
1519
{
16-
function let(OnboardingInitiatorInterface $onboardingInitiator): void
17-
{
18-
$this->beConstructedWith($onboardingInitiator);
20+
function let(
21+
OnboardingInitiatorInterface $onboardingInitiator,
22+
UrlGeneratorInterface $urlGenerator,
23+
FlashBagInterface $flashBag,
24+
PayPalPaymentMethodProviderInterface $payPalPaymentMethodProvider
25+
): void {
26+
$this->beConstructedWith(
27+
$onboardingInitiator,
28+
$urlGenerator,
29+
$flashBag,
30+
$payPalPaymentMethodProvider
31+
);
1932
}
2033

2134
function it_initiates_onboarding_when_creating_a_supported_payment_method(
2235
OnboardingInitiatorInterface $onboardingInitiator,
36+
PayPalPaymentMethodProviderInterface $payPalPaymentMethodProvider,
2337
ResourceControllerEvent $event,
2438
PaymentMethodInterface $paymentMethod
2539
): void {
2640
$event->getSubject()->willReturn($paymentMethod);
41+
$payPalPaymentMethodProvider->provide()->willThrow(PayPalPaymentMethodNotFoundException::class);
2742

2843
$onboardingInitiator->supports($paymentMethod)->willReturn(true);
2944

@@ -34,6 +49,8 @@ function it_initiates_onboarding_when_creating_a_supported_payment_method(
3449
$event->setResponse(Argument::that(static function ($argument): bool {
3550
return $argument instanceof RedirectResponse && $argument->getTargetUrl() === 'https://example.com/onboarding-url';
3651
}))->shouldHaveBeenCalled();
52+
53+
$this->initializeCreate($event);
3754
}
3855

3956
function it_throws_an_exception_if_subject_is_not_a_payment_method(ResourceControllerEvent $event): void
@@ -43,17 +60,42 @@ function it_throws_an_exception_if_subject_is_not_a_payment_method(ResourceContr
4360
$this->shouldThrow(\InvalidArgumentException::class)->during('initializeCreate', [$event]);
4461
}
4562

46-
function it_does_nothing_when_creating_an_unsupported_payment_method(
63+
function it_redirects_with_error_if_the_pay_pal_payment_method_already_exists(
64+
PayPalPaymentMethodProviderInterface $payPalPaymentMethodProvider,
4765
OnboardingInitiatorInterface $onboardingInitiator,
66+
UrlGeneratorInterface $urlGenerator,
67+
FlashBagInterface $flashBag,
4868
ResourceControllerEvent $event,
4969
PaymentMethodInterface $paymentMethod
5070
): void {
5171
$event->getSubject()->willReturn($paymentMethod);
72+
$payPalPaymentMethodProvider->provide()->willReturn($paymentMethod);
5273

53-
$onboardingInitiator->supports($paymentMethod)->willReturn(false);
74+
$flashBag->add('error', 'sylius.pay_pal.more_than_one_seller_not_allowed')->shouldBeCalled();
75+
76+
$urlGenerator->generate('sylius_admin_payment_method_index')->willReturn('http://redirect-url.com');
77+
$event->setResponse(Argument::that(function (RedirectResponse $response): bool {
78+
return $response->getTargetUrl() === 'http://redirect-url.com';
79+
}))->shouldBeCalled();
80+
81+
$onboardingInitiator->initiate(Argument::any())->shouldNotBeCalled();
5482

5583
$this->initializeCreate($event);
84+
}
85+
86+
function it_does_nothing_when_creating_an_unsupported_payment_method(
87+
OnboardingInitiatorInterface $onboardingInitiator,
88+
PayPalPaymentMethodProviderInterface $payPalPaymentMethodProvider,
89+
ResourceControllerEvent $event,
90+
PaymentMethodInterface $paymentMethod
91+
): void {
92+
$event->getSubject()->willReturn($paymentMethod);
93+
$payPalPaymentMethodProvider->provide()->willThrow(PayPalPaymentMethodNotFoundException::class);
94+
95+
$onboardingInitiator->supports($paymentMethod)->willReturn(false);
5696

5797
$event->setResponse(Argument::any())->shouldNotHaveBeenCalled();
98+
99+
$this->initializeCreate($event);
58100
}
59101
}

src/Form/Extension/PaymentMethodTypeExtension.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
3434
});
3535
}
3636

37-
public function getExtendedTypes(): iterable
37+
public static function getExtendedTypes(): iterable
3838
{
3939
return [PaymentMethodType::class];
4040
}

src/Listener/PayPalPaymentMethodListener.php

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,31 +6,69 @@
66

77
use Sylius\Bundle\ResourceBundle\Event\ResourceControllerEvent;
88
use Sylius\Component\Core\Model\PaymentMethodInterface;
9+
use Sylius\PayPalPlugin\Exception\PayPalPaymentMethodNotFoundException;
910
use Sylius\PayPalPlugin\Onboarding\Initiator\OnboardingInitiatorInterface;
11+
use Sylius\PayPalPlugin\Provider\PayPalPaymentMethodProviderInterface;
1012
use Symfony\Component\HttpFoundation\RedirectResponse;
13+
use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface;
14+
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
1115
use Webmozart\Assert\Assert;
1216

1317
final class PayPalPaymentMethodListener
1418
{
1519
/** @var OnboardingInitiatorInterface */
1620
private $onboardingInitiator;
1721

18-
public function __construct(OnboardingInitiatorInterface $onboardingInitiator)
19-
{
22+
/** @var UrlGeneratorInterface */
23+
private $urlGenerator;
24+
25+
/** @var FlashBagInterface */
26+
private $flashBag;
27+
28+
/** @var PayPalPaymentMethodProviderInterface */
29+
private $payPalPaymentMethodProvider;
30+
31+
public function __construct(
32+
OnboardingInitiatorInterface $onboardingInitiator,
33+
UrlGeneratorInterface $urlGenerator,
34+
FlashBagInterface $flashBag,
35+
PayPalPaymentMethodProviderInterface $payPalPaymentMethodProvider
36+
) {
2037
$this->onboardingInitiator = $onboardingInitiator;
38+
$this->urlGenerator = $urlGenerator;
39+
$this->flashBag = $flashBag;
40+
$this->payPalPaymentMethodProvider = $payPalPaymentMethodProvider;
2141
}
2242

2343
public function initializeCreate(ResourceControllerEvent $event): void
2444
{
2545
$paymentMethod = $event->getSubject();
26-
2746
/** @var PaymentMethodInterface $paymentMethod */
2847
Assert::isInstanceOf($paymentMethod, PaymentMethodInterface::class);
2948

49+
if ($this->isTherePayPalPaymentMethod()) {
50+
$this->flashBag->add('error', 'sylius.pay_pal.more_than_one_seller_not_allowed');
51+
52+
$event->setResponse(new RedirectResponse($this->urlGenerator->generate('sylius_admin_payment_method_index')));
53+
54+
return;
55+
}
56+
3057
if (!$this->onboardingInitiator->supports($paymentMethod)) {
3158
return;
3259
}
3360

3461
$event->setResponse(new RedirectResponse($this->onboardingInitiator->initiate($paymentMethod)));
3562
}
63+
64+
private function isTherePayPalPaymentMethod(): bool
65+
{
66+
try {
67+
$this->payPalPaymentMethodProvider->provide();
68+
} catch (PayPalPaymentMethodNotFoundException $exception) {
69+
return false;
70+
}
71+
72+
return true;
73+
}
3674
}

src/Provider/PayPalPaymentMethodProvider.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@ public function __construct(PaymentMethodRepositoryInterface $paymentMethodRepos
2121

2222
public function provide(): PaymentMethodInterface
2323
{
24-
$payments = $this->paymentMethodRepository->findAll();
24+
$paymentMethods = $this->paymentMethodRepository->findAll();
2525

26-
/** @var PaymentMethodInterface $payment */
27-
foreach ($payments as $payment) {
26+
/** @var PaymentMethodInterface $paymentMethod */
27+
foreach ($paymentMethods as $paymentMethod) {
2828
/** @var GatewayConfigInterface $gatewayConfig */
29-
$gatewayConfig = $payment->getGatewayConfig();
29+
$gatewayConfig = $paymentMethod->getGatewayConfig();
3030

3131
if ($gatewayConfig->getFactoryName() === 'sylius.pay_pal') {
32-
return $payment;
32+
return $paymentMethod;
3333
}
3434
}
3535

src/Resources/config/services.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828

2929
<service id="Sylius\PayPalPlugin\Listener\PayPalPaymentMethodListener">
3030
<argument type="service" id="Sylius\PayPalPlugin\Onboarding\Initiator\OnboardingInitiatorInterface" />
31+
<argument type="service" id="router" />
32+
<argument type="service" id="session.flash_bag" />
33+
<argument type="service" id="Sylius\PayPalPlugin\Provider\PayPalPaymentMethodProviderInterface" />
3134
<tag name="kernel.event_listener" event="sylius.payment_method.initialize_create" method="initializeCreate" />
3235
</service>
3336

@@ -248,5 +251,9 @@
248251
<argument type="service" id="router" />
249252
<argument type="service" id="Sylius\PayPalPlugin\Api\WebhookApiInterface" />
250253
</service>
254+
255+
<service id="Sylius\PayPalPlugin\Twig\PayPalExtension">
256+
<tag name="twig.extension" />
257+
</service>
251258
</services>
252259
</container>

src/Resources/translations/flashes.en.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ sylius:
66
payment_not_enabled: 'PayPal payment could not be enabled. Try again later.'
77
something_went_wrong: 'Something went wrong. Please, try again.'
88
webhook_url_not_valid: 'PayPal could not verify provided webhook url. Make sure it is https and try again'
9+
more_than_one_seller_not_allowed: 'You cannot onboard more than one PayPal seller!'

src/Resources/views/Grid/enablePayPal.html.twig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
{% set path = path('sylius_admin_payment_method_create', {'factory': 'sylius.pay_pal'}) %}
44

5+
{% if not sylius_is_pay_pal_enabled(grid.data.currentPageResults) %}
56
<a class="ui labeled buttons icon top floating button yellow" href="{{ path }}" data-confirm-pay-pal-consent>
67
<i class="paypal icon"></i>
78
{{ 'sylius.pay_pal.enable_paypal'|trans }}
89
</a>
10+
{% endif %}

src/Twig/PayPalExtension.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sylius\PayPalPlugin\Twig;
6+
7+
use Payum\Core\Model\GatewayConfigInterface;
8+
use Sylius\Component\Core\Model\PaymentMethodInterface;
9+
use Twig\Extension\AbstractExtension;
10+
use Twig\TwigFunction;
11+
12+
final class PayPalExtension extends AbstractExtension
13+
{
14+
public function getFunctions(): array
15+
{
16+
return [
17+
new TwigFunction('sylius_is_pay_pal_enabled', [$this, 'isPayPalEnabled']),
18+
];
19+
}
20+
21+
public function isPayPalEnabled(iterable $paymentMethods): bool
22+
{
23+
/** @var PaymentMethodInterface $paymentMethod */
24+
foreach ($paymentMethods as $paymentMethod) {
25+
/** @var GatewayConfigInterface $gatewayConfig */
26+
$gatewayConfig = $paymentMethod->getGatewayConfig();
27+
if ($gatewayConfig->getFactoryName() === 'sylius.pay_pal') {
28+
return true;
29+
}
30+
}
31+
32+
return false;
33+
}
34+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
imports:
2-
- { resource: "../../Behat/Resources/services.xml" }
32
- { resource: "../../../vendor/sylius/sylius/src/Sylius/Behat/Resources/config/services.xml" }
3+
- { resource: "../../Behat/Resources/services.xml" }

0 commit comments

Comments
 (0)