custom/plugins/KlarnaPayment/src/Components/EventListener/SessionEventListener.php line 111

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace KlarnaPayment\Components\EventListener;
  4. use KlarnaPayment\Components\CartHasher\CartHasherInterface;
  5. use KlarnaPayment\Components\Client\ClientInterface;
  6. use KlarnaPayment\Components\Client\Hydrator\Request\CreateSession\CreateSessionRequestHydratorInterface;
  7. use KlarnaPayment\Components\Client\Hydrator\Request\UpdateSession\UpdateSessionRequestHydratorInterface;
  8. use KlarnaPayment\Components\Client\Hydrator\Struct\Address\AddressStructHydrator;
  9. use KlarnaPayment\Components\Client\Hydrator\Struct\Address\AddressStructHydratorInterface;
  10. use KlarnaPayment\Components\Client\Hydrator\Struct\Customer\CustomerStructHydratorInterface;
  11. use KlarnaPayment\Components\Client\Response\GenericResponse;
  12. use KlarnaPayment\Components\Client\Struct\Attachment;
  13. use KlarnaPayment\Components\Converter\CustomOrderConverter;
  14. use KlarnaPayment\Components\Extension\ErrorMessageExtension;
  15. use KlarnaPayment\Components\Extension\SessionDataExtension;
  16. use KlarnaPayment\Components\Factory\MerchantDataFactoryInterface;
  17. use KlarnaPayment\Components\Helper\OrderFetcherInterface;
  18. use KlarnaPayment\Components\Helper\PaymentHelper\PaymentHelperInterface;
  19. use KlarnaPayment\Installer\Modules\PaymentMethodInstaller;
  20. use LogicException;
  21. use Shopware\Core\Checkout\Cart\Cart;
  22. use Shopware\Core\Checkout\Cart\Event\CheckoutOrderPlacedEvent;
  23. use Shopware\Core\Checkout\Customer\Event\CustomerLoginEvent;
  24. use Shopware\Core\Checkout\Order\OrderEntity;
  25. use Shopware\Core\Checkout\Payment\PaymentMethodEntity;
  26. use Shopware\Core\Framework\Context;
  27. use Shopware\Core\Framework\Struct\Struct;
  28. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  29. use Shopware\Storefront\Page\Account\Order\AccountEditOrderPageLoadedEvent;
  30. use Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent;
  31. use Shopware\Storefront\Page\Page;
  32. use Shopware\Storefront\Page\PageLoadedEvent;
  33. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  34. use Symfony\Component\HttpFoundation\Session\SessionInterface;
  35. use Symfony\Contracts\EventDispatcher\Event;
  36. class SessionEventListener implements EventSubscriberInterface
  37. {
  38.     /** @var PaymentHelperInterface */
  39.     private $paymentHelper;
  40.     /** @var CreateSessionRequestHydratorInterface */
  41.     private $requestHydrator;
  42.     /** @var UpdateSessionRequestHydratorInterface */
  43.     private $requestUpdateHydrator;
  44.     /** @var AddressStructHydratorInterface */
  45.     private $addressHydrator;
  46.     /** @var CustomerStructHydratorInterface */
  47.     private $customerHydrator;
  48.     /** @var ClientInterface */
  49.     private $client;
  50.     /** @var CartHasherInterface */
  51.     private $cartHasher;
  52.     /** @var MerchantDataFactoryInterface */
  53.     private $merchantDataFactory;
  54.     /** @var CustomOrderConverter */
  55.     private $orderConverter;
  56.     /** @var OrderFetcherInterface */
  57.     private $orderFetcher;
  58.     /** @var SessionInterface */
  59.     private $session;
  60.     public function __construct(
  61.         PaymentHelperInterface $paymentHelper,
  62.         CreateSessionRequestHydratorInterface $requestHydrator,
  63.         UpdateSessionRequestHydratorInterface $requestUpdateHydrator,
  64.         AddressStructHydratorInterface $addressHydrator,
  65.         CustomerStructHydratorInterface $customerHydrator,
  66.         ClientInterface $client,
  67.         CartHasherInterface $cartHasher,
  68.         MerchantDataFactoryInterface $merchantDataFactory,
  69.         CustomOrderConverter $orderConverter,
  70.         OrderFetcherInterface $orderFetcher,
  71.         SessionInterface $session
  72.     ) {
  73.         $this->paymentHelper         $paymentHelper;
  74.         $this->requestHydrator       $requestHydrator;
  75.         $this->requestUpdateHydrator $requestUpdateHydrator;
  76.         $this->addressHydrator       $addressHydrator;
  77.         $this->customerHydrator      $customerHydrator;
  78.         $this->client                $client;
  79.         $this->cartHasher            $cartHasher;
  80.         $this->merchantDataFactory   $merchantDataFactory;
  81.         $this->orderConverter        $orderConverter;
  82.         $this->orderFetcher          $orderFetcher;
  83.         $this->session               $session;
  84.     }
  85.     public static function getSubscribedEvents(): array
  86.     {
  87.         return [
  88.             CheckoutConfirmPageLoadedEvent::class  => 'startKlarnaSession',
  89.             AccountEditOrderPageLoadedEvent::class => 'startKlarnaSession',
  90.             CheckoutOrderPlacedEvent::class        => 'resetKlarnaSession',
  91.             CustomerLoginEvent::class              => 'resetKlarnaSession',
  92.         ];
  93.     }
  94.     public function startKlarnaSession(PageLoadedEvent $event): void
  95.     {
  96.         $context $event->getSalesChannelContext();
  97.         if (!$this->paymentHelper->isKlarnaPaymentsEnabled($context)) {
  98.             return;
  99.         }
  100.         if ($event instanceof CheckoutConfirmPageLoadedEvent) {
  101.             $cart $event->getPage()->getCart();
  102.         } elseif ($event instanceof AccountEditOrderPageLoadedEvent) {
  103.             /** @phpstan-ignore-next-line */
  104.             $cart $this->convertCartFromOrder($event->getPage()->getOrder(), $event->getContext());
  105.         } else {
  106.             return;
  107.         }
  108.         if ($this->hasValidKlarnaSession()) {
  109.             $response $this->updateKlarnaSession($this->session->get(UpdateSessionRequestHydratorInterface::KLARNA_SESSION_ID), $cart$context);
  110.         } else {
  111.             $response $this->createKlarnaSession($cart$context);
  112.         }
  113.         if ($response->getHttpStatus() !== 200 && $response->getHttpStatus() !== 204) {
  114.             if ($this->paymentHelper->isKlarnaPaymentsSelected($context)) {
  115.                 $this->createErrorMessageExtension($event);
  116.             }
  117.             $this->removeAllKlarnaPaymentMethods($event->getPage());
  118.             return;
  119.         }
  120.         if ($response->getHttpStatus() === 200 && !$this->hasValidKlarnaSession()) {
  121.             $this->addKlarnaSessionToShopwareSession($response->getResponse());
  122.         }
  123.         $this->createSessionDataExtension($response$event->getPage(), $cart$context);
  124.         $this->removeDisabledKlarnaPaymentMethods($event->getPage());
  125.         $this->filterPayNowMethods($event->getPage());
  126.     }
  127.     public function resetKlarnaSession(Event $event): void
  128.     {
  129.         $this->session->remove(UpdateSessionRequestHydratorInterface::KLARNA_SESSION_ID);
  130.         $this->session->remove(UpdateSessionRequestHydratorInterface::KLARNA_CLIENT_TOKEN);
  131.         $this->session->remove(UpdateSessionRequestHydratorInterface::KLARNA_PAYMENT_METHOD_CATEGORIES);
  132.     }
  133.     private function filterPayNowMethods(Struct $page): void
  134.     {
  135.         if (!($page instanceof Page)) {
  136.             return;
  137.         }
  138.         /** @var null|SessionDataExtension $sessionData */
  139.         $sessionData $page->getExtension(SessionDataExtension::EXTENSION_NAME);
  140.         if (null === $sessionData) {
  141.             return;
  142.         }
  143.         foreach ($sessionData->getPaymentMethodCategories() as $paymentCategory) {
  144.             if ($paymentCategory['identifier'] === PaymentMethodInstaller::KLARNA_PAYMENTS_PAY_NOW_CODE) {
  145.                 $this->removeSeparatePayNowKlarnaPaymentMethods($page);
  146.                 return;
  147.             }
  148.         }
  149.         $this->removeCombinedKlarnaPaymentPayNowMethod($page);
  150.     }
  151.     private function createErrorMessageExtension(PageLoadedEvent $event): void
  152.     {
  153.         $errorMessageExtension = new ErrorMessageExtension(ErrorMessageExtension::GENERIC_ERROR);
  154.         $event->getPage()->addExtension(ErrorMessageExtension::EXTENSION_NAME$errorMessageExtension);
  155.     }
  156.     private function createSessionDataExtension(GenericResponse $responseStruct $pageCart $cartSalesChannelContext $context): void
  157.     {
  158.         if (!($page instanceof Page)) {
  159.             return;
  160.         }
  161.         $sessionData = new SessionDataExtension();
  162.         $sessionData->assign([
  163.             'selectedPaymentMethodCategory' => $this->getKlarnaCodeFromPaymentMethod($context),
  164.             'cartHash'                      => $this->cartHasher->generate($cart$context),
  165.         ]);
  166.         if ($this->hasValidKlarnaSession()) {
  167.             $sessionData->assign([
  168.                 'sessionId'               => $this->session->get(UpdateSessionRequestHydratorInterface::KLARNA_SESSION_ID),
  169.                 'clientToken'             => $this->session->get(UpdateSessionRequestHydratorInterface::KLARNA_CLIENT_TOKEN),
  170.                 'paymentMethodCategories' => $this->session->get(UpdateSessionRequestHydratorInterface::KLARNA_PAYMENT_METHOD_CATEGORIES),
  171.             ]);
  172.         } else {
  173.             $sessionData->assign([
  174.                 'sessionId'               => $response->getResponse()['session_id'],
  175.                 'clientToken'             => $response->getResponse()['client_token'],
  176.                 'paymentMethodCategories' => $response->getResponse()['payment_method_categories'],
  177.             ]);
  178.         }
  179.         if ($this->paymentHelper->isKlarnaPaymentsSelected($context)) {
  180.             $extraMerchantData $this->merchantDataFactory->getExtraMerchantData($sessionData$cart$context);
  181.             if (!empty($extraMerchantData->getAttachment())) {
  182.                 $attachment = new Attachment();
  183.                 $attachment->assign([
  184.                     'data' => $extraMerchantData->getAttachment(),
  185.                 ]);
  186.             } else {
  187.                 $attachment null;
  188.             }
  189.             $sessionData->assign([
  190.                 'customerData' => [
  191.                     'billing_address'  => $this->addressHydrator->hydrateFromContext($contextAddressStructHydrator::TYPE_BILLING),
  192.                     'shipping_address' => $this->addressHydrator->hydrateFromContext($contextAddressStructHydrator::TYPE_SHIPPING),
  193.                     'customer'         => $this->customerHydrator->hydrate($context),
  194.                     'merchant_data'    => $extraMerchantData->getMerchantData(),
  195.                     'attachment'       => $attachment,
  196.                 ],
  197.             ]);
  198.         }
  199.         $page->addExtension(SessionDataExtension::EXTENSION_NAME$sessionData);
  200.     }
  201.     private function removeDisabledKlarnaPaymentMethods(Struct $page): void
  202.     {
  203.         if (!($page instanceof Page)) {
  204.             return;
  205.         }
  206.         /** @var null|SessionDataExtension $sessionData */
  207.         $sessionData $page->getExtension(SessionDataExtension::EXTENSION_NAME);
  208.         if (null === $sessionData) {
  209.             return;
  210.         }
  211.         if (!method_exists($page'setPaymentMethods') || !method_exists($page'getPaymentMethods')) {
  212.             return;
  213.         }
  214.         $availablePaymentMethods = !empty($sessionData->getPaymentMethodCategories())
  215.             ? array_column($sessionData->getPaymentMethodCategories(), 'identifier')
  216.             : [];
  217.         $page->setPaymentMethods(
  218.             $page->getPaymentMethods()->filter(
  219.                 static function (PaymentMethodEntity $paymentMethod) use ($availablePaymentMethods) {
  220.                     if (!array_key_exists($paymentMethod->getId(), PaymentMethodInstaller::KLARNA_PAYMENTS_CODES)) {
  221.                         return true;
  222.                     }
  223.                     return in_array(PaymentMethodInstaller::KLARNA_PAYMENTS_CODES[$paymentMethod->getId()], $availablePaymentMethodstrue);
  224.                 }
  225.             )
  226.         );
  227.     }
  228.     private function removeSeparatePayNowKlarnaPaymentMethods(Page $page): void
  229.     {
  230.         if (!method_exists($page'setPaymentMethods') || !method_exists($page'getPaymentMethods')) {
  231.             return;
  232.         }
  233.         $page->setPaymentMethods(
  234.             $page->getPaymentMethods()->filter(
  235.                 static function (PaymentMethodEntity $paymentMethod) {
  236.                     if (!array_key_exists($paymentMethod->getId(), PaymentMethodInstaller::KLARNA_PAYMENTS_CODES)) {
  237.                         return true;
  238.                     }
  239.                     return in_array($paymentMethod->getId(), PaymentMethodInstaller::KLARNA_PAYMENTS_CODES_WITH_PAY_NOW_COMBINEDtrue);
  240.                 }
  241.             )
  242.         );
  243.     }
  244.     private function removeCombinedKlarnaPaymentPayNowMethod(Page $page): void
  245.     {
  246.         if (!method_exists($page'setPaymentMethods') || !method_exists($page'getPaymentMethods')) {
  247.             return;
  248.         }
  249.         $page->setPaymentMethods(
  250.             $page->getPaymentMethods()->filter(
  251.                 static function (PaymentMethodEntity $paymentMethod) {
  252.                     if (!array_key_exists($paymentMethod->getId(), PaymentMethodInstaller::KLARNA_PAYMENTS_CODES)) {
  253.                         return true;
  254.                     }
  255.                     return $paymentMethod->getId() !== PaymentMethodInstaller::KLARNA_PAY_NOW;
  256.                 }
  257.             )
  258.         );
  259.     }
  260.     private function removeAllKlarnaPaymentMethods(Struct $page): void
  261.     {
  262.         if (!($page instanceof Page) || !method_exists($page'setPaymentMethods') || !method_exists($page'getPaymentMethods')) {
  263.             return;
  264.         }
  265.         $page->setPaymentMethods(
  266.             $page->getPaymentMethods()->filter(
  267.                 static function (PaymentMethodEntity $paymentMethod) {
  268.                     if (array_key_exists($paymentMethod->getId(), PaymentMethodInstaller::KLARNA_PAYMENTS_CODES)) {
  269.                         return false;
  270.                     }
  271.                     return true;
  272.                 }
  273.             )
  274.         );
  275.     }
  276.     private function createKlarnaSession(Cart $cartSalesChannelContext $context): GenericResponse
  277.     {
  278.         $request $this->requestHydrator->hydrate($cart$context);
  279.         return $this->client->request($request$context->getContext());
  280.     }
  281.     private function updateKlarnaSession(string $sessionIdCart $cartSalesChannelContext $context): GenericResponse
  282.     {
  283.         $request $this->requestUpdateHydrator->hydrate($sessionId$cart$context);
  284.         return $this->client->request($request$context->getContext());
  285.     }
  286.     private function getKlarnaCodeFromPaymentMethod(SalesChannelContext $context): string
  287.     {
  288.         if (!array_key_exists($context->getPaymentMethod()->getId(), PaymentMethodInstaller::KLARNA_PAYMENTS_CODES)) {
  289.             return '';
  290.         }
  291.         return PaymentMethodInstaller::KLARNA_PAYMENTS_CODES[$context->getPaymentMethod()->getId()];
  292.     }
  293.     private function convertCartFromOrder(OrderEntity $orderEntityContext $context): Cart
  294.     {
  295.         $order $this->orderFetcher->getOrderFromOrder($orderEntity->getId(), $context);
  296.         if (null === $order) {
  297.             throw new LogicException('could not find order via id');
  298.         }
  299.         return $this->orderConverter->convertOrderToCart($order$context);
  300.     }
  301.     private function addKlarnaSessionToShopwareSession(array $klarnaSession): void
  302.     {
  303.         $this->session->set(UpdateSessionRequestHydratorInterface::KLARNA_SESSION_ID$klarnaSession['session_id']);
  304.         $this->session->set(UpdateSessionRequestHydratorInterface::KLARNA_CLIENT_TOKEN$klarnaSession['client_token']);
  305.         $this->session->set(UpdateSessionRequestHydratorInterface::KLARNA_PAYMENT_METHOD_CATEGORIES$klarnaSession['payment_method_categories']);
  306.     }
  307.     private function hasValidKlarnaSession(): bool
  308.     {
  309.         return $this->session->has(UpdateSessionRequestHydratorInterface::KLARNA_SESSION_ID) && $this->session->has(UpdateSessionRequestHydratorInterface::KLARNA_CLIENT_TOKEN) && $this->session->has(UpdateSessionRequestHydratorInterface::KLARNA_PAYMENT_METHOD_CATEGORIES);
  310.     }
  311. }