custom/plugins/SwagPayPal/src/Checkout/ExpressCheckout/ExpressCheckoutSubscriber.php line 136

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. /*
  3.  * (c) shopware AG <info@shopware.com>
  4.  * For the full copyright and license information, please view the LICENSE
  5.  * file that was distributed with this source code.
  6.  */
  7. namespace Swag\PayPal\Checkout\ExpressCheckout;
  8. use Psr\Log\LoggerInterface;
  9. use Shopware\Core\Checkout\Customer\CustomerEvents;
  10. use Shopware\Core\Content\Cms\Events\CmsPageLoadedEvent;
  11. use Shopware\Core\Content\Product\ProductCollection;
  12. use Shopware\Core\Framework\Event\DataMappingEvent;
  13. use Shopware\Core\Framework\Struct\ArrayStruct;
  14. use Shopware\Core\Framework\Validation\BuildValidationEvent;
  15. use Shopware\Core\System\SalesChannel\Entity\SalesChannelEntitySearchResultLoadedEvent;
  16. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  17. use Shopware\Core\System\SystemConfig\SystemConfigService;
  18. use Shopware\Storefront\Event\SwitchBuyBoxVariantEvent;
  19. use Shopware\Storefront\Page\Checkout\Cart\CheckoutCartPageLoadedEvent;
  20. use Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent;
  21. use Shopware\Storefront\Page\Checkout\Offcanvas\OffcanvasCartPageLoadedEvent;
  22. use Shopware\Storefront\Page\Checkout\Register\CheckoutRegisterPageLoadedEvent;
  23. use Shopware\Storefront\Page\Navigation\NavigationPageLoadedEvent;
  24. use Shopware\Storefront\Page\PageLoadedEvent;
  25. use Shopware\Storefront\Page\Product\ProductPageLoadedEvent;
  26. use Shopware\Storefront\Page\Search\SearchPageLoadedEvent;
  27. use Shopware\Storefront\Pagelet\PageletLoadedEvent;
  28. use Shopware\Storefront\Pagelet\Wishlist\GuestWishlistPageletLoadedEvent;
  29. use Swag\CmsExtensions\Storefront\Pagelet\Quickview\QuickviewPageletLoadedEvent;
  30. use Swag\PayPal\Checkout\Cart\Service\ExcludedProductValidator;
  31. use Swag\PayPal\Checkout\ExpressCheckout\Service\ExpressCheckoutDataServiceInterface;
  32. use Swag\PayPal\Checkout\ExpressCheckout\Service\ExpressCustomerService;
  33. use Swag\PayPal\Checkout\Payment\PayPalPaymentHandler;
  34. use Swag\PayPal\Setting\Exception\PayPalSettingsInvalidException;
  35. use Swag\PayPal\Setting\Service\SettingsValidationServiceInterface;
  36. use Swag\PayPal\Setting\Settings;
  37. use Swag\PayPal\Util\PaymentMethodUtil;
  38. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  39. /**
  40.  * @internal
  41.  */
  42. class ExpressCheckoutSubscriber implements EventSubscriberInterface
  43. {
  44.     public const PAYPAL_EXPRESS_CHECKOUT_BUTTON_DATA_EXTENSION_ID 'payPalEcsButtonData';
  45.     private ExpressCheckoutDataServiceInterface $expressCheckoutDataService;
  46.     private SettingsValidationServiceInterface $settingsValidationService;
  47.     private SystemConfigService $systemConfigService;
  48.     private PaymentMethodUtil $paymentMethodUtil;
  49.     private ExcludedProductValidator $excludedProductValidator;
  50.     private LoggerInterface $logger;
  51.     public function __construct(
  52.         ExpressCheckoutDataServiceInterface $service,
  53.         SettingsValidationServiceInterface $settingsValidationService,
  54.         SystemConfigService $systemConfigService,
  55.         PaymentMethodUtil $paymentMethodUtil,
  56.         ExcludedProductValidator $excludedProductValidator,
  57.         LoggerInterface $logger
  58.     ) {
  59.         $this->expressCheckoutDataService $service;
  60.         $this->settingsValidationService $settingsValidationService;
  61.         $this->systemConfigService $systemConfigService;
  62.         $this->paymentMethodUtil $paymentMethodUtil;
  63.         $this->excludedProductValidator $excludedProductValidator;
  64.         $this->logger $logger;
  65.     }
  66.     public static function getSubscribedEvents(): array
  67.     {
  68.         return [
  69.             CheckoutCartPageLoadedEvent::class => 'addExpressCheckoutDataToPage',
  70.             CheckoutRegisterPageLoadedEvent::class => 'addExpressCheckoutDataToPage',
  71.             NavigationPageLoadedEvent::class => 'addExpressCheckoutDataToPage',
  72.             OffcanvasCartPageLoadedEvent::class => 'addExpressCheckoutDataToPage',
  73.             ProductPageLoadedEvent::class => 'addExpressCheckoutDataToPage',
  74.             SearchPageLoadedEvent::class => 'addExpressCheckoutDataToPage',
  75.             'sales_channel.product.search.result.loaded' => 'addExcludedProductsToSearchResult',
  76.             QuickviewPageletLoadedEvent::class => 'addExpressCheckoutDataToPagelet',
  77.             GuestWishlistPageletLoadedEvent::class => 'addExpressCheckoutDataToPagelet',
  78.             SwitchBuyBoxVariantEvent::class => 'addExpressCheckoutDataToBuyBoxSwitch',
  79.             'framework.validation.address.create' => 'disableAddressValidation',
  80.             'framework.validation.customer.create' => 'disableCustomerValidation',
  81.             CheckoutConfirmPageLoadedEvent::class => 'onCheckoutConfirmLoaded',
  82.             CustomerEvents::MAPPING_REGISTER_CUSTOMER => 'addPayerIdToCustomer',
  83.         ];
  84.     }
  85.     /**
  86.      * @param CheckoutCartPageLoadedEvent|CheckoutRegisterPageLoadedEvent|NavigationPageLoadedEvent|OffcanvasCartPageLoadedEvent|ProductPageLoadedEvent|SearchPageLoadedEvent $event
  87.      */
  88.     public function addExpressCheckoutDataToPage(PageLoadedEvent $event): void
  89.     {
  90.         $addProductToCart $event instanceof ProductPageLoadedEvent
  91.             || $event instanceof NavigationPageLoadedEvent
  92.             || $event instanceof SearchPageLoadedEvent;
  93.         $expressCheckoutButtonData $this->getExpressCheckoutButtonData($event->getSalesChannelContext(), \get_class($event), $addProductToCart);
  94.         if ($expressCheckoutButtonData === null) {
  95.             return;
  96.         }
  97.         if ($event instanceof ProductPageLoadedEvent
  98.             && $this->excludedProductValidator->isProductExcluded($event->getPage()->getProduct(), $event->getSalesChannelContext())) {
  99.             return;
  100.         }
  101.         if (!$addProductToCart
  102.             && \method_exists($event->getPage(), 'getCart')
  103.             && $this->excludedProductValidator->cartContainsExcludedProduct($event->getPage()->getCart(), $event->getSalesChannelContext())) {
  104.             return;
  105.         }
  106.         $event->getPage()->addExtension(
  107.             self::PAYPAL_EXPRESS_CHECKOUT_BUTTON_DATA_EXTENSION_ID,
  108.             $expressCheckoutButtonData
  109.         );
  110.         $this->logger->debug('Added data to page {page}', ['page' => \get_class($event)]);
  111.     }
  112.     public function addExpressCheckoutDataToPagelet(PageletLoadedEvent $event): void
  113.     {
  114.         $expressCheckoutButtonData $this->getExpressCheckoutButtonData($event->getSalesChannelContext(), \get_class($event), true);
  115.         if ($expressCheckoutButtonData === null) {
  116.             return;
  117.         }
  118.         if ($event instanceof QuickviewPageletLoadedEvent
  119.             && $this->excludedProductValidator->isProductExcluded($event->getPagelet()->getProduct(), $event->getSalesChannelContext())) {
  120.             return;
  121.         }
  122.         $event->getPagelet()->addExtension(
  123.             self::PAYPAL_EXPRESS_CHECKOUT_BUTTON_DATA_EXTENSION_ID,
  124.             $expressCheckoutButtonData
  125.         );
  126.     }
  127.     public function addExpressCheckoutDataToBuyBoxSwitch(SwitchBuyBoxVariantEvent $event): void
  128.     {
  129.         $salesChannelContext $event->getSalesChannelContext();
  130.         $expressCheckoutButtonData $this->getExpressCheckoutButtonData($salesChannelContext\get_class($event), true);
  131.         if ($expressCheckoutButtonData === null) {
  132.             return;
  133.         }
  134.         $event->getProduct()->addExtension(
  135.             self::PAYPAL_EXPRESS_CHECKOUT_BUTTON_DATA_EXTENSION_ID,
  136.             $expressCheckoutButtonData
  137.         );
  138.     }
  139.     public function addExcludedProductsToSearchResult(SalesChannelEntitySearchResultLoadedEvent $event): void
  140.     {
  141.         if (!$this->checkSettings($event->getSalesChannelContext(), \get_class($event))) {
  142.             return;
  143.         }
  144.         $productIds = [];
  145.         /** @var ProductCollection $products */
  146.         $products $event->getResult()->getEntities();
  147.         foreach ($products as $product) {
  148.             $productIds[] = $product->getId();
  149.             $productIds[] = $product->getParentId();
  150.         }
  151.         $excluded $this->excludedProductValidator->findExcludedProducts(\array_filter($productIds), $event->getSalesChannelContext());
  152.         foreach ($products as $product) {
  153.             if (\in_array($product->getId(), $excludedtrue) || ($product->getParentId() && \in_array($product->getParentId(), $excludedtrue))) {
  154.                 $product->addExtension(ExcludedProductValidator::PRODUCT_EXCLUDED_FOR_PAYPAL, new ArrayStruct());
  155.             }
  156.         }
  157.     }
  158.     public function disableAddressValidation(BuildValidationEvent $event): void
  159.     {
  160.         if (!$event->getContext()->hasExtension(ExpressCustomerService::EXPRESS_CHECKOUT_ACTIVE)) {
  161.             return;
  162.         }
  163.         $event->getDefinition()->set('additionalAddressLine1')
  164.                                ->set('additionalAddressLine2')
  165.                                ->set('phoneNumber');
  166.     }
  167.     public function disableCustomerValidation(BuildValidationEvent $event): void
  168.     {
  169.         if (!$event->getContext()->hasExtension(ExpressCustomerService::EXPRESS_CHECKOUT_ACTIVE)) {
  170.             return;
  171.         }
  172.         $event->getDefinition()->set('birthdayDay')
  173.                                ->set('birthdayMonth')
  174.                                ->set('birthdayYear');
  175.     }
  176.     public function addPayerIdToCustomer(DataMappingEvent $event): void
  177.     {
  178.         if (!$event->getContext()->hasExtension(ExpressCustomerService::EXPRESS_CHECKOUT_ACTIVE)) {
  179.             return;
  180.         }
  181.         $input $event->getInput();
  182.         $output $event->getOutput();
  183.         $output['customFields'][ExpressCustomerService::EXPRESS_PAYER_ID] = $input->get(ExpressCustomerService::EXPRESS_PAYER_ID);
  184.         $event->setOutput($output);
  185.     }
  186.     public function onCheckoutConfirmLoaded(CheckoutConfirmPageLoadedEvent $event): void
  187.     {
  188.         if ($event->getRequest()->query->has(PayPalPaymentHandler::PAYPAL_EXPRESS_CHECKOUT_ID) === false) {
  189.             return;
  190.         }
  191.         $confirmPage $event->getPage();
  192.         $payPalPaymentMethodId $this->paymentMethodUtil->getPayPalPaymentMethodId($event->getContext());
  193.         if ($payPalPaymentMethodId === null) {
  194.             return;
  195.         }
  196.         $paymentMethods $confirmPage->getPaymentMethods();
  197.         if ($paymentMethods->has($payPalPaymentMethodId) === false) {
  198.             return;
  199.         }
  200.         $filtered $paymentMethods->filterByProperty('id'$payPalPaymentMethodId);
  201.         $confirmPage->setPaymentMethods($filtered);
  202.         $this->logger->debug('Removed other payment methods from selection for Express Checkout');
  203.     }
  204.     private function getExpressCheckoutButtonData(
  205.         SalesChannelContext $salesChannelContext,
  206.         string $eventName,
  207.         bool $addProductToCart false
  208.     ): ?ExpressCheckoutButtonData {
  209.         if (!$this->checkSettings($salesChannelContext$eventName)) {
  210.             return null;
  211.         }
  212.         return $this->expressCheckoutDataService->buildExpressCheckoutButtonData(
  213.             $salesChannelContext,
  214.             $addProductToCart
  215.         );
  216.     }
  217.     private function checkSettings(SalesChannelContext $contextstring $eventName): bool
  218.     {
  219.         if ($this->paymentMethodUtil->isPaypalPaymentMethodInSalesChannel($context) === false) {
  220.             return false;
  221.         }
  222.         try {
  223.             $this->settingsValidationService->validate($context->getSalesChannelId());
  224.         } catch (PayPalSettingsInvalidException $e) {
  225.             return false;
  226.         }
  227.         if ($this->expressOptionForEventEnabled($context->getSalesChannelId(), $eventName) === false) {
  228.             return false;
  229.         }
  230.         return true;
  231.     }
  232.     private function expressOptionForEventEnabled(string $salesChannelIdstring $eventName): bool
  233.     {
  234.         switch ($eventName) {
  235.             case ProductPageLoadedEvent::class:
  236.             case QuickviewPageletLoadedEvent::class:
  237.                 return $this->systemConfigService->getBool(Settings::ECS_DETAIL_ENABLED$salesChannelId);
  238.             case OffcanvasCartPageLoadedEvent::class:
  239.                 return $this->systemConfigService->getBool(Settings::ECS_OFF_CANVAS_ENABLED$salesChannelId);
  240.             case CheckoutRegisterPageLoadedEvent::class:
  241.                 return $this->systemConfigService->getBool(Settings::ECS_LOGIN_ENABLED$salesChannelId);
  242.             case CheckoutCartPageLoadedEvent::class:
  243.                 return $this->systemConfigService->getBool(Settings::ECS_CART_ENABLED$salesChannelId);
  244.             case NavigationPageLoadedEvent::class:
  245.             case CmsPageLoadedEvent::class:
  246.             case SearchPageLoadedEvent::class:
  247.             case GuestWishlistPageletLoadedEvent::class:
  248.             case SwitchBuyBoxVariantEvent::class:
  249.             case SalesChannelEntitySearchResultLoadedEvent::class:
  250.                 return $this->systemConfigService->getBool(Settings::ECS_LISTING_ENABLED$salesChannelId);
  251.             default:
  252.                 return false;
  253.         }
  254.     }
  255. }