Skip to content

Commit 4cae1af

Browse files
committed
GraphQL-293: [Payment methods] Set Payment Method on Cart
1 parent 287186d commit 4cae1af

15 files changed

+1423
-406
lines changed

app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,12 @@ public function __construct(
4545
* Get cart for user
4646
*
4747
* @param string $cartHash
48-
* @param int|null $userId
48+
* @param int|null $customerId
4949
* @return Quote
5050
* @throws GraphQlAuthorizationException
5151
* @throws GraphQlNoSuchEntityException
5252
*/
53-
public function execute(string $cartHash, ?int $userId): Quote
53+
public function execute(string $cartHash, ?int $customerId): Quote
5454
{
5555
try {
5656
$cartId = $this->maskedQuoteIdToQuoteId->execute($cartHash);
@@ -69,14 +69,14 @@ public function execute(string $cartHash, ?int $userId): Quote
6969
);
7070
}
7171

72-
$customerId = (int)$cart->getCustomerId();
72+
$cartCustomerId = (int)$cart->getCustomerId();
7373

7474
/* Guest cart, allow operations */
75-
if (!$customerId) {
75+
if (!$cartCustomerId && null === $customerId) {
7676
return $cart;
7777
}
7878

79-
if ($customerId !== $userId) {
79+
if ($cartCustomerId !== $customerId) {
8080
throw new GraphQlAuthorizationException(
8181
__(
8282
'The current user cannot perform operations on cart "%masked_cart_id"',
Lines changed: 315 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,315 @@
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\GraphQl\OfflinePayments;
9+
10+
use Magento\Framework\App\Config\ScopeConfigInterface;
11+
use Magento\Integration\Api\CustomerTokenServiceInterface;
12+
use Magento\OfflinePayments\Model\Banktransfer;
13+
use Magento\OfflinePayments\Model\Cashondelivery;
14+
use Magento\OfflinePayments\Model\Checkmo;
15+
use Magento\OfflinePayments\Model\Purchaseorder;
16+
use Magento\Quote\Model\QuoteFactory;
17+
use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface;
18+
use Magento\Quote\Model\ResourceModel\Quote as QuoteResource;
19+
use Magento\TestFramework\Helper\Bootstrap;
20+
use Magento\TestFramework\ObjectManager;
21+
use Magento\TestFramework\TestCase\GraphQlAbstract;
22+
use Magento\Framework\App\Cache\TypeListInterface;
23+
use Magento\Config\Model\ResourceModel\Config;
24+
25+
/**
26+
* Test for setting payment methods on cart
27+
*/
28+
class SetPaymentMethodOnCartTest extends GraphQlAbstract
29+
{
30+
private const OFFLINE_METHOD_CODES = [
31+
Checkmo::PAYMENT_METHOD_CHECKMO_CODE,
32+
// Banktransfer::PAYMENT_METHOD_BANKTRANSFER_CODE,
33+
// Cashondelivery::PAYMENT_METHOD_CASHONDELIVERY_CODE,
34+
// Purchaseorder::PAYMENT_METHOD_PURCHASEORDER_CODE,
35+
];
36+
37+
/**
38+
* @var CustomerTokenServiceInterface
39+
*/
40+
private $customerTokenService;
41+
42+
/**
43+
* @var QuoteResource
44+
*/
45+
private $quoteResource;
46+
47+
/**
48+
* @var QuoteFactory
49+
*/
50+
private $quoteFactory;
51+
52+
/**
53+
* @var QuoteIdToMaskedQuoteIdInterface
54+
*/
55+
private $quoteIdToMaskedId;
56+
57+
/**
58+
* @var Config
59+
*/
60+
private $config;
61+
62+
/**
63+
* @var TypeListInterface
64+
*/
65+
private $cacheList;
66+
67+
/**
68+
* @inheritdoc
69+
*/
70+
protected function setUp()
71+
{
72+
$objectManager = Bootstrap::getObjectManager();
73+
$this->quoteResource = $objectManager->get(QuoteResource::class);
74+
$this->quoteFactory = $objectManager->get(QuoteFactory::class);
75+
$this->quoteIdToMaskedId = $objectManager->get(QuoteIdToMaskedQuoteIdInterface::class);
76+
$this->customerTokenService = $objectManager->get(CustomerTokenServiceInterface::class);
77+
$this->config = $objectManager->get(Config::class);
78+
$this->cacheList = $objectManager->get(TypeListInterface::class);
79+
80+
foreach (static::OFFLINE_METHOD_CODES as $offlineMethodCode) {
81+
$this->config->saveConfig(
82+
'payment/' . $offlineMethodCode . '/active',
83+
'1',
84+
ScopeConfigInterface::SCOPE_TYPE_DEFAULT,
85+
0
86+
);
87+
}
88+
$this->cacheList->cleanType('config');
89+
}
90+
91+
/**
92+
* @inheritdoc
93+
*/
94+
protected function tearDown()
95+
{
96+
// foreach (static::OFFLINE_METHOD_CODES as $offlineMethodCode) {
97+
// //Never no disable checkmo method
98+
// if ($offlineMethodCode === Checkmo::PAYMENT_METHOD_CHECKMO_CODE) {
99+
// continue;
100+
// }
101+
// $this->config->saveConfig(
102+
// 'payment/' . $offlineMethodCode . '/active',
103+
// '0',
104+
// ScopeConfigInterface::SCOPE_TYPE_DEFAULT,
105+
// 0
106+
// );
107+
// }
108+
// $this->cacheList->cleanType('config');
109+
}
110+
111+
/**
112+
* @param string $methodCode
113+
* @dataProvider dataProviderOfflinePaymentMethods
114+
* @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php
115+
*/
116+
public function testSetPaymentMethodOnCart(string $methodCode)
117+
{
118+
$maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1');
119+
120+
$query = $this->prepareMutationQuery(
121+
$maskedQuoteId,
122+
$methodCode
123+
);
124+
125+
$response = $this->sendRequestWithToken($query);
126+
127+
self::assertArrayHasKey('setPaymentMethodOnCart', $response);
128+
self::assertArrayHasKey('cart', $response['setPaymentMethodOnCart']);
129+
self::assertEquals($maskedQuoteId, $response['setPaymentMethodOnCart']['cart']['cart_id']);
130+
self::assertArrayHasKey('selected_payment_method', $response['setPaymentMethodOnCart']['cart']);
131+
self::assertEquals($methodCode, $response['setPaymentMethodOnCart']['cart']['selected_payment_method']['code']);
132+
}
133+
134+
public function dataProviderOfflinePaymentMethods(): array
135+
{
136+
$methods = [];
137+
foreach (static::OFFLINE_METHOD_CODES as $offlineMethodCode) {
138+
//Purchase order requires additional input and is tested separately
139+
if ($offlineMethodCode === Purchaseorder::PAYMENT_METHOD_PURCHASEORDER_CODE) {
140+
continue;
141+
}
142+
$methods[] = [$offlineMethodCode];
143+
}
144+
145+
return $methods;
146+
}
147+
148+
/**
149+
* @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php
150+
*/
151+
public function testSetNonExistingPaymentMethod()
152+
{
153+
$paymentMethod = 'noway';
154+
$maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1');
155+
156+
$query = $this->prepareMutationQuery(
157+
$maskedQuoteId,
158+
$paymentMethod
159+
);
160+
161+
$this->expectExceptionMessage('The requested Payment Method is not available.');
162+
$this->sendRequestWithToken($query);
163+
}
164+
165+
/**
166+
* @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php
167+
*/
168+
public function testSetPaymentMethodByGuestToCustomerCart()
169+
{
170+
$paymentMethod = 'checkmo';
171+
$maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1');
172+
173+
$query = $this->prepareMutationQuery(
174+
$maskedQuoteId,
175+
$paymentMethod
176+
);
177+
178+
$this->expectExceptionMessage(
179+
"The current user cannot perform operations on cart \"$maskedQuoteId\""
180+
);
181+
182+
$this->graphQlQuery($query);
183+
}
184+
185+
/**
186+
* @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php
187+
*/
188+
public function testSetPaymentMethodPurchaseOrderOnCart()
189+
{
190+
$methodCode = \Magento\OfflinePayments\Model\Purchaseorder::PAYMENT_METHOD_PURCHASEORDER_CODE;
191+
$poNumber = 'GQL-19002';
192+
193+
/** @var \Magento\Config\Model\ResourceModel\Config $config */
194+
$config = ObjectManager::getInstance()->get(\Magento\Config\Model\ResourceModel\Config::class);
195+
$config->saveConfig(
196+
'payment/' . $methodCode . '/active',
197+
1,
198+
ScopeConfigInterface::SCOPE_TYPE_DEFAULT,
199+
0
200+
);
201+
202+
$maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1');
203+
204+
$query = <<<QUERY
205+
mutation {
206+
setPaymentMethodOnCart(input:
207+
{
208+
cart_id: "$maskedQuoteId",
209+
payment_method: {
210+
code: "$methodCode"
211+
purchase_order_number: "$poNumber"
212+
}
213+
}) {
214+
215+
cart {
216+
cart_id,
217+
selected_payment_method {
218+
code
219+
purchase_order_number
220+
}
221+
}
222+
}
223+
}
224+
225+
QUERY;
226+
227+
$response = $this->sendRequestWithToken($query);
228+
229+
self::assertArrayHasKey('setPaymentMethodOnCart', $response);
230+
self::assertArrayHasKey('cart', $response['setPaymentMethodOnCart']);
231+
self::assertEquals($maskedQuoteId, $response['setPaymentMethodOnCart']['cart']['cart_id']);
232+
self::assertArrayHasKey('payment_method', $response['setPaymentMethodOnCart']['cart']);
233+
self::assertEquals($methodCode, $response['setPaymentMethodOnCart']['cart']['selected_payment_method']['code']);
234+
self::assertEquals(
235+
$poNumber,
236+
$response['setPaymentMethodOnCart']['cart']['selected_payment_method']['purchase_order_number']
237+
);
238+
}
239+
240+
/**
241+
* @magentoApiDataFixture Magento/Checkout/_files/quote_with_address_saved.php
242+
*/
243+
public function testPurchaseOrderPaymentMethodFailingValidation()
244+
{
245+
$maskedQuoteId = $this->getMaskedQuoteIdByReversedQuoteId('test_order_1');
246+
247+
$query = $this->prepareMutationQuery(
248+
$maskedQuoteId,
249+
Purchaseorder::PAYMENT_METHOD_PURCHASEORDER_CODE
250+
);
251+
252+
$this->expectExceptionMessage('Purchase order number is a required field.');
253+
$this->sendRequestWithToken($query);
254+
}
255+
256+
/**
257+
* Generates query for setting the specified shipping method on cart
258+
*
259+
* @param string $maskedQuoteId
260+
* @param string $methodCode
261+
* @return string
262+
*/
263+
private function prepareMutationQuery(
264+
string $maskedQuoteId,
265+
string $methodCode
266+
) : string {
267+
return <<<QUERY
268+
mutation {
269+
setPaymentMethodOnCart(input:
270+
{
271+
cart_id: "$maskedQuoteId",
272+
payment_method: {
273+
code: "$methodCode"
274+
}
275+
}) {
276+
277+
cart {
278+
cart_id,
279+
selected_payment_method {
280+
code
281+
}
282+
}
283+
}
284+
}
285+
286+
QUERY;
287+
}
288+
289+
/**
290+
* Sends a GraphQL request with using a bearer token
291+
*
292+
* @param string $query
293+
* @return array
294+
* @throws \Magento\Framework\Exception\AuthenticationException
295+
*/
296+
private function sendRequestWithToken(string $query): array
297+
{
298+
$customerToken = $this->customerTokenService->createCustomerAccessToken('[email protected]', 'password');
299+
$headerMap = ['Authorization' => 'Bearer ' . $customerToken];
300+
301+
return $this->graphQlQuery($query, [], '', $headerMap);
302+
}
303+
304+
/**
305+
* @param string $reversedQuoteId
306+
* @return string
307+
*/
308+
private function getMaskedQuoteIdByReversedQuoteId(string $reversedQuoteId): string
309+
{
310+
$quote = $this->quoteFactory->create();
311+
$this->quoteResource->load($quote, $reversedQuoteId, 'reserved_order_id');
312+
313+
return $this->quoteIdToMaskedId->execute((int)$quote->getId());
314+
}
315+
}

0 commit comments

Comments
 (0)