1228 lines
45 KiB
PHP
1228 lines
45 KiB
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
|
||
|
|
*/
|
||
|
|
if (!defined('_PS_VERSION_')) {
|
||
|
|
exit;
|
||
|
|
}
|
||
|
|
|
||
|
|
use Prestashop\ModuleLibMboInstaller\DependencyBuilder;
|
||
|
|
use PrestaShop\PrestaShop\Core\Payment\PaymentOption;
|
||
|
|
use PrestaShop\PsAccountsInstaller\Installer\Facade\PsAccounts;
|
||
|
|
use PrestaShop\PsAccountsInstaller\Installer\Presenter\InstallerPresenter;
|
||
|
|
use PsCheckout\Core\PayPal\Order\Provider\PayPalOrderProvider;
|
||
|
|
use PsCheckout\Core\PayPal\ShippingTracking\Action\AddTrackingAction;
|
||
|
|
use PsCheckout\Core\PayPal\ShippingTracking\Action\ProcessExternalShipmentAction;
|
||
|
|
use PsCheckout\Core\Settings\Configuration\DefaultConfiguration;
|
||
|
|
use PsCheckout\Core\Settings\Configuration\PayPalCodeConfiguration;
|
||
|
|
use PsCheckout\Core\Settings\Configuration\PayPalConfiguration;
|
||
|
|
use PsCheckout\Core\Settings\Configuration\PayPalSdkConfiguration;
|
||
|
|
use PsCheckout\Infrastructure\Adapter\Configuration;
|
||
|
|
use PsCheckout\Infrastructure\Adapter\Link;
|
||
|
|
use PsCheckout\Infrastructure\Adapter\ShopContext;
|
||
|
|
use PsCheckout\Infrastructure\Bootstrap\Install\Installer;
|
||
|
|
use PsCheckout\Infrastructure\Environment\Env;
|
||
|
|
use PsCheckout\Infrastructure\Repository\ConfigurationRepository;
|
||
|
|
use PsCheckout\Infrastructure\Repository\CountryRepository;
|
||
|
|
use PsCheckout\Infrastructure\Repository\CurrencyRepository;
|
||
|
|
use PsCheckout\Infrastructure\Repository\FundingSourceRepository;
|
||
|
|
use PsCheckout\Infrastructure\Repository\PayPalOrderRepository;
|
||
|
|
use PsCheckout\Infrastructure\Validator\FrontControllerValidator;
|
||
|
|
use PsCheckout\Infrastructure\Validator\MerchantValidator;
|
||
|
|
use PsCheckout\Module\Presentation\Translator;
|
||
|
|
use PsCheckout\Presentation\Presenter\FundingSource\FundingSourcePresenter;
|
||
|
|
use PsCheckout\Presentation\Presenter\FundingSource\FundingSourceTokenPresenter;
|
||
|
|
use PsCheckout\Presentation\Presenter\FundingSource\FundingSourceTranslationProvider;
|
||
|
|
use PsCheckout\Presentation\Presenter\OrderSummary\OrderSummaryPresenter;
|
||
|
|
use PsCheckout\Presentation\Presenter\Settings\Admin\AdminSettingsPresenter;
|
||
|
|
use PsCheckout\Presentation\Presenter\Settings\Front\FrontSettingsPresenter;
|
||
|
|
use PsCheckout\Utility\Common\ArrayUtility;
|
||
|
|
use Psr\Log\LoggerInterface;
|
||
|
|
|
||
|
|
require_once __DIR__ . '/vendor/autoload.php';
|
||
|
|
|
||
|
|
class Ps_Checkout extends PaymentModule
|
||
|
|
{
|
||
|
|
/**
|
||
|
|
* @var PrestaShop\ModuleLibServiceContainer\DependencyInjection\ServiceContainer
|
||
|
|
*/
|
||
|
|
private $serviceContainer;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @var bool|null
|
||
|
|
*/
|
||
|
|
private static $merchantIsValid;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @var array|null
|
||
|
|
*/
|
||
|
|
private static $currencyIsAllowed;
|
||
|
|
|
||
|
|
const HOOK_LIST = [
|
||
|
|
'actionAdminControllerSetMedia',
|
||
|
|
'actionFrontControllerSetMedia',
|
||
|
|
'actionObjectProductInCartDeleteAfter',
|
||
|
|
'actionCartUpdateQuantityBefore',
|
||
|
|
'actionObjectShopAddAfter',
|
||
|
|
'actionObjectShopDeleteAfter',
|
||
|
|
'actionObjectOrderPaymentAddAfter',
|
||
|
|
'actionObjectOrderPaymentUpdateAfter',
|
||
|
|
'actionObjectOrderCarrierUpdateAfter',
|
||
|
|
'actionGetOrderShipments',
|
||
|
|
'paymentOptions',
|
||
|
|
'displayPaymentTop',
|
||
|
|
'displayPaymentByBinaries',
|
||
|
|
'displayOrderConfirmation',
|
||
|
|
'displayPaymentReturn',
|
||
|
|
'displayOrderDetail',
|
||
|
|
'displayInvoiceLegalFreeText',
|
||
|
|
'displayAdminAfterHeader',
|
||
|
|
'displayAdminOrderMainBottom',
|
||
|
|
'moduleRoutes',
|
||
|
|
];
|
||
|
|
|
||
|
|
public $tabs = [
|
||
|
|
[
|
||
|
|
'class_name' => 'AdminAjaxPrestashopCheckout',
|
||
|
|
'visible' => false,
|
||
|
|
],
|
||
|
|
];
|
||
|
|
|
||
|
|
public function __construct()
|
||
|
|
{
|
||
|
|
$this->name = 'ps_checkout';
|
||
|
|
$this->tab = 'payments_gateways';
|
||
|
|
$this->version = '9.5.1.1';
|
||
|
|
$this->author = 'PrestaShop';
|
||
|
|
|
||
|
|
parent::__construct();
|
||
|
|
|
||
|
|
$this->displayName = $this->trans('PrestaShop Checkout');
|
||
|
|
$this->description = $this->trans('Provide the most commonly used payment methods to your customers in this all-in-one module, and manage all your sales in a centralized interface.');
|
||
|
|
$this->module_key = '82bc76354cfef947e06f1cc78f5efe2e';
|
||
|
|
$this->ps_versions_compliancy = ['min' => '9.0.0', 'max' => _PS_VERSION_];
|
||
|
|
}
|
||
|
|
|
||
|
|
public function install(): bool
|
||
|
|
{
|
||
|
|
/** @var Installer $installer */
|
||
|
|
$installer = $this->getService(Installer::class);
|
||
|
|
|
||
|
|
/** @var ShopContext $shopContext */
|
||
|
|
$shopContext = $this->getService(ShopContext::class);
|
||
|
|
|
||
|
|
$currentShopContext = $shopContext->getCurrent();
|
||
|
|
$shopContext->setAllShopContext();
|
||
|
|
|
||
|
|
$result = parent::install() && $installer->init() && $this->registerHook(self::HOOK_LIST);
|
||
|
|
|
||
|
|
$this->updatePosition(\Hook::getIdByName('paymentOptions'), false, 1);
|
||
|
|
|
||
|
|
$shopContext->setContext($currentShopContext);
|
||
|
|
|
||
|
|
return $result;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function uninstall(): bool
|
||
|
|
{
|
||
|
|
/* @var Configuration $configuration */
|
||
|
|
$configuration = $this->getService(Configuration::class);
|
||
|
|
|
||
|
|
foreach (array_keys(DefaultConfiguration::DEFAULT_CONFIGURATION_VALUES) as $name) {
|
||
|
|
$configuration->deleteByName($name);
|
||
|
|
}
|
||
|
|
|
||
|
|
return parent::uninstall();
|
||
|
|
}
|
||
|
|
|
||
|
|
public function isUsingNewTranslationSystem(): bool
|
||
|
|
{
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function getContent()
|
||
|
|
{
|
||
|
|
try {
|
||
|
|
/** @var PsAccounts $psAccountsFacade */
|
||
|
|
$psAccountsFacade = $this->getService(PsAccounts::class);
|
||
|
|
/** @var InstallerPresenter $psAccountsPresenter */
|
||
|
|
$psAccountsPresenter = $psAccountsFacade->getPsAccountsPresenter();
|
||
|
|
$contextPsAccounts = $psAccountsPresenter->present();
|
||
|
|
} catch (Exception $exception) {
|
||
|
|
$contextPsAccounts = [];
|
||
|
|
$this->getService(LoggerInterface::class)->error(
|
||
|
|
'Failed to get PsAccounts context',
|
||
|
|
[
|
||
|
|
'exception' => get_class($exception),
|
||
|
|
'exceptionCode' => $exception->getCode(),
|
||
|
|
'exceptionMessage' => $exception->getMessage(),
|
||
|
|
]
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @var AdminSettingsPresenter $settingsPresenter */
|
||
|
|
$settingsPresenter = $this->getService(AdminSettingsPresenter::class);
|
||
|
|
|
||
|
|
Media::addJsDef([
|
||
|
|
'store' => $settingsPresenter->present(),
|
||
|
|
'contextPsAccounts' => $contextPsAccounts,
|
||
|
|
]);
|
||
|
|
|
||
|
|
/** @var Env $env */
|
||
|
|
$env = $this->getService(Env::class);
|
||
|
|
$boSdkUrl = $env->getEnv('CHECKOUT_BO_SDK_URL');
|
||
|
|
|
||
|
|
if (substr($boSdkUrl, -3) !== '.js') {
|
||
|
|
$boSdkVersion = $env->getEnv('CHECKOUT_BO_SDK_VERSION');
|
||
|
|
|
||
|
|
$boSdkUrl = $boSdkUrl . $boSdkVersion . PayPalSdkConfiguration::SDK_BO_ENDPOINT;
|
||
|
|
}
|
||
|
|
|
||
|
|
$this->context->controller->addJS($boSdkUrl, false);
|
||
|
|
$isShopContext = !(Shop::isFeatureActive() && Shop::getContext() !== Shop::CONTEXT_SHOP);
|
||
|
|
$requiredDependencies = [];
|
||
|
|
$hasRequiredDependencies = true;
|
||
|
|
|
||
|
|
if ($isShopContext) {
|
||
|
|
try {
|
||
|
|
$mboInstaller = new DependencyBuilder($this);
|
||
|
|
$requiredDependencies = $mboInstaller->handleDependencies();
|
||
|
|
$hasRequiredDependencies = $mboInstaller->areDependenciesMet();
|
||
|
|
} catch (Exception $exception) {
|
||
|
|
$this->getService(LoggerInterface::class)->error(
|
||
|
|
'Failed to get required dependencies',
|
||
|
|
[
|
||
|
|
'exception' => get_class($exception),
|
||
|
|
'exceptionCode' => $exception->getCode(),
|
||
|
|
'exceptionMessage' => $exception->getMessage(),
|
||
|
|
]
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
$this->context->smarty->assign([
|
||
|
|
'requiredDependencies' => $requiredDependencies,
|
||
|
|
'hasRequiredDependencies' => $hasRequiredDependencies,
|
||
|
|
]);
|
||
|
|
|
||
|
|
return $this->display(__FILE__, 'views/templates/admin/configuration.tpl');
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Load asset on the back office
|
||
|
|
*/
|
||
|
|
public function hookActionAdminControllerSetMedia()
|
||
|
|
{
|
||
|
|
switch (Tools::getValue('controller')) {
|
||
|
|
case 'AdminModules':
|
||
|
|
if ($this->name === Tools::getValue('configure')) {
|
||
|
|
$this->context->controller->addCss(
|
||
|
|
$this->_path . 'views/css/adminModules.css?version=' . $this->version,
|
||
|
|
'all',
|
||
|
|
null,
|
||
|
|
false
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
break;
|
||
|
|
case 'AdminPayment':
|
||
|
|
$this->context->controller->addCss(
|
||
|
|
$this->_path . 'views/css/adminAfterHeader.css?version=' . $this->version,
|
||
|
|
'all',
|
||
|
|
null,
|
||
|
|
false
|
||
|
|
);
|
||
|
|
|
||
|
|
break;
|
||
|
|
case 'AdminCountries':
|
||
|
|
case 'AdminCurrencies':
|
||
|
|
$this->context->controller->addCss(
|
||
|
|
$this->_path . 'views/css/adminIncompatibleBanner.css?version=' . $this->version,
|
||
|
|
'all',
|
||
|
|
null,
|
||
|
|
false
|
||
|
|
);
|
||
|
|
|
||
|
|
break;
|
||
|
|
case 'AdminOrders':
|
||
|
|
$this->context->controller->addJS(
|
||
|
|
$this->getPathUri() . 'views/js/adminOrderView.js?version=' . $this->version,
|
||
|
|
false
|
||
|
|
);
|
||
|
|
$this->context->controller->addCss(
|
||
|
|
$this->_path . 'views/css/adminOrderView.css?version=' . $this->version,
|
||
|
|
'all',
|
||
|
|
null,
|
||
|
|
false
|
||
|
|
);
|
||
|
|
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Load asset on the front office
|
||
|
|
*/
|
||
|
|
public function hookActionFrontControllerSetMedia()
|
||
|
|
{
|
||
|
|
if (!$this->merchantIsValid()) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
$controller = (string) Tools::getValue('controller');
|
||
|
|
|
||
|
|
if (empty($controller) && isset($this->context->controller->php_self)) {
|
||
|
|
$controller = $this->context->controller->php_self;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @var FrontControllerValidator $frontControllerValidator */
|
||
|
|
$frontControllerValidator = $this->getService(FrontControllerValidator::class);
|
||
|
|
|
||
|
|
if ($frontControllerValidator->shouldLoadFrontCss($controller)) {
|
||
|
|
$this->context->controller->registerStylesheet(
|
||
|
|
'ps-checkout-css-paymentOptions',
|
||
|
|
$this->getPathUri() . 'views/css/payments.css?version=' . $this->version,
|
||
|
|
[
|
||
|
|
'server' => 'remote',
|
||
|
|
]
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!$frontControllerValidator->shouldLoadFrontJS($controller)) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @var FrontSettingsPresenter $settingsPresenter */
|
||
|
|
$settingsPresenter = $this->getService(FrontSettingsPresenter::class);
|
||
|
|
|
||
|
|
Media::addJsDef($settingsPresenter->present());
|
||
|
|
Media::addJsDef([
|
||
|
|
$this->name . 'RenderPaymentMethodLogos' => $frontControllerValidator->shouldDisplayFundingLogo($controller),
|
||
|
|
]);
|
||
|
|
/** @var Env $env */
|
||
|
|
$env = $this->getService(Env::class);
|
||
|
|
$foSdkUrl = $env->getEnv('CHECKOUT_FO_SDK_URL');
|
||
|
|
|
||
|
|
if (substr($foSdkUrl, -3) !== '.js') {
|
||
|
|
$foSdkVersion = $env->getEnv('CHECKOUT_FO_SDK_VERSION');
|
||
|
|
|
||
|
|
$foSdkUrl = $foSdkUrl . $foSdkVersion . PayPalSdkConfiguration::SDK_FO_ENDPOINT;
|
||
|
|
}
|
||
|
|
|
||
|
|
$this->context->controller->registerJavascript(
|
||
|
|
$this->name . 'Front',
|
||
|
|
$foSdkUrl,
|
||
|
|
[
|
||
|
|
'position' => 'bottom',
|
||
|
|
'priority' => 201,
|
||
|
|
'server' => 'remote',
|
||
|
|
]
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function hookActionObjectProductInCartDeleteAfter()
|
||
|
|
{
|
||
|
|
$this->hookActionCartUpdateQuantityBefore();
|
||
|
|
}
|
||
|
|
|
||
|
|
public function hookActionCartUpdateQuantityBefore()
|
||
|
|
{
|
||
|
|
if (
|
||
|
|
!Validate::isLoadedObject($this->context->cart)
|
||
|
|
|| !$this->merchantIsValid()
|
||
|
|
) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @var PayPalOrderRepository $payPalOrderRepository */
|
||
|
|
$payPalOrderRepository = $this->getService(PayPalOrderRepository::class);
|
||
|
|
$payPalOrder = $payPalOrderRepository->getOneByCartId($this->context->cart->id);
|
||
|
|
|
||
|
|
if ($payPalOrder && $payPalOrder->isExpressCheckout() || !$this->context->cart->nbProducts()) {
|
||
|
|
$this->context->cookie->__unset('paypalEmail');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* This hook called after a new Shop is created
|
||
|
|
*
|
||
|
|
* @param array{cookie: Cookie, cart: Cart, altern: int, object: Shop} $params
|
||
|
|
*/
|
||
|
|
public function hookActionObjectShopAddAfter(array $params)
|
||
|
|
{
|
||
|
|
/** @var Shop $shop */
|
||
|
|
$shop = $params['object'];
|
||
|
|
$now = date('Y-m-d H:i:s');
|
||
|
|
|
||
|
|
/** @var ConfigurationRepository $configurationRepository */
|
||
|
|
$configurationRepository = $this->getService(ConfigurationRepository::class);
|
||
|
|
$configurationRepository->handleConfigurationOnShopToggle();
|
||
|
|
|
||
|
|
foreach (DefaultConfiguration::DEFAULT_CONFIGURATION_VALUES as $name => $value) {
|
||
|
|
Db::getInstance()->insert(
|
||
|
|
'configuration',
|
||
|
|
[
|
||
|
|
'name' => pSQL($name),
|
||
|
|
'value' => pSQL($value),
|
||
|
|
'date_add' => pSQL($now),
|
||
|
|
'date_upd' => pSQL($now),
|
||
|
|
'id_shop' => (int) $shop->id,
|
||
|
|
'id_shop_group' => (int) $shop->id_shop_group,
|
||
|
|
],
|
||
|
|
true,
|
||
|
|
false
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @var FundingSourceRepository $fundingSourceRepository */
|
||
|
|
$fundingSourceRepository = $this->getService(FundingSourceRepository::class);
|
||
|
|
$fundingSourceRepository->populateWithDefaultValues((int) $shop->id);
|
||
|
|
|
||
|
|
$this->addCheckboxCarrierRestrictionsForModule([(int) $shop->id]);
|
||
|
|
$this->addCheckboxCountryRestrictionsForModule([(int) $shop->id]);
|
||
|
|
|
||
|
|
if ($this->currencies_mode === 'checkbox') {
|
||
|
|
$this->addCheckboxCurrencyRestrictionsForModule([(int) $shop->id]);
|
||
|
|
} elseif ($this->currencies_mode === 'radio') {
|
||
|
|
$this->addRadioCurrencyRestrictionsForModule([(int) $shop->id]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @param array{cookie: Cookie, cart: Cart, altern: int, object: Shop} $params
|
||
|
|
*
|
||
|
|
* @return void
|
||
|
|
*/
|
||
|
|
public function hookActionObjectShopDeleteAfter(array $params)
|
||
|
|
{
|
||
|
|
/** @var ConfigurationRepository $configurationRepository */
|
||
|
|
$configurationRepository = $this->getService(ConfigurationRepository::class);
|
||
|
|
$configurationRepository->handleConfigurationOnShopToggle();
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* When an OrderPayment is created we should update fields payment_method and transaction_id
|
||
|
|
*
|
||
|
|
* @param array{cookie: Cookie, cart: Cart, altern: int, object: OrderPayment} $params
|
||
|
|
*/
|
||
|
|
public function hookActionObjectOrderPaymentAddAfter(array $params)
|
||
|
|
{
|
||
|
|
$this->hookActionObjectOrderPaymentUpdateAfter($params);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* When an OrderPayment is updated we should update fields payment_method and transaction_id
|
||
|
|
*
|
||
|
|
* @param array{cookie: Cookie, cart: Cart, altern: int, object: OrderPayment} $params
|
||
|
|
*/
|
||
|
|
public function hookActionObjectOrderPaymentUpdateAfter(array $params)
|
||
|
|
{
|
||
|
|
if (!isset($params['object'])) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @var \OrderPayment $orderPayment */
|
||
|
|
$orderPayment = $params['object'];
|
||
|
|
|
||
|
|
if (!Validate::isLoadedObject($orderPayment)
|
||
|
|
|| empty($orderPayment->order_reference)
|
||
|
|
|| !empty($orderPayment->transaction_id)
|
||
|
|
|| 1 !== count(OrderPayment::getByOrderReference($orderPayment->order_reference))
|
||
|
|
) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @var Order[] $orderCollection */
|
||
|
|
$orderCollection = Order::getByReference($orderPayment->order_reference);
|
||
|
|
|
||
|
|
foreach ($orderCollection as $order) {
|
||
|
|
if ($this->name !== $order->module) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
$cartId = (int) $order->id_cart;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (empty($cartId)) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @var PayPalOrderRepository $payPalOrderRepository */
|
||
|
|
$payPalOrderRepository = $this->getService(PayPalOrderRepository::class);
|
||
|
|
$payPalOrder = $payPalOrderRepository->getOneByCartId($cartId);
|
||
|
|
|
||
|
|
if (!$payPalOrder) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
/** @var PayPalOrderProvider $paypalOrderProvider */
|
||
|
|
$paypalOrderProvider = $this->getService(PayPalOrderProvider::class);
|
||
|
|
$payPalOrderResponse = $paypalOrderProvider->getById($payPalOrder->getId());
|
||
|
|
} catch (Exception $exception) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!empty($payPalOrderResponse->getCapture())) {
|
||
|
|
$transactionId = $payPalOrderResponse->getCapture()['id'];
|
||
|
|
} elseif (!empty($payPalOrderResponse->getAuthorization())) {
|
||
|
|
$transactionId = $payPalOrderResponse->getAuthorization()['id'];
|
||
|
|
} else {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
$cardNumber = '';
|
||
|
|
$cardBrand = '';
|
||
|
|
|
||
|
|
if (!empty($payPalOrderResponse->getCard())) {
|
||
|
|
$cardNumber = sprintf('#### #### #### %d', $payPalOrderResponse->getCard()['last_digits']);
|
||
|
|
$cardBrand = $payPalOrderResponse->getCard()['brand'];
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @var FundingSourceTranslationProvider $fundingSourceTranslationProvider */
|
||
|
|
$fundingSourceTranslationProvider = $this->getService(FundingSourceTranslationProvider::class);
|
||
|
|
|
||
|
|
\Db::getInstance()->update(
|
||
|
|
'order_payment',
|
||
|
|
[
|
||
|
|
'payment_method' => pSQL($fundingSourceTranslationProvider->getFundingSourceName($payPalOrder->getFundingSource())),
|
||
|
|
'transaction_id' => pSQL($transactionId),
|
||
|
|
'card_number' => pSQL($cardNumber),
|
||
|
|
'card_brand' => pSQL($cardBrand),
|
||
|
|
],
|
||
|
|
'id_order_payment = ' . (int) $orderPayment->id
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Add payment option at the checkout in the front office
|
||
|
|
*
|
||
|
|
* @param array{cookie: Cookie, cart: Cart, altern: int} $params
|
||
|
|
*
|
||
|
|
* @return array
|
||
|
|
*/
|
||
|
|
public function hookPaymentOptions(array $params): array
|
||
|
|
{
|
||
|
|
/** @var Cart $cart */
|
||
|
|
$cart = $params['cart'];
|
||
|
|
|
||
|
|
if (!Validate::isLoadedObject($cart)
|
||
|
|
|| !$this->merchantIsValid()
|
||
|
|
|| !$this->checkCurrency($cart)
|
||
|
|
) {
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @var Configuration $configuration */
|
||
|
|
$configuration = $this->getService(Configuration::class);
|
||
|
|
/** @var FundingSourcePresenter $fundingSourcePresenter */
|
||
|
|
$fundingSourcePresenter = $this->getService(FundingSourcePresenter::class);
|
||
|
|
/** @var FundingSourceTokenPresenter $fundingSourceTokenPresenter */
|
||
|
|
$fundingSourceTokenPresenter = $this->getService(FundingSourceTokenPresenter::class);
|
||
|
|
/** @var FundingSourceTranslationProvider $fundingSourceTranslationProvider */
|
||
|
|
$fundingSourceTranslationProvider = $this->getService(FundingSourceTranslationProvider::class);
|
||
|
|
|
||
|
|
$vaultingEnabled = $configuration->getBoolean(PayPalConfiguration::PS_CHECKOUT_VAULTING)
|
||
|
|
&& $this->context->customer->isLogged();
|
||
|
|
|
||
|
|
$this->context->smarty->assign([
|
||
|
|
'modulePath' => $this->getPathUri(),
|
||
|
|
'vaultingEnabled' => $vaultingEnabled,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$vaultedPayPal = [];
|
||
|
|
|
||
|
|
$paymentOptions = [];
|
||
|
|
|
||
|
|
if ($vaultingEnabled) {
|
||
|
|
foreach ($fundingSourceTokenPresenter->getFundingSourceTokens($cart->id_customer) as $fundingSource) {
|
||
|
|
if ($fundingSource->getPaymentSource() === 'paypal') {
|
||
|
|
$vaultedPayPal = [
|
||
|
|
'paymentIdentifier' => $fundingSource->getName(),
|
||
|
|
'fundingSource' => $fundingSource->getPaymentSource(),
|
||
|
|
'isFavorite' => $fundingSource->isFavorite(),
|
||
|
|
'label' => $fundingSource->getLabel(),
|
||
|
|
'vaultId' => explode('-', $fundingSource->getName())[1],
|
||
|
|
];
|
||
|
|
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
$paymentOption = new PaymentOption();
|
||
|
|
$paymentOption->setModuleName($this->name . '-' . $fundingSource->getName());
|
||
|
|
$paymentOption->setCallToActionText($fundingSource->getLabel());
|
||
|
|
$paymentOption->setBinary(true);
|
||
|
|
|
||
|
|
$this->context->smarty->assign([
|
||
|
|
'paymentIdentifier' => $fundingSource->getName(),
|
||
|
|
'fundingSource' => $fundingSource->getPaymentSource(),
|
||
|
|
'isFavorite' => $fundingSource->isFavorite(),
|
||
|
|
'label' => $fundingSource->getLabel(),
|
||
|
|
'vaultId' => explode('-', $fundingSource->getName())[1],
|
||
|
|
]);
|
||
|
|
$paymentOption->setForm($this->context->smarty->fetch('module:' . $this->name . '/views/templates/hook/partials/vaultTokenForm.tpl'));
|
||
|
|
|
||
|
|
$paymentOptions[] = $paymentOption;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
foreach ($fundingSourcePresenter->getAllActiveForSpecificShop($this->context->shop->id) as $fundingSource) {
|
||
|
|
$paymentOption = new PaymentOption();
|
||
|
|
$paymentOption->setModuleName($this->name . '-' . $fundingSource->getName());
|
||
|
|
$paymentOption->setCallToActionText($fundingSourceTranslationProvider->getPaymentMethodName(
|
||
|
|
$fundingSource->getName(),
|
||
|
|
$fundingSource->getLabel()
|
||
|
|
));
|
||
|
|
$paymentOption->setBinary(true);
|
||
|
|
$this->context->smarty->assign('paymentIdentifier', $fundingSource->getName());
|
||
|
|
|
||
|
|
if (
|
||
|
|
'card' === $fundingSource->getName()
|
||
|
|
&& $configuration->getBoolean(PayPalConfiguration::PS_CHECKOUT_CARD_HOSTED_FIELDS_ENABLED)
|
||
|
|
&& in_array(
|
||
|
|
$configuration->get(PayPalConfiguration::PS_CHECKOUT_CARD_HOSTED_FIELDS_STATUS),
|
||
|
|
[
|
||
|
|
'SUBSCRIBED',
|
||
|
|
'LIMITED',
|
||
|
|
],
|
||
|
|
true
|
||
|
|
)
|
||
|
|
) {
|
||
|
|
$paymentOption->setForm($this->context->smarty->fetch('module:' . $this->name . '/views/templates/hook/partials/cardFields.tpl'));
|
||
|
|
} elseif ($fundingSource->getName() === 'pay_upon_invoice') {
|
||
|
|
$customerBirthday = null;
|
||
|
|
if (
|
||
|
|
!empty($this->context->customer->birthday)
|
||
|
|
&& $this->context->customer->birthday !== '0000-00-00'
|
||
|
|
) {
|
||
|
|
$customerBirthday = $this->context->customer->birthday;
|
||
|
|
}
|
||
|
|
$this->context->smarty->assign([
|
||
|
|
'customerBirthday' => $customerBirthday,
|
||
|
|
'min_date' => '1900-01-01',
|
||
|
|
'max_date' => date('Y-m-d', strtotime('-18 years')),
|
||
|
|
]);
|
||
|
|
$paymentOption->setForm($this->context->smarty->fetch('module:' . $this->name . '/views/templates/hook/partials/payUponInvoiceFields.tpl'));
|
||
|
|
} elseif ($fundingSource->getName() === 'paypal' && empty($vaultedPayPal)) {
|
||
|
|
$paymentOption->setForm($this->context->smarty->fetch('module:' . $this->name . '/views/templates/hook/partials/vaultPaymentForm.tpl'));
|
||
|
|
} elseif ($fundingSource->getName() === 'paypal' && $vaultedPayPal) {
|
||
|
|
$this->context->smarty->assign($vaultedPayPal);
|
||
|
|
$paymentOption->setForm($this->context->smarty->fetch('module:' . $this->name . '/views/templates/hook/partials/vaultTokenForm.tpl'));
|
||
|
|
}
|
||
|
|
|
||
|
|
$paymentOptions[] = $paymentOption;
|
||
|
|
}
|
||
|
|
|
||
|
|
return $paymentOptions;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* This hook display a block on top of PaymentOptions on PrestaShop 1.7
|
||
|
|
*
|
||
|
|
* @return string
|
||
|
|
*/
|
||
|
|
public function hookDisplayPaymentTop(): string
|
||
|
|
{
|
||
|
|
if (!Validate::isLoadedObject($this->context->cart)
|
||
|
|
|| !$this->merchantIsValid()
|
||
|
|
|| !$this->checkCurrency($this->context->cart)
|
||
|
|
) {
|
||
|
|
return '';
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @var PayPalOrderRepository $payPalOrderRepository */
|
||
|
|
$payPalOrderRepository = $this->getService(PayPalOrderRepository::class);
|
||
|
|
$payPalOrder = $payPalOrderRepository->getOneByCartId($this->context->cart->id);
|
||
|
|
|
||
|
|
$isExpressCheckout = $payPalOrder && $payPalOrder->isExpressCheckout();
|
||
|
|
|
||
|
|
$this->context->smarty->assign([
|
||
|
|
'isExpressCheckout' => $isExpressCheckout,
|
||
|
|
'spinnerPath' => $this->getPathUri() . 'views/img/tail-spin.svg',
|
||
|
|
'loaderTranslatedText' => $this->trans('Please wait, loading additional payment methods.', [], 'Modules.Checkout.Pscheckout'),
|
||
|
|
'paypalLogoPath' => $this->getPathUri() . 'views/img/paypal_express.png',
|
||
|
|
'translatedText' => $this->trans(
|
||
|
|
'You have selected your %s PayPal account to proceed to the payment.',
|
||
|
|
[$this->context->cookie->__get('paypalEmail') ?: ''],
|
||
|
|
'Modules.Checkout.Pscheckout'
|
||
|
|
),
|
||
|
|
'shoppingCartWarningPath' => $this->getPathUri() . 'views/img/icons/shopping-cart-warning.svg',
|
||
|
|
'warningTranslatedText' => $this->trans('Warning', [], 'Modules.Checkout.Pscheckout'),
|
||
|
|
]);
|
||
|
|
|
||
|
|
return $this->display(__FILE__, 'views/templates/hook/displayPaymentTop.tpl');
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* This hook displays form generated by binaries during the checkout
|
||
|
|
*
|
||
|
|
* @param array{cookie: Cookie, cart: Cart, altern: int} $params
|
||
|
|
*
|
||
|
|
* @return string
|
||
|
|
*/
|
||
|
|
public function hookDisplayPaymentByBinaries(array $params): string
|
||
|
|
{
|
||
|
|
/** @var Cart $cart */
|
||
|
|
$cart = $params['cart'];
|
||
|
|
|
||
|
|
if (!Validate::isLoadedObject($cart)
|
||
|
|
|| !$this->merchantIsValid()
|
||
|
|
|| !$this->checkCurrency($cart)
|
||
|
|
) {
|
||
|
|
return '';
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @var FundingSourceTokenPresenter $fundingSourceTokenPresenter */
|
||
|
|
$fundingSourceTokenPresenter = $this->getService(FundingSourceTokenPresenter::class);
|
||
|
|
/** @var FundingSourcePresenter $fundingSourcePresenter */
|
||
|
|
$fundingSourcePresenter = $this->getService(FundingSourcePresenter::class);
|
||
|
|
|
||
|
|
$paymentOptions = [];
|
||
|
|
|
||
|
|
foreach ($fundingSourceTokenPresenter->getFundingSourceTokens((int) $cart->id_customer) as $fundingSource) {
|
||
|
|
$paymentOptions[] = $fundingSource->getName();
|
||
|
|
}
|
||
|
|
|
||
|
|
foreach ($fundingSourcePresenter->getAllActiveForSpecificShop($this->context->shop->id) as $fundingSource) {
|
||
|
|
$paymentOptions[] = $fundingSource->getName();
|
||
|
|
}
|
||
|
|
|
||
|
|
$this->context->smarty->assign([
|
||
|
|
'paymentOptions' => $paymentOptions,
|
||
|
|
]);
|
||
|
|
|
||
|
|
return $this->display(__FILE__, 'views/templates/hook/displayPaymentByBinaries.tpl');
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Hook executed at the order confirmation
|
||
|
|
*
|
||
|
|
* @param array{cookie: Cookie, cart: Cart, altern: int, order: Order} $params
|
||
|
|
*
|
||
|
|
* @return string
|
||
|
|
*/
|
||
|
|
public function hookDisplayOrderConfirmation(array $params)
|
||
|
|
{
|
||
|
|
if (!$this->merchantIsValid()) {
|
||
|
|
return '';
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @var Order $order */
|
||
|
|
$order = $params['order'];
|
||
|
|
|
||
|
|
if (!Validate::isLoadedObject($order) || $order->module !== $this->name) {
|
||
|
|
return '';
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @var OrderSummaryPresenter $orderSummaryPresenter */
|
||
|
|
$orderSummaryPresenter = $this->getService(OrderSummaryPresenter::class);
|
||
|
|
|
||
|
|
try {
|
||
|
|
$templateVars = $orderSummaryPresenter->present($order);
|
||
|
|
} catch (Exception $exception) {
|
||
|
|
return '';
|
||
|
|
}
|
||
|
|
|
||
|
|
$this->context->smarty->assign($templateVars);
|
||
|
|
|
||
|
|
return $this->display(__FILE__, 'views/templates/hook/displayOrderConfirmation.tpl');
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Display payment status on order confirmation page
|
||
|
|
*
|
||
|
|
* @param array{cookie: Cookie, cart: Cart, altern: int, order: Order} $params
|
||
|
|
*
|
||
|
|
* @return string
|
||
|
|
*/
|
||
|
|
public function hookDisplayPaymentReturn(array $params)
|
||
|
|
{
|
||
|
|
if (!$this->merchantIsValid()) {
|
||
|
|
return '';
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @var Order $order */
|
||
|
|
$order = $params['order'];
|
||
|
|
|
||
|
|
if (!Validate::isLoadedObject($order) || $order->module !== $this->name) {
|
||
|
|
return '';
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @var OrderSummaryPresenter $orderSummaryPresenter */
|
||
|
|
$orderSummaryPresenter = $this->getService(OrderSummaryPresenter::class);
|
||
|
|
|
||
|
|
try {
|
||
|
|
$templateVars = $orderSummaryPresenter->present($order);
|
||
|
|
} catch (Exception $exception) {
|
||
|
|
return '';
|
||
|
|
}
|
||
|
|
|
||
|
|
$this->context->smarty->assign($templateVars);
|
||
|
|
|
||
|
|
return $this->display(__FILE__, 'views/templates/hook/displayPaymentReturn.tpl');
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Display payment status on order detail page
|
||
|
|
*
|
||
|
|
* @param array{cookie: Cookie, cart: Cart, altern: int, order: Order} $params
|
||
|
|
*
|
||
|
|
* @return string
|
||
|
|
*/
|
||
|
|
public function hookDisplayOrderDetail(array $params)
|
||
|
|
{
|
||
|
|
if (!$this->merchantIsValid()) {
|
||
|
|
return '';
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @var Order $order */
|
||
|
|
$order = $params['order'];
|
||
|
|
|
||
|
|
if (!Validate::isLoadedObject($order) || $order->module !== $this->name) {
|
||
|
|
return '';
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @var OrderSummaryPresenter $orderSummaryPresenter */
|
||
|
|
$orderSummaryPresenter = $this->getService(OrderSummaryPresenter::class);
|
||
|
|
|
||
|
|
try {
|
||
|
|
$templateVars = $orderSummaryPresenter->present($order);
|
||
|
|
} catch (Exception $exception) {
|
||
|
|
return '';
|
||
|
|
}
|
||
|
|
|
||
|
|
$this->context->smarty->assign($templateVars);
|
||
|
|
|
||
|
|
return $this->display(__FILE__, 'views/templates/hook/displayOrderDetail.tpl');
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* This hook allows to add PayPal OrderId and TransactionId on PDF invoice
|
||
|
|
*
|
||
|
|
* @param array{cookie: Cookie, cart: Cart, altern: int, order: Order} $params
|
||
|
|
*
|
||
|
|
* @return string HTML is not allowed in this hook
|
||
|
|
*/
|
||
|
|
public function hookDisplayInvoiceLegalFreeText(array $params)
|
||
|
|
{
|
||
|
|
if (!$this->merchantIsValid()) {
|
||
|
|
return '';
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @var \Order $order */
|
||
|
|
$order = $params['order'];
|
||
|
|
|
||
|
|
if (!Validate::isLoadedObject($order) || $this->name !== $order->module) {
|
||
|
|
// This order has not been paid with this module
|
||
|
|
return '';
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @var PayPalOrderRepository $payPalOrderRepository */
|
||
|
|
$payPalOrderRepository = $this->getService(PayPalOrderRepository::class);
|
||
|
|
$payPalOrder = $payPalOrderRepository->getOneByCartId($order->id_cart);
|
||
|
|
|
||
|
|
if (!$payPalOrder) {
|
||
|
|
return '';
|
||
|
|
}
|
||
|
|
|
||
|
|
/* @var Configuration $configuration */
|
||
|
|
$configuration = $this->getService(Configuration::class);
|
||
|
|
/* @var Translator $translator */
|
||
|
|
$translator = $this->getService(Translator::class);
|
||
|
|
|
||
|
|
$legalFreeText = $configuration->getForSpecificShop('PS_INVOICE_LEGAL_FREE_TEXT', $order->id_shop, $order->id_lang) ?: '';
|
||
|
|
|
||
|
|
if (!empty($legalFreeText)) {
|
||
|
|
// If a legal free text is found, we add blank lines after
|
||
|
|
$legalFreeText .= PHP_EOL . PHP_EOL;
|
||
|
|
}
|
||
|
|
|
||
|
|
$legalFreeText .= $translator->trans('Payment gateway information') . PHP_EOL;
|
||
|
|
$legalFreeText .= $translator->trans('Order identifier') . ' ' . $payPalOrder->getId() . PHP_EOL;
|
||
|
|
$legalFreeText .= $translator->trans('Order status') . ' ' . $payPalOrder->getStatus() . PHP_EOL;
|
||
|
|
|
||
|
|
/** @var \OrderPayment[] $orderPayments */
|
||
|
|
$orderPayments = $order->getOrderPaymentCollection();
|
||
|
|
|
||
|
|
foreach ($orderPayments as $orderPayment) {
|
||
|
|
if (!empty($orderPayment->transaction_id)) {
|
||
|
|
$legalFreeText .= $this->trans('Transaction identifier') . ' ' . $orderPayment->transaction_id . PHP_EOL;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return $legalFreeText;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Hook used to display templates under BO header
|
||
|
|
*/
|
||
|
|
public function hookDisplayAdminAfterHeader()
|
||
|
|
{
|
||
|
|
/* @var Configuration $configuration */
|
||
|
|
$configuration = $this->getService(Configuration::class);
|
||
|
|
/* @var Link $link */
|
||
|
|
$link = $this->getService(Link::class);
|
||
|
|
|
||
|
|
switch (Tools::getValue('controller')) {
|
||
|
|
case 'AdminPayment':
|
||
|
|
$defaultCountryCode = (new Country((int) $configuration->get('PS_COUNTRY_DEFAULT')))->iso_code;
|
||
|
|
|
||
|
|
if (in_array($defaultCountryCode, ['FR', 'IT'])
|
||
|
|
&& Module::isEnabled($this->name)
|
||
|
|
&& $configuration->get(PayPalConfiguration::PS_CHECKOUT_PAYPAL_ID_MERCHANT)
|
||
|
|
) {
|
||
|
|
return '';
|
||
|
|
}
|
||
|
|
|
||
|
|
$params = [
|
||
|
|
'modulePath' => $this->getPathUri(),
|
||
|
|
'configureLink' => $link->getAdminLink('AdminModules', true, [], ['configure' => $this->name]),
|
||
|
|
];
|
||
|
|
$template = 'views/templates/hook/adminAfterHeader/promotionBlock.tpl';
|
||
|
|
|
||
|
|
break;
|
||
|
|
case 'AdminCountries':
|
||
|
|
if (!$this->merchantIsValid()) {
|
||
|
|
return '';
|
||
|
|
}
|
||
|
|
|
||
|
|
/* @var CountryRepository $countryRepository */
|
||
|
|
$countryRepository = $this->getService(CountryRepository::class);
|
||
|
|
$moduleCountryIsoCodes = array_column($countryRepository->getModuleCountryCodes(), 'iso_code');
|
||
|
|
|
||
|
|
$params = [
|
||
|
|
'codesType' => 'countries',
|
||
|
|
'incompatibleCodes' => ArrayUtility::findMissingKeys($moduleCountryIsoCodes, PayPalCodeConfiguration::getCountryCodes()),
|
||
|
|
'paypalLink' => 'https://developer.paypal.com/docs/api/reference/country-codes/#',
|
||
|
|
'paymentPreferencesLink' => $link->getAdminLink('AdminPaymentPreferences'),
|
||
|
|
];
|
||
|
|
$template = 'views/templates/hook/adminAfterHeader/incompatibleCodes.tpl';
|
||
|
|
|
||
|
|
break;
|
||
|
|
case 'AdminCurrencies':
|
||
|
|
if (!$this->merchantIsValid()) {
|
||
|
|
return '';
|
||
|
|
}
|
||
|
|
|
||
|
|
/* @var CurrencyRepository $currencyRepository */
|
||
|
|
$currencyRepository = $this->getService(CurrencyRepository::class);
|
||
|
|
$moduleCurrenciesIsoCodes = array_column($currencyRepository->getModuleCurrencyCodes(), 'iso_code');
|
||
|
|
|
||
|
|
$params = [
|
||
|
|
'codesType' => 'currencies',
|
||
|
|
'incompatibleCodes' => ArrayUtility::findMissingKeys($moduleCurrenciesIsoCodes, PayPalCodeConfiguration::getCurrencyCodes()),
|
||
|
|
'paypalLink' => 'https://developer.paypal.com/docs/api/reference/currency-codes/#',
|
||
|
|
'paymentPreferencesLink' => $link->getAdminLink('AdminPaymentPreferences'),
|
||
|
|
];
|
||
|
|
$template = 'views/templates/hook/adminAfterHeader/incompatibleCodes.tpl';
|
||
|
|
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
return '';
|
||
|
|
}
|
||
|
|
|
||
|
|
$this->context->smarty->assign($params);
|
||
|
|
|
||
|
|
return $this->display(__FILE__, $template);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @param array{cookie: Cookie, cart: Cart, altern: int, id_order: int} $params
|
||
|
|
*
|
||
|
|
* @return string
|
||
|
|
*/
|
||
|
|
public function hookDisplayAdminOrderMainBottom(array $params)
|
||
|
|
{
|
||
|
|
$order = new Order((int) $params['id_order']);
|
||
|
|
|
||
|
|
if ($order->module !== $this->name) {
|
||
|
|
return '';
|
||
|
|
}
|
||
|
|
|
||
|
|
$this->context->smarty->assign([
|
||
|
|
'moduleLogoUri' => $this->getPathUri() . 'logo.png',
|
||
|
|
'moduleName' => $this->displayName,
|
||
|
|
'orderPrestaShopId' => $order->id,
|
||
|
|
'orderPayPalBaseUrl' => $this->context->link->getAdminLink('AdminAjaxPrestashopCheckout'),
|
||
|
|
]);
|
||
|
|
|
||
|
|
return $this->display(__FILE__, 'views/templates/hook/displayAdminOrderMainBottom.tpl');
|
||
|
|
}
|
||
|
|
|
||
|
|
public function hookModuleRoutes()
|
||
|
|
{
|
||
|
|
return [
|
||
|
|
'ps_checkout_applepay' => [
|
||
|
|
'rule' => '.well-known/apple-developer-merchantid-domain-association',
|
||
|
|
'keywords' => [],
|
||
|
|
'controller' => 'applepay',
|
||
|
|
'params' => [
|
||
|
|
'fc' => 'module',
|
||
|
|
'module' => $this->name,
|
||
|
|
'action' => 'getDomainAssociation',
|
||
|
|
],
|
||
|
|
],
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Override method to add "IGNORE" in the SQL Request to prevent duplicate entry and for getting All Carriers installed
|
||
|
|
* Add checkbox carrier restrictions for a new module.
|
||
|
|
*
|
||
|
|
* @see PaymentModuleCore
|
||
|
|
*
|
||
|
|
* @param array $shopsIds List of Shop identifier
|
||
|
|
*
|
||
|
|
* @return bool
|
||
|
|
*/
|
||
|
|
public function addCheckboxCarrierRestrictionsForModule(array $shopsIds = []): bool
|
||
|
|
{
|
||
|
|
$shopsIds = empty($shopsIds) ? Shop::getShops(true, null, true) : $shopsIds;
|
||
|
|
$carriersList = Carrier::getCarriers((int) Context::getContext()->language->id, false, false, false, null, Carrier::ALL_CARRIERS);
|
||
|
|
$carriersIds = array_column($carriersList, 'id_reference');
|
||
|
|
|
||
|
|
$dataToInsert = [];
|
||
|
|
|
||
|
|
foreach ($shopsIds as $shopId) {
|
||
|
|
foreach ($carriersIds as $carrierId) {
|
||
|
|
$dataToInsert[] = [
|
||
|
|
'id_reference' => (int) $carrierId,
|
||
|
|
'id_shop' => (int) $shopId,
|
||
|
|
'id_module' => (int) $this->id,
|
||
|
|
];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return \Db::getInstance()->insert('module_carrier', $dataToInsert, false, true, Db::INSERT_IGNORE);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Override method to add "IGNORE" in the SQL Request to prevent duplicate entry.
|
||
|
|
* Add checkbox country restrictions for a new module.
|
||
|
|
* Associate with all countries allowed in geolocation management
|
||
|
|
*
|
||
|
|
* @see PaymentModuleCore
|
||
|
|
*
|
||
|
|
* @param array $shopsIds List of Shop identifier
|
||
|
|
*
|
||
|
|
* @return bool
|
||
|
|
*/
|
||
|
|
public function addCheckboxCountryRestrictionsForModule(array $shopsIds = [])
|
||
|
|
{
|
||
|
|
parent::addCheckboxCountryRestrictionsForModule($shopsIds);
|
||
|
|
|
||
|
|
// Then add all countries allowed in geolocation management
|
||
|
|
/* @var Configuration $configuration */
|
||
|
|
$configuration = $this->getService(Configuration::class);
|
||
|
|
$db = \Db::getInstance();
|
||
|
|
|
||
|
|
$shopsIds = empty($shopsIds) ? Shop::getShops(true, null, true) : $shopsIds;
|
||
|
|
/** @var array $countries */
|
||
|
|
$countries = $db->executeS('SELECT id_country, iso_code FROM ' . _DB_PREFIX_ . 'country');
|
||
|
|
|
||
|
|
$countryIdByIso = [];
|
||
|
|
|
||
|
|
foreach ($countries as $country) {
|
||
|
|
$countryIdByIso[$country['iso_code']] = $country['id_country'];
|
||
|
|
}
|
||
|
|
|
||
|
|
$dataToInsert = [];
|
||
|
|
|
||
|
|
foreach ($shopsIds as $shopId) {
|
||
|
|
// Get countries allowed in geolocation management for this shop
|
||
|
|
$activeCountries = $configuration->getForSpecificShop('PS_ALLOWED_COUNTRIES', $shopId);
|
||
|
|
$explodedCountries = explode(';', $activeCountries);
|
||
|
|
|
||
|
|
foreach ($explodedCountries as $isoCodeCountry) {
|
||
|
|
if (isset($countryIdByIso[$isoCodeCountry])) {
|
||
|
|
$dataToInsert[] = [
|
||
|
|
'id_country' => (int) $countryIdByIso[$isoCodeCountry],
|
||
|
|
'id_shop' => (int) $shopId,
|
||
|
|
'id_module' => (int) $this->id,
|
||
|
|
];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return $db->insert('module_country', $dataToInsert, false, true, Db::INSERT_IGNORE);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @param string $serviceName
|
||
|
|
*
|
||
|
|
* @return object|null
|
||
|
|
*/
|
||
|
|
public function getService(string $serviceName)
|
||
|
|
{
|
||
|
|
if ($this->serviceContainer === null) {
|
||
|
|
$this->serviceContainer = new PrestaShop\ModuleLibServiceContainer\DependencyInjection\ServiceContainer(
|
||
|
|
$this->name . str_replace(['.', '-', '+'], '', $this->version),
|
||
|
|
$this->getLocalPath()
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
return $this->serviceContainer->getService($serviceName);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Check if the module can process to a payment with the
|
||
|
|
* current currency
|
||
|
|
*
|
||
|
|
* @param Cart $cart
|
||
|
|
*
|
||
|
|
* @return bool
|
||
|
|
*/
|
||
|
|
private function checkCurrency(Cart $cart): bool
|
||
|
|
{
|
||
|
|
if (isset(static::$currencyIsAllowed[$cart->id_currency])) {
|
||
|
|
return static::$currencyIsAllowed[$cart->id_currency];
|
||
|
|
}
|
||
|
|
|
||
|
|
$cartCurrency = Currency::getCurrencyInstance($cart->id_currency);
|
||
|
|
$isCurrencySupported = false;
|
||
|
|
|
||
|
|
foreach (array_keys(PayPalCodeConfiguration::getCurrencyCodes()) as $supportedCurrencyCode) {
|
||
|
|
if (strcasecmp($supportedCurrencyCode, $cartCurrency->iso_code) === 0) {
|
||
|
|
$isCurrencySupported = true;
|
||
|
|
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!$isCurrencySupported) {
|
||
|
|
static::$currencyIsAllowed[$cart->id_currency] = false;
|
||
|
|
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @var array $moduleCurrencies */
|
||
|
|
$moduleCurrencies = $this->getCurrency($cart->id_currency);
|
||
|
|
|
||
|
|
if (empty($moduleCurrencies)) {
|
||
|
|
static::$currencyIsAllowed[$cart->id_currency] = false;
|
||
|
|
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
foreach ($moduleCurrencies as $moduleCurrency) {
|
||
|
|
if ($cartCurrency->id == $moduleCurrency['id_currency']) {
|
||
|
|
static::$currencyIsAllowed[$cart->id_currency] = true;
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static::$currencyIsAllowed[$cart->id_currency] = false;
|
||
|
|
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Check if PayPal and ps account are valid
|
||
|
|
*
|
||
|
|
* @return bool
|
||
|
|
*/
|
||
|
|
private function merchantIsValid()
|
||
|
|
{
|
||
|
|
if (static::$merchantIsValid === null) {
|
||
|
|
/** @var MerchantValidator $merchantValidator */
|
||
|
|
$merchantValidator = $this->getService(MerchantValidator::class);
|
||
|
|
static::$merchantIsValid = $merchantValidator->isValid();
|
||
|
|
}
|
||
|
|
|
||
|
|
return static::$merchantIsValid;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function hookActionObjectOrderCarrierUpdateAfter(array $params)
|
||
|
|
{
|
||
|
|
$orderCarrier = $params['object'] ?? null;
|
||
|
|
|
||
|
|
if (!Validate::isLoadedObject($orderCarrier)) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
$order = new Order((int) $orderCarrier->id_order);
|
||
|
|
$carrier = new Carrier((int) $orderCarrier->id_carrier);
|
||
|
|
|
||
|
|
$this->processTrackingNumberUpdate($order, $carrier);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function hookActionGetOrderShipments(array $params)
|
||
|
|
{
|
||
|
|
foreach ($params as $shipment) {
|
||
|
|
// External shipment data comes in $params
|
||
|
|
$order = $shipment['order'] ?? new \Order($shipment['id_order']);
|
||
|
|
|
||
|
|
if (!Validate::isLoadedObject($order)) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
/** @var ProcessExternalShipmentAction $processExternalShipmentAction */
|
||
|
|
$processExternalShipmentAction = $this->getService(ProcessExternalShipmentAction::class);
|
||
|
|
|
||
|
|
// Process external shipment data (stop on error as requested)
|
||
|
|
$processExternalShipmentAction->execute($order, $shipment);
|
||
|
|
} catch (\Exception $exception) {
|
||
|
|
/** @var LoggerInterface $logger */
|
||
|
|
$logger = $this->getService(LoggerInterface::class);
|
||
|
|
$logger->error('Failed to process external shipment data', [
|
||
|
|
'order_id' => $order->id ?? 'unknown',
|
||
|
|
'exception' => $exception->getMessage()
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Common logic to process tracking number update.
|
||
|
|
*
|
||
|
|
* @param Order|null $order
|
||
|
|
* @param Carrier|null $carrier
|
||
|
|
*
|
||
|
|
* @return void
|
||
|
|
*/
|
||
|
|
private function processTrackingNumberUpdate($order, $carrier)
|
||
|
|
{
|
||
|
|
try {
|
||
|
|
if (!Validate::isLoadedObject($order) || $order->module !== $this->name) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($carrier->external_module_name) {
|
||
|
|
$carrierModule = Module::getInstanceByName($carrier->external_module_name);
|
||
|
|
if ($carrierModule && HookCore::isModuleRegisteredOnHook($carrierModule, 'actionGetOrderShipments', $order->id_shop)) {
|
||
|
|
// Wait for external module to execute that hook
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @var AddTrackingAction $addTrackingAction */
|
||
|
|
$addTrackingAction = $this->getService(AddTrackingAction::class);
|
||
|
|
$addTrackingAction->execute($order, $carrier);
|
||
|
|
} catch (\Exception $exception) {
|
||
|
|
/** @var LoggerInterface $logger */
|
||
|
|
$logger = $this->getService(LoggerInterface::class);
|
||
|
|
$logger->error('Failed to process tracking number update', [
|
||
|
|
'order_id' => $order->id ?? 'unknown',
|
||
|
|
'carrier_id' => $carrier->id ?? 'unknown',
|
||
|
|
'exception' => $exception->getMessage()
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|