Current File : //var/www/vinorea/modules/ps_checkout/controllers/front/payment.php
<?php
/**
 * Copyright since 2007 PrestaShop SA and Contributors
 * PrestaShop is an International Registered Trademark & Property of PrestaShop SA
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Academic Free License version 3.0
 * that is bundled with this package in the file LICENSE.md.
 * It is also available through the world-wide-web at this URL:
 * https://opensource.org/licenses/AFL-3.0
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@prestashop.com so we can send you a copy immediately.
 *
 * @author    PrestaShop SA and Contributors <contact@prestashop.com>
 * @copyright Since 2007 PrestaShop SA and Contributors
 * @license   https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
 */

use PrestaShop\Module\PrestashopCheckout\CommandBus\CommandBusInterface;
use PrestaShop\Module\PrestashopCheckout\Controller\AbstractFrontController;
use PrestaShop\Module\PrestashopCheckout\Exception\PsCheckoutException;
use PrestaShop\Module\PrestashopCheckout\Order\Command\CreateOrderCommand;
use PrestaShop\Module\PrestashopCheckout\PayPal\Card3DSecure;
use PrestaShop\Module\PrestashopCheckout\PayPal\Order\Command\CapturePayPalOrderCommand;
use PrestaShop\Module\PrestashopCheckout\PayPal\Order\Entity\PayPalOrder;
use PrestaShop\Module\PrestashopCheckout\PayPal\Order\Exception\PayPalOrderException;
use PrestaShop\Module\PrestashopCheckout\PayPal\Order\Query\GetPayPalOrderForCheckoutCompletedQuery;
use PrestaShop\Module\PrestashopCheckout\PayPal\Order\Query\GetPayPalOrderForCheckoutCompletedQueryResult;
use PrestaShop\Module\PrestashopCheckout\PayPal\Order\ValueObject\PayPalOrderId;
use PrestaShop\Module\PrestashopCheckout\PayPal\PayPalConfiguration;
use PrestaShop\Module\PrestashopCheckout\Repository\PaymentTokenRepository;
use PrestaShop\Module\PrestashopCheckout\Repository\PayPalOrderRepository;

class Ps_CheckoutPaymentModuleFrontController extends AbstractFrontController
{
    public $ssl = true;

    public $controller_type = 'front';

    public $display_footer = false;

    public $display_header = true;

    private $orderPageUrl;

    /**
     * @var PayPalOrderId
     */
    private $paypalOrderId;

    /**
     * @var CommandBusInterface
     */
    private $commandBus;

    public function checkAccess()
    {
        return $this->context->customer && $this->context->cart;
    }

    public function initContent()
    {
        $this->orderPageUrl = $this->context->link->getPageLink('order');
        parent::initContent();
        $this->setTemplate('module:ps_checkout/views/templates/front/payment.tpl');
        $this->context->smarty->assign('css_url', $this->module->getPathUri() . 'views/css/payment.css');
        $this->context->smarty->assign('order_url', $this->orderPageUrl);
    }

    public function setMedia()
    {
        $this->registerStylesheet('ps_checkout_payment', '/modules/ps_checkout/views/css/payment.css');
        parent::setMedia();
    }

    public function postProcess()
    {
        $orderId = Tools::getValue('orderID');

        if (!$orderId) {
            $this->redirectToOrderPage();
        }

        try {
            $this->paypalOrderId = new PayPalOrderId($orderId);

            $this->commandBus = $this->module->getService('ps_checkout.bus.command');

            /** @var PayPalConfiguration $payPalConfiguration */
            $payPalConfiguration = $this->module->getService(PayPalConfiguration::class);
            /** @var PayPalOrderRepository $payPalOrderRepository */
            $payPalOrderRepository = $this->module->getService(PayPalOrderRepository::class);
            /** @var Psr\SimpleCache\CacheInterface $payPalOrderCache */
            $payPalOrderCache = $this->module->getService('ps_checkout.cache.paypal.order');

            $payPalOrder = $payPalOrderRepository->getPayPalOrderById($this->paypalOrderId);

            $orders = new PrestaShopCollection(Order::class);
            $orders->where('id_cart', '=', $payPalOrder->getIdCart());

            if ($orders->count()) {
                if ($this->context->customer->isLogged()) {
                    $this->redirectToOrderHistoryPage();
                } else {
                    $payPalOrderQueryResult = $this->getPayPalOrder($orderId);
                    $payPalOrderFromCache = $payPalOrderQueryResult->getPayPalOrder();

                    $this->redirectToOrderConfirmationPage($payPalOrder->getIdCart(), $payPalOrderFromCache['purchase_units'][0]['payments']['captures'][0]['id'], $payPalOrderFromCache['status']);
                }
            }

            if ($payPalOrder->getIdCart() !== $this->context->cart->id) {
                $this->redirectToOrderPage();
            }

            $payPalOrderQueryResult = $this->getPayPalOrder($orderId);
            $payPalOrderFromCache = $payPalOrderQueryResult->getPayPalOrder();

            if ($payPalOrderFromCache['status'] === 'COMPLETED') {
                $this->createOrder($payPalOrderFromCache, $payPalOrder);
            }

            if ($payPalOrderFromCache['status'] === 'PAYER_ACTION_REQUIRED') {
                $this->redirectTo3DSVerification($payPalOrderFromCache);
            }

            // WHEN 3DS fails
            if ($payPalOrderFromCache['status'] === 'CREATED') {
                $card3DSecure = new Card3DSecure();
                switch ($card3DSecure->continueWithAuthorization($payPalOrderFromCache)) {
                    case Card3DSecure::RETRY:
                        $this->redirectTo3DSVerification($payPalOrderFromCache);
                        break;
                    case Card3DSecure::PROCEED:
                        $this->commandBus->handle(new CapturePayPalOrderCommand($orderId, array_keys($payPalOrderFromCache['payment_source'])[0]));
                        $payPalOrderFromCache = $payPalOrderCache->get($orderId);
                        $this->createOrder($payPalOrderFromCache, $payPalOrder);
                        break;
                    case Card3DSecure::NO_DECISION:
                        if ($payPalConfiguration->getHostedFieldsContingencies() === 'SCA_WHEN_REQUIRED') {
                            $this->commandBus->handle(new CapturePayPalOrderCommand($orderId, array_keys($payPalOrderFromCache['payment_source'])[0]));
                            $payPalOrderFromCache = $payPalOrderCache->get($orderId);
                            $this->createOrder($payPalOrderFromCache, $payPalOrder);
                        }
                        // no break
                    default:
                        break;
                }
            }

            if ($payPalOrderFromCache['status'] === 'APPROVED') {
                $this->commandBus->handle(new CapturePayPalOrderCommand($orderId, array_keys($payPalOrderFromCache['payment_source'])[0]));
                $payPalOrderFromCache = $payPalOrderCache->get($orderId);
                $this->createOrder($payPalOrderFromCache, $payPalOrder);
            }
        } catch (Exception $exception) {
            $this->context->smarty->assign('error', $exception->getMessage());
        }
    }

