Skip to content

Commit f3c58f2

Browse files
authored
LYNX-306: Guest Order View GraphQL
1 parent 1882686 commit f3c58f2

File tree

10 files changed

+618
-26
lines changed

10 files changed

+618
-26
lines changed

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

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use Magento\QuoteGraphQl\Model\Cart\GetCartForCheckout;
2020
use Magento\QuoteGraphQl\Model\Cart\PlaceOrder as PlaceOrderModel;
2121
use Magento\Sales\Api\OrderRepositoryInterface;
22+
use Magento\SalesGraphQl\Model\Formatter\Order as OrderFormatter;
2223

2324
/**
2425
* Resolver for placing order after payment method has already been set
@@ -53,21 +54,6 @@ class PlaceOrder implements ResolverInterface, ResetAfterRequestInterface
5354
'Some of the products are out of stock' => self::ERROR_UNABLE_TO_PLACE_ORDER,
5455
];
5556

56-
/**
57-
* @var GetCartForCheckout
58-
*/
59-
private $getCartForCheckout;
60-
61-
/**
62-
* @var PlaceOrderModel
63-
*/
64-
private $placeOrder;
65-
66-
/**
67-
* @var OrderRepositoryInterface
68-
*/
69-
private $orderRepository;
70-
7157
/**
7258
* @var \string[]
7359
*/
@@ -77,15 +63,14 @@ class PlaceOrder implements ResolverInterface, ResetAfterRequestInterface
7763
* @param GetCartForCheckout $getCartForCheckout
7864
* @param PlaceOrderModel $placeOrder
7965
* @param OrderRepositoryInterface $orderRepository
66+
* @param OrderFormatter $orderFormatter
8067
*/
8168
public function __construct(
82-
GetCartForCheckout $getCartForCheckout,
83-
PlaceOrderModel $placeOrder,
84-
OrderRepositoryInterface $orderRepository
69+
private readonly GetCartForCheckout $getCartForCheckout,
70+
private readonly PlaceOrderModel $placeOrder,
71+
private readonly OrderRepositoryInterface $orderRepository,
72+
private readonly OrderFormatter $orderFormatter
8573
) {
86-
$this->getCartForCheckout = $getCartForCheckout;
87-
$this->placeOrder = $placeOrder;
88-
$this->orderRepository = $orderRepository;
8974
}
9075

