Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Cart.php #39606

Closed
wants to merge 1 commit into from
Closed

Update Cart.php #39606

wants to merge 1 commit into from

Conversation

Genaker
Copy link

@Genaker Genaker commented Feb 5, 2025

There is an issue and bug in magento. Magento is processing the cart even if the checkout cart is not set or is null. This mainly happened during section load. if no car is in session, it is = null, and still, magento tries to process with the id null. Magento throws an exception and tries to find cart with null id. Sure, it can be done at the repository level, but it breaks other logic. So, I fixed the section controller where it was fired. now, it returns empty data if cart ID is not set especially with the Hyva theme however Luma has the same issue.

[2025-02-05T21:21:23.111368+00:00] main.CRITICAL: Magento\Framework\Exception\NoSuchEntityException: **No such entity with cartId =**  in /var/www/html/react/vendor/magento/framework/Exception/NoSuchEntityException.php:50
Stack trace:
#0 /var/www/html/react/vendor/magento/module-quote/Model/QuoteRepository.php(250): Magento\Framework\Exception\NoSuchEntityException::singleField()
#1 /var/www/html/react/vendor/magento/module-quote/Model/QuoteRepository.php(149): Magento\Quote\Model\QuoteRepository->loadQuote()
#2 /var/www/html/react/vendor/magento/framework/Interception/Interceptor.php(58): Magento\Quote\Model\QuoteRepository->get()
#3 /var/www/html/react/vendor/magento/framework/Interception/Interceptor.php(138): Magento\Quote\Model\QuoteRepository\Interceptor->___callParent()
#4 /var/www/html/react/vendor/magento/framework/Interception/Interceptor.php(153): Magento\Quote\Model\QuoteRepository\Interceptor->Magento\Framework\Interception\{closure}()
#5 /var/www/html/react/generated/code/Magento/Quote/Model/QuoteRepository/Interceptor.php(23): Magento\Quote\Model\QuoteRepository\Interceptor->___callPlugins()
#6 /var/www/html/react/vendor/magento/module-quote/Model/QuoteRepository.php(181): Magento\Quote\Model\QuoteRepository\Interceptor->get()
#7 /var/www/html/react/generated/code/Magento/Quote/Model/QuoteRepository/Interceptor.php(32): Magento\Quote\Model\QuoteRepository->getActive()
#8 /var/www/html/react/vendor/magento/module-quote/Model/Cart/CartTotalRepository.php(89): Magento\Quote\Model\QuoteRepository\Interceptor->getActive()
#9 /var/www/html/react/vendor/magento/framework/Interception/Interceptor.php(58): Magento\Quote\Model\Cart\CartTotalRepository->get()
#10 /var/www/html/react/vendor/magento/framework/Interception/Interceptor.php(138): Magento\Quote\Model\Cart\CartTotalRepository\Interceptor->___callParent()
#11 /var/www/html/react/vendor/magento/framework/Interception/Interceptor.php(153): Magento\Quote\Model\Cart\CartTotalRepository\Interceptor->Magento\Framework\Interception\{closure}()
#12916 /generated/code/Magento/Quote/Model/Cart/CartTotalRepository/Interceptor.php(23): Magento\Quote\Model\Cart\CartTotalRepository\Interceptor->___callPlugins()
#13 /var/www/html/react/vendor/magento/module-checkout/Model/DefaultConfigProvider.php(651): Magento\Quote\Model\Cart\CartTotalRepository\Interceptor->get()
#14 /var/www/html/react/vendor/magento/module-checkout/Model/DefaultConfigProvider.php(349): Magento\Checkout\Model\DefaultConfigProvider->getTotalsData()
#15 /var/www/html/react/vendor/magento/framework/Interception/Interceptor.php(58): Magento\Checkout\Model\DefaultConfigProvider->getConfig()
#16 /var/www/html/react/vendor/magento/framework/Interception/Interceptor.php(138): Magento\Checkout\Model\DefaultConfigProvider\Interceptor->___callParent()
#17 /var/www/html/react/vendor/magento/framework/Interception/Interceptor.php(153): Magento\Checkout\Model\DefaultConfigProvider\Interceptor->Magento\Framework\Interception\{closure}()
#18 /var/www/html/react/generated/code/Magento/Checkout/Model/DefaultConfigProvider/Interceptor.php(23): Magento\Checkout\Model\DefaultConfigProvider\Interceptor->___callPlugins()
#19 /var/www/html/react/vendor/magento/module-checkout/Model/CompositeConfigProvider.php(39): Magento\Checkout\Model\DefaultConfigProvider\Interceptor->getConfig()
#20 /var/www/html/react/vendor/magento/module-payment-services-paypal/Block/SmartButtons.php(186): Magento\Checkout\Model\CompositeConfigProvider->getConfig()
#21 /var/www/html/react/vendor/magento/module-payment-services-paypal/view/frontend/templates/smart_buttons_minicart.phtml(16): Magento\PaymentServicesPaypal\Block\SmartButtons->getSerializedCheckoutConfig()
#21223 /vendor/magento/framework/View/TemplateEngine/Php.php(67): include('...')
#23 /var/www/html/react/vendor/magento/framework/View/Element/Template.php(263): Magento\Framework\View\TemplateEngine\Php->render()
#24 /var/www/html/react/vendor/magento/framework/View/Element/Template.php(293): Magento\Framework\View\Element\Template->fetchView()
#25 /var/www/html/react/vendor/magento/framework/View/Element/AbstractBlock.php(1128): Magento\Framework\View\Element\Template->_toHtml()
#26 /var/www/html/react/vendor/magento/framework/View/Element/AbstractBlock.php(1132): Magento\Framework\View\Element\AbstractBlock->Magento\Framework\View\Element\{closure}()
#27 /var/www/html/react/vendor/magento/framework/View/Element/AbstractBlock.php(676): Magento\Framework\View\Element\AbstractBlock->_loadCache()
#28 /var/www/html/react/vendor/magento/framework/View/Layout.php(578): Magento\Framework\View\Element\AbstractBlock->toHtml()
#29 /var/www/html/react/vendor/magento/framework/View/Layout.php(555): Magento\Framework\View\Layout->_renderBlock()
#30 /var/www/html/react/vendor/magento/framework/View/Layout.php(510): Magento\Framework\View\Layout->renderNonCachedElement()
#31 /var/www/html/react/vendor/magento/framework/View/Element/AbstractBlock.php(527): Magento\Framework\View\Layout->renderElement()
#32 /var/www/html/react/vendor/magento/module-catalog/Block/ShortcutButtons.php(100): Magento\Framework\View\Element\AbstractBlock->getChildHtml()
#33 /var/www/html/react/vendor/magento/framework/View/Element/AbstractBlock.php(1128): Magento\Catalog\Block\ShortcutButtons->_toHtml()
#34 /var/www/html/react/vendor/magento/framework/View/Element/AbstractBlock.php(1132): Magento\Framework\View\Element\AbstractBlock->Magento\Framework\View\Element\{closure}()
#35 /var/www/html/react/vendor/magento/framework/View/Element/AbstractBlock.php(676): Magento\Framework\View\Element\AbstractBlock->_loadCache()
#36 /var/www/html/react/vendor/magento/module-checkout/**CustomerData/Cart.php**(101): Magento\Framework\View\Element\AbstractBlock->toHtml()
#37 /var/www/html/react/vendor/magento/framework/Interception/Interceptor.php(58): Magento\Checkout\CustomerData\Cart->getSectionData()
#38 /var/www/html/react/vendor/magento/framework/Interception/Interceptor.php(138): Magento\Checkout\CustomerData\Cart\Interceptor->___callParent()
#39 /var/www/html/react/vendor/magento/framework/Interception/Interceptor.php(153): Magento\Checkout\CustomerData\Cart\Interceptor->Magento\Framework\Interception\{closure}()
#40 /var/www/html/react/generated/code/Magento/Checkout/CustomerData/Cart/Interceptor.php(23): Magento\Checkout\CustomerData\Cart\Interceptor->___callPlugins()
#41 /var/www/html/react/vendor/magento/module-customer/CustomerData/SectionPool.php(90): Magento\Checkout\CustomerData\Cart\Interceptor->getSectionData()
#42 /var/www/html/react/vendor/magento/module-customer/CustomerData/SectionPool.php(60): Magento\Customer\CustomerData\SectionPool->getSectionDataByNames()
#43 /var/www/html/react/vendor/magento/module-customer/**Controller/Section/**Load.php(81): 

