Skip to content

Commit f73b752

Browse files
committed
Add mutation for setting payment method and placing order
This matching the existing frontend checkout flow and allows capturing additional information for fraud tools such as Kount when placing orders. Fixes #716
1 parent 82fac95 commit f73b752

File tree

6 files changed

+680
-32
lines changed

6 files changed

+680
-32
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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\QuoteGraphQl\Model\Cart;
9+
10+
use Magento\Framework\Exception\LocalizedException;
11+
use Magento\Framework\Exception\NoSuchEntityException;
12+
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
13+
use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException;
14+
use Magento\Quote\Api\Data\PaymentInterface;
15+
use Magento\Quote\Api\Data\PaymentInterfaceFactory;
16+
use Magento\Quote\Api\PaymentMethodManagementInterface;
17+
use Magento\Quote\Model\Quote;
18+
19+
class SetPaymentMethodOnCart
20+
{
21+
/**
22+
* @var PaymentMethodManagementInterface
23+
*/
24+
private $paymentMethodManagement;
25+
26+
/**
27+
* @var PaymentInterfaceFactory
28+
*/
29+
private $paymentFactory;
30+
31+
/**
32+
* @param PaymentMethodManagementInterface $paymentMethodManagement
33+
* @param PaymentInterfaceFactory $paymentFactory
34+
*/
35+
public function __construct(
36+
PaymentMethodManagementInterface $paymentMethodManagement,
37+
PaymentInterfaceFactory $paymentFactory
38+
) {
39+
$this->paymentMethodManagement = $paymentMethodManagement;
40+
$this->paymentFactory = $paymentFactory;
41+
}
42+
43+
public function execute(array $paymentData, Quote $cart): Quote
44+
{
45+
if (!isset($paymentData['code']) || empty($paymentData['code'])) {
46+
throw new GraphQlInputException(__('Required parameter "code" for "payment_method" is missing.'));
47+
}
48+
$paymentMethodCode = $paymentData['code'];
49+
50+
$poNumber = $paymentData['purchase_order_number'] ?? null;
51+
$additionalData = $paymentData['additional_data'] ?? [];
52+
53+
$payment = $this->paymentFactory->create([
54+
'data' => [
55+
PaymentInterface::KEY_METHOD => $paymentMethodCode,
56+
PaymentInterface::KEY_PO_NUMBER => $poNumber,
57+
PaymentInterface::KEY_ADDITIONAL_DATA => $additionalData,
58+
]
59+
]);
60+
61+
try {
62+
$this->paymentMethodManagement->set($cart->getId(), $payment);
63+
} catch (NoSuchEntityException $e) {
64+
throw new GraphQlNoSuchEntityException(__($e->getMessage()), $e);
65+
} catch (LocalizedException $e) {
66+
throw new GraphQlInputException(__($e->getMessage()), $e);
67+
}
68+
69+
return $cart;
70+
}
71+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
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\QuoteGraphQl\Model\Resolver;
9+
10+
use Magento\Framework\Exception\LocalizedException;
11+
use Magento\Framework\Exception\NoSuchEntityException;
12+
use Magento\Framework\GraphQl\Config\Element\Field;
13+
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
14+
use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException;
15+
use Magento\Framework\GraphQl\Query\ResolverInterface;
16+
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
17+
use Magento\Quote\Api\CartManagementInterface;
18+
use Magento\QuoteGraphQl\Model\Cart\GetCartForUser;
19+
use Magento\Sales\Api\OrderRepositoryInterface;
20+
21+
/**
22+
* @inheritdoc
23+
*/
24+
class SetPaymentAndPlaceOrder implements ResolverInterface
25+
{
26+
/**
27+
* @var CartManagementInterface
28+
*/
29+
private $cartManagement;
30+
31+
/**
32+
* @var GetCartForUser
33+
*/
34+
private $getCartForUser;
35+
36+
/**
37+
* @var OrderRepositoryInterface
38+
*/
39+
private $orderRepository;
40+
41+
/**
42+
* @var \Magento\QuoteGraphQl\Model\Cart\SetPaymentMethodOnCart
43+
*/
44+
private $setPaymentMethodOnCart;
45+
46+
/**
47+
* @param GetCartForUser $getCartForUser
48+
* @param CartManagementInterface $cartManagement
49+
* @param OrderRepositoryInterface $orderRepository
50+
* @param \Magento\QuoteGraphQl\Model\Cart\SetPaymentMethodOnCart $setPaymentMethodOnCart
51+
*/
52+
public function __construct(
53+
GetCartForUser $getCartForUser,
54+
CartManagementInterface $cartManagement,
55+
OrderRepositoryInterface $orderRepository,
56+
\Magento\QuoteGraphQl\Model\Cart\SetPaymentMethodOnCart $setPaymentMethodOnCart
57+
) {
58+
$this->getCartForUser = $getCartForUser;
59+
$this->cartManagement = $cartManagement;
60+
$this->orderRepository = $orderRepository;
61+
$this->setPaymentMethodOnCart = $setPaymentMethodOnCart;
62+
}
63+
64+
/**
65+
* @inheritdoc
66+
*/
67+
public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
68+
{
69+
if (!isset($args['input']['cart_id']) || empty($args['input']['cart_id'])) {
70+
throw new GraphQlInputException(__('Required parameter "cart_id" is missing'));
71+
}
72+
$maskedCartId = $args['input']['cart_id'];
73+
74+
if (!isset($args['input']['payment_method']['code']) || empty($args['input']['payment_method']['code'])) {
75+
throw new GraphQlInputException(__('Required parameter "code" for "payment_method" is missing.'));
76+
}
77+
$paymentData = $args['input']['payment_method'];
78+
79+
$cart = $this->getCartForUser->execute($maskedCartId, $context->getUserId());
80+
$cart = $this->setPaymentMethodOnCart->execute($paymentData, $cart);
81+
82+
if ($context->getUserId() === 0) {
83+
if (!$cart->getCustomerEmail()) {
84+
throw new GraphQlInputException(__("Guest email for cart is missing. Please enter"));
85+
}
86+
$cart->setCheckoutMethod(CartManagementInterface::METHOD_GUEST);
87+
}
88+
89+
try {
90+
$orderId = $this->cartManagement->placeOrder($cart->getId());
91+
$order = $this->orderRepository->get($orderId);
92+
93+
return [
94+
'order' => [
95+
'order_id' => $order->getIncrementId(),
96+
],
97+
];
98+
} catch (NoSuchEntityException $e) {
99+
throw new GraphQlNoSuchEntityException(__($e->getMessage()), $e);
100+
} catch (LocalizedException $e) {
101+
throw new GraphQlInputException(__('Unable to place order: %message', ['message' => $e->getMessage()]), $e);
102+
}
103+
}
104+
}

app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentMethodOnCart.php

Lines changed: 7 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -30,28 +30,20 @@ class SetPaymentMethodOnCart implements ResolverInterface
3030
private $getCartForUser;
3131

3232
/**
33-
* @var PaymentMethodManagementInterface
33+
* @var \Magento\QuoteGraphQl\Model\Cart\SetPaymentMethodOnCart
3434
*/
35-
private $paymentMethodManagement;
36-
37-
/**
38-
* @var PaymentInterfaceFactory
39-
*/
40-
private $paymentFactory;
35+
private $setPaymentMethodOnCart;
4136

4237
/**
4338
* @param GetCartForUser $getCartForUser
44-
* @param PaymentMethodManagementInterface $paymentMethodManagement
45-
* @param PaymentInterfaceFactory $paymentFactory
39+
* @param \Magento\QuoteGraphQl\Model\Cart\SetPaymentMethodOnCart $setPaymentMethodOnCart
4640
*/
4741
public function __construct(
4842
GetCartForUser $getCartForUser,
49-
PaymentMethodManagementInterface $paymentMethodManagement,
50-
PaymentInterfaceFactory $paymentFactory
43+
\Magento\QuoteGraphQl\Model\Cart\SetPaymentMethodOnCart $setPaymentMethodOnCart
5144
) {
5245
$this->getCartForUser = $getCartForUser;
53-
$this->paymentMethodManagement = $paymentMethodManagement;
54-
$this->paymentFactory = $paymentFactory;
46+
$this->setPaymentMethodOnCart = $setPaymentMethodOnCart;
5547
}
5648

5749
/**
@@ -67,27 +59,10 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
6759
if (!isset($args['input']['payment_method']['code']) || empty($args['input']['payment_method']['code'])) {
6860
throw new GraphQlInputException(__('Required parameter "code" for "payment_method" is missing.'));
6961
}
70-
$paymentMethodCode = $args['input']['payment_method']['code'];
71-
72-
$poNumber = $args['input']['payment_method']['purchase_order_number'] ?? null;
73-
$additionalData = $args['input']['payment_method']['additional_data'] ?? [];
62+
$paymentData = $args['input']['payment_method'];
7463

7564
$cart = $this->getCartForUser->execute($maskedCartId, $context->getUserId());
76-
$payment = $this->paymentFactory->create([
77-
'data' => [
78-
PaymentInterface::KEY_METHOD => $paymentMethodCode,
79-
PaymentInterface::KEY_PO_NUMBER => $poNumber,
80-
PaymentInterface::KEY_ADDITIONAL_DATA => $additionalData,
81-
]
82-
]);
83-
84-
try {
85-
$this->paymentMethodManagement->set($cart->getId(), $payment);
86-
} catch (NoSuchEntityException $e) {
87-
throw new GraphQlNoSuchEntityException(__($e->getMessage()), $e);
88-
} catch (LocalizedException $e) {
89-
throw new GraphQlInputException(__($e->getMessage()), $e);
90-
}
65+
$cart = $this->setPaymentMethodOnCart->execute($paymentData, $cart);
9166

9267
return [
9368
'cart' => [

app/code/Magento/QuoteGraphQl/etc/schema.graphqls

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ type Mutation {
1818
setShippingMethodsOnCart(input: SetShippingMethodsOnCartInput): SetShippingMethodsOnCartOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetShippingMethodsOnCart")
1919
setPaymentMethodOnCart(input: SetPaymentMethodOnCartInput): SetPaymentMethodOnCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\SetPaymentMethodOnCart")
2020
setGuestEmailOnCart(input: SetGuestEmailOnCartInput): SetGuestEmailOnCartOutput @resolver(class: "Magento\\QuoteGraphQl\\Model\\Resolver\\SetGuestEmailOnCart")
21+
setPaymentMethodAndPlaceOrder(input: SetPaymentMethodOnCartInput): PlaceOrderOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\SetPaymentAndPlaceOrder")
2122
placeOrder(input: PlaceOrderInput): PlaceOrderOutput @resolver(class: "\\Magento\\QuoteGraphQl\\Model\\Resolver\\PlaceOrder")
2223
}
2324

0 commit comments

Comments
 (0)