Add new payment and shipping parsers for various integrations

- Implemented Google Pay parser in bongooglepay.js
- Added Buckaroo 3 payment parser in buckaroo3.js
- Introduced DataTrans CW Mastercard parser in datatranscw.js
- Created DataTrans CW Credit Card parser in datatranscw_creditcard.js
- Developed DHL Assistant shipping parser in dhlassistant.js
- Added Estimated Delivery parser in estimateddelivery.js
- Implemented Floapay payment parser in floapay.js
- Created FS Pickup at Store shipping parser in fspickupatstore.js
- Developed Generic Iframe parser in generic_iframe_parser.js
- Added Geodis Officiel shipping parser in geodisofficiel.js
- Implemented Glob Kurier module shipping parser in globkuriermodule.js
- Created Latvija Post Express Pickup Terminal parser in latvijaspastsexpresspastspostterminalslv.js
- Developed LP Shipping parser in lpshipping.js
- Added Mijora Venipak parser in mijoravenipak.js
- Implemented Apple Pay parser in pm_applepay.js
- Created Przelewy24 payment parser in przelewy24.js
- Developed Pshugls shipping parser in pshugls.js
- Added Redsys Insite payment parser in redsysinsite.js
- Implemented Tpay payment parser in tpay.js
- Updated third-party integration documentation for FedEx DotCom
This commit is contained in:
2025-08-04 23:10:27 +02:00
parent 037a6c5551
commit d39433f0d4
125 changed files with 4986 additions and 1772 deletions

View File

