vendor/shopware/storefront/Controller/AccountOrderController.php line 117

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Storefront\Controller;
  3. use Shopware\Core\Checkout\Cart\Exception\OrderNotFoundException;
  4. use Shopware\Core\Checkout\Cart\Exception\OrderPaymentMethodNotChangeable;
  5. use Shopware\Core\Checkout\Customer\Exception\CustomerAuthThrottledException;
  6. use Shopware\Core\Checkout\Order\Aggregate\OrderDelivery\OrderDeliveryEntity;
  7. use Shopware\Core\Checkout\Order\Exception\GuestNotAuthenticatedException;
  8. use Shopware\Core\Checkout\Order\Exception\WrongGuestCredentialsException;
  9. use Shopware\Core\Checkout\Order\OrderEntity;
  10. use Shopware\Core\Checkout\Order\OrderException;
  11. use Shopware\Core\Checkout\Order\SalesChannel\AbstractCancelOrderRoute;
  12. use Shopware\Core\Checkout\Order\SalesChannel\AbstractOrderRoute;
  13. use Shopware\Core\Checkout\Order\SalesChannel\AbstractSetPaymentOrderRoute;
  14. use Shopware\Core\Checkout\Order\SalesChannel\OrderService;
  15. use Shopware\Core\Checkout\Payment\Exception\PaymentProcessException;
  16. use Shopware\Core\Checkout\Payment\SalesChannel\AbstractHandlePaymentMethodRoute;
  17. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  18. use Shopware\Core\Framework\DataAbstractionLayer\Search\Sorting\FieldSorting;
  19. use Shopware\Core\Framework\Feature;
  20. use Shopware\Core\Framework\Log\Package;
  21. use Shopware\Core\Framework\Routing\Annotation\LoginRequired;
  22. use Shopware\Core\Framework\Routing\Annotation\RouteScope;
  23. use Shopware\Core\Framework\Routing\Annotation\Since;
  24. use Shopware\Core\Framework\Validation\DataBag\RequestDataBag;
  25. use Shopware\Core\System\SalesChannel\Context\SalesChannelContextService;
  26. use Shopware\Core\System\SalesChannel\Context\SalesChannelContextServiceInterface;
  27. use Shopware\Core\System\SalesChannel\Context\SalesChannelContextServiceParameters;
  28. use Shopware\Core\System\SalesChannel\SalesChannel\AbstractContextSwitchRoute;
  29. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  30. use Shopware\Core\System\SystemConfig\SystemConfigService;
  31. use Shopware\Storefront\Event\RouteRequest\CancelOrderRouteRequestEvent;
  32. use Shopware\Storefront\Event\RouteRequest\HandlePaymentMethodRouteRequestEvent;
  33. use Shopware\Storefront\Event\RouteRequest\SetPaymentOrderRouteRequestEvent;
  34. use Shopware\Storefront\Framework\Routing\Annotation\NoStore;
  35. use Shopware\Storefront\Page\Account\Order\AccountEditOrderPageLoadedHook;
  36. use Shopware\Storefront\Page\Account\Order\AccountEditOrderPageLoader;
  37. use Shopware\Storefront\Page\Account\Order\AccountOrderDetailPageLoadedHook;
  38. use Shopware\Storefront\Page\Account\Order\AccountOrderDetailPageLoader;
  39. use Shopware\Storefront\Page\Account\Order\AccountOrderPageLoadedHook;
  40. use Shopware\Storefront\Page\Account\Order\AccountOrderPageLoader;
  41. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  42. use Symfony\Component\HttpFoundation\Request;
  43. use Symfony\Component\HttpFoundation\Response;
  44. use Symfony\Component\Routing\Annotation\Route;
  45. /**
  46. * @Route(defaults={"_routeScope"={"storefront"}})
  47. *
  48. * @deprecated tag:v6.5.0 - reason:becomes-internal - Will be internal
  49. */
  50. #[Package('storefront')]
  51. class AccountOrderController extends StorefrontController
  52. {
  53. private AccountOrderPageLoader $orderPageLoader;
  54. private AbstractContextSwitchRoute $contextSwitchRoute;
  55. private AccountEditOrderPageLoader $accountEditOrderPageLoader;
  56. private AbstractCancelOrderRoute $cancelOrderRoute;
  57. private AbstractSetPaymentOrderRoute $setPaymentOrderRoute;
  58. private AbstractHandlePaymentMethodRoute $handlePaymentMethodRoute;
  59. private EventDispatcherInterface $eventDispatcher;
  60. private AccountOrderDetailPageLoader $orderDetailPageLoader;
  61. private AbstractOrderRoute $orderRoute;
  62. private SalesChannelContextServiceInterface $contextService;
  63. private SystemConfigService $systemConfigService;
  64. private OrderService $orderService;
  65. /**
  66. * @internal
  67. */
  68. public function __construct(
  69. AccountOrderPageLoader $orderPageLoader,
  70. AccountEditOrderPageLoader $accountEditOrderPageLoader,
  71. AbstractContextSwitchRoute $contextSwitchRoute,
  72. AbstractCancelOrderRoute $cancelOrderRoute,
  73. AbstractSetPaymentOrderRoute $setPaymentOrderRoute,
  74. AbstractHandlePaymentMethodRoute $handlePaymentMethodRoute,
  75. EventDispatcherInterface $eventDispatcher,
  76. AccountOrderDetailPageLoader $orderDetailPageLoader,
  77. AbstractOrderRoute $orderRoute,
  78. SalesChannelContextServiceInterface $contextService,
  79. SystemConfigService $systemConfigService,
  80. OrderService $orderService
  81. ) {
  82. $this->orderPageLoader = $orderPageLoader;
  83. $this->contextSwitchRoute = $contextSwitchRoute;
  84. $this->accountEditOrderPageLoader = $accountEditOrderPageLoader;
  85. $this->cancelOrderRoute = $cancelOrderRoute;
  86. $this->setPaymentOrderRoute = $setPaymentOrderRoute;
  87. $this->handlePaymentMethodRoute = $handlePaymentMethodRoute;
  88. $this->eventDispatcher = $eventDispatcher;
  89. $this->orderDetailPageLoader = $orderDetailPageLoader;
  90. $this->orderRoute = $orderRoute;
  91. $this->contextService = $contextService;
  92. $this->systemConfigService = $systemConfigService;
  93. $this->orderService = $orderService;
  94. }
  95. /**
  96. * @Since("6.0.0.0")
  97. * @Route("/account/order", name="frontend.account.order.page", options={"seo"="false"}, methods={"GET", "POST"}, defaults={"XmlHttpRequest"=true, "_loginRequired"=true, "_loginRequiredAllowGuest"=true})
  98. * @Route("/account/order", name="frontend.account.order.page", options={"seo"="false"}, methods={"GET", "POST"}, defaults={"XmlHttpRequest"=true})
  99. * @NoStore
  100. */
  101. public function orderOverview(Request $request, SalesChannelContext $context): Response
  102. {
  103. $page = $this->orderPageLoader->load($request, $context);
  104. $this->hook(new AccountOrderPageLoadedHook($page, $context));
  105. return $this->renderStorefront('@Storefront/storefront/page/account/order-history/index.html.twig', ['page' => $page]);
  106. }
  107. /**
  108. * @Since("6.2.0.0")
  109. * @Route("/account/order/cancel", name="frontend.account.order.cancel", methods={"POST"})
  110. */
  111. public function cancelOrder(Request $request, SalesChannelContext $context): Response
  112. {
  113. $cancelOrderRequest = new Request();
  114. $cancelOrderRequest->request->set('orderId', $request->get('orderId'));
  115. $cancelOrderRequest->request->set('transition', 'cancel');
  116. $event = new CancelOrderRouteRequestEvent($request, $cancelOrderRequest, $context);
  117. $this->eventDispatcher->dispatch($event);
  118. $this->cancelOrderRoute->cancel($event->getStoreApiRequest(), $context);
  119. if ($context->getCustomer() && $context->getCustomer()->getGuest() === true) {
  120. return $this->redirectToRoute(
  121. 'frontend.account.order.single.page',
  122. [
  123. 'deepLinkCode' => $request->get('deepLinkCode'),
  124. ]
  125. );
  126. }
  127. return $this->redirectToRoute('frontend.account.order.page');
  128. }
  129. /**
  130. * @Since("6.2.0.0")
  131. * @Route("/account/order/{deepLinkCode}", name="frontend.account.order.single.page", options={"seo"="false"}, methods={"GET", "POST"})
  132. * @NoStore
  133. */
  134. public function orderSingleOverview(Request $request, SalesChannelContext $context): Response
  135. {
  136. try {
  137. $page = $this->orderPageLoader->load($request, $context);
  138. $this->hook(new AccountOrderPageLoadedHook($page, $context));
  139. } catch (GuestNotAuthenticatedException | WrongGuestCredentialsException | CustomerAuthThrottledException $exception) {
  140. return $this->redirectToRoute(
  141. 'frontend.account.guest.login.page',
  142. [
  143. 'redirectTo' => 'frontend.account.order.single.page',
  144. 'redirectParameters' => ['deepLinkCode' => $request->get('deepLinkCode')],
  145. 'loginError' => ($exception instanceof WrongGuestCredentialsException),
  146. 'waitTime' => ($exception instanceof CustomerAuthThrottledException) ? $exception->getWaitTime() : '',
  147. ]
  148. );
  149. }
  150. return $this->renderStorefront('@Storefront/storefront/page/account/order-history/index.html.twig', ['page' => $page]);
  151. }
  152. /**
  153. * @Since("6.0.0.0")
  154. * @Route("/widgets/account/order/detail/{id}", name="widgets.account.order.detail", options={"seo"="false"}, methods={"GET"}, defaults={"XmlHttpRequest"=true, "_loginRequired"=true})
  155. */
  156. public function ajaxOrderDetail(Request $request, SalesChannelContext $context): Response
  157. {
  158. $page = $this->orderDetailPageLoader->load($request, $context);
  159. $this->hook(new AccountOrderDetailPageLoadedHook($page, $context));
  160. $response = $this->renderStorefront('@Storefront/storefront/page/account/order-history/order-detail-list.html.twig', [
  161. 'orderDetails' => $page->getLineItems(),
  162. 'orderId' => $page->getOrder()->getId(),
  163. 'page' => $page,
  164. ]);
  165. $response->headers->set('x-robots-tag', 'noindex');
  166. return $response;
  167. }
  168. /**
  169. * @Since("6.2.0.0")
  170. * @Route("/account/order/edit/{orderId}", name="frontend.account.edit-order.page", methods={"GET"}, defaults={"_loginRequired"=true, "_loginRequiredAllowGuest"=true})
  171. * @Route("/account/order/edit/{orderId}", name="frontend.account.edit-order.page", methods={"GET"})
  172. * @NoStore
  173. */
  174. public function editOrder(string $orderId, Request $request, SalesChannelContext $context): Response
  175. {
  176. $criteria = new Criteria([$orderId]);
  177. $deliveriesCriteria = $criteria->getAssociation('deliveries');
  178. $deliveriesCriteria->addSorting(new FieldSorting('createdAt', FieldSorting::ASCENDING));
  179. $order = $this->orderRoute->load($request, $context, $criteria)->getOrders()->first();
  180. if ($order === null) {
  181. throw new OrderNotFoundException($orderId);
  182. }
  183. if ($context->getCurrency()->getId() !== $order->getCurrencyId()) {
  184. $this->contextSwitchRoute->switchContext(
  185. new RequestDataBag([SalesChannelContextService::CURRENCY_ID => $order->getCurrencyId()]),
  186. $context
  187. );
  188. return $this->redirectToRoute('frontend.account.edit-order.page', ['orderId' => $orderId]);
  189. }
  190. /** @var OrderDeliveryEntity|null $mostCurrentDelivery */
  191. $mostCurrentDelivery = $order->getDeliveries()->last();
  192. if ($mostCurrentDelivery !== null && $context->getShippingMethod()->getId() !== $mostCurrentDelivery->getShippingMethodId()) {
  193. $this->contextSwitchRoute->switchContext(
  194. new RequestDataBag([SalesChannelContextService::SHIPPING_METHOD_ID => $mostCurrentDelivery->getShippingMethodId()]),
  195. $context
  196. );
  197. return $this->redirectToRoute('frontend.account.edit-order.page', ['orderId' => $orderId]);
  198. }
  199. $page = $this->accountEditOrderPageLoader->load($request, $context);
  200. $this->hook(new AccountEditOrderPageLoadedHook($page, $context));
  201. if ($page->isPaymentChangeable() === false) {
  202. $refundsEnabled = $this->systemConfigService->get('core.cart.enableOrderRefunds');
  203. if ($refundsEnabled) {
  204. $this->addFlash(self::DANGER, $this->trans('account.editOrderPaymentNotChangeableWithRefunds'));
  205. } else {
  206. $this->addFlash(self::DANGER, $this->trans('account.editOrderPaymentNotChangeable'));
  207. }
  208. }
  209. $page->setErrorCode($request->get('error-code'));
  210. return $this->renderStorefront('@Storefront/storefront/page/account/order/index.html.twig', ['page' => $page]);
  211. }
  212. /**
  213. * @Since("6.2.0.0")
  214. * @Route("/account/order/payment/{orderId}", name="frontend.account.edit-order.change-payment-method", methods={"POST"})
  215. */
  216. public function orderChangePayment(string $orderId, Request $request, SalesChannelContext $context): Response
  217. {
  218. $this->contextSwitchRoute->switchContext(
  219. new RequestDataBag(
  220. [
  221. SalesChannelContextService::PAYMENT_METHOD_ID => $request->get('paymentMethodId'),
  222. ]
  223. ),
  224. $context
  225. );
  226. return $this->redirectToRoute('frontend.account.edit-order.page', ['orderId' => $orderId]);
  227. }
  228. /**
  229. * @Since("6.2.0.0")
  230. * @Route("/account/order/update/{orderId}", name="frontend.account.edit-order.update-order", methods={"POST"})
  231. */
  232. public function updateOrder(string $orderId, Request $request, SalesChannelContext $context): Response
  233. {
  234. $finishUrl = $this->generateUrl('frontend.checkout.finish.page', [
  235. 'orderId' => $orderId,
  236. 'changedPayment' => true,
  237. ]);
  238. /** @var OrderEntity|null $order */
  239. $order = $this->orderRoute->load($request, $context, new Criteria([$orderId]))->getOrders()->first();
  240. if ($order === null) {
  241. throw new OrderNotFoundException($orderId);
  242. }
  243. if (!$this->orderService->isPaymentChangeableByTransactionState($order)) {
  244. if (Feature::isActive('v6.5.0.0')) {
  245. throw OrderException::paymentMethodNotChangeable();
  246. }
  247. throw new OrderPaymentMethodNotChangeable();
  248. }
  249. if ($context->getCurrency()->getId() !== $order->getCurrencyId()) {
  250. $this->contextSwitchRoute->switchContext(
  251. new RequestDataBag([SalesChannelContextService::CURRENCY_ID => $order->getCurrencyId()]),
  252. $context
  253. );
  254. $context = $this->contextService->get(
  255. new SalesChannelContextServiceParameters(
  256. $context->getSalesChannelId(),
  257. $context->getToken(),
  258. $context->getContext()->getLanguageId()
  259. )
  260. );
  261. }
  262. $errorUrl = $this->generateUrl('frontend.account.edit-order.page', ['orderId' => $orderId]);
  263. $setPaymentRequest = new Request();
  264. $setPaymentRequest->request->set('orderId', $orderId);
  265. $setPaymentRequest->request->set('paymentMethodId', $request->get('paymentMethodId'));
  266. $setPaymentOrderRouteRequestEvent = new SetPaymentOrderRouteRequestEvent($request, $setPaymentRequest, $context);
  267. $this->eventDispatcher->dispatch($setPaymentOrderRouteRequestEvent);
  268. $this->setPaymentOrderRoute->setPayment($setPaymentOrderRouteRequestEvent->getStoreApiRequest(), $context);
  269. $handlePaymentRequest = new Request();
  270. $handlePaymentRequest->request->set('orderId', $orderId);
  271. $handlePaymentRequest->request->set('finishUrl', $finishUrl);
  272. $handlePaymentRequest->request->set('errorUrl', $errorUrl);
  273. $handlePaymentMethodRouteRequestEvent = new HandlePaymentMethodRouteRequestEvent($request, $handlePaymentRequest, $context);
  274. $this->eventDispatcher->dispatch($handlePaymentMethodRouteRequestEvent);
  275. try {
  276. $routeResponse = $this->handlePaymentMethodRoute->load(
  277. $handlePaymentMethodRouteRequestEvent->getStoreApiRequest(),
  278. $context
  279. );
  280. $response = $routeResponse->getRedirectResponse();
  281. } catch (PaymentProcessException $paymentProcessException) {
  282. return $this->forwardToRoute(
  283. 'frontend.checkout.finish.page',
  284. ['orderId' => $orderId, 'changedPayment' => true, 'paymentFailed' => true]
  285. );
  286. }
  287. return $response ?? $this->redirectToRoute(
  288. 'frontend.checkout.finish.page',
  289. ['orderId' => $orderId, 'changedPayment' => true]
  290. );
  291. }
  292. }