- <?php
- /**
-  * Pimcore
-  *
-  * This source file is available under two different licenses:
-  * - GNU General Public License version 3 (GPLv3)
-  * - Pimcore Enterprise License (PEL)
-  * Full copyright and license information is available in
-  * LICENSE.md which is distributed with this source code.
-  *
-  * @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
-  * @license    http://www.pimcore.org/license     GPLv3 and PEL
-  */
- namespace Pimcore\Bundle\EcommerceFrameworkBundle\CheckoutManager;
- use Pimcore\Bundle\EcommerceFrameworkBundle\CartManager\CartInterface;
- use Pimcore\Bundle\EcommerceFrameworkBundle\EnvironmentInterface;
- use Pimcore\Bundle\EcommerceFrameworkBundle\Exception\UnsupportedException;
- use Pimcore\Bundle\EcommerceFrameworkBundle\Model\AbstractOrder;
- use Pimcore\Bundle\EcommerceFrameworkBundle\OrderManager\Order\Agent;
- use Pimcore\Bundle\EcommerceFrameworkBundle\OrderManager\OrderManager;
- use Pimcore\Bundle\EcommerceFrameworkBundle\OrderManager\OrderManagerLocatorInterface;
- use Pimcore\Bundle\EcommerceFrameworkBundle\PaymentManager\Payment\PaymentInterface;
- use Pimcore\Bundle\EcommerceFrameworkBundle\PaymentManager\Payment\QPay;
- use Pimcore\Bundle\EcommerceFrameworkBundle\PaymentManager\StatusInterface;
- use Pimcore\Bundle\EcommerceFrameworkBundle\PriceSystem\Price;
- use Pimcore\Bundle\EcommerceFrameworkBundle\Type\Decimal;
- use Pimcore\Event\Ecommerce\CheckoutManagerEvents;
- use Pimcore\Event\Model\Ecommerce\CheckoutManagerStepsEvent;
- use Pimcore\Model\DataObject\Fieldcollection\Data\PaymentInfo;
- use Symfony\Component\EventDispatcher\EventDispatcherInterface;
- class CheckoutManager implements CheckoutManagerInterface
- {
-     /**
-      * Constants for custom environment item names for persisting state of checkout
-      * always concatenated with current cart id
-      */
-     const CURRENT_STEP = 'checkout_current_step';
-     const FINISHED = 'checkout_finished';
-     /**
-      * @var CartInterface
-      */
-     protected $cart;
-     /**
-      * @var EnvironmentInterface
-      */
-     protected $environment;
-     /**
-      * @var OrderManagerLocatorInterface
-      */
-     protected $orderManagers;
-     /**
-      * @var CommitOrderProcessorLocatorInterface
-      */
-     protected $commitOrderProcessors;
-     /**
-      * Payment Provider
-      *
-      * @var PaymentInterface
-      */
-     protected $payment;
-     /**
-      * Needed for effective access to one specific checkout step
-      *
-      * @var CheckoutStepInterface[]
-      */
-     protected $checkoutSteps = [];
-     /**
-      * Needed for preserving order of checkout steps
-      *
-      * @var CheckoutStepInterface[]
-      */
-     protected $checkoutStepOrder = [];
-     /**
-      * @var CheckoutStepInterface
-      */
-     protected $currentStep;
-     /**
-      * @var bool
-      */
-     protected $finished = false;
-     /**
-      * @var bool
-      */
-     protected $paid = true;
-     /**
-      * @var EventDispatcherInterface
-      */
-     protected $eventDispatcher;
-     /**
-      * CheckoutManager constructor.
-      *
-      * @param CartInterface $cart
-      * @param EnvironmentInterface $environment
-      * @param OrderManagerLocatorInterface $orderManagers
-      * @param CommitOrderProcessorLocatorInterface $commitOrderProcessors
-      * @param array $checkoutSteps
-      * @param EventDispatcherInterface $eventDispatcher
-      * @param PaymentInterface|null $paymentProvider
-      */
-     public function __construct(
-         CartInterface $cart,
-         EnvironmentInterface $environment,
-         OrderManagerLocatorInterface $orderManagers,
-         CommitOrderProcessorLocatorInterface $commitOrderProcessors,
-         array $checkoutSteps,
-         EventDispatcherInterface $eventDispatcher,
-         PaymentInterface $paymentProvider = null
-     ) {
-         @trigger_error(
-             'Class ' . self::class . ' is deprecated since version 6.1.0 and will be removed in 7.0.0. ' .
-             ' Use ' . \Pimcore\Bundle\EcommerceFrameworkBundle\CheckoutManager\V7\CheckoutManager::class . ' class instead.',
-             E_USER_DEPRECATED
-         );
-         $this->cart = $cart;
-         $this->environment = $environment;
-         $this->orderManagers = $orderManagers;
-         $this->commitOrderProcessors = $commitOrderProcessors;
-         $this->payment = $paymentProvider;
-         $this->eventDispatcher = $eventDispatcher;
-         $this->setCheckoutSteps($checkoutSteps);
-     }
-     /**
-      * @param CheckoutStepInterface[] $checkoutSteps
-      */
-     protected function setCheckoutSteps(array $checkoutSteps)
-     {
-         if (empty($checkoutSteps)) {
-             return;
-         }
-         foreach ($checkoutSteps as $checkoutStep) {
-             $this->addCheckoutStep($checkoutStep);
-         }
-         $this->initializeStepState();
-     }
-     protected function addCheckoutStep(CheckoutStepInterface $checkoutStep)
-     {
-         $this->checkoutStepOrder[] = $checkoutStep;
-         $this->checkoutSteps[$checkoutStep->getName()] = $checkoutStep;
-     }
-     protected function initializeStepState()
-     {
-         // getting state information for checkout from custom environment items
-         $this->finished = (bool) ($this->environment->getCustomItem(self::FINISHED . '_' . $this->cart->getId(), false));
-         if ($currentStepItem = $this->environment->getCustomItem(self::CURRENT_STEP . '_' . $this->cart->getId())) {
-             if (!isset($this->checkoutSteps[$currentStepItem])) {
-                 throw new \RuntimeException(sprintf(
-                     'Environment defines current step as "%s", but step "%s" does not exist',
-                     $currentStepItem,
-                     $currentStepItem
-                 ));
-             }
-             $this->currentStep = $this->checkoutSteps[$currentStepItem];
-         }
-         // if no step is set and cart is not finished -> set current step to first step of checkout
-         if (null === $this->currentStep && !$this->isFinished()) {
-             $this->currentStep = $this->checkoutStepOrder[0];
-         }
-         $event = new CheckoutManagerStepsEvent($this, $this->currentStep);
-         $this->eventDispatcher->dispatch(CheckoutManagerEvents::INITIALIZE_STEP_STATE, $event);
-         $this->currentStep = $event->getCurrentStep();
-     }
-     /**
-      * @inheritdoc
-      */
-     public function hasActivePayment()
-     {
-         $orderManager = $this->orderManagers->getOrderManager();
-         $order = $orderManager->getOrderFromCart($this->cart);
-         if ($order) {
-             $paymentInfo = $orderManager->createOrderAgent($order)->getCurrentPendingPaymentInfo();
-             return !empty($paymentInfo);
-         }
-         return false;
-     }
-     /**
-      * @return AbstractOrder
-      *
-      * @throws UnsupportedException
-      */
-     protected function checkIfPaymentIsPossible()
-     {
-         if (!$this->isFinished()) {
-             throw new UnsupportedException('Checkout is not finished yet.');
-         }
-         if (!$this->payment) {
-             throw new UnsupportedException('Payment is not activated');
-         }
-         // create order
-         $orderManager = $this->orderManagers->getOrderManager();
-         $order = $orderManager->getOrCreateOrderFromCart($this->cart);
-         if ($order->getOrderState() === AbstractOrder::ORDER_STATE_COMMITTED) {
-             throw new UnsupportedException('Order is already committed');
-         }
-         return $order;
-     }
-     /**
-      * @inheritdoc
-      */
-     public function initOrderPayment()
-     {
-         $order = $this->checkIfPaymentIsPossible();
-         $orderManager = $this->orderManagers->getOrderManager();
-         $orderAgent = $orderManager->createOrderAgent($order);
-         $paymentInfo = $orderAgent->initPayment();
-         return $paymentInfo;
-     }
-     /**
-      * @inheritdoc
-      */
-     public function startOrderPayment()
-     {
-         $order = $this->checkIfPaymentIsPossible();
-         $orderManager = $this->orderManagers->getOrderManager();
-         $orderAgent = $orderManager->createOrderAgent($order);
-         $paymentInfo = $orderAgent->startPayment();
-         // always set order state to payment pending when calling start payment
-         if ($order->getOrderState() != $order::ORDER_STATE_PAYMENT_PENDING) {
-             $order->setOrderState($order::ORDER_STATE_PAYMENT_PENDING);
-             $order->save(['versionNote' => 'CheckoutManager::startOrderPayment - set order state to ' . $order::ORDER_STATE_PAYMENT_PENDING . '.']);
-         }
-         return $paymentInfo;
-     }
-     /**
-      * @inheritdoc
-      */
-     public function cancelStartedOrderPayment()
-     {
-         $orderManager = $this->orderManagers->getOrderManager();
-         $order = $orderManager->getOrderFromCart($this->cart);
-         if ($order) {
-             $orderAgent = $orderManager->createOrderAgent($order);
-             return $orderAgent->cancelStartedOrderPayment();
-         }
-     }
-     /**
-      * @inheritdoc
-      */
-     public function getOrder()
-     {
-         return $this->orderManagers->getOrderManager()->getOrCreateOrderFromCart($this->cart);
-     }
-     /**
-      * Updates and cleans up environment after order is committed
-      *
-      * @param AbstractOrder $order
-      */
-     protected function updateEnvironmentAfterOrderCommit(?AbstractOrder $order)
-     {
-         $this->validateCheckoutSteps();
-         if (empty($order) || empty($order->getOrderState())) {
-             // if payment not successful -> set current checkout step to last step and checkout to not finished
-             // last step must be committed again in order to restart payment or e.g. commit without payment?
-             $this->currentStep = $this->checkoutStepOrder[count($this->checkoutStepOrder) - 1];
-             $this->environment->setCustomItem(self::CURRENT_STEP . '_' . $this->cart->getId(), $this->currentStep->getName());
-         } else {
-             $this->cart->delete();
-             $this->environment->removeCustomItem(self::CURRENT_STEP . '_' . $this->cart->getId());
-         }
-         $this->environment->save();
-     }
-     /**
-      * @inheritdoc
-      */
-     public function handlePaymentResponseAndCommitOrderPayment($paymentResponseParams)
-     {
-         $this->validateCheckoutSteps();
-         $commitOrderProcessor = $this->commitOrderProcessors->getCommitOrderProcessor();
-         // check if order is already committed and payment information with same internal payment id has same state
-         // if so, do nothing and return order
-         if ($committedOrder = $commitOrderProcessor->committedOrderWithSamePaymentExists($paymentResponseParams, $this->getPayment())) {
-             return $committedOrder;
-         }
-         if (!$this->payment) {
-             throw new UnsupportedException('Payment is not activated');
-         }
-         // delegate commit order to commit order processor
-         $order = null;
-         try {
-             $order = $commitOrderProcessor->handlePaymentResponseAndCommitOrderPayment($paymentResponseParams, $this->getPayment());
-         } catch (\Exception $e) {
-             throw $e;
-         } finally {
-             $this->updateEnvironmentAfterOrderCommit($order);
-         }
-         return $order;
-     }
-     /**
-      * Verifies if the payment provider is supported for recurring payment
-      *
-      * @param PaymentInterface $provider
-      * @param AbstractOrder $sourceOrder
-      *
-      * @throws \Exception
-      */
-     protected function verifyRecurringPayment(PaymentInterface $provider, AbstractOrder $sourceOrder, string $customerId)
-     {
-         /* @var OrderManager $orderManager */
-         $orderManager = $this->orderManagers->getOrderManager();
-         if (!$provider->isRecurringPaymentEnabled()) {
-             throw new \Exception("Recurring Payment is not enabled or is not supported by payment provider [{$provider->getName()}].");
-         }
-         if (!$orderManager->isValidOrderForRecurringPayment($sourceOrder, $this->getPayment(), $customerId)) {
-             throw new \Exception('The given source order is not valid for recurring payment.');
-         }
-     }
-     /**
-      * @param AbstractOrder $sourceOrder
-      * @param string $customerId
-      *
-      * @return null|AbstractOrder
-      */
-     public function startAndCommitRecurringOrderPayment(AbstractOrder $sourceOrder, string $customerId)
-     {
-         /* @var PaymentInfo $targetPaymentInfo */
-         $targetPaymentInfo = $this->startOrderPayment();
-         /* @var OrderManager $orderManager */
-         $orderManager = $this->orderManagers->getOrderManager();
-         /* @var Agent $sourceOrderAgent */
-         $sourceOrderAgent = $orderManager->createOrderAgent($sourceOrder);
-         /* @var QPay $paymentProvider */
-         $paymentProvider = $sourceOrderAgent->getPaymentProvider();
-         $this->verifyRecurringPayment($paymentProvider, $sourceOrder, $customerId);
-         $targetOrder = $orderManager->getOrderFromCart($this->getCart());
-         /* @var Agent $targetOrderAgent */
-         $targetOrderAgent = $orderManager->createOrderAgent($targetOrder);
-         $targetOrderAgent->setPaymentProvider($paymentProvider, $sourceOrder);
-         $price = new Price(
-             Decimal::create($targetOrder->getTotalPrice(), 2),
-             $sourceOrderAgent->getCurrency()
-         );
-         // execute recurPayment operation
-         $paymentStatus = $paymentProvider->executeDebit(
-             $price,
-             $targetPaymentInfo->getInternalPaymentId()
-         );
-         $targetOrderAgent->updatePayment($paymentStatus);
-         // delegate commit order to commit order processor
-         $targetOrder = $this->commitOrderProcessors->getCommitOrderProcessor()->commitOrderPayment($paymentStatus, $this->getPayment());
-         $this->updateEnvironmentAfterOrderCommit($targetOrder);
-         return $targetOrder;
-     }
-     /**
-      * @inheritdoc
-      */
-     public function commitOrderPayment(StatusInterface $status, AbstractOrder $sourceOrder = null)
-     {
-         $this->validateCheckoutSteps();
-         if (!$this->payment) {
-             throw new UnsupportedException('Payment is not activated');
-         }
-         if ($this->isCommitted()) {
-             throw new UnsupportedException('Order already committed.');
-         }
-         if (!$this->isFinished()) {
-             throw new UnsupportedException('Checkout not finished yet.');
-         }
-         // delegate commit order to commit order processor
-         $order = $this->commitOrderProcessors->getCommitOrderProcessor()->commitOrderPayment($status, $this->getPayment(), $sourceOrder);
-         $this->updateEnvironmentAfterOrderCommit($order);
-         return $order;
-     }
-     /**
-      * @inheritdoc
-      */
-     public function commitOrder()
-     {
-         $this->validateCheckoutSteps();
-         if ($this->isCommitted()) {
-             throw new UnsupportedException('Order already committed.');
-         }
-         if (!$this->isFinished()) {
-             throw new UnsupportedException('Checkout not finished yet.');
-         }
-         // delegate commit order to commit order processor
-         $order = $this->orderManagers->getOrderManager()->getOrCreateOrderFromCart($this->cart);
-         $order = $this->commitOrderProcessors->getCommitOrderProcessor()->commitOrder($order);
-         $this->updateEnvironmentAfterOrderCommit($order);
-         return $order;
-     }
-     /**
-      * @inheritdoc
-      */
-     public function commitStep(CheckoutStepInterface $step, $data)
-     {
-         $this->validateCheckoutSteps();
-         $event = new CheckoutManagerStepsEvent($this, $step, ['data' => $data]);
-         $this->eventDispatcher->dispatch(CheckoutManagerEvents::PRE_COMMIT_STEP, $event);
-         $data = $event->getArgument('data');
-         // get index of current step and index of step to commit
-         $indexCurrentStep = array_search($this->currentStep, $this->checkoutStepOrder);
-         $index = array_search($step, $this->checkoutStepOrder);
-         // if indexCurrentStep is < index -> there are uncommitted previous steps
-         if ($indexCurrentStep < $index) {
-             throw new UnsupportedException('There are uncommitted previous steps.');
-         }
-         // delegate commit to step implementation (for data storage etc.)
-         $result = $step->commit($data);
-         if ($result) {
-             $index++;
-             if (count($this->checkoutStepOrder) > $index) {
-                 //setting checkout manager to next step
-                 $this->currentStep = $this->checkoutStepOrder[$index];
-                 $this->environment->setCustomItem(
-                     self::CURRENT_STEP . '_' . $this->cart->getId(),
-                     $this->currentStep->getName()
-                 );
-                 // checkout NOT finished
-                 $this->finished = false;
-             } else {
-                 // checkout is finished
-                 $this->finished = true;
-             }
-             $this->environment->setCustomItem(
-                 self::FINISHED . '_' . $this->cart->getId(),
-                 $this->finished
-             );
-             $this->cart->save();
-             $this->environment->save();
-         }
-         $event = new CheckoutManagerStepsEvent($this, $step, ['data' => $data]);
-         $this->eventDispatcher->dispatch(CheckoutManagerEvents::POST_COMMIT_STEP, $event);
-         return $result;
-     }
-     /**
-      * @inheritdoc
-      */
-     public function getCart()
-     {
-         return $this->cart;
-     }
-     /**
-      * @inheritdoc
-      */
-     public function getCheckoutStep($stepName)
-     {
-         return $this->checkoutSteps[$stepName] ?? null;
-     }
-     /**
-      * @inheritdoc
-      */
-     public function getCheckoutSteps()
-     {
-         return $this->checkoutStepOrder;
-     }
-     /**
-      * @inheritdoc
-      */
-     public function getCurrentStep()
-     {
-         $this->validateCheckoutSteps();
-         return $this->currentStep;
-     }
-     protected function validateCheckoutSteps()
-     {
-         if (empty($this->checkoutSteps)) {
-             throw new \RuntimeException('Checkout manager does not define any checkout steps');
-         }
-     }
-     /**
-      * @inheritdoc
-      */
-     public function isFinished()
-     {
-         return $this->finished;
-     }
-     /**
-      * @inheritdoc
-      */
-     public function isCommitted()
-     {
-         $order = $this->orderManagers->getOrderManager()->getOrderFromCart($this->cart);
-         return $order && $order->getOrderState() === $order::ORDER_STATE_COMMITTED;
-     }
-     /**
-      * @inheritdoc
-      */
-     public function getPayment()
-     {
-         return $this->payment;
-     }
-     /**
-      * @inheritdoc
-      */
-     public function cleanUpPendingOrders()
-     {
-         $this->commitOrderProcessors->getCommitOrderProcessor()->cleanUpPendingOrders();
-     }
- }
-