diff --git a/UPGRADE.md b/UPGRADE.md index 76d821d5..4aa87617 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -1,3 +1,15 @@ +### UPGRADE FROM 1.3.0 to 1.3.1 + +1. `sylius_paypal_plugin_pay_with_paypal_form` route now operates on both payment ID and order token. URl then changed from + `/pay-with-paypal/{id}` to `/pay-with-paypal/{orderToken}/{paymentId}`. If you use this route anywhere in your application, you + need to change the URL attributes + +### UPGRADE FROM 1.2.3 to 1.2.4 + +1. `sylius_paypal_plugin_pay_with_paypal_form` route now operates on both payment ID and order token. URl then changed from + `/pay-with-paypal/{id}` to `/pay-with-paypal/{orderToken}/{paymentId}`. If you use this route anywhere in your application, you + need to change the URL attributes + ### UPGRADE FROM 1.0.X TO 1.1.0 1. Upgrade your application to [Sylius 1.8](https://github.com/Sylius/Sylius/blob/master/UPGRADE-1.8.md). diff --git a/phpstan.neon b/phpstan.neon index 46e93df9..71b3ff4c 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -14,3 +14,6 @@ parameters: # Test dependencies - 'tests/Application/app/**.php' - 'tests/Application/src/**.php' + + ignoreErrors: + - '/Call to an undefined method Sylius\\Component\\Core\\Repository\\PaymentRepositoryInterface\:\:createQueryBuilder\(\)\./' diff --git a/psalm.xml b/psalm.xml index f38d221b..b117a394 100644 --- a/psalm.xml +++ b/psalm.xml @@ -12,6 +12,7 @@ + diff --git a/spec/Payum/Action/ResolveNextRouteActionSpec.php b/spec/Payum/Action/ResolveNextRouteActionSpec.php index a5a2362d..99a8ec61 100644 --- a/spec/Payum/Action/ResolveNextRouteActionSpec.php +++ b/spec/Payum/Action/ResolveNextRouteActionSpec.php @@ -17,6 +17,7 @@ final class ResolveNextRouteActionSpec extends ObjectBehavior function it_executes_resolve_next_route_request_with_processing_payment( ResolveNextRoute $request, PaymentInterface $payment, + OrderInterface $order, PaymentMethodInterface $paymentMethod, GatewayConfigInterface $gatewayConfig ): void { @@ -28,8 +29,11 @@ function it_executes_resolve_next_route_request_with_processing_payment( $paymentMethod->getGatewayConfig()->willReturn($gatewayConfig); $gatewayConfig->getFactoryName()->willReturn('sylius.pay_pal'); + $payment->getOrder()->willReturn($order); + $order->getTokenValue()->willReturn('123!@#asd'); + $request->setRouteName('sylius_paypal_plugin_pay_with_paypal_form')->shouldBeCalled(); - $request->setRouteParameters(['id' => 12])->shouldBeCalled(); + $request->setRouteParameters(['orderToken' => '123!@#asd', 'paymentId' => 12])->shouldBeCalled(); $this->execute($request); } @@ -37,10 +41,12 @@ function it_executes_resolve_next_route_request_with_processing_payment( function it_executes_resolve_next_route_request_with_completed_payment( ResolveNextRoute $request, PaymentInterface $payment, + OrderInterface $order, PaymentMethodInterface $paymentMethod, GatewayConfigInterface $gatewayConfig ): void { $request->getFirstModel()->willReturn($payment); + $payment->getOrder()->willReturn($order); $payment->getState()->willReturn(PaymentInterface::STATE_COMPLETED); $payment->getMethod()->willReturn($paymentMethod); diff --git a/src/Controller/PayWithPayPalFormAction.php b/src/Controller/PayWithPayPalFormAction.php index d1c13888..4bac1a14 100644 --- a/src/Controller/PayWithPayPalFormAction.php +++ b/src/Controller/PayWithPayPalFormAction.php @@ -44,8 +44,11 @@ public function __construct( public function __invoke(Request $request): Response { + $paymentId = (string) $request->attributes->get('paymentId'); + $orderToken = (string) $request->attributes->get('orderToken'); + /** @var PaymentInterface $payment */ - $payment = $this->paymentRepository->find($request->attributes->get('id')); + $payment = $this->findOneByPaymentIdOrderToken($paymentId, $orderToken); /** @var PaymentMethodInterface $paymentMethod */ $paymentMethod = $payment->getMethod(); @@ -74,4 +77,22 @@ public function __invoke(Request $request): Response 'partner_attribution_id' => $partnerAttributionId, ])); } + + /** + * Need to be used due to support for Sylius 1.8. + * After dropping it, we can switch to Sylius\Component\Core\Repository\PaymentRepositoryInterface::findOneByOrderToken + */ + private function findOneByPaymentIdOrderToken(string $paymentId, string $orderToken): ?PaymentInterface + { + return $this->paymentRepository + ->createQueryBuilder('p') + ->innerJoin('p.order', 'o') + ->andWhere('p.id = :paymentId') + ->andWhere('o.tokenValue = :orderToken') + ->setParameter('paymentId', $paymentId) + ->setParameter('orderToken', $orderToken) + ->getQuery() + ->getOneOrNullResult() + ; + } } diff --git a/src/Payum/Action/ResolveNextRouteAction.php b/src/Payum/Action/ResolveNextRouteAction.php index a7ed2c72..30f6d80a 100644 --- a/src/Payum/Action/ResolveNextRouteAction.php +++ b/src/Payum/Action/ResolveNextRouteAction.php @@ -19,9 +19,14 @@ public function execute($request): void /** @var PaymentInterface $payment */ $payment = $request->getFirstModel(); + /** @var OrderInterface $order */ + $order = $payment->getOrder(); + if ($payment->getState() === PaymentInterface::STATE_NEW) { $request->setRouteName('sylius_paypal_plugin_pay_with_paypal_form'); - $request->setRouteParameters(['id' => $payment->getId()]); + $request->setRouteParameters( + ['orderToken' => $order->getTokenValue(), 'paymentId' => $payment->getId()] + ); return; } @@ -32,9 +37,6 @@ public function execute($request): void return; } - /** @var OrderInterface $order */ - $order = $payment->getOrder(); - $request->setRouteName('sylius_shop_order_show'); $request->setRouteParameters(['tokenValue' => $order->getTokenValue()]); } diff --git a/src/Resources/config/shop_routing.yaml b/src/Resources/config/shop_routing.yaml index 835c8ed1..7310c157 100644 --- a/src/Resources/config/shop_routing.yaml +++ b/src/Resources/config/shop_routing.yaml @@ -1,5 +1,5 @@ sylius_paypal_plugin_pay_with_paypal_form: - path: /pay-with-paypal/{id} + path: /pay-with-paypal/{orderToken}/{paymentId} methods: [GET] defaults: _controller: Sylius\PayPalPlugin\Controller\PayWithPayPalFormAction diff --git a/src/Resources/views/payWithPaypal.html.twig b/src/Resources/views/payWithPaypal.html.twig index ef09d17a..e164e4d1 100644 --- a/src/Resources/views/payWithPaypal.html.twig +++ b/src/Resources/views/payWithPaypal.html.twig @@ -329,6 +329,8 @@ }); if (paypal.HostedFields.isEligible() === true) { + let processingOrderId; + paypal.HostedFields.render({ createOrder: function(data, actions) { document.querySelector('#paypal-payment-container').classList.add('loading'); @@ -339,6 +341,8 @@ }).then(function(res) { return res.json(); }).then(function(data) { + processingOrderId = data.orderID; + return data.orderID; }); }, @@ -419,6 +423,7 @@ if (formValid) { hostedFields.submit({ + contingencies: ['SCA_ALWAYS'], cardholderName: document.getElementById('card-holder-name').value, billingAddress: { streetAddress: document.getElementById('card-billing-address-street').value, @@ -428,20 +433,35 @@ countryCodeAlpha2: document.getElementById('card-billing-address-country').value } }).then(payload => { - return fetch(completePayPalOrderUrl, { - method: 'post' - }).then(function(res) { - return res.json(); + if (payload.authenticationReason == 'SUCCESSFUL' && payload.authenticationStatus == 'YES') { + return fetch(completePayPalOrderUrl, { + method: 'post' + }).then(function(res) { + return res.json(); + }).then(function(data) { + if (data.status == 'processing') { + return fetch(cancelPayPalPaymentUrl, { + method: 'post', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ payPalOrderId: data.orderID }) + }).then(window.location.reload()); + } + + window.location.href = data.return_url; + }); + } + + + return fetch(errorPayPalPaymentUrl, { + method: 'post', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify('Invalid 3D Secure authentication.') }).then(function(data) { - if (data.status == 'processing') { - return fetch(cancelPayPalPaymentUrl, { - method: 'post', - headers: { 'content-type': 'application/json' }, - body: JSON.stringify({ payPalOrderId: data.orderID }) - }).then(window.location.reload()); - } - - window.location.href = data.return_url; + return fetch(cancelPayPalPaymentUrl, { + method: 'post', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ payPalOrderId: processingOrderId }) + }).then(window.location.reload()); }); }); } else {