Current File : /var/www/prestashop/modules/ps_checkout/src/PayPal/Sdk/PayPalSdkConfigurationBuilder.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
 */

namespace PrestaShop\Module\PrestashopCheckout\PayPal\Sdk;

use Exception;
use PrestaShop\Module\PrestashopCheckout\CommandBus\CommandBusInterface;
use PrestaShop\Module\PrestashopCheckout\Context\PrestaShopContext;
use PrestaShop\Module\PrestashopCheckout\Customer\ValueObject\CustomerId;
use PrestaShop\Module\PrestashopCheckout\Environment\Env;
use PrestaShop\Module\PrestashopCheckout\ExpressCheckout\ExpressCheckoutConfiguration;
use PrestaShop\Module\PrestashopCheckout\FundingSource\FundingSourceConfigurationRepository;
use PrestaShop\Module\PrestashopCheckout\FundingSource\FundingSourceEligibilityConstraint;
use PrestaShop\Module\PrestashopCheckout\PayPal\OAuth\Query\GetPayPalGetUserIdTokenQuery;
use PrestaShop\Module\PrestashopCheckout\PayPal\OAuth\Query\GetPayPalGetUserIdTokenQueryResult;
use PrestaShop\Module\PrestashopCheckout\PayPal\PayPalConfiguration;
use PrestaShop\Module\PrestashopCheckout\PayPal\PayPalPayLaterConfiguration;
use PrestaShop\Module\PrestashopCheckout\ShopContext;
use Psr\Log\LoggerInterface;

/**
 * Build sdk link
 */
class PayPalSdkConfigurationBuilder
{
    /**
     * @var PayPalConfiguration
     */
    private $configuration;

    /**
     * @var PayPalPayLaterConfiguration
     */
    private $payLaterConfiguration;

    /**
     * @var FundingSourceConfigurationRepository
     */
    private $fundingSourceConfigurationRepository;

    /** @var ExpressCheckoutConfiguration */
    private $expressCheckoutConfiguration;

    /** @var ShopContext */
    private $shopContext;

    /** @var array */
    private static $cache = [];
    /**
     * @var CommandBusInterface
     */
    private $commandBus;
    /**
     * @var PrestaShopContext
     */
    private $prestaShopContext;
    /**
     * @var LoggerInterface
     */
    private $logger;
    /**
     * @var Env
     */
    private $env;
    /**
     * @var FundingSourceEligibilityConstraint
     */
    private $fundingSourceEligibilityConstraint;

    /**
     * @param \Ps_checkout $module
     * @param Env $env
     * @param PayPalConfiguration $configuration
     * @param PayPalPayLaterConfiguration $payLaterConfiguration
     * @param FundingSourceConfigurationRepository $fundingSourceConfigurationRepository
     * @param ExpressCheckoutConfiguration $expressCheckoutConfiguration
     * @param ShopContext $shopContext
     * @param PrestaShopContext $prestaShopContext
     * @param LoggerInterface $logger
     * @param FundingSourceEligibilityConstraint $fundingSourceEligibilityConstraint
     */
    public function __construct(
        \Ps_checkout $module,
        Env $env,
        PayPalConfiguration $configuration,
        PayPalPayLaterConfiguration $payLaterConfiguration,
        FundingSourceConfigurationRepository $fundingSourceConfigurationRepository,
        ExpressCheckoutConfiguration $expressCheckoutConfiguration,
        ShopContext $shopContext,
        PrestaShopContext $prestaShopContext,
        LoggerInterface $logger,
        FundingSourceEligibilityConstraint $fundingSourceEligibilityConstraint
    ) {
        $this->configuration = $configuration;
        $this->payLaterConfiguration = $payLaterConfiguration;
        $this->fundingSourceConfigurationRepository = $fundingSourceConfigurationRepository;
        $this->expressCheckoutConfiguration = $expressCheckoutConfiguration;
        $this->shopContext = $shopContext;
        $this->commandBus = $module->getService('ps_checkout.bus.command');
        $this->prestaShopContext = $prestaShopContext;
        $this->logger = $logger;
        $this->env = $env;
        $this->fundingSourceEligibilityConstraint = $fundingSourceEligibilityConstraint;
    }

