custom/plugins/theme/src/Subscriber/CheckoutSubscriber.php line 98

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace DonCarneTheme\Subscriber;
  3. use PayonePayment\PaymentHandler\PayoneCreditCardPaymentHandler;
  4. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  5. use DonCarneTheme\Service\GraphQLClient;
  6. use Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent;
  7. use DateTime;
  8. use DonCarneTheme\Service\ExpectedDeliveryTimes;
  9. use PayonePayment\PaymentHandler\PayonePaypalPaymentHandler;
  10. use Shopware\Core\Checkout\Order\Aggregate\OrderDelivery\OrderDeliveryEntity;
  11. use Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionDefinition;
  12. use Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionEntity;
  13. use Shopware\Core\Checkout\Order\OrderStates;
  14. use Shopware\Core\Checkout\Payment\PaymentMethodEntity;
  15. use Shopware\Core\Checkout\Shipping\ShippingMethodEntity;
  16. use Shopware\Core\Framework\Struct\ArrayStruct;
  17. use Shopware\Core\System\StateMachine\StateMachineRegistry;
  18. use Shopware\Core\System\StateMachine\Transition;
  19. use Shopware\Storefront\Page\Checkout\Finish\CheckoutFinishPageLoadedEvent;
  20. use Shopware\Core\Checkout\Cart\Event\CheckoutOrderPlacedEvent;
  21. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
  22. use Symfony\Component\HttpFoundation\RequestStack;
  23. class CheckoutSubscriber implements EventSubscriberInterface
  24. {
  25.     private ExpectedDeliveryTimes $deliveryService;
  26.     private StateMachineRegistry $stateMachineRegistry;
  27.     private EntityRepositoryInterface $orderRepository;
  28.     private RequestStack $requestStack;
  29.     public function __construct(
  30.         ExpectedDeliveryTimes $deliveryService,
  31.         StateMachineRegistry $stateMachineRegistry,
  32.         EntityRepositoryInterface $orderRepository,
  33.         RequestStack $requestStack
  34.     ) {
  35.         $this->deliveryService $deliveryService;
  36.         $this->stateMachineRegistry $stateMachineRegistry;
  37.         $this->orderRepository $orderRepository;
  38.         $this->requestStack $requestStack;
  39.     }
  40.     public static function getSubscribedEvents(): array
  41.     {
  42.         return [
  43.             CheckoutConfirmPageLoadedEvent::class => 'onConfirmPage',
  44.             CheckoutFinishPageLoadedEvent::class => 'onFinishPage',
  45.             CheckoutOrderPlacedEvent::class => 'onOrderPlaced'
  46.         ];
  47.     }
  48.     public function onFinishPage(CheckoutFinishPageLoadedEvent $event)
  49.     {
  50.         /** @var OrderDeliveryEntity|null $delivery */
  51.         $delivery $event->getPage()->getOrder()->getDeliveries()->first();
  52.         $items $event->getPage()->getOrder()->getLineItems();
  53.         if (empty($delivery)) {
  54.             return;
  55.         }
  56.         $customFields $delivery->getShippingMethod()->getCustomFields() ?? [];
  57.         $graphQLId $customFields['custom_shipping_method_graphql_id'] ?? '';
  58.         $countryIso $delivery->getShippingOrderAddress()->getCountry()->getIso();
  59.         $zipcode $delivery->getShippingOrderAddress()->getZipcode();
  60.         $deliveryTimes $this->deliveryService->getDeliveryTimes($countryIso$zipcode$items);
  61.         if ($deliveryTimes && array_key_exists($graphQLId$deliveryTimes)) {
  62.             $event->getPage()->addExtension('delivery_time', new ArrayStruct($deliveryTimes[$graphQLId]));
  63.         }
  64.         /*
  65.          * Change the Payment State for PayOne Paypal from "open" to "in_progress"
  66.          * @see https://developer.shopware.com/docs/guides/plugins/plugins/checkout/order/using-the-state-machine.html#order-transaction-state
  67.          */
  68.         /** @var OrderTransactionEntity $transaction */
  69.         $transaction $event->getPage()->getOrder()->getTransactions()->last();
  70.         if (
  71.             $transaction &&
  72.             (
  73.                 $transaction->getPaymentMethod()->getHandlerIdentifier() === PayonePaypalPaymentHandler::class ||
  74.                 $transaction->getPaymentMethod()->getHandlerIdentifier() === PayoneCreditCardPaymentHandler::class ||
  75.                 $transaction->getPaymentMethod()->getId() === 'fcee8e87a0834a32895d15c7849eadeb'
  76.             ) &&
  77.             $transaction->getStateMachineState()->getTechnicalName() === OrderStates::STATE_OPEN
  78.         ) {
  79.             $this->stateMachineRegistry->transition(new Transition(
  80.                 OrderTransactionDefinition::ENTITY_NAME,
  81.                 $transaction->getId(),
  82.                 'do_pay'// will result in 'in_progress' state
  83.                 'stateId'
  84.             ), $event->getContext());
  85.         }
  86.     }
  87.     public function onConfirmPage(CheckoutConfirmPageLoadedEvent $event)
  88.     {
  89.         $customer $event->getSalesChannelContext()->getCustomer();
  90.         $items $event->getPage()->getCart()->getLineItems();
  91.         $countryIso $customer->getDefaultShippingAddress()->getCountry()->getIso();
  92.         $zipcode $customer->getDefaultShippingAddress()->getZipCode();
  93.         $deliveryTimes $this->deliveryService->getDeliveryTimes($countryIso$zipcode$items);
  94.         // Validate and clean up saved delivery dates in session
  95.         $this->validateAndCleanupSavedDates($deliveryTimes);
  96.         // Sort methods to avoid selected method being first
  97.         $shippingMethods $event->getPage()->getShippingMethods();
  98.         $paymentMethods $event->getPage()->getPaymentMethods();
  99.         $shippingMethods->sort(
  100.             static fn(ShippingMethodEntity $aShippingMethodEntity $b) => $a->getPosition() >= $b->getPosition()
  101.         );
  102.         $paymentMethods->sort(
  103.             static fn(PaymentMethodEntity $aPaymentMethodEntity $b) => $a->getPosition() >= $b->getPosition()
  104.         );
  105.         $event->getPage()->getShippingMethods()->setExtensions([
  106.             'delivery_times' => $deliveryTimes
  107.         ]);
  108.     }
  109.     public function onOrderPlaced(CheckoutOrderPlacedEvent $event) {
  110.         // Selected shipping method has a GraphQL id?
  111.         $delivery $event->getOrder()->getDeliveries()->first();
  112.         $items $event->getOrder()->getLineItems();
  113.         $selectedShippingMethodCustomFields $delivery->getShippingMethod()->getCustomFields();
  114.         // If so, insert the correct delivery time
  115.         if ($selectedShippingMethodCustomFields && $selectedShippingMethodCustomFields['custom_shipping_method_graphql_id']) {
  116.             // Get the infos about the customer's shipping address
  117.             $shippingAddress $delivery->getShippingOrderAddress();
  118.             $deliveryTimes $this->deliveryService->getDeliveryTimes($shippingAddress->getCountry()->getIso(), $shippingAddress->getZipcode(), $items);
  119.             if ($deliveryTimes) {
  120.                 foreach ($deliveryTimes as $key => $deliveryTime) {
  121.                     if ($selectedShippingMethodCustomFields['custom_shipping_method_graphql_id'] == $key) {
  122.                         $this->orderRepository->upsert([
  123.                             [
  124.                                 'id' => $event->getOrderId(),
  125.                                 'customFields' => [
  126.                                     'expected_delivery_date' => isset($deliveryTime['date']['fullDate']) ? ($deliveryTime['date']['fullDate'])->format('Y-m-d') : null
  127.                                 ]
  128.                             ]
  129.                         ], $event->getContext());
  130.                     }
  131.                 }
  132.             }
  133.         }
  134.     }
  135.     /**
  136.      * Validates saved delivery dates against current GraphQL data and removes invalid ones
  137.      */
  138.     private function validateAndCleanupSavedDates(?array $currentDeliveryTimes): void
  139.     {
  140.         $request $this->requestStack->getCurrentRequest();
  141.         if (!$request) {
  142.             return;
  143.         }
  144.         $session $request->getSession();
  145.         $savedDeliveryDates $session->get('selected_delivery_dates', []);
  146.         if (empty($savedDeliveryDates) || !$currentDeliveryTimes) {
  147.             return;
  148.         }
  149.         $validatedDates = [];
  150.         foreach ($savedDeliveryDates as $shippingMethodId => $savedDate) {
  151.             if ($this->isDeliveryDateValid($savedDate$currentDeliveryTimes$shippingMethodId)) {
  152.                 $validatedDates[$shippingMethodId] = $savedDate;
  153.             }
  154.         }
  155.         // Update session with only valid dates
  156.         if (count($validatedDates) !== count($savedDeliveryDates)) {
  157.             $session->set('selected_delivery_dates'$validatedDates);
  158.         }
  159.     }
  160.     /**
  161.      * Checks if a saved delivery date is still available in current delivery times
  162.      */
  163.     private function isDeliveryDateValid(array $savedDate, array $currentDeliveryTimesstring|int $shippingMethodId): bool
  164.     {
  165.         if (!isset($currentDeliveryTimes[$shippingMethodId])) {
  166.             return false;
  167.         }
  168.         $allDates $currentDeliveryTimes[$shippingMethodId]['allDates'] ?? [];
  169.         if (empty($allDates)) {
  170.             return false;
  171.         }
  172.         $savedDateStr $savedDate['date'] ?? '';
  173.         $savedTimeFrom $savedDate['timeFrom'] ?? '';
  174.         $savedTimeUntil $savedDate['timeUntil'] ?? '';
  175.         foreach ($allDates as $availableDate) {
  176.             $availableDateStr $availableDate['date']['fullDate']->format('Y-m-d');
  177.             $availableTimeFrom $availableDate['time']['from'] ?? '';
  178.             $availableTimeUntil $availableDate['time']['until'] ?? '';
  179.             if ($availableDateStr === $savedDateStr &&
  180.                 $availableTimeFrom === $savedTimeFrom &&
  181.                 $availableTimeUntil === $savedTimeUntil) {
  182.                 return true;
  183.             }
  184.         }
  185.         return false;
  186.     }
  187. }