From 7b9585a67f335d428cf20e03ad89d227dc1231fc Mon Sep 17 00:00:00 2001 From: Joshua Shea Date: Tue, 12 Nov 2024 17:12:09 -0600 Subject: [PATCH 01/10] CHK-6326 Adding wallet payment option toggles --- Model/Config.php | 32 ++++++++++++++++++++++++++++++++ etc/adminhtml/system.xml | 10 ++++++++++ etc/config.xml | 2 ++ 3 files changed, 44 insertions(+) diff --git a/Model/Config.php b/Model/Config.php index 4c855e9e..24d72ad1 100644 --- a/Model/Config.php +++ b/Model/Config.php @@ -29,6 +29,8 @@ class Config private const PATH_CONFIGURATION_GROUP_LABEL = 'checkout/bold_checkout_payment_booster/configuration_group_label'; private const PATH_BOLD_BOOSTER_FLOW_ID = 'checkout/bold_checkout_payment_booster/bold_booster_flow_id'; private const PATH_IS_EXPRESS_PAY_ENABLED = 'checkout/bold_checkout_payment_booster/is_express_pay_enabled'; + private const PATH_IS_CART_WALLET_PAY_ENABLED = 'checkout/bold_checkout_payment_booster/is_cart_wallet_pay_enabled'; + private const PATH_IS_PRODUCT_WALLET_PAY_ENABLED = 'checkout/bold_checkout_payment_booster/is_product_wallet_pay_enabled'; /** * @var ScopeConfigInterface @@ -316,5 +318,35 @@ public function isExpressPayEnabled(int $websiteId): bool ScopeInterface::SCOPE_WEBSITES, $websiteId ); + } + + /** + * Check if Wallet Express Pay buttons are enabled On the cart and mini cart pages. + * + * @param int $websiteId + * @return bool + */ + public function isCartWalletPayEnabled(int $websiteId): bool + { + return $this->scopeConfig->isSetFlag( + self::PATH_IS_CART_WALLET_PAY_ENABLED, + ScopeInterface::SCOPE_WEBSITES, + $websiteId + ); + } + + /** + * Check if Wallet Express Pay buttons are enabled on the product pages. + * + * @param int $websiteId + * @return bool + */ + public function isProductWalletPayEnabled(int $websiteId): bool + { + return $this->scopeConfig->isSetFlag( + self::PATH_IS_PRODUCT_WALLET_PAY_ENABLED, + ScopeInterface::SCOPE_WEBSITES, + $websiteId + ); } } diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 343775a2..e121324f 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -55,6 +55,16 @@ Magento\Config\Model\Config\Source\Yesno + + + + Magento\Config\Model\Config\Source\Yesno + + + + + Magento\Config\Model\Config\Source\Yesno + diff --git a/etc/config.xml b/etc/config.xml index cbe4cfff..f0a0e8a9 100644 --- a/etc/config.xml +++ b/etc/config.xml @@ -43,6 +43,8 @@ Credit card Credit card 0 + 0 + 0 https://api.boldcommerce.com/ From cac0be2eae63fcdd28c9586788aca1a1232e365f Mon Sep 17 00:00:00 2001 From: Nicole Norman <114614923+nicolenorman@users.noreply.github.com> Date: Mon, 16 Dec 2024 09:36:19 -0600 Subject: [PATCH 02/10] Add express pay to cart & mini-cart (#134) * Add express pay to mini cart * View model setup * Config data wiring for view model * control block visibility * Bold Order Observer & Session modifications * Buttons rendering * window.checkoutConfig available outside checkout * shelving (+2 squashed commits) Squashed commits: [ebe2f6a] shelving [43ca7e5] staging * spacing * Remove unneeded observer logging * Render buttons in mini-cart * staging * Updates to callbacks for cart & mini-cart express pay * Allow init with empty quote * Init Bold order on predispatch event * Resolve checkout window data availability * Move Express Pay buttons placement in mini-cart * Add FrontControllerPlugin * Remove unneeded default.xml * Cleanup * Addressing comments * Addressing Comments * cast websiteId to int * Remove disused method for initializing Bold config (CHK-4612) * Add missing parameter type to method (CHK-4612) * Import exception object (CHK-4612) * Remove disused dependencies and their properties (CHK-4612) * Remove disused import and resort imports (CHK-4612) * Add loader and remove xml move tag --------- Co-authored-by: Joshua Shea Co-authored-by: Joseph Leedy --- .../ExpressPayShortcutButtons.php | 20 +++ CustomerData/BoldCheckoutData.php | 57 ++++++++ Model/CheckoutData.php | 3 - Model/InitOrderFromQuote.php | 2 +- .../Checkout/InitializeBoldOrderObserver.php | 50 ------- .../AddExpressPayButtonsObserver.php | 49 +++++++ .../Framework/App/FrontControllerPlugin.php | 126 ++++++++++++++++++ UI/PaymentBoosterConfigProvider.php | 1 + ViewModel/ExpressPay.php | 100 ++++++++++++++ composer.json | 2 + etc/frontend/di.xml | 12 ++ etc/frontend/events.xml | 10 +- etc/frontend/sections.xml | 13 ++ etc/module.xml | 2 + view/frontend/layout/checkout_cart_index.xml | 13 ++ view/frontend/templates/express-pay.phtml | 40 ++++++ .../update-quote-address-action.js | 20 ++- .../update-quote-shipping-method-action.js | 29 ++-- .../frontend/web/js/express-pay-storefront.js | 73 ++++++++++ .../on-approve-payment-order-callback.js | 12 +- .../on-create-payment-order-callback.js | 17 ++- 21 files changed, 565 insertions(+), 86 deletions(-) create mode 100644 Block/ShortcutButtons/ExpressPayShortcutButtons.php create mode 100644 CustomerData/BoldCheckoutData.php delete mode 100644 Observer/Checkout/InitializeBoldOrderObserver.php create mode 100644 Observer/ShortcutButtons/AddExpressPayButtonsObserver.php create mode 100644 Plugin/Framework/App/FrontControllerPlugin.php create mode 100644 ViewModel/ExpressPay.php create mode 100644 etc/frontend/sections.xml create mode 100644 view/frontend/layout/checkout_cart_index.xml create mode 100644 view/frontend/templates/express-pay.phtml create mode 100644 view/frontend/web/js/express-pay-storefront.js diff --git a/Block/ShortcutButtons/ExpressPayShortcutButtons.php b/Block/ShortcutButtons/ExpressPayShortcutButtons.php new file mode 100644 index 00000000..a93d4865 --- /dev/null +++ b/Block/ShortcutButtons/ExpressPayShortcutButtons.php @@ -0,0 +1,20 @@ +paymentBoosterConfig = $paymentBoosterConfig; + } + + /** + * @return array{ + * epsAuthToken: string, + * configurationGroupLabel: string, + * epsUrl: string, + * epsStaticUrl: string, + * gatewayId: int, + * jwtToken: string, + * url: string, + * shopId: string, + * publicOrderId: string, + * countries: array{ + * is_region_visible: bool, + * label: string, + * value: string + * }, + * origin: string, + * epsUrl: string, + * shopUrl: string, + * shopName: string, + * isPhoneRequired: bool, + * isExpressPayEnabled: bool, + * isCartWalletPayEnabled: bool, + * paymentBooster: array{ + * payment: object{ + * method: string + * } + * } + * } + */ + public function getSectionData(): array + { + $boldConfig = $this->paymentBoosterConfig->getConfig(); + return $boldConfig['bold'] ?? []; + } +} diff --git a/Model/CheckoutData.php b/Model/CheckoutData.php index 791fa58c..91b22a7b 100644 --- a/Model/CheckoutData.php +++ b/Model/CheckoutData.php @@ -69,9 +69,6 @@ public function __construct( public function initCheckoutData() { $quote = $this->checkoutSession->getQuote(); - if (!$quote->getId()) { - throw new Exception('Quote is not found'); - } if (!$this->isPaymentBoosterAvailable->isAvailable()) { return; } diff --git a/Model/InitOrderFromQuote.php b/Model/InitOrderFromQuote.php index 26aa73ac..7b733f22 100644 --- a/Model/InitOrderFromQuote.php +++ b/Model/InitOrderFromQuote.php @@ -72,7 +72,7 @@ public function init(CartInterface $quote): array $body = [ 'flow_id' => $flowId, 'order_type' => 'simple_order', - 'cart_id' => $quote->getId(), + 'cart_id' => $quote->getId() ?? '', ]; $orderData = $this->client->post( (int)$quote->getStore()->getWebsiteId(), diff --git a/Observer/Checkout/InitializeBoldOrderObserver.php b/Observer/Checkout/InitializeBoldOrderObserver.php deleted file mode 100644 index a968c2db..00000000 --- a/Observer/Checkout/InitializeBoldOrderObserver.php +++ /dev/null @@ -1,50 +0,0 @@ -logger = $logger; - $this->checkoutData = $checkoutData; - } - - /** - * @inheritDoc - */ - public function execute(Observer $observer): void - { - try { - $this->checkoutData->initCheckoutData(); - } catch (Exception $exception) { - $this->logger->critical($exception); - } - } -} diff --git a/Observer/ShortcutButtons/AddExpressPayButtonsObserver.php b/Observer/ShortcutButtons/AddExpressPayButtonsObserver.php new file mode 100644 index 00000000..dde61a8a --- /dev/null +++ b/Observer/ShortcutButtons/AddExpressPayButtonsObserver.php @@ -0,0 +1,49 @@ +expressPayFactory = $expressPayFactory; + } + + public function execute(Observer $observer): void + { + $event = $observer->getEvent(); + /** @var ShortcutButtons $container */ + $container = $event->getData('container'); + /** @var ExpressPayShortcutButtons $expressPayShortcutButtons */ + $expressPayShortcutButtons = $container->getLayout()->createBlock( + ExpressPayShortcutButtons::class, + ExpressPayShortcutButtons::BLOCK_ALIAS, + [ + 'data' => [ + 'express_pay_view_model' => $this->expressPayFactory->create(), + 'render_page_source' => 'mini-cart' + ] + ] + ); + + $container->addShortcut($expressPayShortcutButtons); + } +} diff --git a/Plugin/Framework/App/FrontControllerPlugin.php b/Plugin/Framework/App/FrontControllerPlugin.php new file mode 100644 index 00000000..e57eac1b --- /dev/null +++ b/Plugin/Framework/App/FrontControllerPlugin.php @@ -0,0 +1,126 @@ +checkoutData = $checkoutData; + $this->routerList = $routerList; + $this->logger = $logger; + } + + /** + * @param FrontController $subject + * @param Closure $proceed + * @param RequestInterface $request + * @return Http|ResultInterface + */ + public function aroundDispatch(FrontController $subject, Closure $proceed, RequestInterface $request) + { + $fullActionName = ''; + + foreach ($this->routerList as $router) { + $actionInstance = $router->match($request); + + if (!$actionInstance) { + continue; + } + + $moduleName = $request->getModuleName(); + $controllerName = $request->getControllerName(); + $actionName = $request->getActionName(); + $fullActionName = $moduleName . '_' . $controllerName . '_' . $actionName; + + if ($fullActionName === '__') { + continue; + } + + break; + } + + if (!in_array(strtolower($fullActionName), $this->allowedActions)) { + return $proceed($request); + } + + try { + $this->checkoutData->initCheckoutData(); + } catch (Exception $exception) { + $this->logger->critical($exception); + } + + return $proceed($request); + } +} diff --git a/UI/PaymentBoosterConfigProvider.php b/UI/PaymentBoosterConfigProvider.php index 3b2ecb69..5f352136 100644 --- a/UI/PaymentBoosterConfigProvider.php +++ b/UI/PaymentBoosterConfigProvider.php @@ -121,6 +121,7 @@ public function getConfig(): array 'shopName' => $quote->getStore()->getFrontendName(), 'isPhoneRequired' => $quote->getStore()->getConfig('customer/address/telephone_show') === NooptreqSource::VALUE_REQUIRED, 'isExpressPayEnabled' => $this->config->isExpressPayEnabled($websiteId), + 'isCartWalletPayEnabled' => $this->config->isCartWalletPayEnabled($websiteId), 'paymentBooster' => [ 'payment' => [ 'method' => Service::CODE, diff --git a/ViewModel/ExpressPay.php b/ViewModel/ExpressPay.php new file mode 100644 index 00000000..d3c83b7e --- /dev/null +++ b/ViewModel/ExpressPay.php @@ -0,0 +1,100 @@ +configProvider = $configProvider; + $this->serializer = $serializer; + $this->checkoutSession = $checkoutSession; + $this->storeManager = $storeManager; + $this->config = $config; + } + + /** + * @return bool|string + */ + public function getJsLayout() + { + $this->jsLayout['checkoutConfig'] = $this->configProvider->getConfig(); + return $this->serializer->serialize($this->jsLayout); + } + + /** + * @return bool + * @throws NoSuchEntityException + */ + public function isCartWalletPayEnabled(): bool + { + $websiteId = (int) $this->storeManager->getStore()->getWebsiteId(); + return $this->config->isCartWalletPayEnabled($websiteId); + } + + /** + * @param int $websiteId + * @return bool + */ + public function isProductWalletPayEnabled(int $websiteId): bool + { + return $this->config->isProductWalletPayEnabled($websiteId); + } + + /** + * @return bool + * @throws NoSuchEntityException + * @throws LocalizedException + */ + public function hasActiveQuote(): bool + { + $quote = $this->checkoutSession->getQuote(); + return $quote->getId() !== null; + } +} diff --git a/composer.json b/composer.json index 100f532f..c43e22c6 100644 --- a/composer.json +++ b/composer.json @@ -9,6 +9,8 @@ "require": { "magento/framework": ">=102.0.1 <103.0.8", "magento/module-checkout": ">=100.3.1 <100.4.8", + "magento/module-customer": ">=102.0.1 <103.0.8", + "magento/module-page-cache": ">=100.3.1 <100.4.8", "magento/module-quote": ">=101.1.1 <101.2.8", "magento/module-store": ">=101.0.1 <101.1.8" }, diff --git a/etc/frontend/di.xml b/etc/frontend/di.xml index a021f74d..7029ef9d 100644 --- a/etc/frontend/di.xml +++ b/etc/frontend/di.xml @@ -9,4 +9,16 @@ + + + + + Bold\CheckoutPaymentBooster\CustomerData\BoldCheckoutData + + + + + + + diff --git a/etc/frontend/events.xml b/etc/frontend/events.xml index 0f603013..0ed1de5b 100644 --- a/etc/frontend/events.xml +++ b/etc/frontend/events.xml @@ -1,13 +1,7 @@ - - - - - - - - + + diff --git a/etc/frontend/sections.xml b/etc/frontend/sections.xml new file mode 100644 index 00000000..5c9bdbd5 --- /dev/null +++ b/etc/frontend/sections.xml @@ -0,0 +1,13 @@ + + + +
+ + +
+ + +
+ + diff --git a/etc/module.xml b/etc/module.xml index 0ede3932..0a13ea1f 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -4,7 +4,9 @@ + + diff --git a/view/frontend/layout/checkout_cart_index.xml b/view/frontend/layout/checkout_cart_index.xml new file mode 100644 index 00000000..90b3a856 --- /dev/null +++ b/view/frontend/layout/checkout_cart_index.xml @@ -0,0 +1,13 @@ + + + + + + + + Bold\CheckoutPaymentBooster\ViewModel\ExpressPay + + + + + diff --git a/view/frontend/templates/express-pay.phtml b/view/frontend/templates/express-pay.phtml new file mode 100644 index 00000000..77a44d04 --- /dev/null +++ b/view/frontend/templates/express-pay.phtml @@ -0,0 +1,40 @@ +getData('express_pay_view_model'); +$isEnabled = $expressPayViewModel->isCartWalletPayEnabled(); +$hasActiveQuote = $expressPayViewModel->hasActiveQuote(); + +if (!$isEnabled || !$hasActiveQuote): + return; +endif; +?> + +
+
+
+ + + + diff --git a/view/frontend/web/js/action/express-pay/update-quote-address-action.js b/view/frontend/web/js/action/express-pay/update-quote-address-action.js index 7a1d4f3c..bfc68da7 100644 --- a/view/frontend/web/js/action/express-pay/update-quote-address-action.js +++ b/view/frontend/web/js/action/express-pay/update-quote-address-action.js @@ -3,11 +3,17 @@ define( 'Magento_Checkout/js/model/quote', 'Magento_Customer/js/customer-data', 'Magento_Checkout/js/model/address-converter', + 'Magento_Checkout/js/model/shipping-rate-processor/new-address', + 'Magento_Checkout/js/model/cart/cache', + 'Magento_Checkout/js/model/shipping-service' ], function ( quote, customerData, - magentoAddressConverter + magentoAddressConverter, + newAddressProcessor, + cartCache, + shippingService ) { 'use strict'; @@ -81,9 +87,17 @@ define( if (addressType === 'shipping') { quote.shippingAddress(quoteAddress); - return; + } else { + quote.billingAddress(quoteAddress); } - quote.billingAddress(quoteAddress); + + newAddressProcessor.getRates(quote.shippingAddress()); + shippingService.getShippingRates().subscribe(function (rates) { + cartCache.set('rates', rates); + let shippingAddress = _.pick(quote.shippingAddress(), cartCache.requiredFields); + + cartCache.set('shipping-address', shippingAddress); + }); } } ); diff --git a/view/frontend/web/js/action/express-pay/update-quote-shipping-method-action.js b/view/frontend/web/js/action/express-pay/update-quote-shipping-method-action.js index 7300b142..df62f41c 100644 --- a/view/frontend/web/js/action/express-pay/update-quote-shipping-method-action.js +++ b/view/frontend/web/js/action/express-pay/update-quote-shipping-method-action.js @@ -20,22 +20,27 @@ define( * @return void */ return async function (shippingMethod = null) { - let newMethod = null; + let newMethod; + let timeoutMS = 100; + let carry = 100; + + while (shippingService.isLoading() && timeoutMS < 5000) { + // max total timeout is 8000ms + await new Promise(resolve => setTimeout(resolve, timeoutMS)); + carry = timeoutMS + carry; + timeoutMS = carry - timeoutMS; + } + shippingMethod = shippingMethod ?? quote.shippingMethod(); if (shippingMethod !== null) { - let availableMethods = shippingService.getShippingRates().filter((method) => { + newMethod = shippingService.getShippingRates().filter((method) => { let methodId = `${method.carrier_code}_${method.method_code}`; methodId = methodId.replace(/\s/g, ''); - return methodId === shippingMethod['id'] || methodId === shippingMethod['identifier']; - }); - if (availableMethods.length > 0) { - newMethod = availableMethods[0]; - } - } else { - newMethod = shippingService.getShippingRates().first(); - } - if (newMethod !== null) { - selectShippingMethodAction(newMethod); + return methodId === shippingMethod['id'] + || methodId === shippingMethod['identifier'] + || methodId === `${shippingMethod.carrier_code}_${shippingMethod.method_code}`; + })[0]; } + selectShippingMethodAction(newMethod ?? shippingService.getShippingRates().first()); if (quote.guestEmail === null) { quote.guestEmail = 'test@test.com'; } diff --git a/view/frontend/web/js/express-pay-storefront.js b/view/frontend/web/js/express-pay-storefront.js new file mode 100644 index 00000000..25f6b570 --- /dev/null +++ b/view/frontend/web/js/express-pay-storefront.js @@ -0,0 +1,73 @@ +define([ + 'uiComponent', + 'underscore', + 'Bold_CheckoutPaymentBooster/js/model/spi', + 'Magento_Customer/js/customer-data' +], function( + Component, + _, + spi, + customerData +) { + 'use strict' + + return Component.extend({ + initialize: async function () { + this._super(); + + this._initConfig(); + this._setVisibility(); + }, + + /** + * Set the visibility of the component. + * @private + */ + _setVisibility: function () { + const ppcpExpressContainer = document.getElementById('ppcp-express-payment'); + if (ppcpExpressContainer) { + ppcpExpressContainer.remove(); + } + + this._renderExpressPayments(); + }, + + _initConfig: async function () { + if (!window?.checkoutConfig?.bold) { + window.checkoutConfig.bold = customerData.get('bold-checkout-data')(); + } + }, + + _renderExpressPayments: async function () { + const containerId = 'express-pay-buttons'; + + let boldPaymentsInstance; + + try { + boldPaymentsInstance = await spi.getPaymentsClient(); + } catch (error) { + console.error('Could not instantiate Bold Payments Client.', error); + + return; + } + + const allowedCountries = this._getAllowedCountryCodes(); + const walletOptions = { + shopName: window.checkoutConfig.bold?.shopName ?? '', + isPhoneRequired: window.checkoutConfig.bold?.isPhoneRequired ?? true, + fastlane: window.checkoutConfig.bold?.fastlane, + allowedCountryCodes: allowedCountries + }; + + boldPaymentsInstance.renderWalletPayments(containerId, walletOptions); + }, + + _getAllowedCountryCodes: function () { + const countryCodes = []; + _.each(window.checkoutConfig.bold?.countries, function (countryData) { + countryCodes.push(countryData.value); + }); + return countryCodes; + }, + }); +}); diff --git a/view/frontend/web/js/model/spi/callbacks/on-approve-payment-order-callback.js b/view/frontend/web/js/model/spi/callbacks/on-approve-payment-order-callback.js index 99f2a233..4a6dcd3c 100644 --- a/view/frontend/web/js/model/spi/callbacks/on-approve-payment-order-callback.js +++ b/view/frontend/web/js/model/spi/callbacks/on-approve-payment-order-callback.js @@ -8,6 +8,7 @@ define( 'Bold_CheckoutPaymentBooster/js/action/express-pay/update-quote-ppcp-action', 'Bold_CheckoutPaymentBooster/js/action/express-pay/update-quote-braintree-action', 'Bold_CheckoutPaymentBooster/js/action/express-pay/save-shipping-information-action', + 'Magento_Ui/js/model/messageList' ], function ( registry, @@ -17,7 +18,8 @@ define( redirectOnSuccessAction, updateQuotePPCPAction, updateQuoteBraintreeAction, - saveShippingInformationAction + saveShippingInformationAction, + messageList ) { 'use strict'; @@ -62,12 +64,18 @@ define( return; } } - const messageContainer = registry.get('checkout.errors').messageContainer; + + const messageContainer = registry.get('checkout.errors')?.messageContainer ?? messageList; + $('body').trigger('processStart'); $.when(placeOrderAction(paymentMethodData, messageContainer)) .done( function () { redirectOnSuccessAction.execute(); } + ).always( + function () { + $('body').trigger('processStop'); + } ); }; } diff --git a/view/frontend/web/js/model/spi/callbacks/on-create-payment-order-callback.js b/view/frontend/web/js/model/spi/callbacks/on-create-payment-order-callback.js index b5ab2cf2..8ea73310 100644 --- a/view/frontend/web/js/model/spi/callbacks/on-create-payment-order-callback.js +++ b/view/frontend/web/js/model/spi/callbacks/on-create-payment-order-callback.js @@ -23,23 +23,26 @@ define( const paymentData = paymentPayload['payment_data']; const availableWalletTypes = ['apple', 'google']; const isWalletPayment = availableWalletTypes.includes(paymentData.payment_type); + const addressProvided = Boolean(paymentData['shipping_address'] || paymentData['billing_address']); if (paymentType !== 'ppcp') { return; } - if (isWalletPayment) { - if (paymentData['shipping_address']) { + if (addressProvided) { + if (isWalletPayment && paymentData['shipping_address']) { updateQuoteAddressAction('shipping', paymentData['shipping_address']); } - if (paymentData['billing_address']) { + if (isWalletPayment && paymentData['billing_address']) { updateQuoteAddressAction('billing', paymentData['billing_address']); } - } else { - await updateQuoteShippingMethodAction(paymentData['shipping_options']); - } - await saveShippingInformationAction(true); + if (!isWalletPayment) { + await updateQuoteShippingMethodAction(paymentData['shipping_options']); + } + + await saveShippingInformationAction(true); + } const walletPayResult = await createWalletPayOrderAction(paymentPayload); return { From 3141e3569d665cb00303b02f3c37cd8b8cd7fd6a Mon Sep 17 00:00:00 2001 From: Joshua Shea Date: Tue, 19 Nov 2024 09:33:29 -0600 Subject: [PATCH 03/10] [CHK-4614] Adding Express Pay modal to the PDP Adding before cart add event to clear cart for quick purchase Adding support for SDK onClick event to add item to cart on pdp Adding page source argument to express pay xml to facilitate above add Adding functionality to checkout without a quote active Adding Endpoint to fetch Quote Mask Id for guest shipping estimate endpoints --- Api/Quote/GetQuoteInterface.php | 19 ++++ Model/InitOrderFromQuote.php | 2 +- .../ExpressPayBeforeAddToCartObserver.php | 60 ++++++++++++ .../AddExpressPayButtonsObserver.php | 12 ++- Service/ExpressPay/Order/Create.php | 43 +++++++-- Service/ExpressPay/Order/Update.php | 36 ++++++-- Service/Quote/GetQuote.php | 47 ++++++++++ UI/PaymentBoosterConfigProvider.php | 92 +++++++++++++++++-- ViewModel/ExpressPay.php | 69 ++++++++++++-- etc/di.xml | 3 + etc/frontend/events.xml | 5 +- etc/webapi.xml | 6 ++ view/frontend/layout/catalog_product_view.xml | 21 +++++ .../catalog_product_view_type_bundle.xml | 13 +++ view/frontend/layout/checkout_cart_index.xml | 1 + view/frontend/templates/express-pay.phtml | 11 ++- .../create-wallet-pay-order-action.js | 2 +- .../express-pay/get-active-quote-id-action.js | 20 ++++ .../frontend/web/js/express-pay-storefront.js | 15 ++- view/frontend/web/js/model/spi.js | 36 ++++++-- .../on-click-payment-order-callback.js | 27 ++++++ 21 files changed, 486 insertions(+), 54 deletions(-) create mode 100644 Api/Quote/GetQuoteInterface.php create mode 100644 Observer/Product/ExpressPayBeforeAddToCartObserver.php create mode 100644 Service/Quote/GetQuote.php 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/web/js/action/express-pay/get-active-quote-id-action.js create mode 100644 view/frontend/web/js/model/spi/callbacks/on-click-payment-order-callback.js diff --git a/Api/Quote/GetQuoteInterface.php b/Api/Quote/GetQuoteInterface.php new file mode 100644 index 00000000..d0706840 --- /dev/null +++ b/Api/Quote/GetQuoteInterface.php @@ -0,0 +1,19 @@ + $quote->getId() ?? '', ]; $orderData = $this->client->post( - (int)$quote->getStore()->getWebsiteId(), + (int)$websiteId, self::INIT_SIMPLE_ORDER_URI, $body ); diff --git a/Observer/Product/ExpressPayBeforeAddToCartObserver.php b/Observer/Product/ExpressPayBeforeAddToCartObserver.php new file mode 100644 index 00000000..7aae81ff --- /dev/null +++ b/Observer/Product/ExpressPayBeforeAddToCartObserver.php @@ -0,0 +1,60 @@ +checkoutSession = $checkoutSession; + $this->cartRepository = $cartRepository; + } + + /** + * Clear the cart before checking out with Express Pay from the product detail page + * + * @param Observer $observer + * @return void + */ + public function execute(Observer $observer): void + { + $request = $observer->getEvent()->getRequest(); + $isExpressPayOrder = $request->getParam('source') === 'expresspay'; + + $quote = $this->checkoutSession->getQuote(); + + if (!$isExpressPayOrder) { + return; + } + $this->checkoutSession->setCheckoutState(true); + $quote->removeAllItems(); + $quote->setTotalsCollectedFlag(false); + $this->checkoutSession->clearQuote(); + + $this->cartRepository->save($quote); + $this->checkoutSession->setQuoteId($quote->getId()); + } +} diff --git a/Observer/ShortcutButtons/AddExpressPayButtonsObserver.php b/Observer/ShortcutButtons/AddExpressPayButtonsObserver.php index dde61a8a..fda8857a 100644 --- a/Observer/ShortcutButtons/AddExpressPayButtonsObserver.php +++ b/Observer/ShortcutButtons/AddExpressPayButtonsObserver.php @@ -6,6 +6,7 @@ use Bold\CheckoutPaymentBooster\Block\ShortcutButtons\ExpressPayShortcutButtons; use Bold\CheckoutPaymentBooster\ViewModel\ExpressPayFactory; +use Bold\CheckoutPaymentBooster\UI\PaymentBoosterConfigProvider; use Magento\Catalog\Block\ShortcutButtons; use Magento\Framework\Event\ObserverInterface; use Magento\Framework\Event\Observer; @@ -32,14 +33,21 @@ public function execute(Observer $observer): void $event = $observer->getEvent(); /** @var ShortcutButtons $container */ $container = $event->getData('container'); + + $layout = $container->getLayout(); + + if ($layout->getBlock(ExpressPayShortcutButtons::BLOCK_ALIAS) !== false) { + return; + } + /** @var ExpressPayShortcutButtons $expressPayShortcutButtons */ - $expressPayShortcutButtons = $container->getLayout()->createBlock( + $expressPayShortcutButtons = $layout->createBlock( ExpressPayShortcutButtons::class, ExpressPayShortcutButtons::BLOCK_ALIAS, [ 'data' => [ 'express_pay_view_model' => $this->expressPayFactory->create(), - 'render_page_source' => 'mini-cart' + 'render_page_source' => PaymentBoosterConfigProvider::PAGE_SOURCE_MINICART ] ] ); diff --git a/Service/ExpressPay/Order/Create.php b/Service/ExpressPay/Order/Create.php index 8ea9d2af..6ce158fc 100644 --- a/Service/ExpressPay/Order/Create.php +++ b/Service/ExpressPay/Order/Create.php @@ -7,8 +7,10 @@ use Bold\CheckoutPaymentBooster\Api\Http\ClientInterface; use Bold\CheckoutPaymentBooster\Service\ExpressPay\QuoteConverter; use Exception; +use Magento\Checkout\Model\Session; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Session\SessionManagerInterface; use Magento\Quote\Api\CartRepositoryInterface; use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface; use Magento\Quote\Model\Quote; @@ -30,29 +32,39 @@ class Create * @var MaskedQuoteIdToQuoteIdInterface */ private $maskedQuoteIdToQuoteId; + /** * @var CartRepositoryInterface */ private $cartRepository; + /** * @var QuoteConverter */ private $quoteConverter; + /** * @var ClientInterface */ private $httpClient; + /** + * @var SessionManagerInterface + */ + private $checkoutSession; + public function __construct( MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId, CartRepositoryInterface $cartRepository, QuoteConverter $quoteConverter, - ClientInterface $httpClient + ClientInterface $httpClient, + SessionManagerInterface $checkoutSession ) { $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId; $this->cartRepository = $cartRepository; $this->quoteConverter = $quoteConverter; $this->httpClient = $httpClient; + $this->checkoutSession = $checkoutSession; } /** @@ -77,18 +89,29 @@ public function execute($quoteMaskId, $gatewayId, $shippingStrategy): array $quoteId = $quoteMaskId; } - try { - /** @var Quote $quote */ - $quote = $this->cartRepository->get((int)$quoteId); - } catch (NoSuchEntityException $noSuchEntityException) { - throw new LocalizedException(__('Could not create Express Pay order. Invalid quote ID "%1".', $quoteId)); + if ($quoteId !== '') { + try { + /** @var Quote $quote */ + $quote = $this->cartRepository->get((int)$quoteId); + } catch (NoSuchEntityException $noSuchEntityException) { + throw new LocalizedException(__('Could not create Express Pay order. Invalid quote ID "%1".', $quoteId)); + } + } else { + try { + /** @var Session $session */ + $session = $this->checkoutSession; + /** @var Quote $quote */ + $quote = $session->getQuote(); + } catch (NoSuchEntityException $noSuchEntityException) { + throw new LocalizedException(__('Active quote not found.')); + } } - $hasBillingData = $quote->getBillingAddress()->getFirstname() && $quote->getBillingAddress()->getStreet(); + $hasBillingData = $quote->getBillingAddress()->getFirstname() && $quote->getBillingAddress()->getStreet(); - if (!$hasBillingData && !empty($quote->getShippingAddress()->getShippingMethod())) { - $quote->getShippingAddress()->setShippingMethod(''); - } + if (!$hasBillingData && !empty($quote->getShippingAddress()->getShippingMethod())) { + $quote->getShippingAddress()->setShippingMethod(''); + } $websiteId = (int)$quote->getStore()->getWebsiteId(); $uri = 'checkout/orders/{{shopId}}/wallet_pay'; diff --git a/Service/ExpressPay/Order/Update.php b/Service/ExpressPay/Order/Update.php index b0591a32..9dac590c 100644 --- a/Service/ExpressPay/Order/Update.php +++ b/Service/ExpressPay/Order/Update.php @@ -8,8 +8,10 @@ use Bold\CheckoutPaymentBooster\Service\ExpressPay\Order\Get as GetExpressPayOrder; use Bold\CheckoutPaymentBooster\Service\ExpressPay\QuoteConverter; use Exception; +use Magento\Checkout\Model\Session; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Session\SessionManagerInterface; use Magento\Quote\Api\CartRepositoryInterface; use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface; use Magento\Quote\Model\Quote; @@ -31,35 +33,46 @@ class Update * @var MaskedQuoteIdToQuoteIdInterface */ private $maskedQuoteIdToQuoteId; + /** * @var CartRepositoryInterface */ private $cartRepository; + /** * @var QuoteConverter */ private $quoteConverter; + /** * @var ClientInterface */ private $httpClient; + /** * @var GetExpressPayOrder */ private $getExpressPayOrder; + + /** + * @var SessionManagerInterface + */ + private $checkoutSession; public function __construct( MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId, CartRepositoryInterface $cartRepository, QuoteConverter $quoteConverter, GetExpressPayOrder $getExpressPayOrder, - ClientInterface $httpClient + ClientInterface $httpClient, + SessionManagerInterface $checkoutSession ) { $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId; $this->cartRepository = $cartRepository; $this->quoteConverter = $quoteConverter; $this->getExpressPayOrder = $getExpressPayOrder; $this->httpClient = $httpClient; + $this->checkoutSession = $checkoutSession; } /** @@ -83,11 +96,22 @@ public function execute($quoteMaskId, $gatewayId, $paypalOrderId): void $quoteId = $quoteMaskId; } - try { - /** @var Quote $quote */ - $quote = $this->cartRepository->get((int)$quoteId); - } catch (NoSuchEntityException $noSuchEntityException) { - throw new LocalizedException(__('Could not update Express Pay order. Invalid quote ID "%1".', $quoteId)); + if ($quoteId !== '') { + try { + /** @var Quote $quote */ + $quote = $this->cartRepository->get((int)$quoteId); + } catch (NoSuchEntityException $noSuchEntityException) { + throw new LocalizedException(__('Could not update Express Pay order. Invalid quote ID "%1".', $quoteId)); + } + } else { + try { + /** @var Session $session */ + $session = $this->checkoutSession; + /** @var Quote $quote */ + $quote = $session->getQuote(); + } catch (NoSuchEntityException $noSuchEntityException) { + throw new LocalizedException(__('Active quote not found.')); + } } $websiteId = (int)$quote->getStore()->getWebsiteId(); diff --git a/Service/Quote/GetQuote.php b/Service/Quote/GetQuote.php new file mode 100644 index 00000000..e5750365 --- /dev/null +++ b/Service/Quote/GetQuote.php @@ -0,0 +1,47 @@ +checkoutSession = $checkoutSession; + $this->quoteIdToMaskedQuoteId = $quoteIdToMaskedQuoteId; + } + + /** + * Gets Current Session Quote ID + * + * @return string + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function getQuoteId() + { + $quoteId = (int)$this->checkoutSession->getQuote()->getId(); + $quoteIdMask = $this->quoteIdToMaskedQuoteId->execute($quoteId); + + return $quoteIdMask; + } +} diff --git a/UI/PaymentBoosterConfigProvider.php b/UI/PaymentBoosterConfigProvider.php index 5f352136..2c1f7fd8 100644 --- a/UI/PaymentBoosterConfigProvider.php +++ b/UI/PaymentBoosterConfigProvider.php @@ -1,23 +1,35 @@ checkoutData = $checkoutData; $this->config = $config; $this->allowedCountries = $allowedCountries; $this->collectionFactory = $collectionFactory; $this->logger = $logger; + $this->storeManager = $storeManager; + $this->urlBuilder = $urlBuilder; + $this->scopeConfig = $scopeConfig; + $this->escaper = $escaper; } /** @@ -75,7 +115,7 @@ public function getConfig(): array { if (!$this->checkoutData->getPublicOrderId()) { $errorMsg = "No public order ID."; - $this->logger->critical('Error in PaymentBoosterConfigProvider->getConfig(): '.$errorMsg); + $this->logger->critical('Error in PaymentBoosterConfigProvider->getConfig(): ' . $errorMsg); return []; } @@ -86,6 +126,7 @@ public function getConfig(): array $jwtToken = $this->checkoutData->getJwtToken(); $epsAuthToken = $this->checkoutData->getEpsAuthToken(); $epsGatewayId = $this->checkoutData->getEpsGatewayId(); + $currency = $this->storeManager->getStore()->getCurrentCurrency()->getCode(); if ($jwtToken === null || $epsAuthToken === null || $epsGatewayId === null) { $errorMsgs = []; if ($jwtToken === null) { @@ -99,8 +140,8 @@ public function getConfig(): array if ($epsGatewayId === null) { $errorMsgs[] = '$epsGatewayId is null.'; } - - $this->logger->critical('Error in PaymentBoosterConfigProvider->getConfig(): '.implode(', ', $errorMsgs)); + + $this->logger->critical('Error in PaymentBoosterConfigProvider->getConfig(): ' . implode(', ', $errorMsgs)); return []; } return [ @@ -127,10 +168,24 @@ public function getConfig(): array 'method' => Service::CODE, ], ], + 'currency' => $currency ], ]; } + public function getConfigWithoutQuote(): array + { + $result = $this->getConfig(); + $result['storeCode'] = $this->storeManager->getStore()->getCode(); + $result['quoteData']['entity_id'] = ''; + $result['totalsData'] = []; + $result['checkoutAgreements']['isEnabled'] = false; + $result['defaultSuccessPageUrl'] = $this->getDefaultSuccessPageUrl(); + $result['shippingPolicy'] = $this->getShippingPolicy(); + + return $result; + } + /** * Get Bold Storefront URL. * @@ -162,4 +217,27 @@ private function getAllowedCountries(): array return $this->countries; } + + private function getDefaultSuccessPageUrl() + { + return $this->urlBuilder->getUrl('checkout/onepage/success/'); + } + + private function getShippingPolicy() + { + $policyContent = $this->scopeConfig->getValue( + 'shipping/shipping_policy/shipping_policy_content', + ScopeInterface::SCOPE_STORE + ); + $policyContent = $this->escaper->escapeHtml($policyContent); + $result = [ + 'isEnabled' => $this->scopeConfig->isSetFlag( + 'shipping/shipping_policy/enable_shipping_policy', + ScopeInterface::SCOPE_STORE + ), + 'shippingPolicyContent' => $policyContent ? nl2br($policyContent) : '' + ]; + + return $result; + } } diff --git a/ViewModel/ExpressPay.php b/ViewModel/ExpressPay.php index d3c83b7e..a0cf75c2 100644 --- a/ViewModel/ExpressPay.php +++ b/ViewModel/ExpressPay.php @@ -4,13 +4,15 @@ namespace Bold\CheckoutPaymentBooster\ViewModel; +use Magento\Framework\View\Element\Block\ArgumentInterface; +use Bold\CheckoutPaymentBooster\Model\CheckoutData; use Bold\CheckoutPaymentBooster\Model\Config; use Magento\Checkout\Model\CompositeConfigProvider; -use Magento\Checkout\Model\Session; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; +use Bold\CheckoutPaymentBooster\UI\PaymentBoosterConfigProvider; use Magento\Framework\Serialize\SerializerInterface; -use Magento\Framework\View\Element\Block\ArgumentInterface; +use Magento\Checkout\Model\Session; use Magento\Store\Model\StoreManagerInterface; class ExpressPay implements ArgumentInterface @@ -35,11 +37,23 @@ class ExpressPay implements ArgumentInterface */ private $storeManager; + /** + * @var CheckoutData + */ + private $checkoutData; + /** * @var Config */ private $config; + /** + * @var PaymentBoosterConfigProvider + */ + private $paymentBoosterConfigProvider; + + + /** * @var mixed[] */ @@ -50,13 +64,17 @@ public function __construct( SerializerInterface $serializer, Session $checkoutSession, StoreManagerInterface $storeManager, - Config $config + CheckoutData $checkoutData, + Config $config, + PaymentBoosterConfigProvider $paymentBoosterConfigProvider ) { $this->configProvider = $configProvider; $this->serializer = $serializer; $this->checkoutSession = $checkoutSession; $this->storeManager = $storeManager; + $this->checkoutData = $checkoutData; $this->config = $config; + $this->paymentBoosterConfigProvider = $paymentBoosterConfigProvider; } /** @@ -64,26 +82,57 @@ public function __construct( */ public function getJsLayout() { - $this->jsLayout['checkoutConfig'] = $this->configProvider->getConfig(); + $quoteId = $this->checkoutSession->getQuote()->getId(); + if ($quoteId !== null) { + $this->jsLayout['checkoutConfig'] = $this->configProvider->getConfig(); + } else { + $this->checkoutData->initCheckoutData(); + $this->jsLayout['checkoutConfig'] = $this->paymentBoosterConfigProvider->getConfigWithoutQuote(); + } + return $this->serializer->serialize($this->jsLayout); } /** * @return bool - * @throws NoSuchEntityException */ - public function isCartWalletPayEnabled(): bool + public function isEnabled($pageSource = ''): bool + { + $hasActiveQuote = $this->hasActiveQuote(); + + switch ($pageSource) { + case PaymentBoosterConfigProvider::PAGE_SOURCE_CART: + $isEnabled = $this->isCartWalletPayEnabled() && $hasActiveQuote; + break; + case PaymentBoosterConfigProvider::PAGE_SOURCE_PRODUCT: + $isEnabled = $this->isProductWalletPayEnabled(); + break; + case PaymentBoosterConfigProvider::PAGE_SOURCE_MINICART: + $isEnabled = $this->isCartWalletPayEnabled() && $hasActiveQuote; + break; + default: + $isEnabled = false; + break; + } + + return $isEnabled; + } + + /** + * @return bool + */ + private function isCartWalletPayEnabled(): bool { - $websiteId = (int) $this->storeManager->getStore()->getWebsiteId(); + $websiteId = (int)$this->storeManager->getStore()->getWebsiteId(); return $this->config->isCartWalletPayEnabled($websiteId); } /** - * @param int $websiteId * @return bool */ - public function isProductWalletPayEnabled(int $websiteId): bool + private function isProductWalletPayEnabled(): bool { + $websiteId = (int)$this->storeManager->getStore()->getWebsiteId(); return $this->config->isProductWalletPayEnabled($websiteId); } @@ -92,7 +141,7 @@ public function isProductWalletPayEnabled(int $websiteId): bool * @throws NoSuchEntityException * @throws LocalizedException */ - public function hasActiveQuote(): bool + private function hasActiveQuote(): bool { $quote = $this->checkoutSession->getQuote(); return $quote->getId() !== null; diff --git a/etc/di.xml b/etc/di.xml index 128c0856..e20933e4 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -8,6 +8,7 @@ + @@ -122,12 +123,14 @@ Bold\CheckoutPaymentBooster\Model\Http\BoldClient + Magento\Checkout\Model\Session\Proxy Bold\CheckoutPaymentBooster\Model\Http\BoldClient + Magento\Checkout\Model\Session\Proxy diff --git a/etc/frontend/events.xml b/etc/frontend/events.xml index 0ed1de5b..290e0da9 100644 --- a/etc/frontend/events.xml +++ b/etc/frontend/events.xml @@ -1,6 +1,9 @@ + xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> + + + diff --git a/etc/webapi.xml b/etc/webapi.xml index 6380a56e..79829fca 100644 --- a/etc/webapi.xml +++ b/etc/webapi.xml @@ -63,5 +63,11 @@ + + + + + + diff --git a/view/frontend/layout/catalog_product_view.xml b/view/frontend/layout/catalog_product_view.xml new file mode 100644 index 00000000..3460eed4 --- /dev/null +++ b/view/frontend/layout/catalog_product_view.xml @@ -0,0 +1,21 @@ + + + + + + + Bold\CheckoutPaymentBooster\ViewModel\ExpressPay + Bold\CheckoutPaymentBooster\UI\PaymentBoosterConfigProvider::PAGE_SOURCE_PRODUCT + + + + + + + Bold\CheckoutPaymentBooster\ViewModel\ExpressPay + Bold\CheckoutPaymentBooster\UI\PaymentBoosterConfigProvider::PAGE_SOURCE_PRODUCT + + + + + 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 00000000..068087f2 --- /dev/null +++ b/view/frontend/layout/catalog_product_view_type_bundle.xml @@ -0,0 +1,13 @@ + + + + + + + Bold\CheckoutPaymentBooster\ViewModel\ExpressPay + Bold\CheckoutPaymentBooster\UI\PaymentBoosterConfigProvider::PAGE_SOURCE_PRODUCT + + + + + diff --git a/view/frontend/layout/checkout_cart_index.xml b/view/frontend/layout/checkout_cart_index.xml index 90b3a856..1095aa4a 100644 --- a/view/frontend/layout/checkout_cart_index.xml +++ b/view/frontend/layout/checkout_cart_index.xml @@ -6,6 +6,7 @@ Bold\CheckoutPaymentBooster\ViewModel\ExpressPay + Bold\CheckoutPaymentBooster\UI\PaymentBoosterConfigProvider::PAGE_SOURCE_CART diff --git a/view/frontend/templates/express-pay.phtml b/view/frontend/templates/express-pay.phtml index 77a44d04..e0cbefb6 100644 --- a/view/frontend/templates/express-pay.phtml +++ b/view/frontend/templates/express-pay.phtml @@ -9,10 +9,10 @@ use Bold\CheckoutPaymentBooster\ViewModel\ExpressPay; /** @var ExpressPay $expressPayViewModel */ $expressPayViewModel = $block->getData('express_pay_view_model'); -$isEnabled = $expressPayViewModel->isCartWalletPayEnabled(); -$hasActiveQuote = $expressPayViewModel->hasActiveQuote(); +$pageSource = $block->getData('render_page_source'); +$isEnabled = $expressPayViewModel->isEnabled($pageSource); -if (!$isEnabled || !$hasActiveQuote): +if (!$isEnabled): return; endif; ?> @@ -23,10 +23,10 @@ endif; @@ -34,6 +34,7 @@ endif; { "#express-pay-container": { "Bold_CheckoutPaymentBooster/js/express-pay-storefront": { + "pageSource": "escapeJs($pageSource) ?>" } } } diff --git a/view/frontend/web/js/action/express-pay/create-wallet-pay-order-action.js b/view/frontend/web/js/action/express-pay/create-wallet-pay-order-action.js index 95dc58de..268ad363 100644 --- a/view/frontend/web/js/action/express-pay/create-wallet-pay-order-action.js +++ b/view/frontend/web/js/action/express-pay/create-wallet-pay-order-action.js @@ -13,7 +13,7 @@ define( * @param {{}} * @return {Promise} */ - return async function (paymentPayload) { + return function (paymentPayload) { return platformClient.post( 'rest/V1/express_pay/order/create', { diff --git a/view/frontend/web/js/action/express-pay/get-active-quote-id-action.js b/view/frontend/web/js/action/express-pay/get-active-quote-id-action.js new file mode 100644 index 00000000..0f713847 --- /dev/null +++ b/view/frontend/web/js/action/express-pay/get-active-quote-id-action.js @@ -0,0 +1,20 @@ +define( + [ + 'Bold_CheckoutPaymentBooster/js/model/platform-client' + ], + function ( + platformClient + ) { + 'use strict'; + + /** + * Create Wallet Pay order. + * + * @param {{}} + * @return {Promise} + */ + return function () { + return platformClient.get('rest/V1/cart/getQuoteId', {}); + }; + } +); diff --git a/view/frontend/web/js/express-pay-storefront.js b/view/frontend/web/js/express-pay-storefront.js index 25f6b570..ea67b5f4 100644 --- a/view/frontend/web/js/express-pay-storefront.js +++ b/view/frontend/web/js/express-pay-storefront.js @@ -1,10 +1,12 @@ define([ 'uiComponent', + 'ko', 'underscore', 'Bold_CheckoutPaymentBooster/js/model/spi', 'Magento_Customer/js/customer-data' -], function( +], function ( Component, + ko, _, spi, customerData @@ -12,9 +14,14 @@ define([ 'use strict' return Component.extend({ - initialize: async function () { + defaults: { + _pageSource: ko.observable('') + }, + + initialize: async function (config) { this._super(); + this._pageSource(config.pageSource); this._initConfig(); this._setVisibility(); }, @@ -42,12 +49,10 @@ define([ const containerId = 'express-pay-buttons'; let boldPaymentsInstance; - try { - boldPaymentsInstance = await spi.getPaymentsClient(); + boldPaymentsInstance = await spi.getPaymentsClient(this._pageSource()); } catch (error) { console.error('Could not instantiate Bold Payments Client.', error); - return; } diff --git a/view/frontend/web/js/model/spi.js b/view/frontend/web/js/model/spi.js index d505ff19..6d3d3fbf 100644 --- a/view/frontend/web/js/model/spi.js +++ b/view/frontend/web/js/model/spi.js @@ -3,23 +3,29 @@ define([ 'Magento_Checkout/js/model/full-screen-loader', 'Bold_CheckoutPaymentBooster/js/model/fastlane', 'Bold_CheckoutPaymentBooster/js/action/general/load-script-action', + 'Bold_CheckoutPaymentBooster/js/action/express-pay/get-active-quote-id-action', 'Bold_CheckoutPaymentBooster/js/model/spi/callbacks/on-create-payment-order-callback', 'Bold_CheckoutPaymentBooster/js/model/spi/callbacks/on-update-payment-order-callback', 'Bold_CheckoutPaymentBooster/js/model/spi/callbacks/on-require-order-data-callback', 'Bold_CheckoutPaymentBooster/js/model/spi/callbacks/on-approve-payment-order-callback', 'Bold_CheckoutPaymentBooster/js/model/spi/callbacks/on-sca-payment-order-callback', - 'Magento_Ui/js/model/messageList' + 'Bold_CheckoutPaymentBooster/js/model/spi/callbacks/on-click-payment-order-callback', + 'Magento_Ui/js/model/messageList', + 'mage/url', ], function ( quote, fullScreenLoader, fastlane, loadScriptAction, + getActiveQuoteId, onCreatePaymentOrderCallback, onUpdatePaymentOrderCallback, onRequireOrderDataCallback, onApprovePaymentOrderCallback, onScaPaymentOrderCallback, - messageList + onClickPaymentOrderCallback, + messageList, + urlBuilder ) { 'use strict'; @@ -34,7 +40,7 @@ define([ * * @returns {Promise<{}>} */ - getPaymentsClient: async function () { + getPaymentsClient: async function (pageSource = '') { if (window.boldPaymentsInstance) { return window.boldPaymentsInstance; } @@ -66,7 +72,7 @@ define([ { 'gateway_id': Number(window.checkoutConfig.bold.gatewayId), 'auth_token': window.checkoutConfig.bold.epsAuthToken, - 'currency': quote.totals()['base_currency_code'], + 'currency': window.checkoutConfig.bold.currency, } ], 'callbacks': { @@ -108,7 +114,7 @@ define([ }, 'onRequireOrderData': async function (requirements) { try { - return onRequireOrderDataCallback(requirements); + return await onRequireOrderDataCallback(requirements); } catch (e) { console.error(e); fullScreenLoader.stopLoader(); @@ -117,8 +123,26 @@ define([ }, 'onErrorPaymentOrder': function (errors) { console.error('An unexpected PayPal error occurred', errors); - messageList.addErrorMessage({message: 'Warning: An unexpected error occurred. Please try again.'}); + messageList.addErrorMessage({ message: 'Warning: An unexpected error occurred. Please try again.' }); }, + 'onClickPaymentOrder': async function () { + try { + await onClickPaymentOrderCallback(pageSource); + let isQuoteInitialized = window.checkoutConfig.quoteData.entity_id !== ''; + if (isQuoteInitialized) { + return; + } + + let response = await getActiveQuoteId(); + + window.checkoutConfig.quoteData.entity_id = response; + } catch (e) { + console.error(e); + fullScreenLoader.stopLoader(); + + return; + } + } } }; const paymentsInstance = new window.bold.Payments(initialData); diff --git a/view/frontend/web/js/model/spi/callbacks/on-click-payment-order-callback.js b/view/frontend/web/js/model/spi/callbacks/on-click-payment-order-callback.js new file mode 100644 index 00000000..3bad10cd --- /dev/null +++ b/view/frontend/web/js/model/spi/callbacks/on-click-payment-order-callback.js @@ -0,0 +1,27 @@ +define(['jquery'], function ($) { + 'use strict'; + return async function (pageSource) { + if (pageSource !== 'product') { + return; + } + + const productAddToCartForm = document.getElementById('product_addtocart_form'); + + const productAddToCartUrl = productAddToCartForm.getAttribute('action'); + const addToCartFormData = new FormData(productAddToCartForm); + addToCartFormData.append('source', 'expresspay'); + + if (!($("#product_addtocart_form").validation('isValid'))) { + throw new Error('Product form invalid'); + } + + try { + await fetch(productAddToCartUrl, { + method: "POST", + body: addToCartFormData + }); + } catch (err) { + console.log(err); + } + } +}); From 911916db4d8328ca6e59eac2295d3b70a00ba7c3 Mon Sep 17 00:00:00 2001 From: Nicole Norman <114614923+nicolenorman@users.noreply.github.com> Date: Tue, 7 Jan 2025 14:05:16 -0600 Subject: [PATCH 04/10] CHK-6928: Provide Page Source as Option to EPS Payments SDK (#151) * Add PageSource to sdk payment options * updating escape fns and doc blocks --- .../AddExpressPayButtonsObserver.php | 20 ++++++++++-- UI/PaymentBoosterConfigProvider.php | 3 +- view/frontend/layout/catalog_product_view.xml | 21 ------------- .../catalog_product_view_type_bundle.xml | 13 -------- view/frontend/layout/checkout_cart_index.xml | 14 --------- view/frontend/layout/checkout_index_index.xml | 3 ++ view/frontend/templates/express-pay.phtml | 6 ++-- .../frontend/web/js/express-pay-storefront.js | 31 +++---------------- .../on-click-payment-order-callback.js | 2 +- view/frontend/web/js/view/bold-express-pay.js | 5 ++- 10 files changed, 35 insertions(+), 83 deletions(-) delete mode 100644 view/frontend/layout/catalog_product_view.xml delete mode 100644 view/frontend/layout/catalog_product_view_type_bundle.xml delete mode 100644 view/frontend/layout/checkout_cart_index.xml diff --git a/Observer/ShortcutButtons/AddExpressPayButtonsObserver.php b/Observer/ShortcutButtons/AddExpressPayButtonsObserver.php index fda8857a..66add8d9 100644 --- a/Observer/ShortcutButtons/AddExpressPayButtonsObserver.php +++ b/Observer/ShortcutButtons/AddExpressPayButtonsObserver.php @@ -8,6 +8,7 @@ use Bold\CheckoutPaymentBooster\ViewModel\ExpressPayFactory; use Bold\CheckoutPaymentBooster\UI\PaymentBoosterConfigProvider; use Magento\Catalog\Block\ShortcutButtons; +use Magento\Framework\Event; use Magento\Framework\Event\ObserverInterface; use Magento\Framework\Event\Observer; @@ -43,15 +44,30 @@ public function execute(Observer $observer): void /** @var ExpressPayShortcutButtons $expressPayShortcutButtons */ $expressPayShortcutButtons = $layout->createBlock( ExpressPayShortcutButtons::class, - ExpressPayShortcutButtons::BLOCK_ALIAS, + '', [ 'data' => [ 'express_pay_view_model' => $this->expressPayFactory->create(), - 'render_page_source' => PaymentBoosterConfigProvider::PAGE_SOURCE_MINICART + 'render_page_source' => $this->getPageType($observer->getEvent()) ] ] ); $container->addShortcut($expressPayShortcutButtons); } + + /** + * @param Event $event + * @return string + */ + private function getPageType(Event $event): string + { + if ($event->getIsCatalogProduct()) { + return PaymentBoosterConfigProvider::PAGE_SOURCE_PRODUCT; + } + if ($event->getIsShoppingCart()) { + return PaymentBoosterConfigProvider::PAGE_SOURCE_CART; + } + return PaymentBoosterConfigProvider::PAGE_SOURCE_MINICART; + } } diff --git a/UI/PaymentBoosterConfigProvider.php b/UI/PaymentBoosterConfigProvider.php index 2c1f7fd8..264dfc5d 100644 --- a/UI/PaymentBoosterConfigProvider.php +++ b/UI/PaymentBoosterConfigProvider.php @@ -26,9 +26,10 @@ class PaymentBoosterConfigProvider implements ConfigProviderInterface { - public const PAGE_SOURCE_PRODUCT = 'product'; + public const PAGE_SOURCE_PRODUCT = 'product-details'; public const PAGE_SOURCE_CART = 'cart'; public const PAGE_SOURCE_MINICART = 'mini-cart'; + public const PAGE_SOURCE_CHECKOUT = 'checkout'; /** * @var CheckoutData diff --git a/view/frontend/layout/catalog_product_view.xml b/view/frontend/layout/catalog_product_view.xml deleted file mode 100644 index 3460eed4..00000000 --- a/view/frontend/layout/catalog_product_view.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - Bold\CheckoutPaymentBooster\ViewModel\ExpressPay - Bold\CheckoutPaymentBooster\UI\PaymentBoosterConfigProvider::PAGE_SOURCE_PRODUCT - - - - - - - Bold\CheckoutPaymentBooster\ViewModel\ExpressPay - Bold\CheckoutPaymentBooster\UI\PaymentBoosterConfigProvider::PAGE_SOURCE_PRODUCT - - - - - diff --git a/view/frontend/layout/catalog_product_view_type_bundle.xml b/view/frontend/layout/catalog_product_view_type_bundle.xml deleted file mode 100644 index 068087f2..00000000 --- a/view/frontend/layout/catalog_product_view_type_bundle.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - Bold\CheckoutPaymentBooster\ViewModel\ExpressPay - Bold\CheckoutPaymentBooster\UI\PaymentBoosterConfigProvider::PAGE_SOURCE_PRODUCT - - - - - diff --git a/view/frontend/layout/checkout_cart_index.xml b/view/frontend/layout/checkout_cart_index.xml deleted file mode 100644 index 1095aa4a..00000000 --- a/view/frontend/layout/checkout_cart_index.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - Bold\CheckoutPaymentBooster\ViewModel\ExpressPay - Bold\CheckoutPaymentBooster\UI\PaymentBoosterConfigProvider::PAGE_SOURCE_CART - - - - - diff --git a/view/frontend/layout/checkout_index_index.xml b/view/frontend/layout/checkout_index_index.xml index 57890d61..b04ee1cb 100644 --- a/view/frontend/layout/checkout_index_index.xml +++ b/view/frontend/layout/checkout_index_index.xml @@ -13,6 +13,9 @@ Bold_CheckoutPaymentBooster/js/view/bold-express-pay 0 + + Bold\CheckoutPaymentBooster\UI\PaymentBoosterConfigProvider::PAGE_SOURCE_CHECKOUT + diff --git a/view/frontend/templates/express-pay.phtml b/view/frontend/templates/express-pay.phtml index e0cbefb6..160f103f 100644 --- a/view/frontend/templates/express-pay.phtml +++ b/view/frontend/templates/express-pay.phtml @@ -11,6 +11,7 @@ use Bold\CheckoutPaymentBooster\ViewModel\ExpressPay; $expressPayViewModel = $block->getData('express_pay_view_model'); $pageSource = $block->getData('render_page_source'); $isEnabled = $expressPayViewModel->isEnabled($pageSource); +$containerId = 'express-pay-buttons-' . $pageSource; if (!$isEnabled): return; @@ -18,7 +19,7 @@ endif; ?>
-
+