Media

Sylius PayPal Plugin has an Order Manipulation Vulnerability after PayPal Checkout

  • Publicada: 19.03.2025
  • Actualizado: 11.10.2026 10:22

A discovered vulnerability allows users to modify their shopping cart after completing the PayPal Checkout process and payment authorization. If a user initiates a PayPal transaction from a product page or the cart page and then returns to the order summary page, they can still manipulate the cart contents before finalizing the order. As a result, the order amount in Sylius may be higher than the amount actually captured by PayPal, leading to a scenario where merchants deliver products or services without full payment.

Impact

  • Users can exploit this flaw to receive products/services without paying the full amount.
  • Merchants may suffer financial losses due to underpaid orders.
  • Trust in the integrity of the payment process is compromised.

Patches

The issue is fixed in versions: 1.6.2, 1.7.2, 2.0.2 and above.

Workarounds

To resolve the problem in the end application without updating to the newest patches, there is a need to overwrite PayPalOrderCompleteProcessor with modified logic:

php
<?php

declare(strict_types=1);

namespace App\Processor;

use Sylius\Bundle\PayumBundle\Model\GatewayConfigInterface;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Model\PaymentInterface;
use Sylius\Component\Core\Model\PaymentMethodInterface;
use Sylius\PayPalPlugin\Manager\PaymentStateManagerInterface;

final class PayPalOrderCompleteProcessor
{
    public function __construct(private readonly PaymentStateManagerInterface $paymentStateManager) {
    }

    public function completePayPalOrder(OrderInterface $order): void
    {
        $payment = $order->getLastPayment(PaymentInterface::STATE_PROCESSING);
        if ($payment === null) {
            return;
        }

        /** @var PaymentMethodInterface $paymentMethod */
        $paymentMethod = $payment->getMethod();
        /** @var GatewayConfigInterface $gatewayConfig */
        $gatewayConfig = $paymentMethod->getGatewayConfig();

        if ($gatewayConfig->getFactoryName() !== 'sylius.pay_pal') {
            return;
        }

        try {
            $this->verify($payment);
        } catch (\Exception) {
            $this->paymentStateManager->cancel($payment);

            return;
        }

        $this->paymentStateManager->complete($payment);
    }

    private function verify(PaymentInterface $payment): void
    {
        $totalAmount = $this->getTotalPaymentAmountFromPaypal($payment);

        if ($payment->getOrder()->getTotal() !== $totalAmount) {
            throw new \Exception();
        }
    }

    private function getTotalPaymentAmountFromPaypal(PaymentInterface $payment): int
    {
        $details = $payment->getDetails();

        return $details['payment_amount'] ?? 0;
    }
}

IMPORTANT

For PayPalPlugin 2.x change:

php
$gatewayConfig->getFactoryName() !== 'sylius.pay_pal'

to

php
$gatewayConfig->getFactoryName() !== SyliusPayPalExtension::PAYPAL_FACTORY_NAME

Also there is a need to overwrite CompletePayPalOrderListener with modified logic:

php
<?php

declare(strict_types=1);

namespace App\EventListener\Workflow;

use App\Processor\PayPalOrderCompleteProcessor;
use Sylius\Component\Core\Model\OrderInterface;
use Symfony\Component\Workflow\Event\CompletedEvent;
use Webmozart\Assert\Assert;

final class CompletePayPalOrderListener
{
    public function __construct(private readonly PayPalOrderCompleteProcessor $completeProcessor)
    {
    }

    public function __invoke(CompletedEvent $event): void
    {
        /** @var OrderInterface $order */
        $order = $event->getSubject();
        Assert::isInstanceOf($order, OrderInterface::class);

        $this->completeProcessor->completePayPalOrder($order);
    }
}

