Skip to content

Commit 27a1299

Browse files
committed
MC-23992: Exception occurs when attempting to unset persistence cookie after visiting checkout
1 parent 783f8b0 commit 27a1299

File tree

7 files changed

+348
-18
lines changed

7 files changed

+348
-18
lines changed
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Persistent\Model\Checkout;
9+
10+
use Magento\Checkout\Api\Data\PaymentDetailsInterface;
11+
use Magento\Checkout\Model\GuestShippingInformationManagement;
12+
use Magento\Customer\Model\Session as CustomerSession;
13+
use Magento\Persistent\Helper\Data;
14+
use Magento\Persistent\Helper\Session as PersistentSession;
15+
use Magento\Persistent\Model\QuoteManager;
16+
17+
/**
18+
* Plugin to convert shopping cart from persistent cart to guest cart after shipping information saved
19+
*
20+
* @SuppressWarnings(PHPMD.CookieAndSessionMisuse)
21+
*/
22+
class GuestShippingInformationManagementPlugin
23+
{
24+
/**
25+
* Persistence Session Helper
26+
*
27+
* @var PersistentSession
28+
*/
29+
private $persistenceSessionHelper;
30+
31+
/**
32+
* Persistence Data Helper
33+
*
34+
* @var Data
35+
*/
36+
private $persistenceDataHelper;
37+
38+
/**
39+
* Customer Session
40+
*
41+
* @var CustomerSession
42+
*/
43+
private $customerSession;
44+
45+
/**
46+
* Quote Manager
47+
*
48+
* @var QuoteManager
49+
*/
50+
private $quoteManager;
51+
52+
/**
53+
* Initialize dependencies
54+
*
55+
* @param Data $persistenceDataHelper
56+
* @param PersistentSession $persistenceSessionHelper
57+
* @param CustomerSession $customerSession
58+
* @param QuoteManager $quoteManager
59+
*/
60+
public function __construct(
61+
Data $persistenceDataHelper,
62+
PersistentSession $persistenceSessionHelper,
63+
CustomerSession $customerSession,
64+
QuoteManager $quoteManager
65+
) {
66+
$this->persistenceDataHelper = $persistenceDataHelper;
67+
$this->persistenceSessionHelper = $persistenceSessionHelper;
68+
$this->customerSession = $customerSession;
69+
$this->quoteManager = $quoteManager;
70+
}
71+
72+
/**
73+
* Convert shopping cart from persistent cart to guest cart after shipping information saved
74+
*
75+
* Check if shopping cart is persistent and customer is not logged in, and only one payment method is available,
76+
* then converts the shopping cart guest cart.
77+
* If only one payment is available, it's preselected by default and the payment information is automatically saved.
78+
*
79+
* @param GuestShippingInformationManagement $subject
80+
* @param PaymentDetailsInterface $result
81+
* @return PaymentDetailsInterface
82+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
83+
*/
84+
public function afterSaveAddressInformation(
85+
GuestShippingInformationManagement $subject,
86+
PaymentDetailsInterface $result
87+
): PaymentDetailsInterface {
88+
if ($this->persistenceSessionHelper->isPersistent()
89+
&& !$this->customerSession->isLoggedIn()
90+
&& $this->persistenceDataHelper->isShoppingCartPersist()
91+
&& $this->quoteManager->isPersistent()
92+
&& count($result->getPaymentMethods()) === 1
93+
) {
94+
$this->customerSession->setCustomerId(null);
95+
$this->customerSession->setCustomerGroupId(null);
96+
$this->quoteManager->convertCustomerCartToGuest();
97+
}
98+
return $result;
99+
}
100+
}

app/code/Magento/Persistent/Model/QuoteManager.php

Lines changed: 60 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,17 @@
55
*/
66
namespace Magento\Persistent\Model;
77

8+
use Magento\Customer\Api\Data\GroupInterface;
9+
use Magento\Framework\App\ObjectManager;
10+
use Magento\Persistent\Helper\Data;
11+
use Magento\Quote\Api\CartRepositoryInterface;
12+
use Magento\Quote\Api\Data\CartExtensionFactory;
13+
use Magento\Quote\Api\Data\CartInterface;
14+
use Magento\Quote\Model\Quote;
15+
use Magento\Quote\Model\Quote\ShippingAssignment\ShippingAssignmentProcessor;
16+
817
/**
9-
* Class QuoteManager
18+
* Quote manager model
1019
*
1120
* @SuppressWarnings(PHPMD.CookieAndSessionMisuse)
1221
*/
@@ -29,7 +38,7 @@ class QuoteManager
2938
/**
3039
* Persistent data
3140
*
32-
* @var \Magento\Persistent\Helper\Data
41+
* @var Data
3342
*/
3443
protected $persistentData;
3544

@@ -41,26 +50,44 @@ class QuoteManager
4150
protected $_setQuotePersistent = true;
4251

4352
/**
44-
* @var \Magento\Quote\Api\CartRepositoryInterface
53+
* @var CartRepositoryInterface
4554
*/
4655
protected $quoteRepository;
4756

