Skip to content

Commit e596c60

Browse files
committed
AC-9605: Place order with disabled Payment method working
1 parent bd4aabb commit e596c60

File tree

3 files changed

+300
-0
lines changed

3 files changed

+300
-0
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\QuoteGraphQl\Plugin\Quote;
9+
10+
use Magento\Framework\Exception\LocalizedException;
11+
use Magento\Payment\Helper\Data as PaymentHelper;
12+
use Magento\Quote\Api\CartRepositoryInterface;
13+
use Magento\Quote\Api\Data\PaymentInterface;
14+
use Magento\Quote\Model\QuoteManagement;
15+
16+
class ValidatePaymentOnPlaceOrder
17+
{
18+
public function __construct(
19+
private CartRepositoryInterface $cartRepository,
20+
private PaymentHelper $paymentHelper
21+
) {}
22+
23+
/**
24+
* @param QuoteManagement $subject
25+
* @param $cartId
26+
* @param PaymentInterface|null $paymentMethod
27+
* @return array
28+
* @throws LocalizedException
29+
* @throws \Magento\Framework\Exception\NoSuchEntityException
30+
*/
31+
public function beforePlaceOrder(QuoteManagement $subject, $cartId, PaymentInterface $paymentMethod = null): array
32+
{
33+
$quote = $this->cartRepository->getActive((int)$cartId);
34+
35+
$payment = $quote->getPayment();
36+
$code = $paymentMethod?->getMethod() ?: $payment->getMethod();
37+
38+
if (!$code) {
39+
return [$cartId, $paymentMethod];
40+
}
41+
42+
$methodInstance = $this->paymentHelper->getMethodInstance($code);
43+
$methodInstance->setInfoInstance($payment);
44+
45+
if (!$methodInstance->isAvailable($quote)) {
46+
throw new LocalizedException(__('The requested Payment Method is not available.'));
47+
}
48+
49+
return [$cartId, $paymentMethod];
50+
}
51+
}
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\QuoteGraphQl\Test\Unit\Plugin\Quote;
9+
10+
use Magento\Framework\Exception\LocalizedException;
11+
use Magento\Payment\Helper\Data as PaymentHelper;
12+
use Magento\Payment\Model\MethodInterface;
13+
use Magento\Quote\Api\CartRepositoryInterface;
14+
use Magento\Quote\Api\Data\PaymentInterface;
15+
use Magento\Quote\Model\QuoteManagement;
16+
use Magento\QuoteGraphQl\Plugin\Quote\ValidatePaymentOnPlaceOrder;
17+
use PHPUnit\Framework\MockObject\MockObject;
18+
use PHPUnit\Framework\TestCase;
19+
20+
class ValidatePaymentOnPlaceOrderTest extends TestCase
21+
{
22+
/** @var CartRepositoryInterface&MockObject */
23+
private CartRepositoryInterface $cartRepository;
24+
25+
/** @var PaymentHelper&MockObject */
26+
private PaymentHelper $paymentHelper;
27+
28+
private ValidatePaymentOnPlaceOrder $plugin;
29+
30+
protected function setUp(): void
31+
{
32+
$this->cartRepository = $this->createMock(CartRepositoryInterface::class);
33+
$this->paymentHelper = $this->createMock(PaymentHelper::class);
34+
35+
$this->plugin = new ValidatePaymentOnPlaceOrder(
36+
$this->cartRepository,
37+
$this->paymentHelper
38+
);
39+
}
40+
41+
public function testReturnsArgsWhenNoPaymentCode(): void
42+
{
43+
$subject = $this->createMock(QuoteManagement::class);
44+
45+
$quote = $this->getMockBuilder(\Magento\Quote\Model\Quote::class)
46+
->disableOriginalConstructor()
47+
->onlyMethods(['getPayment'])
48+
->getMock();
49+
50+
$payment = $this->getMockBuilder(\Magento\Quote\Model\Quote\Payment::class)
51+
->disableOriginalConstructor()
52+
->onlyMethods(['getMethod'])
53+
->getMock();
54+
55+
$this->cartRepository
56+
->expects($this->once())
57+
->method('getActive')
58+
->with($this->identicalTo(123))
59+
->willReturn($quote);
60+
61+
$quote
62+
->expects($this->once())
63+
->method('getPayment')
64+
->willReturn($payment);
65+
66+
$payment
67+
->expects($this->once())
68+
->method('getMethod')
69+
->willReturn(null);
70+
71+
$this->paymentHelper
72+
->expects($this->never())
73+
->method('getMethodInstance');
74+
75+
$result = $this->plugin->beforePlaceOrder($subject, '123', null);
76+
77+
$this->assertSame(['123', null], $result);
78+
}
79+
80+
public function testUsesProvidedPaymentMethodCodeAndIsAvailable(): void
81+
{
82+
$subject = $this->createMock(QuoteManagement::class);
83+
84+
$quote = $this->getMockBuilder(\Magento\Quote\Model\Quote::class)
85+
->disableOriginalConstructor()
86+
->onlyMethods(['getPayment'])
87+
->getMock();
88+
89+
$payment = $this->getMockBuilder(\Magento\Quote\Model\Quote\Payment::class)
90+
->disableOriginalConstructor()
91+
->onlyMethods(['getMethod'])
92+
->getMock();
93+
94+
$paymentMethodParam = $this->createMock(PaymentInterface::class);
95+
$methodInstance = $this->createMock(MethodInterface::class);
96+
97+
$this->cartRepository
98+
->expects($this->once())
99+
->method('getActive')
100+
->with($this->identicalTo(10))
101+
->willReturn($quote);
102+
103+
$quote
104+
->expects($this->once())
105+
->method('getPayment')
106+
->willReturn($payment);
107+
108+
$paymentMethodParam
109+
->expects($this->once())
110+
->method('getMethod')
111+
->willReturn('checkmo');
112+
113+
$this->paymentHelper
114+
->expects($this->once())
115+
->method('getMethodInstance')
116+
->with('checkmo')
117+
->willReturn($methodInstance);
118+
119+
$methodInstance
120+
->expects($this->once())
121+
->method('setInfoInstance')
122+
->with($payment);
123+
124+
$methodInstance
125+
->expects($this->once())
126+
->method('isAvailable')
127+
->with($quote)
128+
->willReturn(true);
129+
130+
$result = $this->plugin->beforePlaceOrder($subject, '10', $paymentMethodParam);
131+
132+
$this->assertSame(['10', $paymentMethodParam], $result);
133+
}
134+
135+
public function testFallsBackToQuotePaymentCodeWhenNoParamProvided(): void
136+
{
137+
$subject = $this->createMock(QuoteManagement::class);
138+
139+
$quote = $this->getMockBuilder(\Magento\Quote\Model\Quote::class)
140+
->disableOriginalConstructor()
141+
->onlyMethods(['getPayment'])
142+
->getMock();
143+
144+
$payment = $this->getMockBuilder(\Magento\Quote\Model\Quote\Payment::class)
145+
->disableOriginalConstructor()
146+
->onlyMethods(['getMethod'])
147+
->getMock();
148+
149+
$methodInstance = $this->createMock(MethodInterface::class);
150+
151+
$this->cartRepository
152+
->expects($this->once())
153+
->method('getActive')
154+
->with($this->identicalTo(77))
155+
->willReturn($quote);
156+
157+
$quote
158+
->expects($this->once())
159+
->method('getPayment')
160+
->willReturn($payment);
161+
162+
$payment
163+
->expects($this->once())
164+
->method('getMethod')
165+
->willReturn('banktransfer');
166+
167+
$this->paymentHelper
168+
->expects($this->once())
169+
->method('getMethodInstance')
170+
->with('banktransfer')
171+
->willReturn($methodInstance);
172+
173+
$methodInstance
174+
->expects($this->once())
175+
->method('setInfoInstance')
176+
->with($payment);
177+
178+
$methodInstance
179+
->expects($this->once())
180+
->method('isAvailable')
181+
->with($quote)
182+
->willReturn(true);
183+
184+
$result = $this->plugin->beforePlaceOrder($subject, '77', null);
185+
186+
$this->assertSame(['77', null], $result);
187+
}
188+
189+
public function testThrowsWhenMethodNotAvailable(): void
190+
{
191+
$subject = $this->createMock(QuoteManagement::class);
192+
193+
$quote = $this->getMockBuilder(\Magento\Quote\Model\Quote::class)
194+
->disableOriginalConstructor()
195+
->onlyMethods(['getPayment'])
196+
->getMock();
197+
198+
$payment = $this->getMockBuilder(\Magento\Quote\Model\Quote\Payment::class)
199+
->disableOriginalConstructor()
200+
->onlyMethods(['getMethod'])
201+
->getMock();
202+
203+
$paymentMethodParam = $this->createMock(PaymentInterface::class);
204+
$methodInstance = $this->createMock(MethodInterface::class);
205+
206+
$this->cartRepository
207+
->expects($this->once())
208+
->method('getActive')
209+
->with($this->identicalTo(5))
210+
->willReturn($quote);
211+
212+
$quote
213+
->expects($this->once())
214+
->method('getPayment')
215+
->willReturn($payment);
216+
217+
$paymentMethodParam
218+
->expects($this->once())
219+
->method('getMethod')
220+
->willReturn('cc');
221+
222+
$this->paymentHelper
223+
->expects($this->once())
224+
->method('getMethodInstance')
225+
->with('cc')
226+
->willReturn($methodInstance);
227+
228+
$methodInstance
229+
->expects($this->once())
230+
->method('setInfoInstance')
231+
->with($payment);
232+
233+
$methodInstance
234+
->expects($this->once())
235+
->method('isAvailable')
236+
->with($quote)
237+
->willReturn(false);
238+
239+
$this->expectException(LocalizedException::class);
240+
$this->expectExceptionMessage('The requested Payment Method is not available.');
241+
242+
$this->plugin->beforePlaceOrder($subject, '5', $paymentMethodParam);
243+
}
244+
}

app/code/Magento/QuoteGraphQl/etc/graphql/di.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,4 +114,9 @@
114114
</argument>
115115
</arguments>
116116
</type>
117+
<type name="Magento\Quote\Model\QuoteManagement">
118+
<plugin name="validate_payment_method_on_place_order"
119+
type="Magento\QuoteGraphQl\Plugin\Quote\ValidatePaymentOnPlaceOrder"
120+
sortOrder="10"/>
121+
</type>
117122
</config>

0 commit comments

Comments
 (0)