And to overwrite CaptureAction with modified logic (if you didn't have it already):

php
<?php

declare(strict_types=1);

namespace App\Payum\Action;

use Payum\Core\Action\ActionInterface;
use Payum\Core\Exception\RequestNotSupportedException;
use Payum\Core\Request\Capture;
use Sylius\Component\Core\Model\PaymentInterface;
use Sylius\Component\Core\Model\PaymentMethodInterface;
use Sylius\PayPalPlugin\Api\CacheAuthorizeClientApiInterface;
use Sylius\PayPalPlugin\Api\CreateOrderApiInterface;
use Sylius\PayPalPlugin\Payum\Action\StatusAction;
use Sylius\PayPalPlugin\Provider\UuidProviderInterface;

final class CaptureAction implements ActionInterface
{
    public function __construct(
        private CacheAuthorizeClientApiInterface $authorizeClientApi,
        private CreateOrderApiInterface $createOrderApi,
        private UuidProviderInterface $uuidProvider,
    ) {
    }

    /** @param Capture $request */
    public function execute($request): void
    {
        RequestNotSupportedException::assertSupports($this, $request);

        /** @var PaymentInterface $payment */
        $payment = $request->getModel();
        /** @var PaymentMethodInterface $paymentMethod */
        $paymentMethod = $payment->getMethod();

        $token = $this->authorizeClientApi->authorize($paymentMethod);

        $referenceId = $this->uuidProvider->provide();
        $content = $this->createOrderApi->create($token, $payment, $referenceId);

        if ($content['status'] === 'CREATED') {
            $payment->setDetails([
                'status' => StatusAction::STATUS_CAPTURED,
                'paypal_order_id' => $content['id'],
                'reference_id' => $referenceId,
                'payment_amount' => $payment->getAmount(),
            ]);
        }
    }

    public function supports($request): bool
    {
        return
            $request instanceof Capture &&
            $request->getModel() instanceof PaymentInterface
        ;
    }
}

After that, register services in the container when using PayPal 1.x:

yaml
Sylius\PayPalPlugin\EventListener\Workflow\CompletePayPalOrderListener:
    class: App\EventListener\Workflow\CompletePayPalOrderListener
    public: true
    arguments:
        - '@Sylius\PayPalPlugin\Processor\PayPalOrderCompleteProcessor'
    tags: 
        - { name: 'kernel.event_listener', event: 'workflow.sylius_order_checkout.completed.complete', priority: 100 }
    
Sylius\PayPalPlugin\Processor\PayPalOrderCompleteProcessor:
    class: App\Processor\PayPalOrderCompleteProcessor
    public: true
    arguments:
        - '@Sylius\PayPalPlugin\Manager\PaymentStateManagerInterface'

Sylius\PayPalPlugin\Payum\Action\CaptureAction:
    class: App\Payum\Action\CaptureAction
    public: true
    arguments:
        - '@Sylius\PayPalPlugin\Api\CacheAuthorizeClientApiInterface'
        - '@Sylius\PayPalPlugin\Api\CreateOrderApiInterface'
        - '@Sylius\PayPalPlugin\Provider\UuidProviderInterface'
    tags:
        - { name: 'payum.action', factory: 'sylius.pay_pal', alias: 'payum.action.capture' }

or when using PayPal 2.x:

yaml
sylius_paypal.listener.workflow.complete_paypal_order:
    class: App\EventListener\Workflow\CompletePayPalOrderListener
    public: true
    arguments:
        - '@sylius_paypal.processor.paypal_order_complete'
    tags: 
        - { name: 'kernel.event_listener', event: 'workflow.sylius_order_checkout.completed.complete', priority: 100 }
    
sylius_paypal.processor.paypal_order_complete:
    class: App\Processor\PayPalOrderCompleteProcessor
    public: true
    arguments:
        - '@sylius_paypal.manager.payment_state'

sylius_paypal.payum.action.capture:
    class: App\Payum\Action\CaptureAction
    public: true
    arguments:
        - '@sylius_paypal.api.cache_authorize_client'
        - '@sylius_paypal.api.create_order'
        - '@sylius_paypal.provider.uuid'
    tags:
        - { name: 'payum.action', factory: 'sylius.paypal', alias: 'payum.action.capture' }

For more information

If you have any questions or comments about this advisory:

Software afectado

Haz clic en una tecnología para ver todas sus vulnerabilidades.

PlataformaPaqueteVersiones vulnerablesCorregida en
Paquete de Composer sylius/paypal-plugin < 1.6.2, >= 1.7.0, < 1.7.2, >= 2.0.0, < 2.0.2 1.6.2, 1.7.2, 2.0.2

Referencias

Puntuación CVSS 6.5/10
Vector CVSS
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N
Debilidad (CWE)
CWE-472
Plataforma
Composer
Paquete
sylius/paypal-plugin
Publicada
19.03.2025
Actualizado
11.10.2026 10:22

Fuentes y licencias

OSV / GitHub Advisory Database - GHSA-hxg4-65p5-9w37 - CC BY 4.0

Historial de vulnerabilidades: sylius/paypal-plugin

Todas las vulnerabilidades publicadas de este paquete (Paquete de Composer), de la más reciente a la más antigua. Muestra si es un fallo aislado o un paquete que hay que vigilar.

Entradas en total
3
Primera → última
10.2021 03.2025
Sin corrección
0
Explotada en ataques
0

Distribución por gravedad

  • Alta 1
  • Media 2

Divulgaciones por año

  1. 2021: 1
  2. 2022: 0
  3. 2023: 0
  4. 2024: 0
  5. 2025: 2
  6. 2026: 0
  1. Sylius PayPal Plugin has an Order Manipulation Vulnerability after PayPal Checkout esta entrada
    Media · 6.5 CVE-2025-30152 < 1.6.2, >= 1.7.0, < 1.7.2, >= 2.0.0, < 2.0.2 1.6.2, 1.7.2, 2.0.2
  2. Media · 6.5 CVE-2025-29788 < 1.6.1, >= 1.7.0, < 1.7.1, >= 2.0.0, < 2.0.1 1.6.1, 1.7.1, 2.0.1
  3. Alta · 7.5 CVE-2021-41120 >= 1.0.0, < 1.2.4, >= 1.3.0, < 1.3.1 1.2.4, 1.3.1

Identifica las vulnerabilidades de tu aplicación

Con la integración de DockRay y el inventario de software activado, DockRay compara las versiones notificadas de los componentes con las vulnerabilidades conocidas y avisa de las coincidencias confirmadas. La cobertura depende de la tecnología compatible y de los avisos disponibles.

Conectar tu aplicación
Newsletter DockRay

La seguridad de tus aplicaciones en tu correo

Vulnerabilidades importantes, recomendaciones concretas y consejos prácticos para tus aplicaciones. Elige tus tecnologías y sigue lo que importa para tu proyecto.

  • Un resumen diario de nuevas vulnerabilidades, también las críticas.
  • Avisos sobre actualizaciones importantes.
  • Consejos prácticos sobre seguridad y monitorización de aplicaciones.

Déjalo vacío para recibir avisos de todas las tecnologías.

Suscripción gratuita. Date de baja con un clic en cualquier correo.