<?php
/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);
namespace Ibexa\Bundle\Checkout\EventSubscriber;
use Ibexa\Contracts\Cart\CartServiceInterface;
use Ibexa\Contracts\Checkout\Discounts\DiscountsValidationFailedException;
use Ibexa\Contracts\Checkout\Exception\CheckoutException;
use Ibexa\Contracts\Checkout\Value\CheckoutInterface;
use Ibexa\DiscountsCodes\DiscountCodeSupplier;
use Ibexa\DiscountsCodes\Value\DiscountCondition\IsValidDiscountCode;
use JMS\TranslationBundle\Annotation\Desc;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
final class DiscountsHaveChangedExceptionSubscriber implements EventSubscriberInterface
{
private RouterInterface $router;
private CartServiceInterface $cartService;
private TranslatorInterface $translator;
private bool $enabled;
private ?DiscountCodeSupplier $discountCodeSupplier;
public function __construct(
RouterInterface $router,
CartServiceInterface $cartService,
TranslatorInterface $translator,
bool $enabled = true,
?DiscountCodeSupplier $discountCodeSupplier = null
) {
$this->router = $router;
$this->cartService = $cartService;
$this->translator = $translator;
$this->enabled = $enabled;
$this->discountCodeSupplier = $discountCodeSupplier;
}
public static function getSubscribedEvents(): array
{
return [
KernelEvents::EXCEPTION => [
['handleException', 10],
],
];
}
public function handleException(ExceptionEvent $event): void
{
if ($this->discountCodeSupplier === null || !$this->enabled) {
return;
}
$throwable = $event->getThrowable();
if (
!$throwable instanceof CheckoutException
|| !$throwable->getPrevious() instanceof DiscountsValidationFailedException
) {
return;
}
$checkout = $event->getRequest()->attributes->get('checkout');
if (!$checkout instanceof CheckoutInterface) {
return;
}
$cartIdentifier = $checkout->getCartIdentifier();
$this->dropDiscountCode($cartIdentifier);
$session = $event->getRequest()->getSession();
assert($session instanceof Session);
$session->getFlashBag()->add(
'checkout_errors',
$this->translator->trans(
/** @Desc("The applied discounts have changed since you started the checkout process. Verify your cart.") */
'checkout.error.discounts_have_changed',
[],
'ibexa_checkout'
)
);
$response = new RedirectResponse(
$this->router->generate('ibexa.cart.view', [
'identifier' => $cartIdentifier,
])
);
$event->setResponse($response);
}
/**
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException
*/
private function dropDiscountCode(string $cartIdentifier): void
{
$cart = $this->cartService->getCart($cartIdentifier);
$cartContext = $cart->getContext();
if (
$cartContext === null
|| !$cartContext->has(IsValidDiscountCode::CART_DISCOUNT_CODE_KEY)
) {
return;
}
assert($this->discountCodeSupplier !== null);
$this->discountCodeSupplier->drop(
$cart,
$cartContext->get(IsValidDiscountCode::CART_DISCOUNT_CODE_KEY)
);
}
}