So, I made some fixes for the section data.

I tested everything works :

Screenshot 2025-02-05 at 1 46 22 PM

Please check and merge ASAP. It is a critical production issue affecting many production websites.

Copy link

m2-assistant bot commented Feb 5, 2025

Hi @Genaker. Thank you for your contribution!
Here are some useful tips on how you can test your changes using Magento test environment.
❗ Automated tests can be triggered manually with an appropriate comment:

  • @magento run all tests - run or re-run all required tests against the PR changes
  • @magento run <test-build(s)> - run or re-run specific test build(s)
    For example: @magento run Unit Tests

<test-build(s)> is a comma-separated list of build names.

Allowed build names are:
  1. Database Compare
  2. Functional Tests CE
  3. Functional Tests EE
  4. Functional Tests B2B
  5. Integration Tests
  6. Magento Health Index
  7. Sample Data Tests CE
  8. Sample Data Tests EE
  9. Sample Data Tests B2B
  10. Static Tests
  11. Unit Tests
  12. WebAPI Tests
  13. Semantic Version Checker

You can find more information about the builds here
ℹ️ Run only required test builds during development. Run all test builds before sending your pull request for review.


For more details, review the Code Contributions documentation.
Join Magento Community Engineering Slack and ask your questions in #github channel.

Comment on lines +94 to +96
'summary_count' => null,
'subtotalAmount' => null,
'subtotal' => null,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These could return 0

'subtotalAmount' => null,
'subtotal' => null,
'possible_onepage_checkout' => null,
'items' => null,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could return an empty array

@Genaker
Copy link
Author

Genaker commented Feb 6, 2025 via email