    /**
     * @return array
     */
    public function buildConfiguration()
    {
        $components = [
            'marks',
            'funding-eligibility',
        ];

        if ($this->shouldIncludeButtonsComponent()) {
            $components[] = 'buttons';
        }

        if ($this->shouldIncludeHostedFieldsComponent()) {
            $components[] = 'card-fields';
        }

        if ($this->shouldIncludeMessagesComponent()) {
            $components[] = 'messages';
        }

        if ($this->shouldIncludeGooglePayComponent()) {
            $components[] = 'googlepay';
        }

        $params = [
            'clientId' => $this->env->getPaypalClientId(),
            'merchantId' => $this->configuration->getMerchantId(),
            'currency' => $this->prestaShopContext->getCurrencyIsoCode(),
            'intent' => strtolower($this->configuration->getIntent()),
            'commit' => 'order' === $this->getPageName() ? 'true' : 'false',
            'vault' => 'false',
            'integrationDate' => $this->configuration->getIntegrationDate(),
            'dataPartnerAttributionId' => $this->shopContext->getBnCode(),
            'dataCspNonce' => $this->configuration->getCSPNonce(),
        ];

        if ($this->configuration->isVaultingEnabled() && $this->prestaShopContext->customerIsLogged() && $this->prestaShopContext->getCustomerId() && 'order' === $this->getPageName()) {
            try {
                /** @var GetPayPalGetUserIdTokenQueryResult $queryResult */
                $queryResult = $this->commandBus->handle(new GetPayPalGetUserIdTokenQuery(new CustomerId($this->prestaShopContext->getCustomerId())));
                $params['dataUserIdToken'] = $queryResult->getUserIdToken();
            } catch (Exception $exception) {
                $this->logger->error('Failed to get PayPal User ID token.', ['exception' => $exception]);
            }
        }

        if ($this->configuration->is3dSecureEnabled()) {
            $params['dataEnable3ds'] = 'true';
        }

        if ('SANDBOX' === $this->configuration->getPaymentMode()) {
//            $params['debug'] = 'true';
            $params['buyerCountry'] = $this->getCountry();
        }

        $fundingSourcesDisabled = $this->getFundingSourcesDisabled();

        if (false === empty($fundingSourcesDisabled)) {
            $params['disableFunding'] = implode(',', $fundingSourcesDisabled);
        }

        $eligibleAlternativePaymentMethods = $this->getEligibleAlternativePaymentMethods();

        if (false === empty($eligibleAlternativePaymentMethods) && $this->shouldIncludeButtonsComponent()) {
            $params['locale'] = $this->getLocale();
            $components[] = 'payment-fields';
        }

        if (isset($params['locale']) && empty($params['locale'])) {
            unset($params['locale']);
        }

        if ($this->isPayLaterEnabled()) {
            $eligibleAlternativePaymentMethods[] = 'paylater';
        }

        if (false === empty($eligibleAlternativePaymentMethods)) {
            $params['enableFunding'] = implode(',', $eligibleAlternativePaymentMethods);
        }

        $params['components'] = implode(',', $components);

        return $params;
    }

    /**
     * @see https://developer.paypal.com/docs/checkout/reference/customize-sdk/#disable-funding
     *
     * @return array
     */
    private function getFundingSourcesDisabled()
    {
        $fundingSourcesDisabled = [];

        $fundingSources = $this->fundingSourceConfigurationRepository->getAll();

        if (empty($fundingSources)) {
            return $fundingSourcesDisabled;
        }

        foreach ($fundingSources as $fundingSource) {
            if (!$fundingSource['active']) {
                $fundingSourcesDisabled[] = $fundingSource['name'];
            }
        }

        return $fundingSourcesDisabled;
    }

