custom/plugins/RpayPayments/src/Components/Account/Subscriber/AccountSubscriber.php line 121

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. /*
  4.  * Copyright (c) Ratepay GmbH
  5.  *
  6.  * For the full copyright and license information, please view the LICENSE
  7.  * file that was distributed with this source code.
  8.  */
  9. namespace Ratepay\RpayPayments\Components\Account\Subscriber;
  10. use Ratepay\RpayPayments\Components\Account\Event\PaymentUpdateRequestBagValidatedEvent;
  11. use Ratepay\RpayPayments\Components\Checkout\Model\Extension\OrderExtension;
  12. use Ratepay\RpayPayments\Components\Checkout\Model\RatepayOrderDataEntity;
  13. use Ratepay\RpayPayments\Components\Checkout\Service\ExtensionService;
  14. use Ratepay\RpayPayments\Components\PaymentHandler\AbstractPaymentHandler;
  15. use Ratepay\RpayPayments\Components\PaymentHandler\Event\PaymentFailedEvent;
  16. use Ratepay\RpayPayments\Components\RedirectException\Exception\ForwardException;
  17. use Ratepay\RpayPayments\Util\CriteriaHelper;
  18. use Ratepay\RpayPayments\Util\DataValidationHelper;
  19. use Ratepay\RpayPayments\Util\MethodHelper;
  20. use Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStateHandler;
  21. use Shopware\Core\Checkout\Order\OrderEntity;
  22. use Shopware\Core\Checkout\Payment\Cart\PaymentHandler\PaymentHandlerRegistry;
  23. use Shopware\Core\Checkout\Payment\PaymentMethodEntity;
  24. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
  25. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  26. use Shopware\Core\Framework\Validation\DataBag\RequestDataBag;
  27. use Shopware\Core\Framework\Validation\DataValidationDefinition;
  28. use Shopware\Core\Framework\Validation\DataValidator;
  29. use Shopware\Core\Framework\Validation\Exception\ConstraintViolationException;
  30. use Shopware\Storefront\Event\RouteRequest\HandlePaymentMethodRouteRequestEvent;
  31. use Shopware\Storefront\Event\RouteRequest\SetPaymentOrderRouteRequestEvent;
  32. use Shopware\Storefront\Page\Account\Order\AccountEditOrderPageLoadedEvent;
  33. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  34. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  35. class AccountSubscriber implements EventSubscriberInterface
  36. {
  37.     protected ExtensionService $extensionService;
  38.     private DataValidator $dataValidator;
  39.     private EntityRepositoryInterface $paymentMethodRepository;
  40.     private PaymentHandlerRegistry $paymentHandlerRegistry;
  41.     private EntityRepositoryInterface $orderRepository;
  42.     private EventDispatcherInterface $eventDispatcher;
  43.     private OrderTransactionStateHandler $orderTransactionStateHandler;
  44.     public function __construct(
  45.         ExtensionService $extensionService,
  46.         PaymentHandlerRegistry $paymentHandlerRegistry,
  47.         EntityRepositoryInterface $paymentMethodRepository,
  48.         EntityRepositoryInterface $orderRepository,
  49.         DataValidator $dataValidator,
  50.         EventDispatcherInterface $eventDispatcher,
  51.         OrderTransactionStateHandler $orderTransactionStateHandler
  52.     )
  53.     {
  54.         $this->extensionService $extensionService;
  55.         $this->dataValidator $dataValidator;
  56.         $this->paymentMethodRepository $paymentMethodRepository;
  57.         $this->paymentHandlerRegistry $paymentHandlerRegistry;
  58.         $this->orderRepository $orderRepository;
  59.         $this->eventDispatcher $eventDispatcher;
  60.         $this->orderTransactionStateHandler $orderTransactionStateHandler;
  61.     }
  62.     public static function getSubscribedEvents(): array
  63.     {
  64.         return [
  65.             AccountEditOrderPageLoadedEvent::class => ['addRatepayTemplateData'310],
  66.             HandlePaymentMethodRouteRequestEvent::class => 'onHandlePaymentMethodRouteRequest',
  67.             SetPaymentOrderRouteRequestEvent::class => 'onPaymentOrderRouteRequest',
  68.         ];
  69.     }
  70.     public function addRatepayTemplateData(AccountEditOrderPageLoadedEvent $event): void
  71.     {
  72.         $page $event->getPage();
  73.         $order $page->getOrder();
  74.         /** @var RatepayOrderDataEntity $ratepayData */
  75.         $ratepayData $order->getExtension(OrderExtension::EXTENSION_NAME);
  76.         if ($ratepayData && MethodHelper::isRatepayOrder($order) && $ratepayData->isSuccessful()) {
  77.             // You can't change the payment if it is a ratepay order
  78.             $page->setPaymentChangeable(false);
  79.         } else {
  80.             $order $this->orderRepository->search(CriteriaHelper::getCriteriaForOrder($order->getId()), $event->getContext())->first();
  81.             // Payment change is allowed, prepare ratepay payment data if a ratepay payment method is selected
  82.             $paymentMethod $event->getSalesChannelContext()->getPaymentMethod();
  83.             if (MethodHelper::isRatepayMethod($paymentMethod->getHandlerIdentifier()) &&
  84.                 $event->getPage()->getPaymentMethods()->has($paymentMethod->getId())
  85.             ) {
  86.                 $extension $this->extensionService->buildPaymentDataExtension(
  87.                     $event->getSalesChannelContext(),
  88.                     $order
  89.                 );
  90.                 if ($extension) {
  91.                     $event->getPage()->addExtension(ExtensionService::PAYMENT_PAGE_EXTENSION_NAME$extension);
  92.                 }
  93.             }
  94.         }
  95.     }
  96.     public function onHandlePaymentMethodRouteRequest(HandlePaymentMethodRouteRequestEvent $event): void
  97.     {
  98.         if ($event->getStorefrontRequest()->request->has('ratepay')) {
  99.             $event->getStoreApiRequest()->request->set(
  100.                 'ratepay',
  101.                 $event->getStorefrontRequest()->request->get('ratepay')
  102.             );
  103.         }
  104.     }
  105.     public function onPaymentOrderRouteRequest(SetPaymentOrderRouteRequestEvent $event): void
  106.     {
  107.         if ($event->getStorefrontRequest()->attributes->get('_route') !== 'frontend.account.edit-order.update-order') {
  108.             // make sure that this subscriber only got processed when the order got updated (e.g. switch of payment method or failed payment)
  109.             return;
  110.         }
  111.         $orderId $event->getStoreApiRequest()->get('orderId');
  112.         /** @var OrderEntity $orderEntity */
  113.         $orderEntity $this->orderRepository->search(CriteriaHelper::getCriteriaForOrder($orderId), $event->getContext())->first();
  114.         $paymentMethodId $event->getStoreApiRequest()->get('paymentMethodId');
  115.         /** @var PaymentMethodEntity $paymentMethod */
  116.         $paymentMethod $this->paymentMethodRepository->search(new Criteria([$paymentMethodId]), $event->getContext())->first();
  117.         if ($orderEntity === null ||
  118.             $paymentMethod === null ||
  119.             MethodHelper::isRatepayMethod($paymentMethod->getHandlerIdentifier()) === false
  120.         ) {
  121.             // not a ratepay method - nothing to do.
  122.             return;
  123.         }
  124.         /** @var AbstractPaymentHandler $paymentHandler */
  125.         $paymentHandler $this->paymentHandlerRegistry->getHandler($paymentMethod->getHandlerIdentifier());
  126.         // we need to add some functionality to validate the payment data and the response from the gateway.
  127.         // cause shopware do have the opportunity to output custom messages, we will throw an ForwardException, which
  128.         // got caught in the `RedirectException` component and will be rewritten into forward.
  129.         // we must validate the ratepay data on our own. to prevent errors with other extensions, we will only validate ratepay-data.
  130.         $validationDefinitions $paymentHandler->getValidationDefinitions($event->getStorefrontRequest(), $orderEntity);
  131.         $definition = new DataValidationDefinition();
  132.         $definition->addSub('ratepay'DataValidationHelper::addSubConstraints(new DataValidationDefinition(), $validationDefinitions));
  133.         $requestData $event->getStorefrontRequest()->request->get('ratepay');
  134.         try {
  135.             $this->dataValidator->validate(['ratepay' => $requestData], $definition);
  136.         } catch (ConstraintViolationException $formViolations) {
  137.             throw new ForwardException('frontend.account.edit-order.page', ['orderId' => $orderEntity->getId()], ['formViolations' => $formViolations], $formViolations);
  138.         }
  139.         $this->eventDispatcher->dispatch(new PaymentUpdateRequestBagValidatedEvent(
  140.             $orderEntity,
  141.             $paymentHandler,
  142.             new RequestDataBag(['ratepay' => $requestData]),
  143.             $event->getSalesChannelContext()
  144.         ));
  145.         // we register an payment-failed-subscriber to throw a forward-exception during the payment-complete in the update-payment process.
  146.         // the listener must have a very low priority to make sure that all other event-subscriber can process
  147.         $orderTransactionStateHandler $this->orderTransactionStateHandler;
  148.         $this->eventDispatcher->addListener(PaymentFailedEvent::class, static function (PaymentFailedEvent $event) use ($orderTransactionStateHandler) {
  149.             // set the transaction to failed. Without this, the customer will not be able to try a repayment.
  150.             // NOTE: this is not required during the validation or the PaymentQuery, cause in this state,
  151.             // the transaction is not set as "open". Only after the PaymentHandler got called
  152.             $orderTransactionStateHandler->fail($event->getTransaction()->getOrderTransaction()->getId(), $event->getContext());
  153.             // determine the correct error message for the customer.
  154.             $message $event->getException()->getMessage();
  155.             if ($response $event->getResponse()) {
  156.                 $message $response->getCustomerMessage();
  157.                 $message $message ?: $response->getReasonMessage();
  158.             }
  159.             throw new ForwardException('frontend.account.edit-order.page', ['orderId' => $event->getOrder()->getId()], ['ratepay-errors' => [$message]], $event->getException());
  160.         }, -9999999);
  161.     }
  162. }