@Genaker
Copy link
Author

Genaker commented Feb 6, 2025 via email

@Genaker
Copy link
Author

Genaker commented Feb 6, 2025 via email

@ssx
Copy link
Contributor

ssx commented Feb 7, 2025

Adobe have patched this now:

diff --git a/vendor/magento/module-payment-services-paypal/Block/SmartButtons.php b/vendor/magento/module-payment-services-paypal/Block/SmartButtons.php
index 60beabb8..d74fa01d 100644
--- a/vendor/magento/module-payment-services-paypal/Block/SmartButtons.php
+++ b/vendor/magento/module-payment-services-paypal/Block/SmartButtons.php
@@ -14,6 +14,7 @@ use Magento\Framework\View\Element\Template\Context;
 use Magento\PaymentServicesPaypal\Model\Config;
 use Magento\Catalog\Block\ShortcutInterface;
 use Magento\Framework\View\Element\Template;
+use Magento\Checkout\Model\Session as CheckoutSession;
 
 /**
  * @api
@@ -50,6 +51,11 @@ class SmartButtons extends Template implements ShortcutInterface
      */
     private $serializer;
 
+    /**
+     * @var CheckoutSession
+     */
+    private $checkoutSession;
+
     /**
      * @param Context $context
      * @param Config $config
@@ -59,6 +65,7 @@ class SmartButtons extends Template implements ShortcutInterface
      * @param array $data
      * @param Json|null $serializer
      * @param CompositeConfigProvider|null $compositeConfigProvider
+     * @param CheckoutSession|null $checkoutSession
      */
     public function __construct(
         Context $context,
@@ -68,7 +75,8 @@ class SmartButtons extends Template implements ShortcutInterface
         array $componentConfig = [],
         array $data = [],
         Json $serializer = null,
-        CompositeConfigProvider $compositeConfigProvider = null
+        CompositeConfigProvider $compositeConfigProvider = null,
+        CheckoutSession $checkoutSession = null
     ) {
         $this->config = $config;
         $this->componentConfig = $componentConfig;
@@ -82,6 +90,7 @@ class SmartButtons extends Template implements ShortcutInterface
         $this->setTemplate($data['template'] ?? $componentConfig[$this->pageType]['template']);
         $this->serializer = $serializer ?: ObjectManager::getInstance()->get(Json::class);
         $this->configProvider = $compositeConfigProvider ?: ObjectManager::getInstance()->get(CompositeConfigProvider::class);
+        $this->checkoutSession = $checkoutSession ?: ObjectManager::getInstance()->get(CheckoutSession::class);
     }
 
     /**
@@ -185,4 +194,19 @@ class SmartButtons extends Template implements ShortcutInterface
     {
         return $this->serializer->serialize($this->configProvider->getConfig());
     }
+
+    /**
+     * Check if quote exists
+     *
+     * @return bool
+     * @throws \Magento\Framework\Exception\LocalizedException
+     */
+    public function doesQuoteExist(): bool
+    {
+        try {
+            return $this->checkoutSession->getQuote()->getId() != null;
+        } catch (\Magento\Framework\Exception\NoSuchEntityException $e) {
+            return false;
+        }
+    }
 }
