vendor/shopware/storefront/Controller/CheckoutController.php line 254

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Storefront\Controller;
  3. use Shopware\Core\Checkout\Cart\Error\Error;
  4. use Shopware\Core\Checkout\Cart\Error\ErrorCollection;
  5. use Shopware\Core\Checkout\Cart\Exception\CartTokenNotFoundException;
  6. use Shopware\Core\Checkout\Cart\Exception\CustomerNotLoggedInException;
  7. use Shopware\Core\Checkout\Cart\Exception\InvalidCartException;
  8. use Shopware\Core\Checkout\Cart\Exception\OrderNotFoundException;
  9. use Shopware\Core\Checkout\Cart\SalesChannel\CartService;
  10. use Shopware\Core\Checkout\Customer\SalesChannel\AbstractLogoutRoute;
  11. use Shopware\Core\Checkout\Order\Exception\EmptyCartException;
  12. use Shopware\Core\Checkout\Order\SalesChannel\OrderService;
  13. use Shopware\Core\Checkout\Payment\Exception\InvalidOrderException;
  14. use Shopware\Core\Checkout\Payment\Exception\PaymentProcessException;
  15. use Shopware\Core\Checkout\Payment\Exception\UnknownPaymentMethodException;
  16. use Shopware\Core\Checkout\Payment\PaymentService;
  17. use Shopware\Core\Framework\Routing\Annotation\RouteScope;
  18. use Shopware\Core\Framework\Routing\Annotation\Since;
  19. use Shopware\Core\Framework\Routing\Exception\MissingRequestParameterException;
  20. use Shopware\Core\Framework\Validation\DataBag\RequestDataBag;
  21. use Shopware\Core\Framework\Validation\Exception\ConstraintViolationException;
  22. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  23. use Shopware\Core\System\SystemConfig\SystemConfigService;
  24. use Shopware\Storefront\Checkout\Cart\Error\PaymentMethodChangedError;
  25. use Shopware\Storefront\Checkout\Cart\Error\ShippingMethodChangedError;
  26. use Shopware\Storefront\Framework\AffiliateTracking\AffiliateTrackingListener;
  27. use Shopware\Storefront\Framework\Routing\Annotation\NoStore;
  28. use Shopware\Storefront\Page\Checkout\Cart\CheckoutCartPageLoadedHook;
  29. use Shopware\Storefront\Page\Checkout\Cart\CheckoutCartPageLoader;
  30. use Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedHook;
  31. use Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoader;
  32. use Shopware\Storefront\Page\Checkout\Finish\CheckoutFinishPageLoadedHook;
  33. use Shopware\Storefront\Page\Checkout\Finish\CheckoutFinishPageLoader;
  34. use Shopware\Storefront\Page\Checkout\Offcanvas\CheckoutInfoWidgetLoadedHook;
  35. use Shopware\Storefront\Page\Checkout\Offcanvas\CheckoutOffcanvasWidgetLoadedHook;
  36. use Shopware\Storefront\Page\Checkout\Offcanvas\OffcanvasCartPageLoader;
  37. use Symfony\Component\HttpFoundation\RedirectResponse;
  38. use Symfony\Component\HttpFoundation\Request;
  39. use Symfony\Component\HttpFoundation\Response;
  40. use Symfony\Component\HttpFoundation\Session\SessionInterface;
  41. use Symfony\Component\Routing\Annotation\Route;
  42. /**
  43.  * @RouteScope(scopes={"storefront"})
  44.  */
  45. class CheckoutController extends StorefrontController
  46. {
  47.     private const REDIRECTED_FROM_SAME_ROUTE 'redirected';
  48.     /**
  49.      * @var CartService
  50.      */
  51.     private $cartService;
  52.     /**
  53.      * @var CheckoutCartPageLoader
  54.      */
  55.     private $cartPageLoader;
  56.     /**
  57.      * @var CheckoutConfirmPageLoader
  58.      */
  59.     private $confirmPageLoader;
  60.     /**
  61.      * @var CheckoutFinishPageLoader
  62.      */
  63.     private $finishPageLoader;
  64.     /**
  65.      * @var OrderService
  66.      */
  67.     private $orderService;
  68.     /**
  69.      * @var PaymentService
  70.      */
  71.     private $paymentService;
  72.     /**
  73.      * @var OffcanvasCartPageLoader
  74.      */
  75.     private $offcanvasCartPageLoader;
  76.     /**
  77.      * @var SystemConfigService
  78.      */
  79.     private $config;
  80.     /**
  81.      * @var AbstractLogoutRoute
  82.      */
  83.     private $logoutRoute;
  84.     public function __construct(
  85.         CartService $cartService,
  86.         CheckoutCartPageLoader $cartPageLoader,
  87.         CheckoutConfirmPageLoader $confirmPageLoader,
  88.         CheckoutFinishPageLoader $finishPageLoader,
  89.         OrderService $orderService,
  90.         PaymentService $paymentService,
  91.         OffcanvasCartPageLoader $offcanvasCartPageLoader,
  92.         SystemConfigService $config,
  93.         AbstractLogoutRoute $logoutRoute
  94.     ) {
  95.         $this->cartService $cartService;
  96.         $this->cartPageLoader $cartPageLoader;
  97.         $this->confirmPageLoader $confirmPageLoader;
  98.         $this->finishPageLoader $finishPageLoader;
  99.         $this->orderService $orderService;
  100.         $this->paymentService $paymentService;
  101.         $this->offcanvasCartPageLoader $offcanvasCartPageLoader;
  102.         $this->config $config;
  103.         $this->logoutRoute $logoutRoute;
  104.     }
  105.     /**
  106.      * @Since("6.0.0.0")
  107.      * @NoStore
  108.      * @Route("/checkout/cart", name="frontend.checkout.cart.page", options={"seo"="false"}, methods={"GET"})
  109.      */
  110.     public function cartPage(Request $requestSalesChannelContext $context): Response
  111.     {
  112.         $page $this->cartPageLoader->load($request$context);
  113.         $cart $page->getCart();
  114.         $cartErrors $cart->getErrors();
  115.         // dd($cart->getDeliveries()->first()->getPositions());
  116.         $this->hook(new CheckoutCartPageLoadedHook($page$context));
  117.         $this->addCartErrors($cart);
  118.         if (!$request->query->getBoolean(self::REDIRECTED_FROM_SAME_ROUTE) && $this->routeNeedsReload($cartErrors)) {
  119.             $cartErrors->clear();
  120.             // To prevent redirect loops add the identifier that the request already got redirected from the same origin
  121.             return $this->redirectToRoute(
  122.                 'frontend.checkout.cart.page',
  123.                 [
  124.                     self::REDIRECTED_FROM_SAME_ROUTE => true,
  125.                 ]
  126.             );
  127.         }
  128.         $cartErrors->clear();
  129.         return $this->renderStorefront('@Storefront/storefront/page/checkout/cart/index.html.twig', ['page' => $page]);
  130.     }
  131.     /**
  132.      * @Since("6.0.0.0")
  133.      * @NoStore
  134.      * @Route("/checkout/confirm", name="frontend.checkout.confirm.page", options={"seo"="false"}, methods={"GET"}, defaults={"XmlHttpRequest"=true})
  135.      */
  136.     public function confirmPage(Request $requestSalesChannelContext $context): Response
  137.     {
  138.         if (!$context->getCustomer()) {
  139.             return $this->redirectToRoute('frontend.checkout.register.page');
  140.         }
  141.         if ($this->cartService->getCart($context->getToken(), $context)->getLineItems()->count() === 0) {
  142.             return $this->redirectToRoute('frontend.checkout.cart.page');
  143.         }
  144.         $page $this->confirmPageLoader->load($request$context);
  145.         $this->hook(new CheckoutConfirmPageLoadedHook($page$context));
  146.         $this->addCartErrors($page->getCart());
  147.         return $this->renderStorefront('@Storefront/storefront/page/checkout/confirm/index.html.twig', ['page' => $page]);
  148.     }
  149.     /**
  150.      * @Since("6.0.0.0")
  151.      * @Route("/checkout/finish", name="frontend.checkout.finish.page", options={"seo"="false"}, methods={"GET"})
  152.      * @NoStore
  153.      *
  154.      * @throws CustomerNotLoggedInException
  155.      * @throws MissingRequestParameterException
  156.      * @throws OrderNotFoundException
  157.      */
  158.     public function finishPage(Request $requestSalesChannelContext $contextRequestDataBag $dataBag): Response
  159.     {
  160.         if ($context->getCustomer() === null) {
  161.             return $this->redirectToRoute('frontend.checkout.register.page');
  162.         }
  163.         $page $this->finishPageLoader->load($request$context);
  164.         $this->hook(new CheckoutFinishPageLoadedHook($page$context));
  165.         if ($page->isPaymentFailed() === true) {
  166.             return $this->redirectToRoute(
  167.                 'frontend.account.edit-order.page',
  168.                 [
  169.                     'orderId' => $request->get('orderId'),
  170.                     'error-code' => 'CHECKOUT__UNKNOWN_ERROR',
  171.                 ]
  172.             );
  173.         }
  174.         if ($context->getCustomer()->getGuest() && $this->config->get('core.cart.logoutGuestAfterCheckout'$context->getSalesChannelId())) {
  175.             $this->logoutRoute->logout($context$dataBag);
  176.         }
  177.         return $this->renderStorefront('@Storefront/storefront/page/checkout/finish/index.html.twig', ['page' => $page]);
  178.     }
  179.     /**
  180.      * @Since("6.0.0.0")
  181.      * @Route("/checkout/order", name="frontend.checkout.finish.order", options={"seo"="false"}, methods={"POST"})
  182.      */
  183.     public function order(RequestDataBag $dataSalesChannelContext $contextRequest $request): Response
  184.     {
  185.         if (!$context->getCustomer()) {
  186.             return $this->redirectToRoute('frontend.checkout.register.page');
  187.         }
  188.         try {
  189.             $this->addAffiliateTracking($data$request->getSession());
  190.             $orderId $this->orderService->createOrder($data$context);
  191.         } catch (ConstraintViolationException $formViolations) {
  192.             return $this->forwardToRoute('frontend.checkout.confirm.page', ['formViolations' => $formViolations]);
  193.         } catch (InvalidCartException Error EmptyCartException $error) {
  194.             $this->addCartErrors(
  195.                 $this->cartService->getCart($context->getToken(), $context)
  196.             );
  197.             return $this->forwardToRoute('frontend.checkout.confirm.page');
  198.         }
  199.         try {
  200.             $finishUrl $this->generateUrl('frontend.checkout.finish.page', ['orderId' => $orderId]);
  201.             $errorUrl $this->generateUrl('frontend.account.edit-order.page', ['orderId' => $orderId]);
  202.             $response $this->paymentService->handlePaymentByOrder($orderId$data$context$finishUrl$errorUrl);
  203.             return $response ?? new RedirectResponse($finishUrl);
  204.         } catch (PaymentProcessException InvalidOrderException UnknownPaymentMethodException $e) {
  205.             return $this->forwardToRoute('frontend.checkout.finish.page', ['orderId' => $orderId'changedPayment' => false'paymentFailed' => true]);
  206.         }
  207.     }
  208.     /**
  209.      * @Since("6.0.0.0")
  210.      * @Route("/widgets/checkout/info", name="frontend.checkout.info", methods={"GET"}, defaults={"XmlHttpRequest"=true})
  211.      *
  212.      * @throws CartTokenNotFoundException
  213.      */
  214.     public function info(Request $requestSalesChannelContext $context): Response
  215.     {
  216.         $page $this->offcanvasCartPageLoader->load($request$context);
  217.         $this->hook(new CheckoutInfoWidgetLoadedHook($page$context));
  218.         $response $this->renderStorefront('@Storefront/storefront/layout/header/actions/cart-widget.html.twig', ['page' => $page]);
  219.         $response->headers->set('x-robots-tag''noindex');
  220.         return $response;
  221.     }
  222.     /**
  223.      * @Since("6.0.0.0")
  224.      * @Route("/checkout/offcanvas", name="frontend.cart.offcanvas", options={"seo"="false"}, methods={"GET"}, defaults={"XmlHttpRequest"=true})
  225.      *
  226.      * @throws CartTokenNotFoundException
  227.      */
  228.     public function offcanvas(Request $requestSalesChannelContext $context): Response
  229.     {
  230.         $page $this->offcanvasCartPageLoader->load($request$context);
  231.         $this->hook(new CheckoutOffcanvasWidgetLoadedHook($page$context));
  232.         $cart $page->getCart();
  233.         $this->addCartErrors($cart);
  234.         $cartErrors $cart->getErrors();
  235.         if (!$request->query->getBoolean(self::REDIRECTED_FROM_SAME_ROUTE) && $this->routeNeedsReload($cartErrors)) {
  236.             $cartErrors->clear();
  237.             // To prevent redirect loops add the identifier that the request already got redirected from the same origin
  238.             return $this->redirectToRoute(
  239.                 'frontend.cart.offcanvas',
  240.                 [
  241.                     self::REDIRECTED_FROM_SAME_ROUTE => true,
  242.                 ]
  243.             );
  244.         }
  245.         $cartErrors->clear();
  246.         return $this->renderStorefront('@Storefront/storefront/component/checkout/offcanvas-cart.html.twig', ['page' => $page]);
  247.     }
  248.     private function addAffiliateTracking(RequestDataBag $dataBagSessionInterface $session): void
  249.     {
  250.         $affiliateCode $session->get(AffiliateTrackingListener::AFFILIATE_CODE_KEY);
  251.         $campaignCode $session->get(AffiliateTrackingListener::CAMPAIGN_CODE_KEY);
  252.         if ($affiliateCode) {
  253.             $dataBag->set(AffiliateTrackingListener::AFFILIATE_CODE_KEY$affiliateCode);
  254.         }
  255.         if ($campaignCode) {
  256.             $dataBag->set(AffiliateTrackingListener::CAMPAIGN_CODE_KEY$campaignCode);
  257.         }
  258.     }
  259.     private function routeNeedsReload(ErrorCollection $cartErrors): bool
  260.     {
  261.         foreach ($cartErrors as $error) {
  262.             if ($error instanceof ShippingMethodChangedError || $error instanceof PaymentMethodChangedError) {
  263.                 return true;
  264.             }
  265.         }
  266.         return false;
  267.     }
  268. }