@@ -10,8 +10,13 @@
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
*/
if (!defined('_PS_VERSION_')) {
exit;
}
use PrestaShop\PrestaShop\Adapter\Product\PriceFormatter;
use \PrestaShop\PrestaShop\Core\Module\WidgetInterface;
use PrestaShop\PrestaShop\Core\Addon\Module\ModuleManagerBuilder;
use PrestaShop\PrestaShop\Adapter\ObjectPresenter;
use module\thecheckout\Config;
use module\thecheckout\SocialLogin;
@@ -39,6 +44,8 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
private $amazonpayOngoingSession = false;
private $tcCopyInvoicePhoneToDelivery = true;
public function __construct()
{
$_GET['module'] = $this->name;
@@ -72,7 +79,13 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
// oyejorge/less.php v1.7.1
private function autoCompileLess($inputFile, $outputFile)
{
require_once $this->module_root . "/lib/less.php_1.7.0.10/Less.php";
$lessLib = $this->module_root . "/lib/less.php_1.7.0.10/Less.php";
// If less library is not present (e.g. in production, when used package from Addons), do not compile .less files at all
if (!file_exists($lessLib)) {
return;
}
require_once $lessLib;
$cacheDir = _PS_CACHE_DIR_ . 'thecheckout/';
$less_files = array($inputFile => '');
@@ -95,37 +108,6 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
}
}
//
// lessc 0.4 implementation
// private function autoCompileLess($inputFile, $outputFile)
// {
// require $this->module_root . "/lib/lessc.inc.php";
//
// $cacheFile = $inputFile . ".cache";
//
// if (file_exists($cacheFile)) {
// $cache = unserialize(file_get_contents($cacheFile));
// } else {
// $cache = $inputFile;
// }
//
// $less = new lessc;
// if (!$this->module->debug) {
// $less->setFormatter("compressed");
// }
//
// $forceCompile = ($this->module->debug) ? true : false;
//
// $newCache = $less->cachedCompile($cache, $forceCompile);
//
// if (!is_array($cache) || $newCache["updated"] > $cache["updated"]) {
// file_put_contents($cacheFile, serialize($newCache));
// file_put_contents($outputFile, $newCache['compiled']);
// }
// }
private function compileLess()
{
try {
@@ -166,6 +148,12 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
array('media' => 'all', 'priority' => 140, 'server' => 'remote'));
}
if ($this->module->config->social_login_fb) {
$this->context->controller->registerStylesheet('modules-thecheckout-' . ($i++),
'//fonts.googleapis.com/css?family=Roboto:500',
array('media' => 'all', 'priority' => 140, 'server' => 'remote'));
}
// Include all views/css/*.css and views/js/*.js files
foreach (glob(_PS_ROOT_DIR_ . '/modules/' . $this->name . "/views/css/*.css") as $filename) {
$this->context->controller->registerStylesheet('modules-thecheckout-' . ($i++),
@@ -404,6 +392,11 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
// by other modules, not bound to specific field
$formField->setType('hidden');
}
if ($fieldName === 'other') {
if ($this->module->config->use_other_field_for_business_private) {
$formField->setCssClass('use-other-for-business-private');
}
}
} elseif (count($fieldParts) === 2) {
list($entity, $entityField) = $fieldParts;
@@ -447,7 +440,7 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
} elseif ($entity === 'State') {
if ($country->contains_states) {
$states = State::getStatesByIdCountry($country->id, true); // true = only active states
// Sort states by alphabet
// Sort states by alphabet - uncomment to activate
// usort($states, function ($a, $b) {
// $ax = strtr($a['name'], 'Ñ', 'N');
// $bx = strtr($b['name'], 'Ñ', 'N');
@@ -655,6 +648,9 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
->setName('token')
->setType('hidden')
->setValue($this->makeAddressPersister()->getToken()),
'general_error' => (new CheckoutFormField)
->setName('general_error')
->setType('hidden')
);
foreach ($this->module->config->customer_fields as $fieldName => $fieldOptions) {
@@ -781,6 +777,7 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
$format[$moduleField->moduleName . '_' . $moduleField->getName()] = $moduleField;
}
// Sample format structure
/*
$format = [
'back' => (new CheckoutFormField)
@@ -955,7 +952,7 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
if ($this->context->customer->isLogged()) {
$customerAddresses = $this->context->customer->getSimpleAddresses();
foreach ($customerAddresses as &$a) {
$a['formatted'] = AddressFormat::generateAddress(new Address($a['id']), array(), '<br>');
$a['formatted'] = AddressFormat::generateAddress(new Address($a['id']), array(), $this->module->tagIt('br', ''));
}
$allCustomerUsedAddresses = $this->getAllCustomerUsedAddresses();
@@ -988,51 +985,7 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
if ($usedForInvoice || !$usedForDelivery) {
$addressesList['invoice'][$addressId] = $customerAddresses[$addressId];
}
// Data preparation for other purposes, e.g. setting up this address filter in PS 'addresses'
// For that, controllers/front/AddressesController.php needs to include this in initContent():
//
// if (file_exists(_PS_MODULE_DIR_ . 'thecheckout/controllers/front/front.php')) {
// include_once(_PS_MODULE_DIR_ . 'thecheckout/controllers/front/front.php');
// $tc_frontController = new TheCheckoutModuleFrontController();
// $delivery_invoice_addresses = $tc_frontController->api_getAddressSelectionTplVars();
//
// $this->context->smarty->assign('delivery_invoice_addresses', $delivery_invoice_addresses);
// }
//
// And respective template, /themes/classic/templates/customer/addresses.tpl shall be also updated
// $addressesList['invoice'] + $addressesList['usedDeliveryExclusive'] make up "full set",
// as 'invoice' includes also addresses we can't exactly say are invoice or delivery:
//
// {if isset($delivery_invoice_addresses) && isset($delivery_invoice_addresses.addressesList)}
// {if isset($delivery_invoice_addresses.addressesList.invoice)}
// <div class="invoice-primary-addresses" style="float:left;width:100%">
// <h2>{l s='Primary and invoice addresses' d='Shop.Theme.Customeraccount'}</h2>
// {foreach $delivery_invoice_addresses.addressesList.invoice as $address}
// <div class="col-lg-4 col-md-6 col-sm-6">
// {block name='customer_address'}
// {include file='customer/_partials/block-address.tpl' address=$address}
// {/block}
// </div>
// {/foreach}
// </div>
// {/if}
// {/if}
//
// {if isset($delivery_invoice_addresses) && isset($delivery_invoice_addresses.addressesList)}
// {if isset($delivery_invoice_addresses.addressesList.usedDeliveryExclusive)}
// <div class="delivery-addresses" style="float:left;width:100%">
// <h2>{l s='Delivery addresses' d='Shop.Theme.Customeraccount'}</h2>
// {foreach $delivery_invoice_addresses.addressesList.usedDeliveryExclusive as $address}
// <div class="col-lg-4 col-md-6 col-sm-6">
// {block name='customer_address'}
// {include file='customer/_partials/block-address.tpl' address=$address}
// {/block}
// </div>
// {/foreach}
// </div>
// {/if}
// {/if}
//
if ($usedForDelivery && !$usedForInvoice) {
$addressesList['usedDeliveryExclusive'][$addressId] = $customerAddresses[$addressId];
}
@@ -1101,7 +1054,8 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
if (
'' != $businessFieldName &&
isset($formFieldsInvoiceMapped[$businessFieldName]) &&
null != trim($formFieldsInvoiceMapped[$businessFieldName]['value']) &&
null != $formFieldsInvoiceMapped[$businessFieldName]['value'] &&
'' != trim($formFieldsInvoiceMapped[$businessFieldName]['value']) &&
'id_state' !== $formFieldsInvoiceMapped[$businessFieldName]['name'] &&
'id_country' !== $formFieldsInvoiceMapped[$businessFieldName]['name'] &&
('dni' !== $businessFieldName || 'need-dni' !== $formFieldsInvoice['dni']->getCssClass())
@@ -1109,6 +1063,14 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
$hideBusinessFields = false;
}
}
if ($hideBusinessFields && $this->module->config->use_other_field_for_business_private &&
isset($formFieldsInvoiceMapped['other']) &&
null != $formFieldsInvoiceMapped['other']['value'] &&
$this->trans('business', [], 'Modules.Thecheckout.front') === trim($formFieldsInvoiceMapped['other']['value'])
) {
$hideBusinessFields = false;
}
$hidePrivateFields = true;
// if businessFields are visible (=not $hideBusinessFields), private fields will be hidden; otherwise, let's make check:
@@ -1117,7 +1079,8 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
if (
'' != $privateFieldName &&
isset($formFieldsInvoiceMapped[$privateFieldName]) &&
null != trim($formFieldsInvoiceMapped[$privateFieldName]['value']) &&
null != $formFieldsInvoiceMapped[$privateFieldName]['value'] &&
'' != trim($formFieldsInvoiceMapped[$privateFieldName]['value']) &&
'id_state' !== $formFieldsInvoiceMapped[$privateFieldName]['name'] &&
'id_country' !== $formFieldsInvoiceMapped[$privateFieldName]['name'] &&
('dni' !== $privateFieldName || 'need-dni' !== $formFieldsInvoice['dni']->getCssClass())
@@ -1125,6 +1088,13 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
$hidePrivateFields = false;
}
}
if ($hidePrivateFields && $this->module->config->use_other_field_for_business_private &&
isset($formFieldsInvoiceMapped['other']) &&
null != $formFieldsInvoiceMapped['other']['value'] &&
$this->trans('private', [], 'Modules.Thecheckout.front') === trim($formFieldsInvoiceMapped['other']['value'])
) {
$hidePrivateFields = false;
}
}
// Same for delivery address fields:
@@ -1141,7 +1111,8 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
if (
'' != $businessFieldName &&
isset($formFieldsDeliveryMapped[$businessFieldName]) &&
null != trim($formFieldsDeliveryMapped[$businessFieldName]['value']) &&
null != $formFieldsDeliveryMapped[$businessFieldName]['value'] &&
'' != trim($formFieldsDeliveryMapped[$businessFieldName]['value']) &&
'id_state' !== $formFieldsDeliveryMapped[$businessFieldName]['name'] &&
'id_country' !== $formFieldsDeliveryMapped[$businessFieldName]['name'] &&
('dni' !== $businessFieldName || 'need-dni' !== $formFieldsInvoice['dni']->getCssClass())
@@ -1149,6 +1120,13 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
$hideBusinessFieldsDelivery = false;
}
}
if ($hideBusinessFieldsDelivery && $this->module->config->use_other_field_for_business_private &&
isset($formFieldsDeliveryMapped['other']) &&
null != $formFieldsDeliveryMapped['other']['value'] &&
$this->trans('business', [], 'Modules.Thecheckout.front') === trim($formFieldsDeliveryMapped['other']['value'])
) {
$hideBusinessFieldsDelivery = false;
}
$hidePrivateFieldsDelivery = true;
// if businessFields are visible (=not $hideBusinessFields), private fields will be hidden; otherwise, let's make check:
@@ -1157,7 +1135,8 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
if (
'' != $privateFieldName &&
isset($formFieldsDeliveryMapped[$privateFieldName]) &&
null != trim($formFieldsDeliveryMapped[$privateFieldName]['value']) &&
null != $formFieldsDeliveryMapped[$privateFieldName]['value'] &&
'' != trim($formFieldsDeliveryMapped[$privateFieldName]['value']) &&
'id_state' !== $formFieldsDeliveryMapped[$privateFieldName]['name'] &&
'id_country' !== $formFieldsDeliveryMapped[$privateFieldName]['name'] &&
('dni' !== $privateFieldName || 'need-dni' !== $formFieldsInvoice['dni']->getCssClass())
@@ -1165,6 +1144,13 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
$hidePrivateFieldsDelivery = false;
}
}
if ($hidePrivateFieldsDelivery && $this->module->config->use_other_field_for_business_private &&
isset($formFieldsDeliveryMapped['other']) &&
null != $formFieldsDeliveryMapped['other']['value'] &&
$this->trans('private', [], 'Modules.Thecheckout.front') === trim($formFieldsDeliveryMapped['other']['value'])
) {
$hidePrivateFieldsDelivery = false;
}
}
// Old code, when business fields were hard-coded
@@ -1248,35 +1234,78 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
$this->context->cookie->id_cart = $duplication['cart']->id;
$context = $this->context;
$context->cart = $duplication['cart'];
if ($this->module->config->use_old_address_on_reorder) {
// assign oldCart's addresses to the new cart
$oldDeliveryAddress = new Address($oldCart->id_address_delivery);
$oldInvoiceAddress = new Address($oldCart->id_address_invoice);
if (!$oldDeliveryAddress->deleted) {
$this->context->cart->id_address_delivery = $oldCart->id_address_delivery;
}
if (!$oldInvoiceAddress->deleted) {
$this->context->cart->id_address_invoice = $oldCart->id_address_invoice;
}
$this->context->cart->update();
}
CartRule::autoAddToCart($context);
$this->context->cookie->write();
Tools::redirect('index.php?controller=order');
// Tools::redirect('index.php?controller=order');
}
}
}
private function isShopVersion17Plus()
{
return version_compare(_PS_VERSION_, '1.7.0.0', '>=');
}
private function isShopVersion8Plus()
{
return version_compare(_PS_VERSION_, '8.0.0', '>=');
}
private function isShopVersion813Plus()
{
return version_compare(_PS_VERSION_, '8.1.3', '>=');
}
private function isModuleEnabled($moduleName) {
if (false === $this->isShopVersion17Plus()) {
return \Module::isInstalled($moduleName) && \Module::isEnabled($moduleName);
}
$moduleManagerBuilder = ModuleManagerBuilder::getInstance();
$moduleManager = $moduleManagerBuilder->build();
return $moduleManager->isInstalled($moduleName) && \Module::isEnabled($moduleName);
}
public function initContent()
{
// kernel initialization moved here from thecheckout.php, so that it executes less often
$this->module->initPsKernel();
// Can we skip it for ajax calls? parent::initContent set caches for delivery options,
// if enabled here, we'd need to flush caches before ajax call
//parent::initContent();
// Initiate checkoutProcess object for ps_checkout module
if (version_compare(_PS_VERSION_, '1.7.3') >= 0 &&
Module::isInstalled('xps_checkout') && Module::isEnabled('xps_checkout')) {
$deliveryOptionsFinder = new DeliveryOptionsFinder(
$this->context,
$this->getTranslator(),
new ObjectPresenter(),
new PriceFormatter()
);
$session = new CheckoutSession(
$this->context,
$deliveryOptionsFinder
);
$this->checkoutProcess = new CheckoutProcess($this->context, $session);
}
// if (version_compare(_PS_VERSION_, '1.7.3') >= 0 &&
// Module::isInstalled('xps_checkout') && Module::isEnabled('xps_checkout')) {
// $deliveryOptionsFinder = new DeliveryOptionsFinder(
// $this->context,
// $this->getTranslator(),
// new ObjectPresenter(),
// new PriceFormatter()
// );
//
// $session = new CheckoutSession(
// $this->context,
// $deliveryOptionsFinder
// );
// $this->checkoutProcess = new CheckoutProcess($this->context, $session);
// }
if (Configuration::get('PS_RESTRICT_DELIVERED_COUNTRIES')) {
@@ -1308,6 +1337,11 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
} else {
$this->parentInitContent();
// Reset carrier selection on full-page load
// $opc_form_radios = json_decode($this->context->cookie->opc_form_radios, true);
// unset($opc_form_radios['delivery_option']);
// $this->context->cookie->opc_form_radios = json_encode($opc_form_radios);
// Remove potentially unwanted JS includes from payment method - if we include them in hook call
//print_r($this->context->controller->getJavascript());
$this->context->controller->unregisterJavascript('paypal-plus-payment-js');
@@ -1348,9 +1382,21 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
if ($this->context->cart->id != $this->context->cookie->addreses_reset_at_cart_id) {
$lastOrderAddresses = $this->getCustomerLastUsedAddresses($this->getAllCustomerUsedAddresses());
if (count($lastOrderAddresses)) {
$this->context->cart->id_address_invoice = $lastOrderAddresses['id_address_invoice'];
$this->context->cart->id_address_delivery = $lastOrderAddresses['id_address_delivery'];
$this->context->cart->update();
if (!$this->module->config->use_old_address_on_reorder) {
$this->context->cart->id_address_invoice = $lastOrderAddresses['id_address_invoice'];
$this->context->cart->id_address_delivery = $lastOrderAddresses['id_address_delivery'];
$this->context->cart->update();
} else {
$oldDeliveryAddress = new Address($this->context->cart->id_address_delivery);
$oldInvoiceAddress = new Address($this->context->cart->id_address_invoice);
if ($oldDeliveryAddress->deleted) {
$this->context->cart->id_address_delivery = $lastOrderAddresses['id_address_delivery'];
}
if ($oldInvoiceAddress->deleted) {
$this->context->cart->id_address_invoice = $lastOrderAddresses['id_address_invoice'];
}
}
$this->context->cart->setNoMultishipping();
$this->updateAddressIdInDeliveryOptions();
$this->context->cookie->addreses_reset_at_cart_id = $this->context->cart->id;
@@ -1391,7 +1437,7 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
}
// myparcel loads iframe picker, and thus markup is always same even though, we need to change iframe content always
$forceRefreshShipping = Module::isInstalled('myparcel');
$forceRefreshShipping = $this->isModuleEnabled('myparcel');
// Logged-in customer groups
$customer_groups = $this->context->customer->getGroups();
@@ -1423,18 +1469,19 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
$page["body_classes"]["collapse-shipping-methods"] = $this->module->config->collapse_shipping_methods;
$page["body_classes"]["collapse-payment-methods"] = $this->module->config->collapse_payment_methods;
$page["body_classes"]["fetchifyuk-enabled"] = Module::isEnabled('fetchifyuk'); // formerly craftyclicks
$page["body_classes"]["logos-on-the-right"] = $this->module->config->logos_on_the_right;
if ((Configuration::get('PAYPAL_EXPRESS_CHECKOUT_SHORTCUT') || Configuration::get('PAYPAL_EXPRESS_CHECKOUT_SHORTCUT_CART')) && (isset($this->context->cookie->paypal_ecs) || isset($this->context->cookie->paypal_pSc))) {
$page["body_classes"]["paypal-express-checkout-session"] = true;
}
$installedModules = array();
foreach (array('mondialrelay', 'einvoicingprestalia') as $moduleName) {
$installedModules[$moduleName] = Module::isInstalled($moduleName) && Module::isEnabled($moduleName);
foreach (array('mondialrelay', 'einvoicingprestalia', 'chronopost') as $moduleName) {
$installedModules[$moduleName] = $this->isModuleEnabled($moduleName);
}
$sendcloud_moduleName = 'sendcloud';
$sendcloud_script = '';
if (Module::isInstalled($sendcloud_moduleName) && Module::isEnabled($sendcloud_moduleName)) {
if ($this->isModuleEnabled($sendcloud_moduleName)) {
$sendcloud_moduleInstance = Module::getInstanceByName($sendcloud_moduleName);
if (isset($sendcloud_moduleInstance->connector) && $sendcloud_moduleInstance->connector) {
$sendcloud_script = $sendcloud_moduleInstance->connector->getServicePointScript();
@@ -1451,7 +1498,7 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
} else {
$needAddressModules = array('paypal', 'sendcloud', 'mondialrelay', 'omniva', 'multisafepay');
foreach ($needAddressModules as $moduleName) {
if (Module::isInstalled($moduleName) && Module::isEnabled($moduleName)) {
if ($this->isModuleEnabled($moduleName)) {
$forceAddressCreation = true;
break;
}
@@ -1465,6 +1512,20 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
);
$this->unifyAddresses(true, false);
}
$needCheckoutSessionModules = array('mondialrelay');
foreach ($needCheckoutSessionModules as $moduleName) {
if ($this->isModuleEnabled($moduleName)) {
$this->updateCheckoutSession(false);
break;
}
}
}
// render paypal express checkout button in sign-up?
$paypal_express_checkout = '';
if ($this->module->config->paypal_express_checkout) {
$paypal_express_checkout = Hook::exec('displayExpressCheckout');
}
$this->context->smarty->assign(array(
@@ -1489,7 +1550,8 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
'forceRefreshShipping' => $forceRefreshShipping,
'installedModules' => $installedModules,
'separatePaymentKeyName' => Config::SEPARATE_PAYMENT_KEY_NAME,
'sendcloud_script' => $sendcloud_script
'sendcloud_script' => $sendcloud_script,
'paypal_express_checkout' => $paypal_express_checkout
));
$amazonPayCheckoutSessionClass = "AmazonPayCheckoutSession";
@@ -1579,7 +1641,7 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
if (version_compare(_PS_VERSION_, '1.7.3') >= 0) {
$this->context->cart->delivery_option = json_encode($newDeliveryOptions);
} else {
$this->context->cart->delivery_option = serialize($newDeliveryOptions);
// 16.10.2023 - we won't support older PS versions anymore, so this is left empty
}
}
$this->context->cart->autosetProductAddress();
@@ -1614,6 +1676,24 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
return $self->getCheckoutSession()->getDeliveryOptions();
});
$priceFormatter = new PriceFormatter();
$freeShippingLabel = $this->translator->trans(
'Free',
[],
'Shop.Theme.Checkout'
);
foreach ($deliveryOptions as &$option) {
if ($option['price'] === $freeShippingLabel) {
$option['price_with_tax_formatted'] = $option['price'];
$option['price_without_tax_formatted'] = $option['price'];
} else {
$option['price_with_tax_formatted'] = $priceFormatter->format($option['price_with_tax']);
$option['price_without_tax_formatted'] = $priceFormatter->format($option['price_without_tax']);
}
// $option['price'] = " -- [price] '" . $option['price'] . "' == [freeShippingLabel] '" . $freeShippingLabel ."' (". ($option['price'] === $freeShippingLabel) .")";
}
return
array(
'hookDisplayBeforeCarrier' => Hook::exec('displayBeforeCarrier',
@@ -1640,24 +1720,17 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
);
}
// public function selectPaymentOption(array $requestParams = array())
// {
// if (isset($requestParams['select_payment_option'])) {
// $this->selected_payment_option = $requestParams['select_payment_option'];
// }
//
// $this->setTitle(
// $this->getTranslator()->trans(
// 'Payment',
// array(),
// 'Shop.Theme.Checkout'
// )
// );
// }
public function getPaymentOptions()
{
$isFree = 0 == (float)$this->getCheckoutSession()->getCart()->getOrderTotal(true, Cart::BOTH);
# if paypal module is installed and enabled, make sure that if customer object is empty, set is_guest to 1
if ($this->isModuleEnabled('paypal')) {
if (empty($this->context->customer->id)) {
$this->context->customer->is_guest = 1;
}
}
$paymentOptionsFinder = new PaymentOptionsFinder();
$paymentOptions = $paymentOptionsFinder->present($isFree);
@@ -1669,6 +1742,17 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
$paymentOptions["amazonpay"] = $amazonPayOption;
}
// pm_applepay module doesn't use paymentOptions hook, so we'll add it manually
if ($this->isModuleEnabled('pm_applepay') && Module::getInstanceByName('pm_applepay')->isPaymentAvailable()) {
$paymentOptions['pm_applepay'][] = array(
'id' => 'payment-option-1001',
'module_name' => 'pm_applepay',
'call_to_action_text' => $this->module->getTranslation('Apple Pay'),
'form' => $this->module->tagIt('div', '', 'id="pm_applepay-popup-container"'),
'binary' => true
);
}
return array(
'is_free' => $isFree,
'payment_options' => $paymentOptions,
@@ -1680,29 +1764,20 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
public function ajaxCall()
{
// Uncomment to debug ajaxCall
// @error_reporting(E_ALL & ~E_NOTICE);
// multisafepay fix BEGIN
// multisafepay.php:hasSetApiKey(), loads multisafepay.sdk_service, but the $kernel is not started in this ajax request yet
// Commented out due to validator requirements (no globals), uncomment for multisafepay support
// global $kernel;
// if(!$kernel){
// require_once _PS_ROOT_DIR_.'/app/AppKernel.php';
// $kernel = new \AppKernel('prod', false);
// $kernel->boot();
// }
// multisafepay fix END
if ($this->module->debug) {
$this->module->logDebug("[AJAX*Start] " . Tools::getValue('action'));
}
$action = Tools::ucfirst(Tools::getValue('action'));
$action = Tools::ucfirst(strip_tags(Tools::getValue('action')));
if (!empty($action) && method_exists($this, 'ajax' . $action)) {
$this->context->smarty->assign("tc_config", $this->module->config);
$this->context->smarty->assign("z_tc_config", $this->module->config);
$result = $this->{'ajax' . $action}();
} else {
$result = (array('error' => 'Ajax parameter used, but action \'' . Tools::getValue('action') . '\' is not defined'));
$result = (array('error' => 'Ajax parameter used, but action \'' . strip_tags(Tools::getValue('action')) . '\' is not defined'));
}
if ($this->module->debug) {
@@ -1944,10 +2019,11 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
}
}
// add dateofdelivery module name into external shipping modules list, so that its parser is loaded at JS level
$dateofdelivery_moduleName = 'dateofdelivery';
if (Module::isInstalled($dateofdelivery_moduleName) && Module::isEnabled($dateofdelivery_moduleName)) {
$externalShippingModules[$dateofdelivery_moduleName] = 0;
// add dateofdelivery and apaczka module name into external shipping modules list, so that its parser is loaded at JS level
$externalShippingModuleNames = ['dateofdelivery', 'apaczka', 'upsservice', 'globkuriermodule'];
foreach ($externalShippingModuleNames as $ext_moduleName)
if (Module::isInstalled($ext_moduleName) && Module::isEnabled($ext_moduleName)) {
$externalShippingModules[$ext_moduleName][] = 0;
}
$this->getCheckoutSession()->setDeliveryOption(
@@ -1999,7 +2075,7 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
}
}
$this->context->smarty->assign('shippingAddressNotice', $shippingAddressNotice);
$this->context->smarty->assign('shippingAddressNotice', implode(', ', $shippingAddressNotice));
}
@@ -2076,11 +2152,30 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
$paymentMethods = $this->getPaymentOptions();
// add md5 checksum to payment options (+exceptions)
// Some payment modules (e.g. paynow) do not always return same order of payment methods, so we need an ID that would be unique for every option
foreach ($paymentMethods['payment_options'] as $modName => &$options) {
foreach ($options as &$option) {
// ps_checkout needs exact data-module-name attribute, it won't initialize widget otherwise
if ($modName == 'ps_checkout' ||
strpos($modName, 'paypal') === 0 ||
strpos($modName, 'przelewy-method') === 0 ||
strpos($modName, 'przelewy24') === 0 ||
strpos($modName, 'stripe_official') === 0
) {
$option['call_to_action_text_md5'] = '';
} else {
$option['call_to_action_text_md5'] = '-'.substr(md5($option['call_to_action_text']), -5);
}
}
}
$this->context->smarty->assign($paymentMethods);
// // Payment data, used by Stripe payment for live refresh (and possibly other modules in future)
// $currency = $this->context->currency->iso_code;
// $currency = $this->context->currency->iso_code;
// $orderTotal = $this->context->cart->getOrderTotal();
// $stripeAmount = Tools::ps_round($orderTotal, 2);
// $stripeAmount = $this->isZeroDecimalCurrency($currency) ? $stripeAmount : $stripeAmount * 100;
@@ -2390,6 +2485,35 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
// $this->context->country->need_identification_number = false;
// }
// Copy invoice phone or phone_mobile to delivery phone or phone_mobile, if delivery phone or phone_mobile is empty and both phone fields are disabled in delivery form
if ($this->tcCopyInvoicePhoneToDelivery && $this->context->cart->id_address_invoice !== $this->context->cart->id_address_delivery &&
!$this->module->config->delivery_fields['phone_mobile']['visible'] && !$this->module->config->delivery_fields['phone']['visible']) {
# if we're updating invoice address, take phone/phone_mobile from $formData
# if we're updating delivery address, take phone/phone_mobile from invoice address ojbect
if ($isAddressTypeInvoice) {
$invoicePhoneMobile = isset($formData['phone_mobile']) && $formData['phone_mobile'] ? trim($formData['phone_mobile']) : '';
$invoicePhone = isset($formData['phone']) && $formData['phone'] ? trim($formData['phone']) : '';
$deliveryAddress = new Address($this->context->cart->id_address_delivery);
if ($invoicePhoneMobile !== "") {
$deliveryAddress->phone_mobile = $invoicePhoneMobile;
$deliveryAddress->save();
} elseif ($invoicePhone !== "") {
$deliveryAddress->phone = $invoicePhone;
$deliveryAddress->save();
}
} else {
$invoiceAddress = new Address($this->context->cart->id_address_invoice);
$invoicePhoneMobile = trim($invoiceAddress->phone_mobile);
$invoicePhone = trim($invoiceAddress->phone);
if ($invoicePhoneMobile !== "") {
$formData['phone_mobile'] = $invoicePhoneMobile;
} elseif ($invoicePhone !== "") {
$formData['phone'] = $invoicePhone;
}
}
}
$theCheckout_addressForm = new CheckoutAddressForm(
$this->module,
$this->context->smarty,
@@ -2630,9 +2754,18 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
//$this->updateAddressIdInDeliveryOptions();
$tpl_hasErrors = $registerForm->hasErrors();
$tpl_errors = $registerForm->getErrors();
if ($this->isModuleEnabled('eicaptcha')) {
Hook::exec('actionCustomerRegisterSubmitCaptcha');
$tpl_hasErrors = $tpl_hasErrors || sizeof($this->context->controller->errors);
$tpl_errors = array_merge($tpl_errors, array('captcha' => $this->context->controller->errors));
}
return array(
"hasErrors" => $registerForm->hasErrors(),
"errors" => $registerForm->getErrors(),
"hasErrors" => $tpl_hasErrors,
"errors" => $tpl_errors,
"customerId" => $this->context->customer->id,
"newToken" => Tools::getToken(true, $this->context),
"newStaticToken" => Tools::getToken(false),
@@ -2676,10 +2809,18 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
return $isComplete;
}
private function copyPropertyFromToIfEmpty(&$source, &$target, $propertyName) {
if ((!isset($target[$propertyName]) || "" == trim($target[$propertyName])) &&
isset($source[$propertyName]) && "" != trim($source[$propertyName])) {
$target[$propertyName] = $source[$propertyName];
private function copyPropertyFromToIfEmpty(&$source, &$target, $propertyName, $alternativeValue) {
$emptyValues = array('');
if (in_array($propertyName, array('firstname', 'lastname'))) {
$emptyValues = array('', 'a', 'A');
}
if ((!isset($target[$propertyName]) || in_array(trim($target[$propertyName]), $emptyValues)))
{
if (isset($source[$propertyName]) && !in_array(trim($source[$propertyName]), $emptyValues)) {
$target[$propertyName] = $source[$propertyName];
} elseif (!in_array(trim($alternativeValue), $emptyValues)) {
$target[$propertyName] = $alternativeValue;
}
}
}
@@ -2693,14 +2834,16 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
$passwordRequired
) {
// check if shipping methods has any validations
$shippingModuleStepComplete = $this->isShippingModuleComplete(Tools::getAllValues());
$shippingResult = null;
if (!$shippingModuleStepComplete) {
$shippingResult = array(
'errors' => $this->context->controller->errors,
'hasErrors' => !empty($this->context->controller->errors)
);
if (!$this->context->cart->isVirtualCart()) {
// check if shipping methods has any validations
$shippingModuleStepComplete = $this->isShippingModuleComplete(Tools::getAllValues());
if (!$shippingModuleStepComplete) {
$shippingResult = array(
'errors' => $this->context->controller->errors,
'hasErrors' => !empty($this->context->controller->errors)
);
}
}
// Initialization defaults
@@ -2711,16 +2854,17 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
if ($invoiceVisible) {
// update invoice/delivery address firstname/lastname from customer name, if
// address' name is empty and customer's not (e.g. firstname/lastname can be hidden in address section)
$this->copyPropertyFromToIfEmpty($accountFormData, $invoiceFormData, 'firstname');
$this->copyPropertyFromToIfEmpty($accountFormData, $invoiceFormData, 'lastname');
$this->copyPropertyFromToIfEmpty($accountFormData, $invoiceFormData, 'firstname', $this->context->customer->firstname);
$this->copyPropertyFromToIfEmpty($accountFormData, $invoiceFormData, 'lastname', $this->context->customer->lastname);
if (isset($invoiceFormData['firstname']) && isset($invoiceFormData["lastname"])) {
$firstname = $invoiceFormData['firstname'];
$lastname = $invoiceFormData['lastname'];
}
} elseif($deliveryVisible) {
$this->copyPropertyFromToIfEmpty($accountFormData, $deliveryFormData, 'firstname');
$this->copyPropertyFromToIfEmpty($accountFormData, $deliveryFormData, 'lastname');
}
if($deliveryVisible) {
$this->copyPropertyFromToIfEmpty($accountFormData, $deliveryFormData, 'firstname', $this->context->customer->firstname);
$this->copyPropertyFromToIfEmpty($accountFormData, $deliveryFormData, 'lastname', $this->context->customer->lastname);
if (isset($deliveryFormData['firstname']) && isset($deliveryFormData["lastname"])) {
$firstname = $deliveryFormData['firstname'];
@@ -2804,6 +2948,15 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
$invoiceAddressResult = $deliveryAddressResult = null;
$finalConfirmation = true;
// copy phone or phone_mobile from invoice to delivery, if both delivery phones are not visible
if ($this->tcCopyInvoicePhoneToDelivery && $invoiceVisible && $deliveryVisible && !$this->module->config->delivery_fields['phone_mobile']['visible'] && !$this->module->config->delivery_fields['phone']['visible']) {
if ("" == trim($deliveryFormData['phone_mobile']) && "" != trim($invoiceFormData['phone_mobile'])) {
$deliveryFormData['phone_mobile'] = $invoiceFormData['phone_mobile'];
} elseif ("" == trim($deliveryFormData['phone']) && "" != trim($invoiceFormData['phone'])) {
$deliveryFormData['phone'] = $invoiceFormData['phone'];
}
}
if ($invoiceVisible) {
$invoiceAddressResult = $this->modifyAddress('invoice', $invoiceFormData, $shallCreateNewAddress,
$finalConfirmation);
@@ -2835,34 +2988,7 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
&& !$invoiceAddressErrors
&& !$deliveryAddressErrors
) {
$cartChecksum = new CartChecksum(new AddressChecksum());
// Update cart's secure key:
$this->context->cart->secure_key = $this->context->customer->secure_key;
$checkout_session_data = array(
"checkout-personal-information-step" => array(
"step_is_reachable" => true,
"step_is_complete" => true
),
"checkout-addresses-step" => array(
"step_is_reachable" => true,
"step_is_complete" => true,
"use_same_address" => ($this->context->cart->id_address_delivery == $this->context->cart->id_address_invoice)
),
"checkout-delivery-step" => array(
"step_is_reachable" => true,
"step_is_complete" => true
),
"checkout-payment-step" => array(
"step_is_reachable" => true,
"step_is_complete" => false
),
"checksum" => $cartChecksum->generateChecksum($this->context->cart)
);
$this->DB_saveCheckoutSessionData($checkout_session_data);
$this->updateCheckoutSession();
}
return array_merge(
@@ -2873,6 +2999,36 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
);
}
private function updateCheckoutSession($deliveryStepComplete = true) {
$cartChecksum = new CartChecksum(new AddressChecksum());
// Update cart's secure key:
$this->context->cart->secure_key = $this->context->customer->secure_key;
$checkout_session_data = array(
"checkout-personal-information-step" => array(
"step_is_reachable" => true,
"step_is_complete" => true
),
"checkout-addresses-step" => array(
"step_is_reachable" => true,
"step_is_complete" => true,
"use_same_address" => ($this->context->cart->id_address_delivery == $this->context->cart->id_address_invoice)
),
"checkout-delivery-step" => array(
"step_is_reachable" => true,
"step_is_complete" => $deliveryStepComplete
),
"checkout-payment-step" => array(
"step_is_reachable" => true,
"step_is_complete" => false
),
"checksum" => $cartChecksum->generateChecksum($this->context->cart)
);
$this->DB_saveCheckoutSessionData($checkout_session_data);
}
private function unifyAddresses($invoiceVisible, $deliveryVisible)
{
// We need to unify addresses, if only one is visible - so that shipping methods are always reflecting selected zone
@@ -2898,7 +3054,7 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
parse_str(Tools::getValue('account'), $accountFormData);
$email = (isset($accountFormData['email'])) ? $accountFormData['email'] : (isset($accountFormData['forced-email'])?$accountFormData['forced-email']:'');
$id_customer = Customer::customerExists($email, true, true);
$id_customer = Customer::customerExists($email, true, true); // email, returnId, ignoreGuest
// is email valid?
if ("" !== trim($email) && !filter_var($email, FILTER_VALIDATE_EMAIL)) {
@@ -2921,13 +3077,20 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
'The email "%mail%" is already used, please choose another one or sign in',
array('%mail%' => $email),
'Shop.Notifications.Error'
) . '<' . 'span id="sign-in-link"' . '>' . $this->translator->trans('Sign in', array(),
'Shop.Theme.Actions') . '<' . '/' . 'span' . '>';
) . $this->module->tagIt('span', $this->translator->trans('Sign in', array(), 'Shop.Theme.Actions'), 'id="sign-in-link"');
}
} elseif (
($this->module->config->force_email_overlay || $this->module->config->register_guest_on_blur)
&& Configuration::get('PS_GUEST_CHECKOUT_ENABLED')) {
// pre-set customer's first/lastnames, if we have that already in the session
// if this won't be done, checkEmail and consequently silentRegistration will create "a a" customer
if ($this->context->cart->id_customer) {
$customer = new Customer($this->context->cart->id_customer);
$accountFormData['firstname'] = $customer->firstname;
$accountFormData['lastname'] = $customer->lastname;
}
$this->silentRegistration($accountFormData);
} elseif ($this->module->config->show_button_save_personal_info && "tc_save_account" == Tools::getValue('triggerEl'))
{
@@ -2940,6 +3103,8 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
$this->getPaymentOptionsBlock(),
$this->getCartSummaryBlock());
}
} elseif (Configuration::get('PS_GUEST_CHECKOUT_ENABLED') && $id_customer) {
$accountResult['notices']['email'] = $this->module->getTranslation('You already have an account with us. Sign in or continue as guest.');
}
$accountResult['hasErrors'] = (isset($accountResult['hasErrors'])?$accountResult['hasErrors'] : false) || (count($errors) > 0);
@@ -3339,12 +3504,27 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
'Shop.Notifications.Error'
);
} elseif (!$eachProduct['allow_oosp'] && $eachProduct['cart_quantity'] > $eachProduct['stock_quantity']) {
$cartQuantityError =
$this->trans(
'The item %product% in your cart is no longer available in this quantity. You cannot proceed with your order until the quantity is adjusted.',
array('%product%' => $eachProduct['name']),
if ($this->isShopVersion813Plus()) {
$cartQuantityError = $this->trans(
'You can only buy %quantity% "%product%". Please adjust the quantity in your cart to continue.',
[
'%product%' => $eachProduct['name'],
'%quantity%' => $eachProduct['stock_quantity'],
],
'Shop.Notifications.Error'
);
} else {
$err_str = 'The item %product% in your cart is no longer available in this quantity. You cannot proceed with your order until the quantity is adjusted.';
if ($this->isShopVersion8Plus()) {
$err_str = '%product% is no longer available in this quantity. You cannot proceed with your order until the quantity is adjusted.';
}
$cartQuantityError =
$this->trans(
$err_str,
array('%product%' => $eachProduct['name']),
'Shop.Notifications.Error'
);
}
}
}
@@ -3369,6 +3549,21 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
reset($opc_form_radios['delivery_option']));
}
$otherErrors = [];
// 18.10.2023 - support for blockproductsbycountry module
if ($this->isModuleEnabled('blockproductsbycountry')) {
$bpbc_module = Module::getInstanceByName('blockproductsbycountry');
$bpbc_context = Context::getContext();
$bpbc_id_country = $bpbc_context->country->id;
if ($bpbc_id_country) {
foreach ($bpbc_context->cart->getProducts() as $product) {
if ($bpbc_module->isProductBlocked((int)$product['id_product'], $bpbc_id_country)) {
$otherErrors['blockproductsbycountry'] = sprintf(Configuration::get('BPBC_TEXT_BLOCKED_CART', $bpbc_context->language->id), Product::getProductName((int)$product['id_product']), $bpbc_context->country->name[$bpbc_context->language->id]);
}
}
}
}
$this->context->smarty->assign(array(
'cart' => $presentedCart,
'cartQuantityError' => $cartQuantityError,
@@ -3376,7 +3571,8 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
'js_custom_vars' => $js_custom_vars,
'forceToChooseCarrier' => (bool)$this->module->config->force_customer_to_choose_carrier,
'customerDeliveryOption' => $customerSelectedDeliveryOption,
'carrierSelected' => $this->context->cart->id_carrier
'carrierSelected' => $this->context->cart->id_carrier,
'otherErrors' => $otherErrors
));
$minimalPurchase = array();
@@ -3394,7 +3590,7 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
'emptyCart' => !($presentedCart['products_count']),
'isVirtualCart' => $this->context->cart->isVirtualCart(),
'minimalPurchaseError' => !empty($minimalPurchase),
'cartQuantityError' => ($cartQuantityError !== false),
'cartQuantityError' => ($cartQuantityError !== false)
));
}
@@ -3498,12 +3694,6 @@ class TheCheckoutModuleFrontController extends ModuleFrontController
return array("errors" => $loginForm->getErrors(), "hasErrors" => $loginForm->hasErrors());
}
// private function ajaxModifyAccount()
// {
// // Is this still used? Probably not (20.3.2019).
// return $this->modifyAccount(Tools::getAllValues());
// }
private function DB_saveCheckoutSessionData($data)
{
Db::getInstance()->execute(