From 57c7836021aabb43a21f27de5cb38e5f1f60ca00 Mon Sep 17 00:00:00 2001 From: Pavel Bystritsky Date: Fri, 8 Dec 2023 13:45:19 +0100 Subject: [PATCH] Q1-657: Display the PayPal Checkout button in the PDP and in search results. (#6) * Q1-657: Display the PayPal Checkout button in the PDP and in search results. * Make PayPal Checkout button in the PDP configurable * Make PayPal Checkout button in the PDP configurable * Make PayPal Checkout button in the PDP configurable --- Block/System/Config/Form/Field/Status.php | 71 ++++++++++ Block/System/Config/Form/Field/Toggle.php | 2 +- Model/Config.php | 48 ++++++- .../Config/Backend/InvalidateBlockBackend.php | 46 +++++++ Model/Config/Source/ProductTypeSource.php | 54 ++++++++ ViewModel/Instant.php | 129 ++++++++++++++++++ etc/adminhtml/system.xml | 30 +++- etc/config.xml | 2 + .../layout/adminhtml_system_config_edit.xml | 7 + view/adminhtml/web/css/system-styles.less | 25 ++++ view/adminhtml/web/images/disabled.svg | 10 ++ view/adminhtml/web/images/enabled.svg | 10 ++ view/frontend/layout/catalog_product_view.xml | 28 ++++ .../catalog_product_view_type_bundle.xml | 19 +++ .../templates/instant/product_button.phtml | 27 ++++ view/frontend/web/js/proceed-to-checkout.js | 40 ++++++ 16 files changed, 539 insertions(+), 9 deletions(-) create mode 100644 Block/System/Config/Form/Field/Status.php create mode 100644 Model/Config/Backend/InvalidateBlockBackend.php create mode 100644 Model/Config/Source/ProductTypeSource.php create mode 100644 ViewModel/Instant.php create mode 100644 view/adminhtml/layout/adminhtml_system_config_edit.xml create mode 100644 view/adminhtml/web/css/system-styles.less create mode 100644 view/adminhtml/web/images/disabled.svg create mode 100644 view/adminhtml/web/images/enabled.svg create mode 100644 view/frontend/layout/catalog_product_view.xml create mode 100644 view/frontend/layout/catalog_product_view_type_bundle.xml create mode 100644 view/frontend/templates/instant/product_button.phtml create mode 100644 view/frontend/web/js/proceed-to-checkout.js diff --git a/Block/System/Config/Form/Field/Status.php b/Block/System/Config/Form/Field/Status.php new file mode 100644 index 0000000..e510fb1 --- /dev/null +++ b/Block/System/Config/Form/Field/Status.php @@ -0,0 +1,71 @@ +config = $config; + $this->payPalConfig = $payPalConfig; + $this->storeManager = $storeManager; + } + + /** + * @inheritDoc + */ + protected function _renderValue(AbstractElement $element) + { + $websiteId = (int)$this->config->getWebsite() ?: (int)$this->storeManager->getWebsite(true)->getId(); + $status = $this->payPalConfig->getIsPaypalFlowEnabled($websiteId); + $statusText = $status ? __('PayPal Checkout Flow is enabled') : __('PayPal Checkout Flow is disabled'); + $class = $status ? 'enabled' : 'disabled'; + $element->setText( + sprintf('%s', $class, $statusText) + ); + + return parent::_renderValue($element); + } +} diff --git a/Block/System/Config/Form/Field/Toggle.php b/Block/System/Config/Form/Field/Toggle.php index b3535db..4294385 100644 --- a/Block/System/Config/Form/Field/Toggle.php +++ b/Block/System/Config/Form/Field/Toggle.php @@ -84,7 +84,7 @@ public function getButtonHtml() */ private function getLabel(): string { - return $this->isEnabled() ? 'Disable' : 'Enable'; + return $this->isEnabled() ? 'Disable PayPal Checkout Flow' : 'Enable PayPal Checkout Flow'; } /** diff --git a/Model/Config.php b/Model/Config.php index 6ca4aa1..d204e7e 100644 --- a/Model/Config.php +++ b/Model/Config.php @@ -18,6 +18,8 @@ class Config { private const PATH_TYPE = 'checkout/bold_checkout_base/type'; private const PATH_IS_PAYPAL_FLOW_ENABLED = 'checkout/bold_checkout_paypal/is_enabled'; + private const PATH_IS_INSTANT_ON_PRODUCT_PAGE_ENABLED = 'checkout/bold_checkout_paypal/is_instant_product'; + private const PATH_IS_INSTANT_ENABLED_FOR = 'checkout/bold_checkout_paypal/instant_for'; private const PATH_PAYPAL_FLOW_ID = 'checkout/bold_checkout_paypal/flow_id'; /** @@ -47,10 +49,10 @@ class Config * @param TypeListInterface $cacheTypeList */ public function __construct( - ScopeConfigInterface $scopeConfig, + ScopeConfigInterface $scopeConfig, ConfigManagementInterface $configManagement, - WriterInterface $configWriter, - TypeListInterface $cacheTypeList + WriterInterface $configWriter, + TypeListInterface $cacheTypeList ) { $this->scopeConfig = $scopeConfig; $this->configManagement = $configManagement; @@ -65,7 +67,8 @@ public function __construct( * @return bool * @throws LocalizedException */ - public function getIsPaypalFlowEnabled(int $websiteId): bool { + public function getIsPaypalFlowEnabled(int $websiteId): bool + { return $this->configManagement->isSetFlag( self::PATH_IS_PAYPAL_FLOW_ENABLED, $websiteId @@ -123,4 +126,41 @@ public function setCheckoutTypeParallel(int $websiteId): void $this->cacheTypeList->cleanType('config'); $this->scopeConfig->clean(); } + + /** + * Get if the Instant Checkout button is enabled on Product page. + * + * @param int $websiteId + * @return bool + * @throws LocalizedException + */ + public function isProductPageInstantCheckoutEnabled(int $websiteId): bool + { + return $this->configManagement->isSetFlag( + self::PATH_IS_INSTANT_ON_PRODUCT_PAGE_ENABLED, + $websiteId + ); + } + + /** + * Get if the Instant Checkout button is enabled for Product type. + * + * @param int $websiteId + * @param string $productType + * @return bool + * @throws LocalizedException + */ + public function isProductPageInstantCheckoutEnabledForType(int $websiteId, string $productType): bool + { + return in_array( + $productType, + explode( + ',', + $this->configManagement->getValue( + self::PATH_IS_INSTANT_ENABLED_FOR, + $websiteId + ) + ) + ); + } } diff --git a/Model/Config/Backend/InvalidateBlockBackend.php b/Model/Config/Backend/InvalidateBlockBackend.php new file mode 100644 index 0000000..c90b0bc --- /dev/null +++ b/Model/Config/Backend/InvalidateBlockBackend.php @@ -0,0 +1,46 @@ +isValueChanged()) { + $this->cacheTypeList->invalidate(Block::TYPE_IDENTIFIER); + $this->cacheTypeList->invalidate(Type::TYPE_IDENTIFIER); + } + + return parent::afterSave(); + } +} diff --git a/Model/Config/Source/ProductTypeSource.php b/Model/Config/Source/ProductTypeSource.php new file mode 100644 index 0000000..5741130 --- /dev/null +++ b/Model/Config/Source/ProductTypeSource.php @@ -0,0 +1,54 @@ +additionalProductTypes = $additionalProductTypes; + } + + /** + * @inheritDoc + */ + public function toOptionArray() + { + $result = [ + ['value' => Type::TYPE_SIMPLE, 'label' => __('Simple Products')], + ['value' => Type::TYPE_VIRTUAL, 'label' => __('Virtual Products')], + ['value' => DownloadableType::TYPE_DOWNLOADABLE, 'label' => __('Downloadable Products')], + ['value' => GroupedType::TYPE_CODE, 'label' => __('Grouped Products')], + ['value' => ConfigurableType::TYPE_CODE, 'label' => __('Configurable Products')], + ['value' => BundleType::TYPE_CODE, 'label' => __('Bundle Products')], + ]; + if (class_exists(GiftcardType::class)) { + $result[] = ['value' => GiftcardType::TYPE_GIFTCARD, 'label' => __('Gift Cards')]; + } + foreach ($this->additionalProductTypes as $typeValue => $typeLabel) { + $result[] = ['value' => $typeValue, 'label' => __($typeLabel)]; + } + + return $result; + } +} diff --git a/ViewModel/Instant.php b/ViewModel/Instant.php new file mode 100644 index 0000000..2474a23 --- /dev/null +++ b/ViewModel/Instant.php @@ -0,0 +1,129 @@ +config = $config; + $this->storeManager = $storeManager; + $this->request = $request; + $this->url = $url; + $this->assetRepository = $assetRepository; + $this->view = $view; + } + + /** + * Render Instant Checkout button on Product page. + * + * @return bool + * @throws LocalizedException + */ + public function enabledOnProductPage(): bool + { + $websiteId = (int)$this->storeManager->getWebsite()->getId(); + $productType = $this->view->getProduct()->getTypeId(); + + return $this->config->getIsPaypalFlowEnabled($websiteId) + && $this->config->isProductPageInstantCheckoutEnabled($websiteId) + && $this->config->isProductPageInstantCheckoutEnabledForType($websiteId, $productType); + } + + /** + * Get parallel checkout url. + * + * @return string + */ + public function getCheckoutUrl(): string + { + return $this->url->getUrl( + 'checkout', + [ + '_secure' => $this->request->isSecure(), + Button::KEY_PARALLEL => true, + ] + ); + } + + /** + * Get Product form selector. + * + * @return string + */ + public function getProductFormSelector(): string + { + return self::PRODUCT_FORM_SELECTOR; + } + + /** + * Get ULR for loader image. + * + * @return string + */ + public function getLoaderIcon(): string + { + return $this->assetRepository->getUrl('images/loader-2.gif'); + } +} diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 7c69648..5295342 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -5,11 +5,14 @@
- - - Magento\Config\Model\Config\Source\Yesno Bold\CheckoutFlowPaypal\Block\System\Config\Form\Field\Disabled + + + + Bold\CheckoutFlowPaypal\Block\System\Config\Form\Field\Status documentation. @@ -17,8 +20,27 @@ Bold\CheckoutFlowPaypal\Block\System\Config\Form\Field\Toggle + + + + Magento\Config\Model\Config\Source\Yesno + Bold\CheckoutFlowPaypal\Model\Config\Backend\InvalidateBlockBackend - + + + 1 + + + + + Bold\CheckoutFlowPaypal\Model\Config\Source\ProductTypeSource + Bold\CheckoutFlowPaypal\Model\Config\Backend\InvalidateBlockBackend + + 1 + 1 +
diff --git a/etc/config.xml b/etc/config.xml index 6b43a0a..ca7f3ce 100644 --- a/etc/config.xml +++ b/etc/config.xml @@ -4,6 +4,8 @@ paypal_branded + 1 + simple,virtual,downloadable,grouped,configurable,bundle,giftcard diff --git a/view/adminhtml/layout/adminhtml_system_config_edit.xml b/view/adminhtml/layout/adminhtml_system_config_edit.xml new file mode 100644 index 0000000..11b8d3b --- /dev/null +++ b/view/adminhtml/layout/adminhtml_system_config_edit.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/view/adminhtml/web/css/system-styles.less b/view/adminhtml/web/css/system-styles.less new file mode 100644 index 0000000..0730d7b --- /dev/null +++ b/view/adminhtml/web/css/system-styles.less @@ -0,0 +1,25 @@ +#checkout_bold_checkout_paypal_status { + padding-top: 0; + + .enabled { + color: green; + display: flex; + font-size: 1em; + + &:before { + content: url('../images/enabled.svg'); + margin-right: 0.5em; + } + } + + .disabled { + display: flex; + color: red; + font-size: 1em; + + &:before { + content: url('../images/disabled.svg'); + margin-right: 0.5em; + } + } +} diff --git a/view/adminhtml/web/images/disabled.svg b/view/adminhtml/web/images/disabled.svg new file mode 100644 index 0000000..ac2520c --- /dev/null +++ b/view/adminhtml/web/images/disabled.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/view/adminhtml/web/images/enabled.svg b/view/adminhtml/web/images/enabled.svg new file mode 100644 index 0000000..86934ea --- /dev/null +++ b/view/adminhtml/web/images/enabled.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/view/frontend/layout/catalog_product_view.xml b/view/frontend/layout/catalog_product_view.xml new file mode 100644 index 0000000..1b144ef --- /dev/null +++ b/view/frontend/layout/catalog_product_view.xml @@ -0,0 +1,28 @@ + + + + + + + + Bold\CheckoutFlowPaypal\ViewModel\Instant + + + + + + + Bold\CheckoutFlowPaypal\ViewModel\Instant + + + + + diff --git a/view/frontend/layout/catalog_product_view_type_bundle.xml b/view/frontend/layout/catalog_product_view_type_bundle.xml new file mode 100644 index 0000000..fd92b6f --- /dev/null +++ b/view/frontend/layout/catalog_product_view_type_bundle.xml @@ -0,0 +1,19 @@ + + + + + + + + Bold\CheckoutFlowPaypal\ViewModel\Instant + + + + + + diff --git a/view/frontend/templates/instant/product_button.phtml b/view/frontend/templates/instant/product_button.phtml new file mode 100644 index 0000000..ee5d8de --- /dev/null +++ b/view/frontend/templates/instant/product_button.phtml @@ -0,0 +1,27 @@ +getData('viewModel'); +?> + +enabledOnProductPage()): ?> + + diff --git a/view/frontend/web/js/proceed-to-checkout.js b/view/frontend/web/js/proceed-to-checkout.js new file mode 100644 index 0000000..2c7ae0f --- /dev/null +++ b/view/frontend/web/js/proceed-to-checkout.js @@ -0,0 +1,40 @@ +define([ + 'jquery', + 'Magento_Customer/js/model/authentication-popup', + 'Magento_Customer/js/customer-data' +], function ( + $, + authenticationPopup, + customerData +) { + 'use strict'; + + return function (config, element) { + $(element).click(function (event) { + var cart = customerData.get('cart'), + customer = customerData.get('customer'); + event.preventDefault(); + event.stopImmediatePropagation(); + if (!customer().firstname && cart().isGuestCheckoutAllowed === false) { + authenticationPopup.showModal(); + return false; + } + $(element).attr('disabled', true); + const form = $(config.productFormSelector); + const loader = $('body').loader( + { + icon: config.loaderIcon + } + ); + form.submit(() => { + location.href = config.checkoutUrl + }); + if (form.validation('isValid')) { + loader.loader('show'); + form.submit(); + } else { + $(element).attr('disabled', false); + } + }); + }; +});