    private function getPageName()
    {
        $controller = \Context::getContext()->controller;

        if (empty($controller)) {
            return '';
        }

        if (isset($controller->php_self)) {
            return 'order-opc' === $controller->php_self ? 'order' : $controller->php_self;
        }

        return '';
    }

    private function isPayLaterEnabled()
    {
        $payLaterConfig = $this->fundingSourceConfigurationRepository->get('paylater');

        return $payLaterConfig === null || !empty($payLaterConfig) && (int) $payLaterConfig['active'] === 1;
    }

    /**
     * @return bool
     */
    private function shouldIncludeButtonsComponent()
    {
        if ('cart' === $this->getPageName()
            && (
                $this->expressCheckoutConfiguration->isOrderPageEnabled()
                || $this->expressCheckoutConfiguration->isCheckoutPageEnabled()
                || $this->payLaterConfiguration->isCartPageButtonActive()
            )
        ) {
            return true;
        }

        if ('product' === $this->getPageName()
            && (
                $this->expressCheckoutConfiguration->isProductPageEnabled()
                || $this->payLaterConfiguration->isProductPageButtonActive()
            )
        ) {
            return true;
        }

        return 'order' === $this->getPageName();
    }

    /**
     * @return bool
     */
    private function shouldIncludeHostedFieldsComponent()
    {
        if ('order' !== $this->getPageName() || in_array('card', $this->getFundingSourcesDisabled(), true)) {
            return false;
        }

        return $this->configuration->isHostedFieldsEnabled() && in_array($this->configuration->getCardHostedFieldsStatus(), ['SUBSCRIBED', 'LIMITED'], true);
    }

    /**
     * @return bool
     */
    private function shouldIncludeMessagesComponent()
    {
        if ('index' === $this->getPageName() && $this->payLaterConfiguration->isHomePageBannerActive()) {
            return true;
        }

        if ('category' === $this->getPageName() && $this->payLaterConfiguration->isCategoryPageBannerActive()) {
            return true;
        }

        if ('cart' === $this->getPageName() && ($this->payLaterConfiguration->isOrderPageMessageActive() || $this->payLaterConfiguration->isOrderPageBannerActive())) {
            return true;
        }

        if ('order' === $this->getPageName() && ($this->payLaterConfiguration->isOrderPageMessageActive() || $this->payLaterConfiguration->isOrderPageBannerActive())) {
            return true;
        }

        if ('product' === $this->getPageName() && ($this->payLaterConfiguration->isProductPageMessageActive() || $this->payLaterConfiguration->isProductPageBannerActive())) {
            return true;
        }

        return false;
    }

    /**
     * @return string
     */
    private function getCountry()
    {
        $context = \Context::getContext();
        $code = '';
        $taxAddressType = \Configuration::get('PS_TAX_ADDRESS_TYPE');

        if (\Validate::isLoadedObject($context->country)) {
            $code = strtoupper($context->country->iso_code);
        }

        if (\Validate::isLoadedObject($context->cart)) {
            $taxAddressId = property_exists($context->cart, $taxAddressType) ? $context->cart->{$taxAddressType} : $context->cart->id_address_delivery;
            $address = new \Address($taxAddressId);
            $country = new \Country($address->id_country);

            $code = \Validate::isLoadedObject($country) ? strtoupper($country->iso_code) : $code;
        }

        if ($code === 'UK') {
            $code = 'GB';
        }

        return $code;
    }

    /**
     * @return string
     */
    private function getLanguage()
    {
        $context = \Context::getContext();
        $code = '';

        if (\Validate::isLoadedObject($context->language)) {
            $code = strtoupper($context->language->iso_code);
        }

        return $code;
    }