57+
/**
58+
* @var ShippingAssignmentProcessor
59+
*/
60+
private $shippingAssignmentProcessor;
61+
62+
/**
63+
* @var CartExtensionFactory
64+
*/
65+
private $cartExtensionFactory;
66+
4867
/**
4968
* @param \Magento\Persistent\Helper\Session $persistentSession
50-
* @param \Magento\Persistent\Helper\Data $persistentData
69+
* @param Data $persistentData
5170
* @param \Magento\Checkout\Model\Session $checkoutSession
52-
* @param \Magento\Quote\Api\CartRepositoryInterface $quoteRepository
71+
* @param CartRepositoryInterface $quoteRepository
72+
* @param CartExtensionFactory|null $cartExtensionFactory
73+
* @param ShippingAssignmentProcessor|null $shippingAssignmentProcessor
5374
*/
5475
public function __construct(
5576
\Magento\Persistent\Helper\Session $persistentSession,
56-
\Magento\Persistent\Helper\Data $persistentData,
77+
Data $persistentData,
5778
\Magento\Checkout\Model\Session $checkoutSession,
58-
\Magento\Quote\Api\CartRepositoryInterface $quoteRepository
79+
CartRepositoryInterface $quoteRepository,
80+
?CartExtensionFactory $cartExtensionFactory = null,
81+
?ShippingAssignmentProcessor $shippingAssignmentProcessor = null
5982
) {
6083
$this->persistentSession = $persistentSession;
6184
$this->persistentData = $persistentData;
6285
$this->checkoutSession = $checkoutSession;
6386
$this->quoteRepository = $quoteRepository;
87+
$this->cartExtensionFactory = $cartExtensionFactory
88+
?? ObjectManager::getInstance()->get(CartExtensionFactory::class);
89+
$this->shippingAssignmentProcessor = $shippingAssignmentProcessor
90+
?? ObjectManager::getInstance()->get(ShippingAssignmentProcessor::class);
6491
}
6592

6693
/**
@@ -71,7 +98,7 @@ public function __construct(
7198
*/
7299
public function setGuest($checkQuote = false)
73100
{
74-
/** @var $quote \Magento\Quote\Model\Quote */
101+
/** @var $quote Quote */
75102
$quote = $this->checkoutSession->getQuote();
76103
if ($quote && $quote->getId()) {
77104
if ($checkQuote && !$this->persistentData->isShoppingCartPersist() && !$quote->getIsPersistent()) {
@@ -87,17 +114,19 @@ public function setGuest($checkQuote = false)
87114
->setCustomerEmail(null)
88115
->setCustomerFirstname(null)
89116
->setCustomerLastname(null)
90-
->setCustomerGroupId(\Magento\Customer\Api\Data\GroupInterface::NOT_LOGGED_IN_ID)
117+
->setCustomerGroupId(GroupInterface::NOT_LOGGED_IN_ID)
91118
->setIsPersistent(false)
92119
->removeAllAddresses();
93120
//Create guest addresses
94121
$quote->getShippingAddress();
95122
$quote->getBillingAddress();
123+
$this->setShippingAssignments($quote);
96124
$quote->collectTotals();
97125
$this->quoteRepository->save($quote);
98126
}
99127

100128
$this->persistentSession->getSession()->removePersistentCookie();
129+
$this->persistentSession->setSession(null);
101130
}
102131

103132
/**
@@ -111,7 +140,7 @@ public function setGuest($checkQuote = false)
111140
public function convertCustomerCartToGuest()
112141
{
113142
$quoteId = $this->checkoutSession->getQuoteId();
114-
/** @var $quote \Magento\Quote\Model\Quote */
143+
/** @var $quote Quote */
115144
$quote = $this->quoteRepository->get($quoteId);
116145
if ($quote && $quote->getId()) {
117146
$this->_setQuotePersistent = false;
@@ -126,6 +155,7 @@ public function convertCustomerCartToGuest()
126155
$quote->getAddressesCollection()->walk('setEmail', ['email' => null]);
127156
$quote->collectTotals();
128157
$this->persistentSession->getSession()->removePersistentCookie();
158+
$this->persistentSession->setSession(null);
129159
$this->quoteRepository->save($quote);
130160
}
131161
}
@@ -144,7 +174,7 @@ public function expire()
144174
$quote->setIsActive(true)
145175
->setIsPersistent(false)
146176
->setCustomerId(null)
147-
->setCustomerGroupId(\Magento\Customer\Api\Data\GroupInterface::NOT_LOGGED_IN_ID);
177+
->setCustomerGroupId(GroupInterface::NOT_LOGGED_IN_ID);
148178
}
149179
}
150180

