custom/plugins/MoptWorldline/src/Subscriber/OrderChangesSubscriber.php line 82

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace MoptWorldline\Subscriber;
  3. use Monolog\Logger;
  4. use MoptWorldline\Bootstrap\Form;
  5. use MoptWorldline\Service\Helper;
  6. use MoptWorldline\Service\Payment;
  7. use MoptWorldline\Service\PaymentHandler;
  8. use Psr\Log\LogLevel;
  9. use Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStateHandler;
  10. use Shopware\Core\Checkout\Order\OrderEntity;
  11. use Shopware\Core\Checkout\Order\OrderEvents;
  12. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
  13. use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityWrittenEvent;
  14. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  15. use Shopware\Core\System\StateMachine\Aggregation\StateMachineTransition\StateMachineTransitionActions;
  16. use Shopware\Core\System\SystemConfig\SystemConfigService;
  17. use Shopware\Storefront\Event\RouteRequest\HandlePaymentMethodRouteRequestEvent;
  18. use Shopware\Storefront\Event\StorefrontRenderEvent;
  19. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  20. use Shopware\Core\Framework\Context;
  21. use Symfony\Component\HttpFoundation\RequestStack;
  22. use Symfony\Contracts\Translation\TranslatorInterface;
  23. use Symfony\Component\HttpFoundation\Session\Session;
  24. class OrderChangesSubscriber implements EventSubscriberInterface
  25. {
  26.     private SystemConfigService $systemConfigService;
  27.     private EntityRepositoryInterface $orderRepository;
  28.     private EntityRepositoryInterface $customerRepository;
  29.     private Logger $logger;
  30.     private RequestStack $requestStack;
  31.     private TranslatorInterface $translator;
  32.     private OrderTransactionStateHandler $transactionStateHandler;
  33.     private Session $session;
  34.     /**
  35.      * @param SystemConfigService $systemConfigService
  36.      * @param EntityRepositoryInterface $orderRepository
  37.      * @param EntityRepositoryInterface $customerRepository
  38.      * @param Logger $logger
  39.      * @param RequestStack $requestStack
  40.      * @param TranslatorInterface $translator
  41.      * @param OrderTransactionStateHandler $transactionStateHandler
  42.      * @param Session $session
  43.      */
  44.     public function __construct(
  45.         SystemConfigService          $systemConfigService,
  46.         EntityRepositoryInterface    $orderRepository,
  47.         EntityRepositoryInterface    $customerRepository,
  48.         Logger                       $logger,
  49.         RequestStack                 $requestStack,
  50.         TranslatorInterface          $translator,
  51.         OrderTransactionStateHandler $transactionStateHandler,
  52.         Session                      $session
  53.     )
  54.     {
  55.         $this->systemConfigService = $systemConfigService;
  56.         $this->orderRepository = $orderRepository;
  57.         $this->customerRepository = $customerRepository;
  58.         $this->logger = $logger;
  59.         $this->requestStack = $requestStack;
  60.         $this->translator = $translator;
  61.         $this->transactionStateHandler = $transactionStateHandler;
  62.         $this->session = $session;
  63.     }
  64.     public static function getSubscribedEvents(): array
  65.     {
  66.         return [
  67.             HandlePaymentMethodRouteRequestEvent::class => 'setIframeFields',
  68.             // 22.03.2023 - should be disabled before Worldline will fix status notifications.
  69.             //OrderEvents::ORDER_WRITTEN_EVENT => 'onOrderWritten',
  70.         ];
  71.     }
  72.     /**
  73.      * @param HandlePaymentMethodRouteRequestEvent $event
  74.      * @return void
  75.      */
  76.     public function setIframeFields(HandlePaymentMethodRouteRequestEvent $event)
  77.     {
  78.         $iframeData = [];
  79.         $request = $event->getStorefrontRequest()->request;
  80.         if (!is_null($request->get(Form::WORLDLINE_CART_FORM_HOSTED_TOKENIZATION_ID))) {
  81.             foreach (Form::WORLDLINE_CART_FORM_KEYS as $key) {
  82.                 $iframeData[$key] = $request->get($key);
  83.                 if (is_null($iframeData[$key])) {
  84.                     return;
  85.                 }
  86.             }
  87.             $this->session->set(Form::SESSION_IFRAME_DATA, $iframeData);
  88.             return;
  89.         }
  90.         $tokenField = Form::WORLDLINE_CART_FORM_REDIRECT_TOKEN;
  91.         if (!is_null($request->get($tokenField))) {
  92.             $iframeData[$tokenField] = $request->get($tokenField);
  93.             $this->session->set(Form::SESSION_IFRAME_DATA, $iframeData);
  94.         }
  95.     }
  96.     /**
  97.      * @param EntityWrittenEvent $event
  98.      * @return void
  99.      * @throws \Doctrine\DBAL\Driver\Exception
  100.      * @throws \Doctrine\DBAL\Exception
  101.      */
  102.     public function onOrderWritten(EntityWrittenEvent $event): void
  103.     {
  104.         $request = $this->requestStack->getCurrentRequest();
  105.         $uri = $request->getUri();
  106.         $uriArr = explode('/', $uri);
  107.         $newState = $uriArr[count($uriArr) - 1];
  108.         if (is_null($newState)
  109.             || !in_array(
  110.                 $newState,
  111.                 [StateMachineTransitionActions::ACTION_CANCEL, StateMachineTransitionActions::ACTION_REFUND]
  112.             )
  113.         ) {
  114.             return;
  115.         }
  116.         foreach ($event->getWriteResults() as $result) {
  117.             $orderId = $result->getPrimaryKey();
  118.             if (is_null($orderId)) {
  119.                 continue;
  120.             }
  121.             if (Payment::isOrderLocked($this->requestStack->getSession(), $orderId)) {
  122.                 continue;
  123.             }
  124.             //For order transaction changes payload is empty
  125.             if (empty($result->getPayload())) {
  126.                 $this->processOrder($orderId, $newState, $event->getContext());
  127.             //Order cancel should lead to payment transaction refund.
  128.             //For order changes payload is NOT empty.
  129.             } else {
  130.                 $this->processOrder($orderId, StateMachineTransitionActions::ACTION_REFUND, $event->getContext());
  131.             }
  132.         }
  133.     }
  134.     /**
  135.      * @param string $orderId
  136.      * @param string $state
  137.      * @param Context $context
  138.      * @return void
  139.      * @throws \Exception
  140.      */
  141.     private function processOrder(string $orderId, string $state, Context $context)
  142.     {
  143.         if (!$order = $this->getOrder($orderId, $context)) {
  144.             return;
  145.         }
  146.         $customFields = $order->getCustomFields();
  147.         if (!is_array($customFields)) {
  148.             return;
  149.         }
  150.         if (!array_key_exists('payment_transaction_id', $customFields)) {
  151.             return;
  152.         }
  153.         $hostedCheckoutId = $customFields['payment_transaction_id'];
  154.         $order = PaymentHandler::getOrder($context, $this->orderRepository, $hostedCheckoutId);
  155.         $paymentHandler = new PaymentHandler(
  156.             $this->systemConfigService,
  157.             $this->logger,
  158.             $order,
  159.             $this->translator,
  160.             $this->orderRepository,
  161.             $this->customerRepository,
  162.             $context,
  163.             $this->transactionStateHandler
  164.         );
  165.         $customFields = $order->getCustomFields();
  166.         switch ($state) {
  167.             case StateMachineTransitionActions::ACTION_CANCEL:
  168.             {
  169.                 Payment::lockOrder($this->requestStack->getSession(), $orderId);
  170.                 $amount = $customFields[Form::CUSTOM_FIELD_WORLDLINE_PAYMENT_TRANSACTION_CAPTURE_AMOUNT];
  171.                 if ($amount > 0) {
  172.                     $paymentHandler->cancelPayment($hostedCheckoutId, $amount, []);
  173.                 }
  174.                 break;
  175.             }
  176.             case StateMachineTransitionActions::ACTION_REFUND:
  177.             {
  178.                 Payment::lockOrder($this->requestStack->getSession(), $orderId);
  179.                 $amount = $customFields[Form::CUSTOM_FIELD_WORLDLINE_PAYMENT_TRANSACTION_REFUND_AMOUNT];
  180.                 if ($amount > 0) {
  181.                     $paymentHandler->refundPayment($hostedCheckoutId, $amount, []);
  182.                 }
  183.                 break;
  184.             }
  185.             default :
  186.             {
  187.                 break;
  188.             }
  189.         }
  190.         Payment::unlockOrder($this->requestStack->getSession(), $orderId);
  191.     }
  192.     /**
  193.      * @param string $orderId
  194.      * @param Context $context
  195.      * @return OrderEntity|mixed
  196.      */
  197.     private function getOrder(string $orderId, Context $context)
  198.     {
  199.         $orders = $this->orderRepository->search(new Criteria([$orderId]), $context);
  200.         /* @var $order OrderEntity */
  201.         foreach ($orders->getElements() as $order) {
  202.             return $order;
  203.         }
  204.         $this->logger->log(LogLevel::ERROR, "There is no order with id = $orderId");
  205.         return false;
  206.     }
  207. }