9176
/**
@@ -130,6 +115,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
130115
// @deprecated The order_id field is deprecated, use order_number instead
131116
'order_id' => $order->getIncrementId(),
132117
],
118+
'orderV2' => $this->orderFormatter->format($order),
133119
'errors' => []
134120
];
135121
}

app/code/Magento/QuoteGraphQl/composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"magento/module-customer": "*",
1313
"magento/module-customer-graph-ql": "*",
1414
"magento/module-sales": "*",
15+
"magento/module-sales-graph-ql": "*",
1516
"magento/module-directory": "*",
1617
"magento/module-graph-ql": "*",
1718
"magento/module-gift-message": "*",

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,8 @@ type ApplyCouponToCartOutput @doc(description: "Contains details about the cart
224224
}
225225

226226
type PlaceOrderOutput @doc(description: "Contains the results of the request to place an order.") {
227-
order: Order @doc(description: "The ID of the order.")
227+
order: Order @deprecated(reason: "Use `orderV2` instead.") @doc(description: "The ID of the order.")
228+
orderV2: CustomerOrder! @doc(description: "Full order information.")
228229
errors: [PlaceOrderError!]! @doc(description:"An array of place order errors.")
229230
}
230231

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
/************************************************************************
3+
*
4+
* Copyright 2024 Adobe
5+
* All Rights Reserved.
6+
*
7+
* NOTICE: All information contained herein is, and remains
8+
* the property of Adobe and its suppliers, if any. The intellectual
9+
* and technical concepts contained herein are proprietary to Adobe
10+
* and its suppliers and are protected by all applicable intellectual
11+
* property laws, including trade secret and copyright laws.
12+
* Dissemination of this information or reproduction of this material
13+
* is strictly forbidden unless prior written permission is obtained
14+
* from Adobe.
15+
* ***********************************************************************
16+
*/
17+
declare(strict_types=1);
18+
19+
namespace Magento\SalesGraphQl\Model\Order;
20+
21+
use Magento\Framework\Encryption\EncryptorInterface;
22+
23+
/**
24+
* Encrypt or decrypt order token
25+
*/
26+
class Token
27+
{
28+
/**
29+
* @param EncryptorInterface $encryptor
30+
*/
31+
public function __construct(
32+
private readonly EncryptorInterface $encryptor
33+
) {
34+
}
35+
36+
/**
37+
* Encrypt number, email and postcode to create a token
38+
*
39+
* @param string $number
40+
* @param string $email
41+
* @param string $postcode
42+
* @return string
43+
*/
44+
public function encrypt(string $number, string $email, string $postcode): string
45+
{
46+
return $this->encryptor->encrypt(implode('|', [$number, $email, $postcode]));
47+
}
48+
49+
/**
50+
* Retrieve number, email and postcode from token
51+
*
52+
* @param string $token
53+
* @return string[]
54+
*/
55+
public function decrypt(string $token): array
56+
{
57+
return explode('|', $this->encryptor->decrypt($token));
58+
}
59+
}
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
<?php
2+
/************************************************************************
3+
*
4+
* Copyright 2024 Adobe
5+
* All Rights Reserved.
6+
*
7+
* NOTICE: All information contained herein is, and remains
8+
* the property of Adobe and its suppliers, if any. The intellectual
9+
* and technical concepts contained herein are proprietary to Adobe
10+
* and its suppliers and are protected by all applicable intellectual
11+
* property laws, including trade secret and copyright laws.
12+
* Dissemination of this information or reproduction of this material
13+
* is strictly forbidden unless prior written permission is obtained
14+
* from Adobe.
15+
* ***********************************************************************
16+
*/
17+
declare(strict_types=1);
18+
19+
namespace Magento\SalesGraphQl\Model\Resolver;
20+
21+
use Magento\Framework\Api\SearchCriteriaBuilderFactory;
22+
use Magento\Framework\GraphQl\Config\Element\Field;
23+
use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException;
24+
use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException;
25+
use Magento\Framework\GraphQl\Query\ResolverInterface;
26+
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
27+
use Magento\Sales\Api\Data\OrderInterface;
28+
use Magento\Sales\Api\OrderRepositoryInterface;
29+
use Magento\SalesGraphQl\Model\Formatter\Order as OrderFormatter;
30+
use Magento\SalesGraphQl\Model\Order\Token;
31+
use Magento\Store\Model\StoreManagerInterface;
32+
33+
/**
34+
* Retrieve guest order based
35+
*/
36+
class GuestOrder implements ResolverInterface
37+
{
38+
/**
39+
* @param OrderFormatter $orderFormatter
40+
* @param OrderRepositoryInterface $orderRepository
41+
* @param SearchCriteriaBuilderFactory $searchCriteriaBuilderFactory
42+
* @param StoreManagerInterface $storeManager
43+
* @param Token $token
44+
*/
45+
public function __construct(
46+
private readonly OrderFormatter $orderFormatter,
47+
private readonly OrderRepositoryInterface $orderRepository,
48+
private readonly SearchCriteriaBuilderFactory $searchCriteriaBuilderFactory,
49+
private readonly StoreManagerInterface $storeManager,
50+
private readonly Token $token
51+
) {
52+
}
53+
54+
/**
55+
* @inheritDoc
56+
*/
57+
public function resolve(
58+
Field $field,
59+
$context,
60+
ResolveInfo $info,
61+
array $value = null,
62+
array $args = null
63+
) {
64+
list($number, $email, $postcode) = $this->getNumberEmailPostcode($args['input'] ?? []);
65+
$order = $this->getOrder($number);
66+
$this->validateOrder($order, $postcode, $email);
67+
return $this->orderFormatter->format($order);
68+
}
69+
70+
/**
71+
* Retrieve order based on order number
72+
*
73+
* @param string $number
74+
* @return OrderInterface
75+
* @throws GraphQlNoSuchEntityException
76+
*/
77+
private function getOrder(string $number): OrderInterface
78+
{
79+
$searchCriteria = $this->searchCriteriaBuilderFactory->create()
80+
->addFilter('increment_id', $number)
81+
->addFilter('store_id', $this->storeManager->getStore()->getId())
82+
->create();
83+
84+
$orders = $this->orderRepository->getList($searchCriteria)->getItems();
85+
if (empty($orders)) {
86+
$this->cannotLocateOrder();
87+
}
88+
89+
return reset($orders);
90+
}
91+
92+
/**
93+
* Ensure the order matches the provided criteria
94+
*
95+
* @param OrderInterface $order
96+
* @param string $postcode
97+
* @param string $email
98+
* @return void
99+
* @throws GraphQlAuthorizationException
100+
* @throws GraphQlNoSuchEntityException
101+
*/
102+
private function validateOrder(OrderInterface $order, string $postcode, string $email): void
103+
{
104+
if ($order->getBillingAddress()->getPostcode() !== $postcode) {
105+
$this->cannotLocateOrder();
106+
}
107+
108+
if ($order->getBillingAddress()->getEmail() !== $email) {
109+
$this->cannotLocateOrder();
110+
}
111+
112+
if ($order->getCustomerId()) {
113+
$this->customerHasToLogin();
114+
}
115+
}
116+
117+
/**
118+
* Retrieve number, email and postcode from input
119+
*
120+
* @param array $input
121+
* @return array
122+
* @throws GraphQlNoSuchEntityException
123+
*/
124+
private function getNumberEmailPostcode(array $input): array
125+
{
126+
if (isset($input['token'])) {
127+
$data = $this->token->decrypt($input['token']);
128+
if (count($data) !== 3) {
129+
$this->cannotLocateOrder();
130+
}
131+
return $data;
132+
}
133+
if (!isset($input['number']) || !isset($input['email']) || !isset($input['postcode'])) {
134+
$this->cannotLocateOrder();
135+
}
136+
return [$input['number'], $input['email'], $input['postcode']];
137+
}
138+
139+
/**
140+
* Throw exception when the order cannot be found or does not match the criteria
141+
*
142+
* @return void
143+
* @throws GraphQlNoSuchEntityException
144+
*/
145+
private function cannotLocateOrder(): void
146+
{
147+
throw new GraphQlNoSuchEntityException(__('We couldn\'t locate an order with the information provided.'));
148+
}
149+
150+
/**
151+
* Throw exception when the guest checkout is not enabled or order is customer order
152+
*
153+
* @return void
154+
* @throws GraphQlAuthorizationException
155+
*/
156+
private function customerHasToLogin(): void
157+
{
158+
throw new GraphQlAuthorizationException(__('Please login to view the order.'));
159+
}
160+
}

