custom/plugins/AdyenPaymentShopware6/src/Subscriber/PaymentSubscriber.php line 295

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. /**
  3.  *                       ######
  4.  *                       ######
  5.  * ############    ####( ######  #####. ######  ############   ############
  6.  * #############  #####( ######  #####. ######  #############  #############
  7.  *        ######  #####( ######  #####. ######  #####  ######  #####  ######
  8.  * ###### ######  #####( ######  #####. ######  #####  #####   #####  ######
  9.  * ###### ######  #####( ######  #####. ######  #####          #####  ######
  10.  * #############  #############  #############  #############  #####  ######
  11.  *  ############   ############  #############   ############  #####  ######
  12.  *                                      ######
  13.  *                               #############
  14.  *                               ############
  15.  *
  16.  * Adyen Payment Module
  17.  *
  18.  * Copyright (c) 2020 Adyen B.V.
  19.  * This file is open source and available under the MIT license.
  20.  * See the LICENSE file for more info.
  21.  *
  22.  * Author: Adyen <shopware@adyen.com>
  23.  */
  24. namespace Adyen\Shopware\Subscriber;
  25. use Adyen\Shopware\Handlers\OneClickPaymentMethodHandler;
  26. use Adyen\Shopware\Provider\AdyenPluginProvider;
  27. use Adyen\Shopware\Service\ConfigurationService;
  28. use Adyen\Shopware\Service\PaymentMethodsFilterService;
  29. use Adyen\Shopware\Service\PaymentMethodsService;
  30. use Adyen\Shopware\Service\PaymentStateDataService;
  31. use Adyen\Shopware\Service\Repository\SalesChannelRepository;
  32. use Adyen\Util\Currency;
  33. use Shopware\Core\Checkout\Cart\AbstractCartPersister;
  34. use Shopware\Core\Checkout\Cart\CartCalculator;
  35. use Shopware\Core\Checkout\Cart\Exception\CartTokenNotFoundException;
  36. use Shopware\Core\Framework\Struct\ArrayEntity;
  37. use Shopware\Core\Framework\Validation\DataBag\RequestDataBag;
  38. use Shopware\Core\System\SalesChannel\Context\AbstractSalesChannelContextFactory;
  39. use Shopware\Core\System\SalesChannel\Context\SalesChannelContextService;
  40. use Shopware\Core\System\SalesChannel\SalesChannel\AbstractContextSwitchRoute;
  41. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  42. use Shopware\Storefront\Framework\AffiliateTracking\AffiliateTrackingListener;
  43. use Shopware\Storefront\Page\Account\Order\AccountEditOrderPageLoadedEvent;
  44. use Shopware\Storefront\Page\Checkout\Cart\CheckoutCartPage;
  45. use Shopware\Storefront\Page\Checkout\Cart\CheckoutCartPageLoadedEvent;
  46. use Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent;
  47. use Shopware\Storefront\Page\Checkout\Offcanvas\OffcanvasCartPage;
  48. use Shopware\Storefront\Page\Checkout\Offcanvas\OffcanvasCartPageLoadedEvent;
  49. use Shopware\Storefront\Page\Checkout\Register\CheckoutRegisterPageLoadedEvent;
  50. use Shopware\Storefront\Page\PageLoadedEvent;
  51. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  52. use Symfony\Component\HttpFoundation\RedirectResponse;
  53. use Symfony\Component\HttpFoundation\RequestStack;
  54. use Symfony\Component\HttpKernel\Event\RequestEvent;
  55. use Symfony\Component\Routing\RouterInterface;
  56. class PaymentSubscriber extends StorefrontSubscriber implements EventSubscriberInterface
  57. {
  58.     /**
  59.      * @var PaymentStateDataService
  60.      */
  61.     private $paymentStateDataService;
  62.     /**
  63.      * @var PaymentMethodsFilterService
  64.      */
  65.     private $paymentMethodsFilterService;
  66.     /**
  67.      * @var RouterInterface
  68.      */
  69.     private $router;
  70.     /**
  71.      * @var SalesChannelRepository
  72.      */
  73.     private $salesChannelRepository;
  74.     /**
  75.      * @var ConfigurationService
  76.      */
  77.     private $configurationService;
  78.     /**
  79.      * @var PaymentMethodsService
  80.      */
  81.     private $paymentMethodsService;
  82.     /**
  83.      * @var RequestStack $requestStack
  84.      */
  85.     private $requestStack;
  86.     /**
  87.      * @var AbstractCartPersister
  88.      */
  89.     private $cartPersister;
  90.     /**
  91.      * @var CartCalculator
  92.      */
  93.     private $cartCalculator;
  94.     /**
  95.      * @var Currency
  96.      */
  97.     private $currency;
  98.     /**
  99.      * @var AdyenPluginProvider
  100.      */
  101.     private $adyenPluginProvider;
  102.     /**
  103.      * @var AbstractContextSwitchRoute
  104.      */
  105.     private $contextSwitchRoute;
  106.     /**
  107.      * @var AbstractSalesChannelContextFactory
  108.      */
  109.     private $salesChannelContextFactory;
  110.     /**
  111.      * PaymentSubscriber constructor.
  112.      *
  113.      * @param AdyenPluginProvider $adyenPluginProvider
  114.      * @param PaymentMethodsFilterService $paymentMethodsFilterService
  115.      * @param PaymentStateDataService $paymentStateDataService
  116.      * @param RouterInterface $router
  117.      * @param SalesChannelRepository $salesChannelRepository
  118.      * @param ConfigurationService $configurationService
  119.      * @param PaymentMethodsService $paymentMethodsService
  120.      * @param RequestStack $requestStack
  121.      * @param AbstractCartPersister $cartPersister
  122.      * @param CartCalculator $cartCalculator
  123.      * @param AbstractContextSwitchRoute $contextSwitchRoute
  124.      * @param AbstractSalesChannelContextFactory $salesChannelContextFactory
  125.      * @param Currency $currency
  126.      */
  127.     public function __construct(
  128.         AdyenPluginProvider $adyenPluginProvider,
  129.         PaymentMethodsFilterService $paymentMethodsFilterService,
  130.         PaymentStateDataService $paymentStateDataService,
  131.         RouterInterface $router,
  132.         SalesChannelRepository $salesChannelRepository,
  133.         ConfigurationService $configurationService,
  134.         PaymentMethodsService $paymentMethodsService,
  135.         RequestStack $requestStack,
  136.         AbstractCartPersister $cartPersister,
  137.         CartCalculator $cartCalculator,
  138.         AbstractContextSwitchRoute $contextSwitchRoute,
  139.         AbstractSalesChannelContextFactory $salesChannelContextFactory,
  140.         Currency $currency
  141.     ) {
  142.         $this->paymentStateDataService = $paymentStateDataService;
  143.         $this->paymentMethodsFilterService = $paymentMethodsFilterService;
  144.         $this->router = $router;
  145.         $this->salesChannelRepository = $salesChannelRepository;
  146.         $this->configurationService = $configurationService;
  147.         $this->paymentMethodsService = $paymentMethodsService;
  148.         $this->requestStack = $requestStack;
  149.         $this->cartPersister = $cartPersister;
  150.         $this->cartCalculator = $cartCalculator;
  151.         $this->contextSwitchRoute = $contextSwitchRoute;
  152.         $this->salesChannelContextFactory = $salesChannelContextFactory;
  153.         $this->currency = $currency;
  154.         $this->adyenPluginProvider = $adyenPluginProvider;
  155.     }
  156.     /**
  157.      * @return array|string[]
  158.      */
  159.     public static function getSubscribedEvents(): array
  160.     {
  161.         return [
  162.             CheckoutCartPageLoadedEvent::class => 'onShoppingCartLoaded',
  163.             OffcanvasCartPageLoadedEvent::class => 'onShoppingCartLoaded',
  164.             CheckoutRegisterPageLoadedEvent::class => 'onShoppingCartLoaded',
  165.             CheckoutConfirmPageLoadedEvent::class => 'onCheckoutConfirmLoaded',
  166.             AccountEditOrderPageLoadedEvent::class => 'onCheckoutConfirmLoaded',
  167.             RequestEvent::class => 'onKernelRequest',
  168.         ];
  169.     }
  170.     private function getComponentData(SalesChannelContext $salesChannelContext): array
  171.     {
  172.         $salesChannelId = $salesChannelContext->getSalesChannelId();
  173.         return [
  174.             'clientKey' => $this->configurationService->getClientKey($salesChannelId),
  175.             'locale' => $this->salesChannelRepository->getSalesChannelLocale($salesChannelContext),
  176.             'environment' => $this->configurationService->getEnvironment($salesChannelId),
  177.             'merchantAccount' => $this->configurationService->getMerchantAccount($salesChannelId)
  178.         ];
  179.     }
  180.     /**
  181.      * @param PageLoadedEvent $event
  182.      */
  183.     public function onShoppingCartLoaded(PageLoadedEvent $event)
  184.     {
  185.         /** @var CheckoutCartPage|OffcanvasCartPage $page */
  186.         $page = $event->getPage();
  187.         if ($page->getCart()->getLineItems()->count() === 0) {
  188.             return;
  189.         }
  190.         $salesChannelContext = $event->getSalesChannelContext();
  191.         $shopwarePaymentMethods = null;
  192.         if ($page instanceof CheckoutCartPage) {
  193.             $shopwarePaymentMethods = $page->getPaymentMethods();
  194.         }
  195.         $currency = $salesChannelContext->getCurrency()->getIsoCode();
  196.         $currencySymbol = $salesChannelContext->getCurrency()->getSymbol();
  197.         $amountInMinorUnits = $this->currency->sanitize($page->getCart()->getPrice()->getTotalPrice(), $currency);
  198.         $paymentMethods = $this->paymentMethodsService->getPaymentMethods($salesChannelContext);
  199.         $giftcards = $this->paymentMethodsFilterService->getAvailableGiftcards(
  200.             $salesChannelContext,
  201.             $paymentMethods,
  202.             $this->adyenPluginProvider->getAdyenPluginId(),
  203.             $shopwarePaymentMethods
  204.         );
  205.         $selectedPaymentMethodId = null;
  206.         $giftcardData = $this->paymentStateDataService
  207.             ->getPaymentStateDataFromContextToken($salesChannelContext->getToken());
  208.         $giftcardDiscount = 0;
  209.         $giftcardBalance = 0;
  210.         if ($giftcardData) {
  211.             $stateData = $giftcardData->getStateData();
  212.             $giftcardDiscount = json_decode($stateData, true)['additionalData']['amount'] ?? 0;
  213.             $selectedPaymentMethodId = json_decode($stateData, true)['additionalData']['paymentMethodId'] ?? 0;
  214.             $giftcardBalance = json_decode($stateData, true)['additionalData']['balance'] ?? 0;
  215.             // update discount amount if total becomes less than discount
  216.             if ((int) $giftcardDiscount > $amountInMinorUnits) {
  217.                 $newBalance = ($giftcardDiscount - $amountInMinorUnits) + $giftcardBalance;
  218.                 $this->paymentStateDataService->insertPaymentStateData(
  219.                     $salesChannelContext->getToken(),
  220.                     $stateData,
  221.                     [
  222.                         'amount' => $amountInMinorUnits,
  223.                         'paymentMethodId' => $selectedPaymentMethodId,
  224.                         'balance' => $newBalance,
  225.                     ]
  226.                 );
  227.                 $giftcardDiscount = $amountInMinorUnits;
  228.                 $this->contextSwitchRoute->switchContext(
  229.                     new RequestDataBag(
  230.                         [
  231.                             SalesChannelContextService::PAYMENT_METHOD_ID => $selectedPaymentMethodId
  232.                         ]
  233.                     ),
  234.                     $salesChannelContext
  235.                 );
  236.             }
  237.         }
  238.         $minorUnitsQuotient = 1;
  239.         if (0 < $page->getCart()->getPrice()->getTotalPrice()) {
  240.             $minorUnitsQuotient = $amountInMinorUnits / $page->getCart()->getPrice()->getTotalPrice();
  241.         }
  242.         $page->addExtension(
  243.             self::ADYEN_DATA_EXTENSION_ID,
  244.             new ArrayEntity(
  245.                 array_merge($this->getComponentData($salesChannelContext), [
  246.                     'giftcards' => $giftcards->getElements(),
  247.                     'totalPrice' => $page->getCart()->getPrice()->getTotalPrice(),
  248.                     'totalInMinorUnits' => $amountInMinorUnits,
  249.                     'currency' => $currency,
  250.                     'currencySymbol' => $currencySymbol,
  251.                     'giftcardDiscount' => $giftcardDiscount,
  252.                     'giftcardDiscountDisplay' => $giftcardDiscount / $minorUnitsQuotient,
  253.                     'giftcardBalance' => $giftcardBalance,
  254.                     'checkBalanceUrl' => $this->router
  255.                         ->generate('payment.adyen.proxy-check-balance'),
  256.                     'setGiftcardUrl' => $this->router->generate('payment.adyen.proxy-store-giftcard-state-data'),
  257.                     'removeGiftcardUrl' => $this->router->generate('payment.adyen.proxy-remove-giftcard-state-data'),
  258.                     'switchContextUrl' => $this->router->generate('payment.adyen.proxy-switch-context'),
  259.                     'shoppingCartPageUrl' => $this->router->generate('frontend.checkout.cart.page'),
  260.                 ])
  261.             )
  262.         );
  263.     }
  264.     /**
  265.      * Adds vars to frontend template to be used in JS
  266.      *
  267.      * @param PageLoadedEvent $event
  268.      */
  269.     public function onCheckoutConfirmLoaded(PageLoadedEvent $event)
  270.     {
  271.         $salesChannelContext = $event->getSalesChannelContext();
  272.         $selectedPaymentMethod = $salesChannelContext->getPaymentMethod();
  273.         $page = $event->getPage();
  274.         $orderId = '';
  275.         $affiliateCode = $this->requestStack->getSession()->get(AffiliateTrackingListener::AFFILIATE_CODE_KEY);
  276.         $campaignCode = $this->requestStack->getSession()->get(AffiliateTrackingListener::CAMPAIGN_CODE_KEY);
  277.         if (method_exists($page, 'getOrder')) {
  278.             $orderId = $page->getOrder()->getId();
  279.         }
  280.         $currency = $salesChannelContext->getCurrency()->getIsoCode();
  281.         $totalPrice = 0;
  282.         try {
  283.             $cart = $this->cartCalculator->calculate(
  284.                 $this->cartPersister->load($salesChannelContext->getToken(), $salesChannelContext),
  285.                 $salesChannelContext
  286.             );
  287.             $totalPrice = $cart->getPrice()->getTotalPrice();
  288.         } catch (CartTokenNotFoundException $exception) {
  289.             $cart = null;
  290.             if (!empty($orderId)) {
  291.                 $totalPrice = $page->getOrder()->getPrice()->getTotalPrice();
  292.             }
  293.         }
  294.         $amount = $this->currency->sanitize($totalPrice, $currency);
  295.         $adyenPluginId = $this->adyenPluginProvider->getAdyenPluginId();
  296.         $displaySaveCreditCardOption = $this->paymentMethodsFilterService->isPaymentMethodInCollection(
  297.             $page->getPaymentMethods(),
  298.             OneClickPaymentMethodHandler::getPaymentMethodCode(),
  299.             $adyenPluginId,
  300.         );
  301.         $paymentMethodsResponse = $this->paymentMethodsService->getPaymentMethods($salesChannelContext, $orderId);
  302.         $giftcardData = $this->paymentStateDataService
  303.             ->getPaymentStateDataFromContextToken($salesChannelContext->getToken());
  304.         $giftcardDiscount = 0;
  305.         $giftcardSelectedId = null;
  306.         $adyenGiftcardSelected = ($selectedPaymentMethod->getPluginId() === $adyenPluginId)
  307.             && $selectedPaymentMethod->getHandlerIdentifier()::$isGiftCard;
  308.         if ($giftcardData) {
  309.             $stateData = $giftcardData->getStateData();
  310.             $giftcardDiscount = json_decode($stateData, true)['additionalData']['amount'] ?? 0;
  311.             if ($giftcardDiscount >= $amount) {
  312.                 $giftcardSelectedId = json_decode($stateData, true)['additionalData']['paymentMethodId'];
  313.             }
  314.         }
  315.         $filteredPaymentMethods = $this->paymentMethodsFilterService->filterShopwarePaymentMethods(
  316.             $page->getPaymentMethods(),
  317.             $salesChannelContext,
  318.             $adyenPluginId,
  319.             $paymentMethodsResponse,
  320.             $giftcardSelectedId
  321.         );
  322.         if (!isset($giftcardSelectedId) && $adyenGiftcardSelected) {
  323.             $selectedPaymentMethod = $filteredPaymentMethods->first();
  324.             $this->contextSwitchRoute->switchContext(
  325.                 new RequestDataBag(
  326.                     [
  327.                         SalesChannelContextService::PAYMENT_METHOD_ID => $selectedPaymentMethod->getId()
  328.                     ]
  329.                 ),
  330.                 $salesChannelContext
  331.             );
  332.             $adyenGiftcardSelected = false;
  333.         }
  334.         $currencySymbol = $salesChannelContext->getCurrency()->getSymbol();
  335.         $page->setPaymentMethods($filteredPaymentMethods);
  336.         $page->addExtension(
  337.             self::ADYEN_DATA_EXTENSION_ID,
  338.             new ArrayEntity(
  339.                 array_merge(
  340.                     $this->getComponentData($salesChannelContext),
  341.                     [
  342.                         'paymentStatusUrl' => $this->router->generate('payment.adyen.proxy-payment-status'),
  343.                         'createOrderUrl' => $this->router->generate('payment.adyen.proxy-create-adyen-order'),
  344.                         'checkoutOrderUrl' => $this->router->generate('payment.adyen.proxy-checkout-order'),
  345.                         'paymentHandleUrl' => $this->router->generate('payment.adyen.proxy-handle-payment'),
  346.                         'paymentDetailsUrl' => $this->router->generate('payment.adyen.proxy-payment-details'),
  347.                         'updatePaymentUrl' => $this->router->generate('payment.adyen.proxy-set-payment'),
  348.                         'paymentFinishUrl' => $this->router->generate(
  349.                             'frontend.checkout.finish.page',
  350.                             ['orderId' => '']
  351.                         ),
  352.                         'paymentErrorUrl' => $this->router->generate(
  353.                             'frontend.checkout.finish.page',
  354.                             [
  355.                                 'orderId' => '',
  356.                                 'changedPayment' => false,
  357.                                 'paymentFailed' => true,
  358.                             ]
  359.                         ),
  360.                         'cancelOrderTransactionUrl' => $this->router->generate(
  361.                             'payment.adyen.proxy-cancel-order-transaction',
  362.                         ),
  363.                         'languageId' => $salesChannelContext->getContext()->getLanguageId(),
  364.                         'currency' => $currency,
  365.                         'amount' => $amount,
  366.                         'paymentMethodsResponse' => json_encode($paymentMethodsResponse),
  367.                         'orderId' => $orderId,
  368.                         'pluginId' => $this->adyenPluginProvider->getAdyenPluginId(),
  369.                         'totalPrice' => $totalPrice,
  370.                         'giftcardDiscount' => $giftcardDiscount,
  371.                         'currencySymbol' => $currencySymbol,
  372.                         'payInFullWithGiftcard' => (int) isset($giftcardSelectedId),
  373.                         'adyenGiftcardSelected' => (int) $adyenGiftcardSelected,
  374.                         'storedPaymentMethods' => $paymentMethodsResponse['storedPaymentMethods'] ?? [],
  375.                         'selectedPaymentMethodHandler' => $selectedPaymentMethod->getFormattedHandlerIdentifier(),
  376.                         'selectedPaymentMethodPluginId' => $selectedPaymentMethod->getPluginId(),
  377.                         'displaySaveCreditCardOption' => $displaySaveCreditCardOption,
  378.                         'billingAddressStreetHouse' => $this->paymentMethodsService->getSplitStreetAddressHouseNumber(
  379.                             $salesChannelContext->getCustomer()->getActiveBillingAddress()->getStreet()
  380.                         ),
  381.                         'shippingAddressStreetHouse' => $this->paymentMethodsService->getSplitStreetAddressHouseNumber(
  382.                             $salesChannelContext->getCustomer()->getActiveShippingAddress()->getStreet()
  383.                         ),
  384.                         'affiliateCode' => $affiliateCode,
  385.                         'campaignCode' => $campaignCode,
  386.                     ]
  387.                 )
  388.             )
  389.         );
  390.     }
  391.     public function onKernelRequest(RequestEvent $event)
  392.     {
  393.         $request = $event->getRequest();
  394.         if (($request->attributes->get('_route') === 'frontend.account.edit-order.change-payment-method')
  395.             && $request->request->has('adyenStateData')) {
  396.             $this->contextSwitchRoute->switchContext(
  397.                 new RequestDataBag(
  398.                     [
  399.                         SalesChannelContextService::PAYMENT_METHOD_ID => $request->request->get('paymentMethodId'),
  400.                         'adyenStateData' => $request->request->get('adyenStateData'),
  401.                         'adyenOrigin' => $request->request->get('adyenOrigin'),
  402.                     ]
  403.                 ),
  404.                 $this->salesChannelContextFactory->create(
  405.                     $this->requestStack->getSession()->get('sw-context-token'),
  406.                     $request->attributes->get('sw-sales-channel-id')
  407.                 )
  408.             );
  409.             $event->setResponse(
  410.                 new RedirectResponse(
  411.                     $this->router->generate(
  412.                         'frontend.account.edit-order.page',
  413.                         ['orderId' => $request->attributes->get('orderId')]
  414.                     )
  415.                 )
  416.             );
  417.         }
  418.     }
  419. }