diff --git a/vendor/magento/module-payment-services-paypal/Controller/SmartButtons/SetQuoteAsInactive.php b/vendor/magento/module-payment-services-paypal/Controller/SmartButtons/SetQuoteAsInactive.php
index a7b1f0c4..e82abc52 100644
--- a/vendor/magento/module-payment-services-paypal/Controller/SmartButtons/SetQuoteAsInactive.php
+++ b/vendor/magento/module-payment-services-paypal/Controller/SmartButtons/SetQuoteAsInactive.php
@@ -80,8 +80,11 @@ class SetQuoteAsInactive implements HttpPostActionInterface
 
         try {
             $quote = $this->getQuote();
-            $quote->setIsActive(false);
-            $this->quoteRepository->save($quote);
+
+            if ($quote->getIsActive()) {
+                $quote->setIsActive(false);
+                $this->quoteRepository->save($quote);
+            }
 
             $result->setHttpResponseCode(200);
             return $result;
@@ -103,7 +106,7 @@ class SetQuoteAsInactive implements HttpPostActionInterface
     private function getQuote() : CartInterface
     {
         if ($this->paypalSession->getQuoteId()) {
-            return $this->quoteRepository->getActive($this->paypalSession->getQuoteId());
+            return $this->quoteRepository->get($this->paypalSession->getQuoteId());
         }
 
         return $this->checkoutSession->getQuote();
diff --git a/vendor/magento/module-payment-services-paypal/view/frontend/templates/smart_buttons_minicart.phtml b/vendor/magento/module-payment-services-paypal/view/frontend/templates/smart_buttons_minicart.phtml
index 54fb0aad..108b84e8 100644
--- a/vendor/magento/module-payment-services-paypal/view/frontend/templates/smart_buttons_minicart.phtml
+++ b/vendor/magento/module-payment-services-paypal/view/frontend/templates/smart_buttons_minicart.phtml
@@ -13,11 +13,15 @@ declare(strict_types=1);
 /** @var \Magento\Framework\View\Helper\SecureHtmlRenderer $secureRenderer */
 ?>
 
-<?php $serializedCheckoutConfig = /* @noEscape */ $block->getSerializedCheckoutConfig();
-
-$scriptString = <<<script
+<?php
+if ($block->isEnabled() && $block->doesQuoteExist()) {
+    $serializedCheckoutConfig = /* @noEscape */ $block->getSerializedCheckoutConfig();
+    $scriptString = <<<script
             window.checkoutConfig = {$serializedCheckoutConfig};
+            window.customerData = window.checkoutConfig.customerData;
+            window.isCustomerLoggedIn = window.checkoutConfig.isCustomerLoggedIn;
 script;
+}
 ?>
 
 <?php if ($block->isLocationEnabled('minicart')): ?>
@@ -39,4 +43,8 @@ script;
     </div>
 <?php endif ?>
 
-<?= /* @noEscape */ $secureRenderer->renderTag('script', [], $scriptString, false) ?>
+<?php /* @noEscape */
+if ($block->isEnabled() && $block->doesQuoteExist()) {
+    echo $secureRenderer->renderTag('script', [], $scriptString, false);
+}
+?>
diff --git a/vendor/magento/module-payment-services-paypal/view/frontend/web/js/view/payment/apple-pay-cart.js b/vendor/magento/module-payment-services-paypal/view/frontend/web/js/view/payment/apple-pay-cart.js
index 2fe5692d..7eafe4c0 100644
--- a/vendor/magento/module-payment-services-paypal/view/frontend/web/js/view/payment/apple-pay-cart.js
+++ b/vendor/magento/module-payment-services-paypal/view/frontend/web/js/view/payment/apple-pay-cart.js
@@ -15,7 +15,8 @@ define([
     'Magento_PaymentServicesPaypal/js/view/payment/methods/apple-pay',
     'Magento_Checkout/js/model/quote',
     'Magento_Checkout/js/model/cart/totals-processor/default',
-], function (_, $, utils, Component, $t, customerData, ResponseError, ApplePayButton, quote, totalsProcessor) {
+    'Magento_Customer/js/model/customer',
+], function (_, $, utils, Component, $t, customerData, ResponseError, ApplePayButton, quote, totalsProcessor, customer) {
     'use strict';
 
     return Component.extend({
@@ -41,12 +42,6 @@ define([
                 .then(this.initApplePayButton)
                 .catch(console.log);
 
-            // Reload quote totals in minicart to have the correct grand_total for the Apple Popup
-            if (this.pageType === 'minicart') {
-                totalsProcessor.estimateTotals().done(function (result) {
-                    quote.setTotals(result);
-                });
-            }
             return this;
         },
 
@@ -100,19 +95,27 @@ define([
         },
 
         onClick: function () {
-            this.isErrorDisplayed = false;
+            // Reload customer data to use correct loggedin/guest urls in the applepay button
+            // See smart_buttons_minicart.phtml:21-22
+            if (this.pageType === 'minicart') {
+                this.fixCustomerData();
+            }
 
+            // Show popup with initial order amount from window.checkoutConfig
+            // See smart_buttons_minicart.phtml:20
             this.applePayButton.showLoaderAsync(true).then(() => {
                 const data = {
                     response: {
                         'paypal-order': {
-                            currency_code: String(quote.totals().quote_currency_code),
-                            amount: Number(quote.totals().grand_total).toString(),
+                            currency_code: window.checkoutConfig.quoteData.base_currency_code,
+                            amount: window.checkoutConfig.quoteData.grand_total.toString(),
                         }
                     }
                 }
                 this.applePayButton.showPopup(data);
             })
+
+            this.isErrorDisplayed = false;
         },
 
         /**
@@ -163,5 +166,33 @@ define([
 
             this.applePaySession.begin();
         },
+
+        /**
+         * Fix customer data
+         *
+         * Why do we need this?
+         * See: src/app/code/Magento/Customer/view/frontend/web/js/model/customer.js:17
+         *
+         * When we initialise customer data on the page where the minicart was not rendered yet,
+         * the customer data in the "window" object is 'undefined' at first because . This makes this line
+         *      var isLoggedIn = ko.observable(window.isCustomerLoggedIn),
+         * to create an observable of undefined variable, that does not work in knockout.
+         * knockout expects an existing variable to create an observable.
+         *
+         * Later, when we render minicart and update "window" object with customer data,
+         * it's not being picked up by customer.js logic and when try to read the data, it's still undefined,
+         * even though it exists in the "window" object.
+         *
+         * This function forces the customer data to be updated from the "window" object.
+         */
+        fixCustomerData: function () {
+            if (customer.isLoggedIn() === undefined && window.isCustomerLoggedIn !== undefined) {
+                customer.setIsLoggedIn(window.isCustomerLoggedIn);
+            }
+
+            if (customer.isLoggedIn() && _.isEmpty(customer.customerData)) {
+                customer.customerData = window.customerData;
+            }
+        }
     });
 });

@Genaker
Copy link
Author

Genaker commented Feb 9, 2025

Adobe have patched this now:


diff --git a/vendor/magento/module-payment-services-paypal/Block/SmartButtons.php b/vendor/magento/module-payment-services-paypal/Block/SmartButtons.php

index 60beabb8..d74fa01d 100644

--- a/vendor/magento/module-payment-services-paypal/Block/SmartButtons.php

+++ b/vendor/magento/module-payment-services-paypal/Block/SmartButtons.php

@@ -14,6 +14,7 @@ use Magento\Framework\View\Element\Template\Context;

 use Magento\PaymentServicesPaypal\Model\Config;

 use Magento\Catalog\Block\ShortcutInterface;

 use Magento\Framework\View\Element\Template;

+use Magento\Checkout\Model\Session as CheckoutSession;

 

 /**

  * @api

@@ -50,6 +51,11 @@ class SmartButtons extends Template implements ShortcutInterface

      */

     private $serializer;

 

+    /**

+     * @var CheckoutSession

+     */

+    private $checkoutSession;

+

     /**

      * @param Context $context

      * @param Config $config

@@ -59,6 +65,7 @@ class SmartButtons extends Template implements ShortcutInterface

      * @param array $data

      * @param Json|null $serializer

      * @param CompositeConfigProvider|null $compositeConfigProvider

+     * @param CheckoutSession|null $checkoutSession

      */

     public function __construct(

         Context $context,

@@ -68,7 +75,8 @@ class SmartButtons extends Template implements ShortcutInterface

         array $componentConfig = [],

         array $data = [],

         Json $serializer = null,

-        CompositeConfigProvider $compositeConfigProvider = null

+        CompositeConfigProvider $compositeConfigProvider = null,

+        CheckoutSession $checkoutSession = null

     ) {

         $this->config = $config;

         $this->componentConfig = $componentConfig;

@@ -82,6 +90,7 @@ class SmartButtons extends Template implements ShortcutInterface

         $this->setTemplate($data['template'] ?? $componentConfig[$this->pageType]['template']);

         $this->serializer = $serializer ?: ObjectManager::getInstance()->get(Json::class);

         $this->configProvider = $compositeConfigProvider ?: ObjectManager::getInstance()->get(CompositeConfigProvider::class);

+        $this->checkoutSession = $checkoutSession ?: ObjectManager::getInstance()->get(CheckoutSession::class);

     }

 

     /**

@@ -185,4 +194,19 @@ class SmartButtons extends Template implements ShortcutInterface

     {

         return $this->serializer->serialize($this->configProvider->getConfig());

     }

+

+    /**

+     * Check if quote exists

+     *

+     * @return bool

+     * @throws \Magento\Framework\Exception\LocalizedException

+     */

+    public function doesQuoteExist(): bool

+    {

+        try {

+            return $this->checkoutSession->getQuote()->getId() != null;

+        } catch (\Magento\Framework\Exception\NoSuchEntityException $e) {

+            return false;

+        }

+    }

 }

diff --git a/vendor/magento/module-payment-services-paypal/Controller/SmartButtons/SetQuoteAsInactive.php b/vendor/magento/module-payment-services-paypal/Controller/SmartButtons/SetQuoteAsInactive.php

index a7b1f0c4..e82abc52 100644

--- a/vendor/magento/module-payment-services-paypal/Controller/SmartButtons/SetQuoteAsInactive.php

+++ b/vendor/magento/module-payment-services-paypal/Controller/SmartButtons/SetQuoteAsInactive.php

@@ -80,8 +80,11 @@ class SetQuoteAsInactive implements HttpPostActionInterface

 

         try {

             $quote = $this->getQuote();

-            $quote->setIsActive(false);

-            $this->quoteRepository->save($quote);

+

+            if ($quote->getIsActive()) {

+                $quote->setIsActive(false);

+                $this->quoteRepository->save($quote);

+            }

 

             $result->setHttpResponseCode(200);

             return $result;

@@ -103,7 +106,7 @@ class SetQuoteAsInactive implements HttpPostActionInterface

     private function getQuote() : CartInterface

     {

         if ($this->paypalSession->getQuoteId()) {

-            return $this->quoteRepository->getActive($this->paypalSession->getQuoteId());

+            return $this->quoteRepository->get($this->paypalSession->getQuoteId());

         }

 

         return $this->checkoutSession->getQuote();

diff --git a/vendor/magento/module-payment-services-paypal/view/frontend/templates/smart_buttons_minicart.phtml b/vendor/magento/module-payment-services-paypal/view/frontend/templates/smart_buttons_minicart.phtml

index 54fb0aad..108b84e8 100644

--- a/vendor/magento/module-payment-services-paypal/view/frontend/templates/smart_buttons_minicart.phtml

+++ b/vendor/magento/module-payment-services-paypal/view/frontend/templates/smart_buttons_minicart.phtml

@@ -13,11 +13,15 @@ declare(strict_types=1);

 /** @var \Magento\Framework\View\Helper\SecureHtmlRenderer $secureRenderer */

 ?>

 

-<?php $serializedCheckoutConfig = /* @noEscape */ $block->getSerializedCheckoutConfig();

-

-$scriptString = <<<script

+<?php

+if ($block->isEnabled() && $block->doesQuoteExist()) {

+    $serializedCheckoutConfig = /* @noEscape */ $block->getSerializedCheckoutConfig();

+    $scriptString = <<<script

             window.checkoutConfig = {$serializedCheckoutConfig};

+            window.customerData = window.checkoutConfig.customerData;

+            window.isCustomerLoggedIn = window.checkoutConfig.isCustomerLoggedIn;

 script;

+}

 ?>

 

 <?php if ($block->isLocationEnabled('minicart')): ?>

@@ -39,4 +43,8 @@ script;

     </div>

 <?php endif ?>

 

-<?= /* @noEscape */ $secureRenderer->renderTag('script', [], $scriptString, false) ?>

+<?php /* @noEscape */

+if ($block->isEnabled() && $block->doesQuoteExist()) {

+    echo $secureRenderer->renderTag('script', [], $scriptString, false);

+}

+?>

diff --git a/vendor/magento/module-payment-services-paypal/view/frontend/web/js/view/payment/apple-pay-cart.js b/vendor/magento/module-payment-services-paypal/view/frontend/web/js/view/payment/apple-pay-cart.js

index 2fe5692d..7eafe4c0 100644

--- a/vendor/magento/module-payment-services-paypal/view/frontend/web/js/view/payment/apple-pay-cart.js

+++ b/vendor/magento/module-payment-services-paypal/view/frontend/web/js/view/payment/apple-pay-cart.js

@@ -15,7 +15,8 @@ define([

     'Magento_PaymentServicesPaypal/js/view/payment/methods/apple-pay',

     'Magento_Checkout/js/model/quote',

     'Magento_Checkout/js/model/cart/totals-processor/default',

-], function (_, $, utils, Component, $t, customerData, ResponseError, ApplePayButton, quote, totalsProcessor) {

+    'Magento_Customer/js/model/customer',

+], function (_, $, utils, Component, $t, customerData, ResponseError, ApplePayButton, quote, totalsProcessor, customer) {

     'use strict';

 

     return Component.extend({

@@ -41,12 +42,6 @@ define([

                 .then(this.initApplePayButton)

                 .catch(console.log);

 

-            // Reload quote totals in minicart to have the correct grand_total for the Apple Popup

-            if (this.pageType === 'minicart') {

-                totalsProcessor.estimateTotals().done(function (result) {

-                    quote.setTotals(result);

-                });

-            }

             return this;

         },

 

@@ -100,19 +95,27 @@ define([

         },

 

         onClick: function () {

-            this.isErrorDisplayed = false;

+            // Reload customer data to use correct loggedin/guest urls in the applepay button

+            // See smart_buttons_minicart.phtml:21-22

+            if (this.pageType === 'minicart') {

+                this.fixCustomerData();

+            }

 

+            // Show popup with initial order amount from window.checkoutConfig

+            // See smart_buttons_minicart.phtml:20

             this.applePayButton.showLoaderAsync(true).then(() => {

                 const data = {

                     response: {

                         'paypal-order': {

-                            currency_code: String(quote.totals().quote_currency_code),

-                            amount: Number(quote.totals().grand_total).toString(),

+                            currency_code: window.checkoutConfig.quoteData.base_currency_code,

+                            amount: window.checkoutConfig.quoteData.grand_total.toString(),

                         }

                     }

                 }

                 this.applePayButton.showPopup(data);

             })

+

+            this.isErrorDisplayed = false;

         },

 

         /**

@@ -163,5 +166,33 @@ define([

 

             this.applePaySession.begin();

         },

+

+        /**

+         * Fix customer data

+         *

+         * Why do we need this?

+         * See: src/app/code/Magento/Customer/view/frontend/web/js/model/customer.js:17

+         *

+         * When we initialise customer data on the page where the minicart was not rendered yet,

+         * the customer data in the "window" object is 'undefined' at first because . This makes this line

+         *      var isLoggedIn = ko.observable(window.isCustomerLoggedIn),

+         * to create an observable of undefined variable, that does not work in knockout.

+         * knockout expects an existing variable to create an observable.

+         *

+         * Later, when we render minicart and update "window" object with customer data,

+         * it's not being picked up by customer.js logic and when try to read the data, it's still undefined,

+         * even though it exists in the "window" object.

+         *

+         * This function forces the customer data to be updated from the "window" object.

+         */

+        fixCustomerData: function () {

+            if (customer.isLoggedIn() === undefined && window.isCustomerLoggedIn !== undefined) {

+                customer.setIsLoggedIn(window.isCustomerLoggedIn);

+            }

+

+            if (customer.isLoggedIn() && _.isEmpty(customer.customerData)) {

+                customer.customerData = window.customerData;

+            }

+        }

     });

 });



Where I can see this patch on GitHub, link?

@ssx
Copy link
Contributor

ssx commented Feb 10, 2025

Adobe have patched this now:


diff --git a/vendor/magento/module-payment-services-paypal/Block/SmartButtons.php b/vendor/magento/module-payment-services-paypal/Block/SmartButtons.php

index 60beabb8..d74fa01d 100644

--- a/vendor/magento/module-payment-services-paypal/Block/SmartButtons.php

+++ b/vendor/magento/module-payment-services-paypal/Block/SmartButtons.php

@@ -14,6 +14,7 @@ use Magento\Framework\View\Element\Template\Context;

 use Magento\PaymentServicesPaypal\Model\Config;

 use Magento\Catalog\Block\ShortcutInterface;

 use Magento\Framework\View\Element\Template;

+use Magento\Checkout\Model\Session as CheckoutSession;

 

 /**

  * @api

@@ -50,6 +51,11 @@ class SmartButtons extends Template implements ShortcutInterface

      */

     private $serializer;

 

+    /**

+     * @var CheckoutSession

+     */

+    private $checkoutSession;

+

     /**

      * @param Context $context

      * @param Config $config

@@ -59,6 +65,7 @@ class SmartButtons extends Template implements ShortcutInterface

      * @param array $data

      * @param Json|null $serializer

      * @param CompositeConfigProvider|null $compositeConfigProvider

+     * @param CheckoutSession|null $checkoutSession

      */

     public function __construct(

         Context $context,

@@ -68,7 +75,8 @@ class SmartButtons extends Template implements ShortcutInterface

         array $componentConfig = [],

         array $data = [],

         Json $serializer = null,

-        CompositeConfigProvider $compositeConfigProvider = null

+        CompositeConfigProvider $compositeConfigProvider = null,

+        CheckoutSession $checkoutSession = null

     ) {

         $this->config = $config;

         $this->componentConfig = $componentConfig;

@@ -82,6 +90,7 @@ class SmartButtons extends Template implements ShortcutInterface

         $this->setTemplate($data['template'] ?? $componentConfig[$this->pageType]['template']);

         $this->serializer = $serializer ?: ObjectManager::getInstance()->get(Json::class);

         $this->configProvider = $compositeConfigProvider ?: ObjectManager::getInstance()->get(CompositeConfigProvider::class);

+        $this->checkoutSession = $checkoutSession ?: ObjectManager::getInstance()->get(CheckoutSession::class);

     }

 

     /**

@@ -185,4 +194,19 @@ class SmartButtons extends Template implements ShortcutInterface

     {

         return $this->serializer->serialize($this->configProvider->getConfig());

     }

+

+    /**

+     * Check if quote exists

+     *

+     * @return bool

+     * @throws \Magento\Framework\Exception\LocalizedException

+     */

+    public function doesQuoteExist(): bool

+    {

+        try {

+            return $this->checkoutSession->getQuote()->getId() != null;

+        } catch (\Magento\Framework\Exception\NoSuchEntityException $e) {

+            return false;

+        }

+    }

 }

diff --git a/vendor/magento/module-payment-services-paypal/Controller/SmartButtons/SetQuoteAsInactive.php b/vendor/magento/module-payment-services-paypal/Controller/SmartButtons/SetQuoteAsInactive.php

index a7b1f0c4..e82abc52 100644

--- a/vendor/magento/module-payment-services-paypal/Controller/SmartButtons/SetQuoteAsInactive.php

+++ b/vendor/magento/module-payment-services-paypal/Controller/SmartButtons/SetQuoteAsInactive.php

@@ -80,8 +80,11 @@ class SetQuoteAsInactive implements HttpPostActionInterface

 

         try {

             $quote = $this->getQuote();

-            $quote->setIsActive(false);

-            $this->quoteRepository->save($quote);

+

+            if ($quote->getIsActive()) {

+                $quote->setIsActive(false);

+                $this->quoteRepository->save($quote);

+            }

 

             $result->setHttpResponseCode(200);

             return $result;

@@ -103,7 +106,7 @@ class SetQuoteAsInactive implements HttpPostActionInterface

     private function getQuote() : CartInterface

     {

         if ($this->paypalSession->getQuoteId()) {

-            return $this->quoteRepository->getActive($this->paypalSession->getQuoteId());

+            return $this->quoteRepository->get($this->paypalSession->getQuoteId());

         }

 

         return $this->checkoutSession->getQuote();

diff --git a/vendor/magento/module-payment-services-paypal/view/frontend/templates/smart_buttons_minicart.phtml b/vendor/magento/module-payment-services-paypal/view/frontend/templates/smart_buttons_minicart.phtml

index 54fb0aad..108b84e8 100644

--- a/vendor/magento/module-payment-services-paypal/view/frontend/templates/smart_buttons_minicart.phtml

+++ b/vendor/magento/module-payment-services-paypal/view/frontend/templates/smart_buttons_minicart.phtml

@@ -13,11 +13,15 @@ declare(strict_types=1);

 /** @var \Magento\Framework\View\Helper\SecureHtmlRenderer $secureRenderer */

 ?>

 

-<?php $serializedCheckoutConfig = /* @noEscape */ $block->getSerializedCheckoutConfig();

-

-$scriptString = <<<script

+<?php

+if ($block->isEnabled() && $block->doesQuoteExist()) {

+    $serializedCheckoutConfig = /* @noEscape */ $block->getSerializedCheckoutConfig();

+    $scriptString = <<<script

             window.checkoutConfig = {$serializedCheckoutConfig};

+            window.customerData = window.checkoutConfig.customerData;

+            window.isCustomerLoggedIn = window.checkoutConfig.isCustomerLoggedIn;

 script;

+}

 ?>

 

 <?php if ($block->isLocationEnabled('minicart')): ?>

@@ -39,4 +43,8 @@ script;

     </div>

 <?php endif ?>

 

-<?= /* @noEscape */ $secureRenderer->renderTag('script', [], $scriptString, false) ?>

+<?php /* @noEscape */

+if ($block->isEnabled() && $block->doesQuoteExist()) {

+    echo $secureRenderer->renderTag('script', [], $scriptString, false);

+}

+?>

diff --git a/vendor/magento/module-payment-services-paypal/view/frontend/web/js/view/payment/apple-pay-cart.js b/vendor/magento/module-payment-services-paypal/view/frontend/web/js/view/payment/apple-pay-cart.js

index 2fe5692d..7eafe4c0 100644

--- a/vendor/magento/module-payment-services-paypal/view/frontend/web/js/view/payment/apple-pay-cart.js

+++ b/vendor/magento/module-payment-services-paypal/view/frontend/web/js/view/payment/apple-pay-cart.js

@@ -15,7 +15,8 @@ define([

     'Magento_PaymentServicesPaypal/js/view/payment/methods/apple-pay',

     'Magento_Checkout/js/model/quote',

     'Magento_Checkout/js/model/cart/totals-processor/default',

-], function (_, $, utils, Component, $t, customerData, ResponseError, ApplePayButton, quote, totalsProcessor) {

+    'Magento_Customer/js/model/customer',

+], function (_, $, utils, Component, $t, customerData, ResponseError, ApplePayButton, quote, totalsProcessor, customer) {

     'use strict';

 

     return Component.extend({

@@ -41,12 +42,6 @@ define([

                 .then(this.initApplePayButton)

                 .catch(console.log);

 

-            // Reload quote totals in minicart to have the correct grand_total for the Apple Popup

-            if (this.pageType === 'minicart') {

-                totalsProcessor.estimateTotals().done(function (result) {

-                    quote.setTotals(result);

-                });

-            }

             return this;

         },

 

@@ -100,19 +95,27 @@ define([

         },

 

         onClick: function () {

-            this.isErrorDisplayed = false;

+            // Reload customer data to use correct loggedin/guest urls in the applepay button

+            // See smart_buttons_minicart.phtml:21-22

+            if (this.pageType === 'minicart') {

+                this.fixCustomerData();

+            }

 

+            // Show popup with initial order amount from window.checkoutConfig

+            // See smart_buttons_minicart.phtml:20

             this.applePayButton.showLoaderAsync(true).then(() => {

                 const data = {

                     response: {

                         'paypal-order': {

-                            currency_code: String(quote.totals().quote_currency_code),

-                            amount: Number(quote.totals().grand_total).toString(),

+                            currency_code: window.checkoutConfig.quoteData.base_currency_code,

+                            amount: window.checkoutConfig.quoteData.grand_total.toString(),

                         }

                     }

                 }

                 this.applePayButton.showPopup(data);

             })

+

+            this.isErrorDisplayed = false;

         },

 

         /**

@@ -163,5 +166,33 @@ define([

 

             this.applePaySession.begin();

         },

+

+        /**

+         * Fix customer data

+         *

+         * Why do we need this?

+         * See: src/app/code/Magento/Customer/view/frontend/web/js/model/customer.js:17

+         *

+         * When we initialise customer data on the page where the minicart was not rendered yet,

+         * the customer data in the "window" object is 'undefined' at first because . This makes this line

+         *      var isLoggedIn = ko.observable(window.isCustomerLoggedIn),

+         * to create an observable of undefined variable, that does not work in knockout.

+         * knockout expects an existing variable to create an observable.

+         *

+         * Later, when we render minicart and update "window" object with customer data,

+         * it's not being picked up by customer.js logic and when try to read the data, it's still undefined,

+         * even though it exists in the "window" object.

+         *

+         * This function forces the customer data to be updated from the "window" object.

+         */

+        fixCustomerData: function () {

+            if (customer.isLoggedIn() === undefined && window.isCustomerLoggedIn !== undefined) {

+                customer.setIsLoggedIn(window.isCustomerLoggedIn);

+            }

+

+            if (customer.isLoggedIn() && _.isEmpty(customer.customerData)) {

+                customer.customerData = window.customerData;

+            }

+        }

     });

 });

Where I can see this patch on GitHub, link?

You'll need to get in touch with support for it @Genaker.

@engcom-Hotel
Copy link
Contributor

After reviewing the conversation on this PR, it appears that the issue has already been addressed by the internal team.

Therefore, I am closing this PR. Thank you for your contribution @Genaker.

@Genaker
Copy link
Author

Genaker commented Feb 18, 2025 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants