src/Controller/Shop/CheckoutController.php line 254

Open in your IDE?
  1. <?php
  2. /**
  3.  * Created by Elements.at New Media Solutions GmbH
  4.  *
  5.  */
  6. namespace App\Controller\Shop;
  7. use App\Ecommerce\CartManager\CartPriceModificator\CashbackInsurance;
  8. use App\Ecommerce\CartManager\SessionCart;
  9. use App\Ecommerce\Checkout\PersonalisationDataHandler;
  10. use App\Ecommerce\Checkout\Step\ConfirmStep;
  11. use App\Ecommerce\Checkout\Step\PaymentStep;
  12. use App\Form\AddressDataType;
  13. use App\Form\CheckoutConfirmType;
  14. use App\Form\PaymentType;
  15. use App\Form\ProductDataItemType;
  16. use App\Model\Shop\Checkout\AddressData;
  17. use App\Model\Shop\Checkout\PaymentData;
  18. use App\Model\Shop\Checkout\TicketPersonalisationData;
  19. use App\Model\Shop\Ticket\TicketProduct;
  20. use App\Model\Type\PaymentMethodB2CType;
  21. use App\Service\Shop\VoucherService;
  22. use Carbon\Carbon;
  23. use Elements\Bundle\IncertBundle\PriceSystem\IncertPriceModificator;
  24. use Elements\Bundle\IncertBundle\Service\IncertService;
  25. use Elements\Bundle\TicketShopFrameworkBundle\Ecommerce\Checkout\Step\Address;
  26. use Elements\Bundle\TicketShopFrameworkBundle\Ecommerce\Checkout\Step\ProductData;
  27. use Elements\Bundle\TicketShopFrameworkBundle\Model\DataObject\OnlineShopOrder;
  28. use Pimcore\Bundle\EcommerceFrameworkBundle\CartManager\CartInterface;
  29. use Pimcore\Bundle\EcommerceFrameworkBundle\Factory;
  30. use Pimcore\Model\Asset\Image;
  31. use Pimcore\Model\DataObject\Concrete;
  32. use Pimcore\Model\DataObject\Customer;
  33. use Pimcore\Model\DataObject\CustomerContact;
  34. use Pimcore\Model\DataObject\Fieldcollection\Data\IncertVoucher;
  35. use Pimcore\Model\DataObject\Service;
  36. use Pimcore\Model\User;
  37. use Symfony\Component\Form\FormFactoryInterface;
  38. use Symfony\Component\HttpFoundation\JsonResponse;
  39. use Symfony\Component\HttpFoundation\RedirectResponse;
  40. use Symfony\Component\HttpFoundation\Request;
  41. use Symfony\Component\HttpFoundation\Response;
  42. use Symfony\Component\Routing\Annotation\Route;
  43. /**
  44.  * Class CheckoutController
  45.  *
  46.  * @Route("/{_locale}/shop/checkout")
  47.  *
  48.  * @package App\Controller\Shop
  49.  */
  50. class CheckoutController extends AbstractShopController
  51. {
  52.     /**
  53.      * @Route("/login", name="checkout_login")
  54.      *
  55.      * @param Request $request
  56.      *
  57.      * @return Response
  58.      *
  59.      * @throws \Exception
  60.      */
  61.     public function loginAction(Request $request): Response
  62.     {
  63.         $trackingManager $this->factory->getTrackingManager();
  64.         $trackingManager->trackCheckout($this->getCart());
  65.         if (!empty($this->userManagerService->loginPrecheck($request))) {
  66.             $trackingManager->forwardTrackedCodesAsFlashMessage();
  67.             return $this->redirect($this->generateUrl('checkout_address'));
  68.         }
  69.         return $this->render('shop/checkout/login.html.twig');
  70.     }
  71.     /**
  72.      * Step 1
  73.      *
  74.      * @Route("/address", name="checkout_address")
  75.      *
  76.      * @param Request $request
  77.      * @param Factory $factory
  78.      *
  79.      * @return Response
  80.      */
  81.     public function addressAction(Request $requestFactory $factory): Response
  82.     {
  83.         //if cart empty or not checkoutable , redirect to cart list
  84.         if (!$this->getCart()->isCheckoutable()) {
  85.             return $this->redirectToRoute('cart_list');
  86.         }
  87.         $cart $this->getCart();
  88.         $checkoutManager $factory->getCheckoutManager($cart);
  89.         /** @var Address $addressStep */
  90.         $addressStep $checkoutManager->getCheckoutStep(Address::STEP_NAME);
  91.         $addressData $addressStep->getData();
  92.         $form $this->createForm(AddressDataType::class, $addressData);
  93.         $form->handleRequest($request);
  94.         if ($form->isSubmitted() && $form->isValid()) {
  95.             try {
  96.                 /** @var AddressData $addressData */
  97.                 $addressData $form->getData();
  98.                 // skidata doesn't allow fullStreetNames over 64 chars (62 chars + 2 empty spaces)
  99.                 $fullCustomerStreet trim($addressData->getCustomerStreet() . ' ' $addressData->getCustomerStreetNumber() . ' ' $addressData->getCustomerStreetAddon());
  100.                 $fullDeliverySteet trim($addressData->getDeliveryStreet() . ' ' $addressData->getDeliveryStreetNumber() . ' ' $addressData->getDeliveryStreetAddon());
  101.                 if (strlen($fullCustomerStreet) > 64) {
  102.                     $this->addFlash('error''checkout.address.full-customer_street-too-long');
  103.                     return $this->redirectToRoute('checkout_address');
  104.                 }
  105.                 if (strlen($fullDeliverySteet) > 64) {
  106.                     $this->addFlash('error''checkout.address.full-delivery_street-too-long');
  107.                     return $this->redirectToRoute('checkout_address');
  108.                 }
  109.                 $checkoutManager->commitStep($addressStep$addressData);
  110.                 if (!$addressStep->isValid()) {
  111.                     return $this->redirectToRoute('checkout_address');
  112.                 }
  113.                 $trackingManager $this->factory->getTrackingManager();
  114.                 /** @phpstan-ignore-next-line  */
  115.                 $trackingManager->trackShippingInfo($this->getCart());
  116.                 $trackingManager->forwardTrackedCodesAsFlashMessage();
  117.                 if ($cart->needsPersonalisation()) {
  118.                     return $this->redirectToRoute('checkout_personalization');
  119.                 } else {
  120.                     $personalisationStep $checkoutManager->getCheckoutStep(ProductData::STEP_NAME);
  121.                     $checkoutManager->commitStep($personalisationStep, []);
  122.                     return $this->redirectToRoute('checkout_payment');
  123.                 }
  124.             } catch (\Throwable  $throwable) {
  125.                 return $this->redirectToRoute('checkout_address');
  126.             }
  127.         }
  128.         //        $factory->getTrackingManager()->trackCheckout($cart);
  129.         return $this->render('shop/checkout/address.html.twig', [
  130.             'form' => $form->createView(),
  131.         ]);
  132.     }
  133.     /**
  134.      * Step 2.1
  135.      *
  136.      * @Route("/personalization", name="checkout_personalization")
  137.      *
  138.      * @param Request $request
  139.      * @param Factory $factory
  140.      *
  141.      * @return Response
  142.      */
  143.     public function personalizationAction(Request $requestFactory $factory): Response
  144.     {
  145.         //if cart empty or not checkoutable , redirect to cart list
  146.         if (!$this->getCart()->isCheckoutable()) {
  147.             return $this->redirectToRoute('cart_list');
  148.         }
  149.         $cart $this->getCart();
  150.         $checkoutManager $factory->getCheckoutManager($cart);
  151.         $productDataStep $checkoutManager->getCheckoutStep(ProductData::STEP_NAME);
  152.         if (!$cart->needsPersonalisation()) {
  153.             $productDataStep->commit([]);
  154.             return $this->redirectToRoute('checkout_payment');
  155.         }
  156.         //previous step is not finished
  157.         /** @phpstan-ignore-next-line */
  158.         if (!$checkoutManager->getCheckoutStep(Address::STEP_NAME)->isComplete()) {
  159.             return $this->redirectToRoute('checkout_address');
  160.         }
  161.         if ($request->get('submit')) {
  162.             /** @var ProductData $productDataStep */
  163.             $productDataStep $checkoutManager->getCheckoutStep(ProductData::STEP_NAME);
  164.             if ($productDataStep->isComplete()) {
  165.                 $checkoutManager->commitStep($productDataStep$productDataStep->getData());
  166.                 return $this->redirectToRoute('checkout_payment');
  167.             }
  168.         }
  169.         return $this->render('shop/checkout/personalization.html.twig', [
  170.             'productDataStep' => $productDataStep,
  171.         ]);
  172.     }
  173.     /**
  174.      * Step 2.2
  175.      *
  176.      * @Route("/personalization/{itemKey}", name="checkout_personalization_item")
  177.      *
  178.      * @param Request $request
  179.      * @param string $itemKey
  180.      * @param PersonalisationDataHandler $personalisationDataHandler
  181.      * @param FormFactoryInterface $formFactory
  182.      * @param Factory $factory
  183.      *
  184.      * @return Response
  185.      *
  186.      * @throws \Exception
  187.      */
  188.     public function personalizeItem(
  189.         Request $request,
  190.         string $itemKey,
  191.         PersonalisationDataHandler $personalisationDataHandler,
  192.         FormFactoryInterface $formFactory,
  193.         Factory $factory
  194.     ): Response {
  195.         /** @var Customer|User|null $user */
  196.         $user $this->getUser();
  197.         $cart $this->getCart();
  198.         $checkoutManager $factory->getCheckoutManager($cart);
  199.         $isLoggedIn $user instanceof Customer;
  200.         $isEditingContact $request->get('edit');
  201.         $createNewPerson false;
  202.         $contactId $request->get('contactId');
  203.         $isPersonalized $request->get('isPersonalized');
  204.         /** @var ProductData $productDataStep */
  205.         $productDataStep $checkoutManager->getCheckoutStep(ProductData::STEP_NAME);
  206.         /** @var Address $addressStep */
  207.         $addressStep $checkoutManager->getCheckoutStep(Address::STEP_NAME);
  208.         $addressData $addressStep->getData();
  209.         if ($addressData) {
  210.             $deliveryCountry $addressData->isUseDeliveryAddress() ? $addressData->getDeliveryCountry() : $addressData->getCustomerCountry();
  211.         } else {
  212.             $deliveryCountry null;
  213.         }
  214.         $cartItem $cart->getItem($itemKey);
  215.         /** @var TicketProduct $product */
  216.         $product $cartItem->getProduct();
  217.         /** @var Customer|CustomerContact|null $contact */
  218.         $contact Concrete::getById($contactId);
  219.         /** @var TicketPersonalisationData $itemData */
  220.         $itemData $productDataStep->getDataForItem($cartItem) ?: new TicketPersonalisationData();
  221.         $itemData->setCartItem($cartItem);
  222.         $itemData->setContactId((int)$contactId);
  223.         $itemData->setIsHTAConsumer((bool)$product->getTicketConsumerCategory()->getIsSwisspassHTAConsumer());
  224.         if ($isLoggedIn) {
  225.             $personalisationDataHandler->setProfilePictureDirectory(
  226.                 Service::createFolderByPath("/protected/{$user->getEmail()}-{$user->getId()}")
  227.             );
  228.             if (empty($contact) && $contactId === '0') {
  229.                 $createNewPerson true;
  230.             }
  231.             if (in_array($contact$user->getContacts()) || $contact === $user) {
  232.                 $itemData->setFirstname($contact->getFirstname());
  233.                 $itemData->setLastname($contact->getLastname());
  234.                 $itemData->setProfilePictureAsset($contact->getImage());
  235.                 if ($contact === $user) {
  236.                     $itemData->setBirthday($contact->getBirthdate());
  237.                     if ($keycard $contact->getKeyCard()) {
  238.                         $itemData->setKeycard($keycard->getNumber());
  239.                     }
  240.                 } else {
  241.                     $itemData->setBirthday($contact->getBirthday());
  242.                     if ($keycard $contact->getActiveKeycard()) {
  243.                         $itemData->setKeycard($keycard->getNumber());
  244.                     }
  245.                 }
  246.             }
  247.         }
  248.         $form $formFactory->createNamed('productData-' $cartItem->getItemKey(),
  249.             ProductDataItemType::class, $itemData, [
  250.                 'item' => $cartItem,
  251.                 'deliveryCountry' => $deliveryCountry,
  252.                 'useDeliveryAddress' => $addressData?->isUseDeliveryAddress(),
  253.                 'isLoggedIn' => $isLoggedIn,
  254.                 'validityDate'=> $product->getTicketStartDate(),
  255.             ]);
  256.         $params = [
  257.             'user' => $user,
  258.             'isLoggedIn' => $isLoggedIn,
  259.             'cartItem' => $cartItem,
  260.             'createNewPerson' => $createNewPerson,
  261.             'contact' => $contact,
  262.             'productDataStep' => $productDataStep,
  263.         ];
  264.         $form->handleRequest($request);
  265.         if ($form->isSubmitted()) {
  266.             $profilePictureFile null;
  267.             if ($form->has('profilePicture') && $form->get('profilePicture')->isValid()) {
  268.                 $profilePictureFile $form->get('profilePicture')->getData();
  269.             }
  270.             if ($form->isValid()) {
  271.                 $isNew false;
  272.                 $personalisationDataHandler->handleData($cartItem$itemData$profilePictureFile);
  273.                 if ($isLoggedIn && ($createNewPerson || $isEditingContact)) {
  274.                     if (!$contact && $createNewPerson) {
  275.                         $key Service::getValidKey("{$itemData->getFirstname()} {$itemData->getLastname()}"'object');
  276.                         $contact CustomerContact::getByPath($user->getFullPath() . '/' $key);
  277.                         if (empty($contact)) {
  278.                             $isNew true;
  279.                             $contact = new CustomerContact();
  280.                             $contact->setPublished(true);
  281.                             $contact->setKey($key);
  282.                             $contact->setParent($user);
  283.                         }
  284.                     }
  285.                     $contact->setFirstname($itemData->getFirstname());
  286.                     $contact->setLastname($itemData->getLastname());
  287.                     if ($itemData->getBirthday()) {
  288.                         if ($contact === $user) {
  289.                             $contact->setBirthdate(Carbon::parse($itemData->getBirthday()));
  290.                         } else {
  291.                             $contact->setBirthday(Carbon::parse($itemData->getBirthday()));
  292.                         }
  293.                     }
  294.                     if ($image Image::getById($itemData->getAssetId())) {
  295.                         $contact->setImage($image);
  296.                     }
  297.                     $contact->save();
  298.                     $itemData->setContactId($contact->getId());
  299.                     if ($isNew) {
  300.                         $newContactsArray array_merge($user->getContacts(), [$contact]);
  301.                         $user->setContacts($newContactsArray);
  302.                         $user->save();
  303.                     }
  304.                 }
  305.                 $teaserResultContent $this->renderTemplate('shop/includes/personalization/personalization-result.html.twig')->getContent();
  306.                 $footerTopContent $this->renderTemplate('shop/includes/checkout/checkout-cart-footer-top.html.twig', [
  307.                     'step' => 'personalization',
  308.                 ])->getContent();
  309.                 $footerBottomContent $this->renderTemplate('shop/includes/checkout/checkout-cart-footer-details.html.twig', [
  310.                     'redirectUri' => $this->generateUrl('checkout_personalization'),
  311.                 ])->getContent();
  312.                 return $this->json([
  313.                     'success' => true,
  314.                     'content' => [
  315.                         'teaser-result' => $teaserResultContent,
  316.                         'checkout-footer-top' => $footerTopContent,
  317.                         'checkout-footer-bottom' => $footerBottomContent,
  318.                     ],
  319.                 ]);
  320.             } else {
  321.                 // in case of invalid, at least upload the picture if exists to make it easier for user ;)
  322.                 $personalisationDataHandler->handleData($cartItem$itemData$profilePictureFiletrue);
  323.                 $params['form'] = $form->createView();
  324.                 $failedContent $this->renderTemplate('shop/includes/personalization/personalization-modal-content-result.html.twig'$params)->getContent();
  325.                 $content = ['failed-form-result' => $failedContent];
  326.             }
  327.         } elseif ($contactId !== null && !$isPersonalized) {
  328.             $params['form'] = $form->createView();
  329.             $content $this->renderTemplate('shop/includes/personalization/personalization-person-result.html.twig'$params)->getContent();
  330.         } else {
  331.             $params['form'] = $form->createView();
  332.             $content $this->renderTemplate('shop/includes/personalization/personalization-modal-content.html.twig'$params)->getContent();
  333.         }
  334.         return $this->json([
  335.             'success' => true,
  336.             'content' => $content,
  337.         ]);
  338.     }
  339.     /**
  340.      * Step 3
  341.      *
  342.      * @Route("/payment", name="checkout_payment")
  343.      *
  344.      * @param Request $request
  345.      * @param Factory $factory
  346.      *
  347.      * @return Response
  348.      */
  349.     public function paymentAction(Request $requestFactory $factoryVoucherService $voucherService): Response
  350.     {
  351.         //if cart empty or not checkoutable , redirect to cart list
  352.         if (!$this->getCart()->isCheckoutable()) {
  353.             return $this->redirectToRoute('cart_list');
  354.         }
  355.         $cart $this->getCart();
  356.         $checkoutManager $factory->getCheckoutManager($cart);
  357.         /** @var ProductData $personalizationStep */
  358.         $personalizationStep $checkoutManager->getCheckoutStep(ProductData::STEP_NAME);
  359.         if (!$personalizationStep->isComplete()) {
  360.             return $this->redirectToRoute('checkout_personalization');
  361.         } else {
  362.             //if a personalized ticket is removed, we have to commit this step again
  363.             $checkoutManager->commitStep($personalizationStep$personalizationStep->getData() ?: []);
  364.         }
  365.         /** @var PaymentStep $paymentStep */
  366.         $paymentStep $checkoutManager->getCheckoutStep(PaymentStep::STEP_NAME);
  367.         $paymentData $paymentStep->getData() ?: new PaymentData();
  368.         $paymentData->setOrderLanguage($request->getLocale());
  369.         $this->preselectCashbackPriceModificator($cart);
  370.         if ($cart->getPriceCalculator()->getGrandTotal()->getAmount()->isZero()) {
  371.             $paymentData->setNeedsPayment(false);
  372.         } else {
  373.             $paymentData->setNeedsPayment(true);
  374.         }
  375.         $form $this->createForm(PaymentType::class, $paymentData);
  376.         $form->handleRequest($request);
  377.         if ($form->isSubmitted() && $form->isValid()) {
  378.             $data $form->getData();
  379.             $checkoutManager->commitStep($paymentStep$data);
  380.             $paymentType PaymentMethodB2CType::search($data->getPaymentMethod()) ?: $data->getPaymentMethod();
  381.             $trackingManager $this->factory->getTrackingManager();
  382.             /** @phpstan-ignore-next-line  */
  383.             $trackingManager->trackPaymentInfo($this->getCart(), $paymentType);
  384.             $trackingManager->forwardTrackedCodesAsFlashMessage();
  385.             return $this->redirectToRoute('checkout_confirm');
  386.         }
  387.         $incertVouchers $cart->getCheckoutData(IncertPriceModificator::CHECKOUT_DATA_NAME) ?: [];
  388.         $params['incertVouchers'] = $voucherService->getIncertVoucherData($incertVouchers$cart);
  389.         $params['pimcoreVouchers'] = $voucherService->getPimcoreVoucherData($cart);
  390.         $params['form'] = $form->createView();
  391.         return $this->render('shop/checkout/payment.html.twig'$params);
  392.     }
  393.     /**
  394.      * Step 4
  395.      *
  396.      * @Route("/confirm", name="checkout_confirm")
  397.      *
  398.      * @param Request $request
  399.      * @param Factory $factory
  400.      *
  401.      * @return Response
  402.      */
  403.     public function confirmAction(
  404.         Request $request,
  405.         Factory $factory
  406.     ): Response {
  407.         //if cart empty or not checkoutable , redirect to cart list
  408.         if (!$this->getCart()->isCheckoutable()) {
  409.             return $this->redirectToRoute('cart_list');
  410.         }
  411.         $cart $this->getCart();
  412.         $checkoutManager $factory->getCheckoutManager($cart);
  413.         $confirmOrderStep $checkoutManager->getCheckoutStep(ConfirmStep::STEP_NAME);
  414.         $form $this->createForm(CheckoutConfirmType::class, $confirmOrderStep->getData());
  415.         $form->handleRequest($request);
  416.         /** @phpstan-ignore-next-line */
  417.         if (!$checkoutManager->getCheckoutStep(ProductData::STEP_NAME)->isComplete()) {
  418.             return $this->redirectToRoute('checkout_personalization');
  419.         }
  420.         /** @phpstan-ignore-next-line */
  421.         if (!$checkoutManager->getCheckoutStep(PaymentStep::STEP_NAME)->isComplete()) {
  422.             return $this->redirectToRoute('checkout_payment');
  423.         }
  424.         if ($form->isSubmitted() && $form->isValid()) {
  425.             $data $form->getData();
  426.             $checkoutManager->commitStep($confirmOrderStep$data);
  427.             return $this->redirectToRoute('payment_init');
  428.         }
  429.         $addressStep $checkoutManager->getCheckoutStep(Address::STEP_NAME);
  430.         $paymentStep $checkoutManager->getCheckoutStep(PaymentStep::STEP_NAME);
  431.         $productDataStep $checkoutManager->getCheckoutStep(ProductData::STEP_NAME);
  432.         return $this->render('shop/checkout/confirm.html.twig', [
  433.             'addressData' => $addressStep->getData(),
  434.             'paymentData' => $paymentStep->getData(),
  435.             'productData' => $productDataStep->getData(),
  436.             'form' => $form->createView(),
  437.         ]);
  438.     }
  439.     /**
  440.      * @Route("/complete", name="checkout_complete")
  441.      *
  442.      * @param Request $request
  443.      *
  444.      * @return RedirectResponse|Response
  445.      */
  446.     public function completeAction(Request $request)
  447.     {
  448.         $order null;
  449.         if (!$this->editmode) {
  450.             $session $request->getSession();
  451.             $orderId $session->get('last_order_id');
  452.             $order OnlineShopOrder::getById($orderId);
  453.             if ($order) {
  454.                 if ($order->getOrderState() != $order::ORDER_STATE_COMMITTED) {
  455.                     return $this->redirectToRoute('cart_list');
  456.                 }
  457.                 $this->factory->getTrackingManager()->trackCheckoutComplete($order);
  458.             }
  459.         }
  460.         return $this->renderTemplate('shop/checkout/complete.html.twig', ['order' => $order]);
  461.     }
  462.     /**
  463.      * @Route("/validate-voucher", name="checkout_validate_voucher", methods={"POST"})
  464.      *
  465.      * @param Request $request
  466.      * @param IncertService $incertService
  467.      * @param VoucherService $voucherService
  468.      *
  469.      * @return JsonResponse
  470.      *
  471.      * @throws \Exception
  472.      */
  473.     public function validateVoucher(Request $requestIncertService $incertServiceVoucherService $voucherService): JsonResponse
  474.     {
  475.         /** @var SessionCart $cart */
  476.         $cart $this->getCart();
  477.         $voucherData $cart->getVoucherTokenCodes();
  478.         $checkoutManager Factory::getInstance()->getCheckoutManager($cart);
  479.         $requestBody json_decode($request->getContent(), true);
  480.         $voucherAction $requestBody['action'] ?? '';
  481.         $voucherId $requestBody['voucher'] ?? '';
  482.         $voucherId trim($voucherId);
  483.         $voucherType $requestBody['type'] ?? '';
  484.         $html = [];
  485.         $errors = [];
  486.         $pimcoreErrors = [];
  487.         $infos = [];
  488.         $incertVouchers $cart->getCheckoutData(IncertPriceModificator::CHECKOUT_DATA_NAME) ?: [];
  489.         if ($voucherId) {
  490.             if ($voucherType == 'incert') {
  491.                 /** @var array<mixed> $incertVouchers */
  492.                 $currentIncertVoucher = [];
  493.                 if ($voucherAction == 'remove') {
  494.                     if ($incertVouchers) {
  495.                         /** @var IncertVoucher $incertVoucher */
  496.                         foreach ($incertVouchers as $incertVoucher) {
  497.                             if ($incertVoucher->getVoucherCode() != $voucherId) {
  498.                                 $currentIncertVoucher[] = $incertVoucher;
  499.                             }
  500.                         }
  501.                         if (count($currentIncertVoucher) > 0) {
  502.                             /** @phpstan-ignore-next-line  */
  503.                             $cart->setCheckoutData(IncertPriceModificator::CHECKOUT_DATA_NAME$currentIncertVoucher);
  504.                         } else {
  505.                             /** @phpstan-ignore-next-line  */
  506.                             $cart->setCheckoutData(IncertPriceModificator::CHECKOUT_DATA_NAMEfalse);
  507.                         }
  508.                         $cart->save();
  509.                         $cart->modified();
  510.                         $infos[] = 'checkout.payment.removed-voucher';
  511.                         $incertVouchers $currentIncertVoucher;
  512.                     }
  513.                 } elseif ($voucherAction == 'add') {
  514.                     try {
  515.                         $voucher $incertService->getIncertVoucherData($voucherId);
  516.                         if (!$voucher->isValid()) {
  517.                             $errors[] = $incertService->getVoucherStatusMessage($voucher->getVoucherStatus());
  518.                         } else {
  519.                             // Check that voucher isn't already added - Voucher is unique per Order
  520.                             /** @var IncertVoucher $voucherInfo */
  521.                             foreach ($incertVouchers as $voucherInfo) {
  522.                                 if ($voucherInfo->getVoucherCode() === $voucher->getVoucherCode()) {
  523.                                     $errors[] = 'checkout.payment.addvoucher.duplicate';
  524.                                     break;
  525.                                 }
  526.                             }
  527.                             if (empty($errors)) {
  528.                                 $voucherItem = new IncertVoucher();
  529.                                 $voucherItem->setCurrency($voucher->getCurrency());
  530.                                 $voucherItem->setVoucherCode($voucher->getVoucherCode());
  531.                                 $voucherItem->setOriginalRedeemedAmount($voucher->getInitialAmount());
  532.                                 $voucherItem->setRedeemedAmount($voucher->getInitialAmount() - $voucher->getCurrentAmount());
  533.                                 $voucherItem->setAmount($voucher->getCurrentAmount());
  534.                                 $voucherItem->setStatus('valid');
  535.                                 $voucherItem->setPartlyRedeemable($voucher->isPartlyRedeemable());
  536.                                 $incertVouchers[] = $voucherItem;
  537.                                 /** @phpstan-ignore-next-line  */
  538.                                 $cart->setCheckoutData(IncertPriceModificator::CHECKOUT_DATA_NAME$incertVouchers);
  539.                                 $cart->save();
  540.                             }
  541.                         }
  542.                     } catch (\Exception $e) {
  543.                         $errors[] = 'checkout.payment.addvoucher.unavailable';
  544.                     }
  545.                 }
  546.             }
  547.             if ($voucherType == 'discount') {
  548.                 if ($voucherAction == 'remove') {
  549.                     $cart->removeVoucherToken($voucherId);
  550.                     $infos[] = 'checkout.payment.removed-voucher';
  551.                 }
  552.                 if ($voucherAction == 'add') {
  553.                     try {
  554.                         $success $cart->addVoucherToken($voucherId);
  555.                         if (!$success) {
  556.                             $pimcoreErrors[] = 'checkout.payment.addvoucher.could-not-be-added';
  557.                         }
  558.                     } catch (\Exception $e) {
  559.                         $pimcoreErrors[] = 'checkout.payment.error-voucher-code' $e->getCode();
  560.                     }
  561.                 }
  562.             }
  563.         }
  564.         $requestBody json_decode($request->getContent(), true);
  565.         //cashback
  566.         if (isset($requestBody['cashback'])) {
  567.             $cart->setCheckoutData('cashback'$requestBody['cashback']);
  568.             $cart->save();
  569.             $cart->modified();
  570.         }
  571.         //merch mailing
  572.         if (isset($requestBody['certified_mail_merchandise'])) {
  573.             $cart->setCheckoutData('certified_mail_merchandise'$requestBody['certified_mail_merchandise'] ?: null);
  574.             $cart->save();
  575.             $cart->modified();
  576.         }
  577.         $voucherResult $this->renderTemplate('shop/includes/checkout/voucher.html.twig', [
  578.             'vouchers' => $voucherService->getPimcoreVoucherData($cart),
  579.             'type' => 'discount',
  580.             'errors' => $pimcoreErrors,
  581.         ]);
  582.         $html['voucher-result-discount'] = $voucherResult->getContent();
  583.         $voucherResult $this->renderTemplate('shop/includes/checkout/voucher.html.twig', [
  584.             'vouchers' => $voucherService->getIncertVoucherData($incertVouchers$cart),
  585.             'type' => 'incert',
  586.             'errors' => $errors,
  587.             'infos' => $infos,
  588.         ]);
  589.         $html['voucher-result'] = $voucherResult->getContent();
  590.         $footerTopContent $this->renderTemplate('shop/includes/checkout/checkout-cart-footer-top.html.twig', [
  591.             'step' => 'payment',
  592.         ])->getContent();
  593.         $html['checkout-footer-top'] = $footerTopContent;
  594.         $footerBottomContent $this->renderTemplate('shop/includes/checkout/checkout-cart-footer-details.html.twig', [
  595.             'redirectUri' => $this->generateUrl('checkout_payment'),
  596.         ])->getContent();
  597.         $html['checkout-footer-bottom'] = $footerBottomContent;
  598.         /** @var PaymentStep $paymentStep */
  599.         $paymentStep $checkoutManager->getCheckoutStep(PaymentStep::STEP_NAME);
  600.         $paymentData $paymentStep->getData() ?: new PaymentData();
  601.         //commit payment step if value is zero and voucher was used
  602.         if ($cart->getPriceCalculator()->getGrandTotal()->getAmount()->isZero()) {
  603.             $checkoutManager->commitStep($paymentStep$voucherData);
  604.             $paymentData->setNeedsPayment(false);
  605.         } else {
  606.             $paymentData->setNeedsPayment(true);
  607.         }
  608.         $paymentBlockResult $this->renderTemplate('shop/includes/checkout/payment-block.html.twig', [
  609.             'form' => $this->createForm(PaymentType::class, $paymentData)->createView(),
  610.         ]);
  611.         $html['payments'] = $paymentBlockResult->getContent();
  612.         $data = [
  613.             'success' => true,
  614.             'html' => $html,
  615.         ];
  616.         return new JsonResponse($data);
  617.     }
  618.     /**
  619.      * @Route("/configure-cart-modificators", name="checkout_cart_modificators", methods={"POST"})
  620.      *
  621.      * @param Request $request
  622.      * @param Factory $factory
  623.      *
  624.      * @return JsonResponse
  625.      */
  626.     public function configureCartPriceModificators(Request $requestFactory $factory): JsonResponse
  627.     {
  628.         $cart $this->getCart();
  629.         $html '';
  630.         //form cart summary
  631.         if ($request->get('action') == 'remove') {
  632.             $name $request->get('name');
  633.             /** @phpstan-ignore-next-line */
  634.             $cart->setCheckoutData($namefalse);
  635.             $cart->save();
  636.             $cart->modified();
  637.             $checkoutManager $factory->getCheckoutManager($cart);
  638.             $confirmOrderStep $checkoutManager->getCheckoutStep(ConfirmStep::STEP_NAME);
  639.             $form $this->createForm(CheckoutConfirmType::class, $confirmOrderStep->getData());
  640.             $html $this->renderTemplate('shop/includes/cart/cart-summary.html.twig', [
  641.                 'hideForm' => $request->get('hide-form'),
  642.                 'showCheckoutLink' => $request->get('show-checkout-link'),
  643.                 'showPaymentMethods' => $request->get('show-payment-methods'),
  644.                 'form' => $form->createView(),
  645.             ])->getContent();
  646.         }
  647.         $data = [
  648.             'success' => true,
  649.             'html' => $html,
  650.         ];
  651.         return new JsonResponse($data);
  652.     }
  653.     protected function preselectCashbackPriceModificator(CartInterface $cart): void
  654.     {
  655.         //Preselection for Optional price modificators such as Cashback
  656.         $data $cart->getCheckoutData('cashback');
  657.         $currentData $data;
  658.         foreach ($cart->getPriceCalculator()->getModificators() as $modificator) {
  659.             if ($modificator instanceof CashbackInsurance) {
  660.                 if ($data === null && $modificator->isOptionPreSelected()) {
  661.                     $data true;
  662.                 }
  663.             }
  664.         }
  665.         if ($currentData != $data) {
  666.             $cart->setCheckoutData('cashback'$data);
  667.             $cart->save();
  668.         }
  669.     }
  670.     /**
  671.      * @Route("/register-modal", name="checkout_register_modal")
  672.      *
  673.      * @param Request $request
  674.      *
  675.      * @return JsonResponse
  676.      */
  677.     public function registerModal(Request $request): JsonResponse
  678.     {
  679.         $template $this->renderTemplate('shop/includes/checkout/register-modal.html.twig');
  680.         $data = [
  681.             'success' => true,
  682.             'html' => $template->getContent(),
  683.         ];
  684.         return new JsonResponse($data);
  685.     }
  686. }