app/code/Magento/SalesGraphQl/Model/Resolver/OrderItems.php

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,6 @@ public function __construct(
4949
*/
5050
public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
5151
{
52-
/** @var ContextInterface $context */
53-
if (false === $context->getExtensionAttributes()->getIsCustomer()) {
54-
throw new GraphQlAuthorizationException(__('The current customer isn\'t authorized.'));
55-
}
5652
if (!(($value['model'] ?? null) instanceof OrderInterface)) {
5753
throw new LocalizedException(__('"model" value should be specified'));
5854
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php
2+
/************************************************************************
3+
*
4+
* Copyright 2024 Adobe
5+
* All Rights Reserved.
6+
*
7+
* NOTICE: All information contained herein is, and remains
8+
* the property of Adobe and its suppliers, if any. The intellectual
9+
* and technical concepts contained herein are proprietary to Adobe
10+
* and its suppliers and are protected by all applicable intellectual
11+
* property laws, including trade secret and copyright laws.
12+
* Dissemination of this information or reproduction of this material
13+
* is strictly forbidden unless prior written permission is obtained
14+
* from Adobe.
15+
* ***********************************************************************
16+
*/
17+
declare(strict_types=1);
18+
19+
namespace Magento\SalesGraphQl\Model\Resolver;
20+
21+
use Magento\Framework\Exception\LocalizedException;
22+
use Magento\Framework\GraphQl\Config\Element\Field;
23+
use Magento\Framework\GraphQl\Query\ResolverInterface;
24+
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
25+
use Magento\Sales\Api\Data\OrderInterface;
26+
27+
/**
28+
* Retrieve order token
29+
*/
30+
class Token implements ResolverInterface
31+
{
32+
/**
33+
* @param Token $token
34+
*/
35+
public function __construct(
36+
private readonly \Magento\SalesGraphQl\Model\Order\Token $token
37+
) {
38+
}
39+
40+
/**
41+
* @inheritDoc
42+
*/
43+
public function resolve(
44+
Field $field,
45+
$context,
46+
ResolveInfo $info,
47+
array $value = null,
48+
array $args = null
49+
) {
50+
if (!(($value['model'] ?? null) instanceof OrderInterface)) {
51+
throw new LocalizedException(__('"model" value should be specified'));
52+
}
53+
/** @var OrderInterface $order */
54+
$order = $value['model'];
55+
return $this->token->encrypt(
56+
$order->getIncrementId(),
57+
$order->getBillingAddress()->getEmail(),
58+
$order->getBillingAddress()->getPostcode()
59+
);
60+
}
61+
}

0 commit comments

Comments
 (0)