    /**
     * @param array $payPalOrderFromCache
     * @param PayPalOrder$payPalOrder
     *
     * @return void
     *
     * @throws PrestaShopException
     * @throws PsCheckoutException
     * @throws PayPalOrderException
     */
    private function createOrder($payPalOrderFromCache, $payPalOrder)
    {
        $capture = isset($payPalOrderFromCache['purchase_units'][0]['payments']['captures'][0]) ? $payPalOrderFromCache['purchase_units'][0]['payments']['captures'][0] : null;
        $this->commandBus->handle(new CreateOrderCommand($payPalOrder->getId()->getValue(), $capture));
        if ($payPalOrder->getPaymentTokenId() && $payPalOrder->checkCustomerIntent(PayPalOrder::CUSTOMER_INTENT_FAVORITE)) {
            /** @var PaymentTokenRepository $paymentTokenRepository */
            $paymentTokenRepository = $this->module->getService(PaymentTokenRepository::class);
            $paymentTokenRepository->setTokenFavorite($payPalOrder->getPaymentTokenId());
        }
        $this->redirectToOrderConfirmationPage($payPalOrder->getIdCart(), $capture ? $capture['id'] : null, $payPalOrderFromCache['status']);
    }

    /**
     * @param array $order
     *
     * @return void
     */
    private function redirectTo3DSVerification($order)
    {
        $payerActionLinks = array_filter($order['links'], function ($link) {
            return $link['rel'] === 'payer-action';
        });
        if (!empty($payerActionLinks)) {
            Tools::redirect(reset($payerActionLinks)['href'] . '&redirect_uri=' . urlencode($this->context->link->getModuleLink('ps_checkout', 'payment', ['orderID' => $this->paypalOrderId->getValue()])));
        }
    }

    private function redirectToOrderPage()
    {
        Tools::redirect($this->orderPageUrl);
    }

    /**
     * @param int $cartId
     * @param string $captureId
     * @param string $payPalOrderStatus
     *
     * @return void
     *
     * @throws PrestaShopException
     */
    private function redirectToOrderConfirmationPage($cartId, $captureId, $payPalOrderStatus)
    {
        $orders = new PrestaShopCollection(Order::class);
        $orders->where('id_cart', '=', $cartId);

        if (!$orders->count()) {
            return;
        }

        /** @var Order $order */
        $order = $orders->getFirst();

        $cart = new Cart($cartId);

        if ($_SERVER['REQUEST_METHOD'] === 'GET') {
            Tools::redirect($this->context->link->getPageLink(
                'order-confirmation',
                true,
                (int) $order->id_lang,
                [
                    'paypal_status' => $payPalOrderStatus,
                    'paypal_order' => $this->paypalOrderId->getValue(),
                    'paypal_transaction' => $captureId,
                    'id_cart' => $cartId,
                    'id_module' => (int) $this->module->id,
                    'id_order' => (int) $order->id,
                    'key' => $cart->secure_key,
                ]
            ));
        }
    }

    /**
     * @param string $orderId
     *
     * @return GetPayPalOrderForCheckoutCompletedQueryResult
     *
     * @throws PayPalOrderException
     */
    private function getPayPalOrder($orderId)
    {
        $payPalOrderQuery = new GetPayPalOrderForCheckoutCompletedQuery($orderId);

        return $this->commandBus->handle($payPalOrderQuery);
    }

    private function redirectToOrderHistoryPage()
    {
        Tools::redirect($this->context->link->getPageLink('history'));
    }
}