@@ -157,4 +187,23 @@ public function isPersistent()
157187
{
158188
return $this->_setQuotePersistent;
159189
}
190+
191+
/**
192+
* Create shipping assignment for shopping cart
193+
*
194+
* @param CartInterface $quote
195+
*/
196+
private function setShippingAssignments(CartInterface $quote): void
197+
{
198+
$shippingAssignments = [];
199+
if (!$quote->isVirtual() && $quote->getItemsQty() > 0) {
200+
$shippingAssignments[] = $this->shippingAssignmentProcessor->create($quote);
201+
}
202+
$cartExtension = $quote->getExtensionAttributes();
203+
if ($cartExtension === null) {
204+
$cartExtension = $this->cartExtensionFactory->create();
205+
}
206+
$cartExtension->setShippingAssignments($shippingAssignments);
207+
$quote->setExtensionAttributes($cartExtension);
208+
}
160209
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Persistent\Test\Unit\Model\Checkout;
9+
10+
use Magento\Checkout\Api\Data\PaymentDetailsInterface;
11+
use Magento\Checkout\Model\GuestShippingInformationManagement;
12+
use Magento\Customer\Model\Session as CustomerSession;
13+
use Magento\Persistent\Helper\Data;
14+
use Magento\Persistent\Helper\Session as PersistenceSession;
15+
use Magento\Persistent\Model\Checkout\GuestShippingInformationManagementPlugin;
16+
use Magento\Persistent\Model\QuoteManager;
17+
use PHPUnit\Framework\MockObject\MockObject;
18+
use PHPUnit\Framework\TestCase;
19+
20+
class GuestShippingInformationManagementPluginTest extends TestCase
21+
{
22+
/**
23+
* @var Data|MockObject
24+
*/
25+
private $persistenceDataHelper;
26+
27+
/**
28+
* @var PersistenceSession|MockObject
29+
*/
30+
private $persistenceSessionHelper;
31+
32+
/**
33+
* @var CustomerSession|MockObject
34+
*/
35+
private $customerSession;
36+
37+
/**
38+
* @var QuoteManager|MockObject
39+
*/
40+
private $quoteManager;
41+
42+
/**
43+
* @var GuestShippingInformationManagementPlugin
44+
*/
45+
private $model;
46+
47+
/**
48+
* @inheritDoc
49+
*/
50+
protected function setUp(): void
51+
{
52+
parent::setUp();
53+
$this->persistenceDataHelper = $this->createMock(Data::class);
54+
$this->persistenceSessionHelper = $this->createMock(PersistenceSession::class);
55+
$this->customerSession = $this->createMock(CustomerSession::class);
56+
$this->quoteManager = $this->createMock(QuoteManager::class);
57+
$this->model = new GuestShippingInformationManagementPlugin(
58+
$this->persistenceDataHelper,
59+
$this->persistenceSessionHelper,
60+
$this->customerSession,
61+
$this->quoteManager
62+
);
63+
}
64+
65+
/**
66+
* @param array $paymentMethods
67+
* @param bool $isLoggedIn
68+
* @param bool $isPersistentSessionEnabled
69+
* @param bool $isPersistentCartEnabled
70+
* @param bool $isCartPersistent
71+
* @param bool $isCartConverted
72+
* @dataProvider afterSaveAddressInformationDataProvider
73+
*/
74+
public function testAfterSaveAddressInformation(
75+
array $paymentMethods,
76+
bool $isLoggedIn,
77+
bool $isPersistentSessionEnabled,
78+
bool $isPersistentCartEnabled,
79+
bool $isCartPersistent,
80+
bool $isCartConverted
81+
): void {
82+
$subject = $this->createMock(GuestShippingInformationManagement::class);
83+
$result = $this->createMock(PaymentDetailsInterface::class);
84+
$result->method('getPaymentMethods')
85+
->willReturn($paymentMethods);
86+
$this->customerSession->method('isLoggedIn')
87+
->willReturn($isLoggedIn);
88+
$this->persistenceSessionHelper->method('isPersistent')
89+
->willReturn($isPersistentSessionEnabled);
90+
$this->persistenceDataHelper->method('isShoppingCartPersist')
91+
->willReturn($isPersistentCartEnabled);
92+
$this->quoteManager->method('isPersistent')
93+
->willReturn($isCartPersistent);
94+
$this->customerSession->expects($this->exactly($isCartConverted ? 1 : 0))
95+
->method('setCustomerId')
96+
->with(null);
97+
$this->customerSession->expects($this->exactly($isCartConverted ? 1 : 0))
98+
->method('setCustomerGroupId')
99+
->with(null);
100+
$this->quoteManager->expects($this->exactly($isCartConverted ? 1 : 0))
101+
->method('convertCustomerCartToGuest');
102+
$this->assertSame($result, $this->model->afterSaveAddressInformation($subject, $result));
103+
}
104+
105+
/**
106+
* @return array
107+
*/
108+
public function afterSaveAddressInformationDataProvider(): array
109+
{
110+
return [
111+
[['paypal'], false, true, true, true, true],
112+
[['paypal'], true, true, true, true, false],
113+
[['paypal', 'money_order'], false, true, true, true, false],
114+
[['paypal', 'money_order'], true, true, true, true, false],
115+
];
116+
}
117+
}

0 commit comments

Comments
 (0)