    /**
     * @return string
     */
    private function getLocale()
    {
        $country = $this->getCountry();
        $language = $this->getLanguage();

        if ('DE' === $country) {
            return 'DE' === $language ? 'de_DE' : 'en_DE';
        }

        if ('US' === $country) {
            return 'en_US';
        }

        if ('GB' === $country) {
            return 'en_GB';
        }

        if ('ES' === $country) {
            return 'ES' === $language ? 'es_ES' : 'en_ES';
        }

        if ('FR' === $country) {
            return 'FR' === $language ? 'fr_FR' : 'en_FR';
        }

        if ('IT' === $country) {
            return 'IT' === $language ? 'it_IT' : 'en_IT';
        }

        if ('NL' === $country) {
            return 'NL' === $language ? 'nl_NL' : 'en_NL';
        }

        if ('PL' === $country) {
            return 'PL' === $language ? 'pl_PL' : 'en_PL';
        }

        if ('PT' === $country) {
            return 'PT' === $language ? 'pt_PT' : 'en_PT';
        }

        if ('AU' === $country) {
            return 'en_AU';
        }

        if ('AT' === $country) {
            return 'DE' === $language ? 'de_AT' : 'en_AT';
        }

        return '';
    }

    private function getEligibleAlternativePaymentMethods()
    {
        $fundingSourcesEnabled = [];

        $fundingSources = $this->fundingSourceConfigurationRepository->getAll();

        if (empty($fundingSources)) {
            return $fundingSourcesEnabled;
        }

        $context = \Context::getContext();
        $country = $this->getCountry();

        foreach ($fundingSources as $fundingSource) {
            if ($fundingSource['active']
                && $fundingSource['name'] === 'bancontact'
                && $country === 'BE'
                && $context->currency->iso_code === 'EUR'
            ) {
                $fundingSourcesEnabled[] = $fundingSource['name'];
            }
            if ($fundingSource['active']
                && $fundingSource['name'] === 'blik'
                && $country === 'PL'
                && $context->currency->iso_code === 'PLN'
            ) {
                $fundingSourcesEnabled[] = $fundingSource['name'];
            }
            if ($fundingSource['active']
                && $fundingSource['name'] === 'eps'
                && $country === 'AT'
                && $context->currency->iso_code === 'EUR'
            ) {
                $fundingSourcesEnabled[] = $fundingSource['name'];
            }
            if ($fundingSource['active']
                && $fundingSource['name'] === 'giropay'
                && $country === 'DE'
                && $context->currency->iso_code === 'EUR'
            ) {
                $fundingSourcesEnabled[] = $fundingSource['name'];
            }
            if ($fundingSource['active']
                && $fundingSource['name'] === 'ideal'
                && $country === 'NL'
                && $context->currency->iso_code === 'EUR'
            ) {
                $fundingSourcesEnabled[] = $fundingSource['name'];
            }
            if ($fundingSource['active']
                && $fundingSource['name'] === 'mybank'
                && $country === 'IT'
                && $context->currency->iso_code === 'EUR'
            ) {
                $fundingSourcesEnabled[] = $fundingSource['name'];
            }
            if ($fundingSource['active']
                && $fundingSource['name'] === 'p24'
                && $country === 'PL'
                && in_array($context->currency->iso_code, ['EUR', 'PLN'], true)
            ) {
                $fundingSourcesEnabled[] = $fundingSource['name'];
            }
        }

        return $fundingSourcesEnabled;
    }

    private function shouldIncludeGooglePayComponent()
    {
        $context = \Context::getContext();
        $country = $this->getCountry();
        $fundingSource = $this->fundingSourceConfigurationRepository->get('google_pay');

        return $fundingSource && $fundingSource['active']
            && $this->configuration->isGooglePayEligible()
            && in_array($country, $this->fundingSourceEligibilityConstraint->getCountries('google_pay'), true)
            && in_array($context->currency->iso_code, $this->fundingSourceEligibilityConstraint->getCurrencies('google_pay'), true);
    }
}