diff --git a/changelog.md b/changelog.md index 4dc83f73a..3fe31cd34 100755 --- a/changelog.md +++ b/changelog.md @@ -196,4 +196,7 @@ ## [2.0.2] - Remove WL Crypto payment method - Added setting to toggle order confirmation email sending -- Added feature to group card payment methods into unified "Card" payment method \ No newline at end of file +- Added feature to group card payment methods into unified "Card" payment method +- Fixed issue when newly enabled payment methods did not appear in checkout because default "all countries/currencies" restriction was not created on save +- Fixed issue when payment method country/currency dropdowns showed "0" instead of indicating that all countries/currencies are allowed +- BO : Added validation for Merchant Emails field (frontend + backend) to prevent saving invalid addresses diff --git a/controllers/admin/AdminSaferPayOfficialFieldsController.php b/controllers/admin/AdminSaferPayOfficialFieldsController.php deleted file mode 100755 index 7508d5986..000000000 --- a/controllers/admin/AdminSaferPayOfficialFieldsController.php +++ /dev/null @@ -1,90 +0,0 @@ - - *@copyright SIX Payment Services - *@license SIX Payment Services - */ - -use Invertus\SaferPay\Config\SaferPayConfig; - -require_once dirname(__FILE__) . '/../../vendor/autoload.php'; - -if (!defined('_PS_VERSION_')) { - exit; -} - -class AdminSaferPayOfficialFieldsController extends ModuleAdminController -{ - public function __construct() - { - parent::__construct(); - $this->bootstrap = true; - - $this->tpl_folder = 'field-option-settings/'; - $this->initOptions(); - } - - public function initContent() - { - parent::initContent(); - } - - public function initOptions() - { - $this->fields_options = [ - 'hosted_fields_settings' => [ - 'title' => $this->module->l('Hosted fields settings'), - 'icon' => 'icon-settings', - 'fields' => [ - SaferPayConfig::HOSTED_FIELDS_TEMPLATE . '_description' => [ - 'type' => 'desc', - 'class' => 'col-lg-12', - 'template' => 'field-hosted-field-template-desc.tpl', - ], - - SaferPayConfig::HOSTED_FIELDS_TEMPLATE => [ - 'type' => 'select-template', - 'name' => SaferPayConfig::HOSTED_FIELDS_TEMPLATE, - 'templateOptions' => [ - "{$this->module->getPathUri()}views/img/hosted-templates/template1.jpg", - "{$this->module->getPathUri()}views/img/hosted-templates/template2.jpg", - "{$this->module->getPathUri()}views/img/hosted-templates/template3.jpg", - ], - ], - ], - 'buttons' => [ - 'save_and_connect' => [ - 'title' => $this->module->l('Save'), - 'icon' => 'process-icon-save', - 'class' => 'btn btn-default pull-right', - 'type' => 'submit', - ], - ], - ], - ]; - } - - public function setMedia($isNewTheme = false) - { - parent::setMedia($isNewTheme); - - $this->addJS('modules/' . $this->module->name . '/views/js/admin/saferpay_fields.js'); - $this->addCSS('modules/' . $this->module->name . '/views/css/admin/saferpay_fields.css'); - } -} diff --git a/controllers/admin/AdminSaferPayOfficialSettingsController.php b/controllers/admin/AdminSaferPayOfficialSettingsController.php index 2b0ad9073..0d4f5ffcf 100755 --- a/controllers/admin/AdminSaferPayOfficialSettingsController.php +++ b/controllers/admin/AdminSaferPayOfficialSettingsController.php @@ -29,6 +29,8 @@ use Invertus\SaferPay\Repository\SaferPaySavedCreditCardRepository; use Invertus\SaferPay\Adapter\Configuration as SaferPayConfiguration; use Invertus\SaferPay\Service\SaferPayFieldCreator; +use Invertus\SaferPay\Service\SaferPayGenerateFieldAccessToken; +use Invertus\SaferPay\Service\SaferPayGetLicense; use Invertus\SaferPay\Service\SaferPayGetTerminals; use Invertus\SaferPay\Service\SaferPayLogoCreator; use Invertus\SaferPay\Service\SaferPayObtainPaymentMethods; @@ -38,6 +40,7 @@ use Invertus\SaferPay\Service\SaferPayRestrictionCreator; use Invertus\SaferPay\Exception\Api\SaferPayApiException; use Invertus\SaferPay\Exception\Restriction\RestrictionException; +use Invertus\SaferPay\Logger\LoggerInterface; require_once dirname(__FILE__) . '/../../vendor/autoload.php'; @@ -57,6 +60,7 @@ class AdminSaferPayOfficialSettingsController extends ModuleAdminController 'saveGeneralSettings', 'savePaymentMethods', 'getTerminals', + 'generateFieldAccessToken', 'refreshData', ]; @@ -175,6 +179,17 @@ public function ajaxProcessSaveCredentials() } } + $testMerchantEmails = $this->getStringValue($data, 'testMerchantEmails'); + $liveMerchantEmails = $this->getStringValue($data, 'liveMerchantEmails'); + $invalidEmail = $this->findInvalidEmail($testMerchantEmails) ?: $this->findInvalidEmail($liveMerchantEmails); + if ($invalidEmail !== null) { + $this->ajaxResponse(false, sprintf( + $this->module->l('Invalid merchant email address: %s', self::FILE_NAME), + $invalidEmail + )); + return; + } + // Credentials validated — now save $configuration->set(SaferPayConfig::TEST_MODE, $isTestMode ? 1 : 0); @@ -190,7 +205,6 @@ public function ajaxProcessSaveCredentials() $configuration->set(SaferPayConfig::MERCHANT_EMAILS . SaferPayConfig::TEST_SUFFIX, $this->getStringValue($data, 'testMerchantEmails')); $configuration->set(SaferPayConfig::FIELDS_ACCESS_TOKEN . SaferPayConfig::TEST_SUFFIX, $this->getStringValue($data, 'testFieldAccessToken')); $configuration->set(SaferPayConfig::FIELDS_LIBRARY . SaferPayConfig::TEST_SUFFIX, $this->getStringValue($data, 'testFieldJsUrl')); - $configuration->set(SaferPayConfig::BUSINESS_LICENSE . SaferPayConfig::TEST_SUFFIX, !empty($data['testBusinessLicense']) ? 1 : 0); // Live credentials $liveUsername = $this->getStringValue($data, 'liveUsername'); @@ -204,28 +218,52 @@ public function ajaxProcessSaveCredentials() $configuration->set(SaferPayConfig::MERCHANT_EMAILS, $this->getStringValue($data, 'liveMerchantEmails')); $configuration->set(SaferPayConfig::FIELDS_ACCESS_TOKEN, $this->getStringValue($data, 'liveFieldAccessToken')); $configuration->set(SaferPayConfig::FIELDS_LIBRARY, $this->getStringValue($data, 'liveFieldJsUrl')); - $configuration->set(SaferPayConfig::BUSINESS_LICENSE, !empty($data['liveBusinessLicense']) ? 1 : 0); - // Validate: business license requires field access token - $suffix = SaferPayConfig::getConfigSuffix(); - $haveFieldToken = $configuration->get(SaferPayConfig::FIELDS_ACCESS_TOKEN . $suffix); - $haveBusinessLicense = $configuration->get(SaferPayConfig::BUSINESS_LICENSE . $suffix); - $businessLicenseDisabled = false; + // Auto-detect license features from Saferpay Management API + $suffix = $isTestMode ? SaferPayConfig::TEST_SUFFIX : ''; + $hasBusinessLicense = false; + $licenseFetchFailed = false; - if (!$haveFieldToken && $haveBusinessLicense) { + if (!empty($activeUsername) && !empty($activePassword) && !empty($activeCustomerId)) { + try { + /** @var SaferPayGetLicense $getLicense */ + $getLicense = $this->module->getService(SaferPayGetLicense::class); + $licenseInfo = $getLicense->fetchLicenseWithCredentials( + $activeUsername, + $activePassword, + $activeCustomerId, + $isTestMode + ); + + $hasBusinessLicense = $licenseInfo['hasBusinessLicense']; + $configuration->set(SaferPayConfig::BUSINESS_LICENSE . $suffix, $hasBusinessLicense ? 1 : 0); + } catch (\Exception $e) { + $configuration->set(SaferPayConfig::BUSINESS_LICENSE . $suffix, 0); + $licenseFetchFailed = true; + + /** @var LoggerInterface $logger */ + $logger = $this->module->getService(LoggerInterface::class); + $logger->error('License fetch failed on credentials save: ' . $e->getMessage(), [ + 'context' => ['exception_class' => get_class($e)], + ]); + } + } else { $configuration->set(SaferPayConfig::BUSINESS_LICENSE . $suffix, 0); - $businessLicenseDisabled = true; - } - - if ($businessLicenseDisabled) { - $this->ajaxResponse( - true, - $this->module->l('Credentials saved. Field Access Token is required for business license — it has been disabled.', self::FILE_NAME) - ); - return; } - $this->ajaxResponse(true, $this->module->l('API Credentials saved successfully', self::FILE_NAME)); + $message = $licenseFetchFailed + ? $this->module->l('Settings saved, but Saferpay Fields availability could not be confirmed. Please try again later or check the module Logs for details.', self::FILE_NAME) + : $this->module->l('Settings saved successfully.', self::FILE_NAME); + + $this->ajaxResponse( + true, + $message, + [ + 'testHasBusinessLicense' => (bool) $configuration->get(SaferPayConfig::BUSINESS_LICENSE . SaferPayConfig::TEST_SUFFIX), + 'liveHasBusinessLicense' => (bool) $configuration->get(SaferPayConfig::BUSINESS_LICENSE), + 'warning' => $licenseFetchFailed, + ] + ); } /** @@ -297,7 +335,19 @@ public function ajaxProcessSaveGeneralSettings() $configuration->set(SaferPayConfig::SAFERPAY_ORDER_STATE_CHOICE_AWAITING_PAYMENT, $this->getIntValue($data, 'orderStateAwaitingPayment')); $configuration->set(SaferPayConfig::SAFERPAY_PAYMENT_DESCRIPTION, $this->getStringValue($data, 'paymentDescription')); - $configuration->set(SaferPayConfig::CONFIGURATION_NAME, $this->getStringValue($data, 'configurationName')); + + $configurationName = $this->getStringValue($data, 'configurationName'); + if ($configurationName !== '' && (strlen($configurationName) > 20 || !preg_match('/^[A-Za-z0-9.:\-_]+$/', $configurationName))) { + $this->ajaxResponse(false, $this->module->l('Only letters, numbers, dots, colons, hyphens, and underscores are allowed. Max 20 characters.', self::FILE_NAME)); + return; + } + $configuration->set(SaferPayConfig::CONFIGURATION_NAME, $configurationName); + $hostedFieldsTemplate = $this->getIntValue($data, 'hostedFieldsTemplate'); + if ($hostedFieldsTemplate < 1 || $hostedFieldsTemplate > 3) { + $hostedFieldsTemplate = SaferPayConfig::HOSTED_FIELDS_TEMPLATE_DEFAULT; + } + $configuration->set(SaferPayConfig::HOSTED_FIELDS_TEMPLATE, $hostedFieldsTemplate); + $configuration->set(SaferPayConfig::SAFERPAY_ORDER_ID_OPTION, $this->getIntValue($data, 'orderIdOption')); $configuration->set(SaferPayConfig::SAFERPAY_DEBUG_MODE, !empty($data['debugMode']) ? 1 : 0); $this->ajaxResponse(true, $this->module->l('General settings saved successfully', self::FILE_NAME)); @@ -351,6 +401,13 @@ public function ajaxProcessSavePaymentMethods() $countries = isset($method['countries']) ? $method['countries'] : []; $currencies = isset($method['currencies']) ? $method['currencies'] : []; + if (empty($countries)) { + $countries = [SaferPayRestrictionCreator::RESTRICTION_ALL]; + } + if (empty($currencies)) { + $currencies = [SaferPayRestrictionCreator::RESTRICTION_ALL]; + } + $success = $restrictionCreator->updateRestriction( $paymentName, SaferPayRestrictionCreator::RESTRICTION_COUNTRY, @@ -381,25 +438,21 @@ public function ajaxProcessSavePaymentMethods() public function ajaxProcessGetTerminals() { $data = $this->getJsonInput(); - if (!$data) { - $this->ajaxResponse(false, $this->module->l('Invalid request data', self::FILE_NAME)); - return; - } + $isTestMode = isset($data['env']) && $data['env'] === 'test'; + $suffix = $isTestMode ? SaferPayConfig::TEST_SUFFIX : ''; - $username = isset($data['username']) ? $data['username'] : ''; + $username = isset($data['username']) ? trim($data['username']) : ''; $password = isset($data['password']) ? $data['password'] : ''; $customerId = $this->parseCustomerIdFromUsername($username); - $isTestMode = isset($data['env']) && $data['env'] === 'test'; if ($password === self::PASSWORD_PLACEHOLDER) { - $suffix = $isTestMode ? SaferPayConfig::TEST_SUFFIX : ''; /** @var SaferPayConfiguration $configuration */ $configuration = $this->module->getService(SaferPayConfiguration::class); $password = (string) $configuration->get(SaferPayConfig::PASSWORD . $suffix); } if (empty($username) || empty($password) || empty($customerId)) { - $this->ajaxResponse(false, $this->module->l('Username and password are required', self::FILE_NAME)); + $this->ajaxResponse(false, $this->module->l('Invalid credentials. Please check your username and password.', self::FILE_NAME)); return; } @@ -413,7 +466,61 @@ public function ajaxProcessGetTerminals() 'terminals' => $terminals, ]); } catch (\Exception $e) { - $this->ajaxResponse(false, $this->module->l('Failed to fetch terminals. Please check your credentials.', self::FILE_NAME)); + $this->ajaxResponse(false, $this->module->l('Invalid credentials. Please check your username and password.', self::FILE_NAME)); + } + } + + /** + * AJAX: Generate Saferpay Fields access token + */ + public function ajaxProcessGenerateFieldAccessToken() + { + $data = $this->getJsonInput(); + $isTestMode = isset($data['env']) && $data['env'] === 'test'; + $suffix = $isTestMode ? SaferPayConfig::TEST_SUFFIX : ''; + + $username = isset($data['username']) ? trim($data['username']) : ''; + $password = isset($data['password']) ? $data['password'] : ''; + $terminalId = isset($data['terminalId']) ? trim($data['terminalId']) : ''; + $customerId = $this->parseCustomerIdFromUsername($username); + + if ($password === self::PASSWORD_PLACEHOLDER) { + /** @var SaferPayConfiguration $configuration */ + $configuration = $this->module->getService(SaferPayConfiguration::class); + $password = (string) $configuration->get(SaferPayConfig::PASSWORD . $suffix); + } + + if (empty($username) || empty($password) || empty($customerId) || empty($terminalId)) { + $this->ajaxResponse(false, $this->module->l('Please enter valid credentials and select a terminal first.', self::FILE_NAME)); + return; + } + + try { + /** @var SaferPayGenerateFieldAccessToken $tokenGenerator */ + $tokenGenerator = $this->module->getService(SaferPayGenerateFieldAccessToken::class); + $shopUrl = $this->context->link->getBaseLink(); + $token = $tokenGenerator->generateWithCredentials($username, $password, $customerId, $terminalId, $isTestMode, $shopUrl); + + /** @var SaferPayConfiguration $configuration */ + $configuration = $this->module->getService(SaferPayConfiguration::class); + $configuration->set(SaferPayConfig::FIELDS_ACCESS_TOKEN . $suffix, $token); + + $this->sendJsonResponse([ + 'success' => true, + 'message' => $this->module->l('Access token generated successfully.', self::FILE_NAME), + 'token' => $token, + ]); + } catch (\Exception $e) { + \PrestaShopLogger::addLog( + 'SaferPay: Failed to generate field access token - ' . $e->getMessage(), + 3, + null, + null, + null, + true + ); + + $this->ajaxResponse(false, $this->module->l('Failed to generate access token.', self::FILE_NAME)); } } @@ -448,7 +555,6 @@ private function collectSettingsData() 'testMerchantEmails' => (string) $configuration->get(SaferPayConfig::MERCHANT_EMAILS . SaferPayConfig::TEST_SUFFIX) ?: (string) \Configuration::get('PS_SHOP_EMAIL'), 'testFieldAccessToken' => (string) $configuration->get(SaferPayConfig::FIELDS_ACCESS_TOKEN . SaferPayConfig::TEST_SUFFIX), 'testFieldJsUrl' => (string) $configuration->get(SaferPayConfig::FIELDS_LIBRARY . SaferPayConfig::TEST_SUFFIX), - 'testBusinessLicense' => (bool) $configuration->get(SaferPayConfig::BUSINESS_LICENSE . SaferPayConfig::TEST_SUFFIX), // Live credentials 'liveUsername' => (string) $configuration->get(SaferPayConfig::USERNAME), @@ -457,7 +563,10 @@ private function collectSettingsData() 'liveMerchantEmails' => (string) $configuration->get(SaferPayConfig::MERCHANT_EMAILS) ?: (string) \Configuration::get('PS_SHOP_EMAIL'), 'liveFieldAccessToken' => (string) $configuration->get(SaferPayConfig::FIELDS_ACCESS_TOKEN), 'liveFieldJsUrl' => (string) $configuration->get(SaferPayConfig::FIELDS_LIBRARY), - 'liveBusinessLicense' => (bool) $configuration->get(SaferPayConfig::BUSINESS_LICENSE), + + // License (auto-detected, per environment) + 'testHasBusinessLicense' => (bool) $configuration->get(SaferPayConfig::BUSINESS_LICENSE . SaferPayConfig::TEST_SUFFIX), + 'liveHasBusinessLicense' => (bool) $configuration->get(SaferPayConfig::BUSINESS_LICENSE), // Payment Processing 'paymentBehavior' => (int) $configuration->get(SaferPayConfig::PAYMENT_BEHAVIOR), @@ -477,6 +586,9 @@ private function collectSettingsData() 'orderStateAwaitingPayment' => (int) $configuration->get(SaferPayConfig::SAFERPAY_ORDER_STATE_CHOICE_AWAITING_PAYMENT), 'paymentDescription' => (string) $configuration->get(SaferPayConfig::SAFERPAY_PAYMENT_DESCRIPTION), 'configurationName' => (string) $configuration->get(SaferPayConfig::CONFIGURATION_NAME), + 'hostedFieldsTemplate' => (int) $configuration->get(SaferPayConfig::HOSTED_FIELDS_TEMPLATE), + 'modulePath' => $this->module->getPathUri(), + 'orderIdOption' => (int) $configuration->get(SaferPayConfig::SAFERPAY_ORDER_ID_OPTION), 'debugMode' => (bool) $configuration->get(SaferPayConfig::SAFERPAY_DEBUG_MODE), // Reference data @@ -621,12 +733,12 @@ private function getJsonInput() /** * Send AJAX JSON response */ - private function ajaxResponse($success, $message = '') + private function ajaxResponse($success, $message = '', $extraData = []) { - $this->sendJsonResponse([ + $this->sendJsonResponse(array_merge([ 'success' => $success, 'message' => $message, - ]); + ], $extraData)); } /** @@ -691,4 +803,24 @@ private function getIntValue($data, $key) { return isset($data[$key]) ? (int) $data[$key] : 0; } + + /** + * Returns the first invalid email in a comma-separated list, or null if all are valid. + */ + private function findInvalidEmail($emails) + { + if ($emails === '') { + return null; + } + foreach (explode(',', $emails) as $email) { + $email = trim($email); + if ($email === '') { + continue; + } + if (!\Validate::isEmail($email)) { + return $email; + } + } + return null; + } } diff --git a/controllers/front/notify.php b/controllers/front/notify.php index 870464e7c..667a1fd4d 100755 --- a/controllers/front/notify.php +++ b/controllers/front/notify.php @@ -140,27 +140,50 @@ public function postProcess() //NOTE must be left below assert action to get newest information. $order = new Order($orderId); + $paymentBehaviorWithout3D = (int) Configuration::get(SaferPayConfig::PAYMENT_BEHAVIOR_WITHOUT_3D); + if (!$assertResponseBody->getLiability()->getLiabilityShift() && - in_array($order->payment, SaferPayConfig::SUPPORTED_3DS_PAYMENT_METHODS) && - (int) Configuration::get(SaferPayConfig::PAYMENT_BEHAVIOR_WITHOUT_3D) === SaferPayConfig::PAYMENT_BEHAVIOR_WITHOUT_3D_CANCEL + in_array($order->payment, SaferPayConfig::SUPPORTED_3DS_PAYMENT_METHODS) ) { /** @var SaferPayOrderStatusService $orderStatusService */ $orderStatusService = $this->module->getService(SaferPayOrderStatusService::class); - $orderStatusService->cancel($order); - $logger->debug(sprintf('%s - Liability shift is false', self::FILE_NAME), [ - 'context' => [ - 'id_order' => $order->id, - ], - ]); + if ($paymentBehaviorWithout3D === SaferPayConfig::PAYMENT_BEHAVIOR_WITHOUT_3D_CANCEL) { + $orderStatusService->cancel($order); - $logger->debug(sprintf('%s - liability shift is false', self::FILE_NAME), [ - 'context' => [ - 'id_order' => $order->id, - ], - ]); + $logger->debug(sprintf('%s - Liability shift is false, canceling order', self::FILE_NAME), [ + 'context' => [ + 'id_order' => $order->id, + ], + ]); - die($this->module->l('Liability shift is false', self::FILE_NAME)); + die($this->module->l('Liability shift is false', self::FILE_NAME)); + } + + if ($paymentBehaviorWithout3D === SaferPayConfig::PAYMENT_BEHAVIOR_WITHOUT_3D_AUTHORIZE) { + $logger->debug(sprintf('%s - Liability shift is false, order left authorized', self::FILE_NAME), [ + 'context' => [ + 'id_order' => $order->id, + ], + ]); + + die($this->module->l('Liability shift is false, order left authorized', self::FILE_NAME)); + } + + if ($paymentBehaviorWithout3D === SaferPayConfig::PAYMENT_BEHAVIOR_WITHOUT_3D_CAPTURE + && SaferPayConfig::supportsOrderCapture($order->payment) + && $transactionStatus !== TransactionStatus::CAPTURED + ) { + $orderStatusService->capture($order); + + $logger->debug(sprintf('%s - Liability shift is false, capturing order', self::FILE_NAME), [ + 'context' => [ + 'id_order' => $order->id, + ], + ]); + + die($this->module->l('Liability shift is false, capturing order', self::FILE_NAME)); + } } //NOTE to get latest information possible and not override new information. diff --git a/controllers/front/return.php b/controllers/front/return.php index b5fc37c69..a65984b32 100755 --- a/controllers/front/return.php +++ b/controllers/front/return.php @@ -274,7 +274,9 @@ private function executeTransaction($orderId, $selectedCard) */ private function getRedirectionToControllerUrl($controllerName) { - $cartId = $this->context->cart->id ? $this->context->cart->id : Tools::getValue('cartId'); + $cartId = (int) Tools::getValue('cartId') ?: (int) $this->context->cart->id; + $cart = new Cart($cartId); + $secureKey = Validate::isLoadedObject($cart) ? $cart->secure_key : $this->context->cart->secure_key; return $this->context->link->getModuleLink( $this->module->name, @@ -282,7 +284,7 @@ private function getRedirectionToControllerUrl($controllerName) [ 'cartId' => $cartId, 'orderId' => Order::getIdByCartId($cartId), - 'secureKey' => $this->context->cart->secure_key, + 'secureKey' => $secureKey, 'moduleId' => $this->module->id, ] ); @@ -329,19 +331,39 @@ private function createAndValidateOrder($assertResponseBody, $transactionStatus, $orderId = Order::getIdByCartId($cartId); $order = new Order($orderId); + $paymentBehaviorWithout3D = (int) Configuration::get(SaferPayConfig::PAYMENT_BEHAVIOR_WITHOUT_3D); + if (!$assertResponseBody->getLiability()->getLiabilityShift() && - in_array($order->payment, SaferPayConfig::SUPPORTED_3DS_PAYMENT_METHODS) && - (int) Configuration::get(SaferPayConfig::PAYMENT_BEHAVIOR_WITHOUT_3D) === SaferPayConfig::PAYMENT_BEHAVIOR_WITHOUT_3D_CANCEL + in_array($order->payment, SaferPayConfig::SUPPORTED_3DS_PAYMENT_METHODS) ) { /** @var SaferPayOrderStatusService $orderStatusService */ $orderStatusService = $this->module->getService(SaferPayOrderStatusService::class); - $orderStatusService->cancel($order); + + if ($paymentBehaviorWithout3D === SaferPayConfig::PAYMENT_BEHAVIOR_WITHOUT_3D_CANCEL) { + $orderStatusService->cancel($order); + + return; + } + + if ($paymentBehaviorWithout3D === SaferPayConfig::PAYMENT_BEHAVIOR_WITHOUT_3D_AUTHORIZE) { + return; + } + + if ($paymentBehaviorWithout3D === SaferPayConfig::PAYMENT_BEHAVIOR_WITHOUT_3D_CAPTURE + && SaferPayConfig::supportsOrderCapture($order->payment) + && $transactionStatus !== TransactionStatus::CAPTURED + ) { + $orderStatusService->capture($order); + + return; + } } //NOTE to get latest information possible and not override new information. - $paymentMethod = $assertResponseBody->getPaymentMeans()->getBrand()->getPaymentMethod();// if payment does not support order capture, it means it always auto-captures it (at least with accountToAccount payment), + $paymentMethod = $assertResponseBody->getPaymentMeans()->getBrand()->getPaymentMethod(); + // if payment does not support order capture, it means it always auto-captures it (at least with accountToAccount payment), // so in this case if status comes back "captured" we just update the order state accordingly if (!SaferPayConfig::supportsOrderCapture($paymentMethod) && $transactionStatus === TransactionStatus::CAPTURED diff --git a/cypress/integration/01_ps1764.Module.Configure.cy.js b/cypress/integration/01_ps1764.Module.Configure.cy.js index b7313cb4c..f8988d2b8 100755 --- a/cypress/integration/01_ps1764.Module.Configure.cy.js +++ b/cypress/integration/01_ps1764.Module.Configure.cy.js @@ -117,14 +117,6 @@ it('04 Fields and Logs tabs are shown OK', () => { cy.get('.pstaggerAddTagInput').type('saferpay') cy.get('#module-search-button').click() cy.get('.btn-group > .btn-primary-reverse').click() //clicking the Congifure - cy.get('#subtab-AdminSaferPayOfficialFields').click() - cy.get('[id="configuration_form"]').should('be.visible') - cy.get('.field-container > :nth-child(1) > img').click() - cy.get(':nth-child(2) > img').click() - cy.get(':nth-child(3) > img').click() - cy.get('[class="alert alert-info"]').should('be.visible') - cy.get('[name="submitOptionsconfiguration"]').click() - cy.get('[class="alert alert-success"]').should('be.visible') cy.get('#subtab-AdminSaferPayOfficialLogs').click() cy.get('[id="form-saferpay_log"]').should('be.visible') }) diff --git a/cypress/integration/01_ps1770.Module.Configure.cy.js b/cypress/integration/01_ps1770.Module.Configure.cy.js index 9696c496c..1de0a18c6 100755 --- a/cypress/integration/01_ps1770.Module.Configure.cy.js +++ b/cypress/integration/01_ps1770.Module.Configure.cy.js @@ -117,14 +117,6 @@ it('04 Fields and Logs tabs are shown OK', () => { cy.get('.pstaggerAddTagInput').type('saferpay') cy.get('#module-search-button').click() cy.get('.btn-group > .btn-primary-reverse').click() //clicking the Congifure - cy.get('#subtab-AdminSaferPayOfficialFields').click() - cy.get('[id="configuration_form"]').should('be.visible') - cy.get('.field-container > :nth-child(1) > img').click() - cy.get(':nth-child(2) > img').click() - cy.get(':nth-child(3) > img').click() - cy.get('[class="alert alert-info"]').should('be.visible') - cy.get('[name="submitOptionsconfiguration"]').click() - cy.get('[class="alert alert-success"]').should('be.visible') cy.get('#subtab-AdminSaferPayOfficialLogs').click() cy.get('[id="form-saferpay_log"]').should('be.visible') }) diff --git a/cypress/integration/01_ps1784.Module.Configure.cy.js b/cypress/integration/01_ps1784.Module.Configure.cy.js index 8da2aae5b..716aa4837 100755 --- a/cypress/integration/01_ps1784.Module.Configure.cy.js +++ b/cypress/integration/01_ps1784.Module.Configure.cy.js @@ -117,14 +117,6 @@ it('04 Fields and Logs tabs are shown OK', () => { cy.get('.pstaggerAddTagInput').type('saferpay') cy.get('#module-search-button').click() cy.get('.btn-group > .btn-primary-reverse').click() //clicking the Congifure - cy.get('#subtab-AdminSaferPayOfficialFields').click() - cy.get('[id="configuration_form"]').should('be.visible') - cy.get('.field-container > :nth-child(1) > img').click() - cy.get(':nth-child(2) > img').click() - cy.get(':nth-child(3) > img').click() - cy.get('[class="alert alert-info"]').should('be.visible') - cy.get('[name="submitOptionsconfiguration"]').click() - cy.get('[class="alert alert-success"]').should('be.visible') cy.get('#subtab-AdminSaferPayOfficialLogs').click() cy.get('[id="form-saferpay_log"]').should('be.visible') }) diff --git a/cypress/integration/01_ps1786.Module.Configure.cy.js b/cypress/integration/01_ps1786.Module.Configure.cy.js index 0c8259a9d..863cb240b 100755 --- a/cypress/integration/01_ps1786.Module.Configure.cy.js +++ b/cypress/integration/01_ps1786.Module.Configure.cy.js @@ -117,14 +117,6 @@ it('04 Fields and Logs tabs are shown OK', () => { cy.get('.pstaggerAddTagInput').type('saferpay') cy.get('#module-search-button').click() cy.get('.btn-group > .btn-primary-reverse').click() //clicking the Congifure - cy.get('#subtab-AdminSaferPayOfficialFields').click() - cy.get('[id="configuration_form"]').should('be.visible') - cy.get('.field-container > :nth-child(1) > img').click() - cy.get(':nth-child(2) > img').click() - cy.get(':nth-child(3) > img').click() - cy.get('[class="alert alert-info"]').should('be.visible') - cy.get('[name="submitOptionsconfiguration"]').click() - cy.get('[class="alert alert-success"]').should('be.visible') cy.get('#subtab-AdminSaferPayOfficialLogs').click() cy.get('[id="form-saferpay_log"]').should('be.visible') }) diff --git a/saferpayofficial.php b/saferpayofficial.php index b0acd4630..dd997aa5e 100755 --- a/saferpayofficial.php +++ b/saferpayofficial.php @@ -55,7 +55,6 @@ class SaferPayOfficial extends PaymentModule const ADMIN_SAFERPAY_MODULE_CONTROLLER = 'AdminSaferPayOfficialModule'; const ADMIN_SETTINGS_CONTROLLER = 'AdminSaferPayOfficialSettings'; const ADMIN_PAYMENTS_CONTROLLER = 'AdminSaferPayOfficialPayment'; - const ADMIN_FIELDS_CONTROLLER = 'AdminSaferPayOfficialFields'; const ADMIN_ORDER_CONTROLLER = 'AdminSaferPayOfficialOrder'; const ADMIN_LOGS_CONTROLLER = 'AdminSaferPayOfficialLogs'; @@ -65,7 +64,7 @@ public function __construct($name = null) { $this->name = 'saferpayofficial'; $this->author = 'Invertus'; - $this->version = '2.0.2'; + $this->version = '2.1.1'; $this->module_key = '3d3506c3e184a1fe63b936b82bda1bdf'; $this->displayName = 'SaferpayOfficial'; $this->description = 'Saferpay Payment module'; diff --git a/src/Api/ApiRequest.php b/src/Api/ApiRequest.php index 70c9323a1..5df54cd59 100755 --- a/src/Api/ApiRequest.php +++ b/src/Api/ApiRequest.php @@ -114,14 +114,16 @@ public function get($url, $params = []) return json_decode($response->raw_body); } catch (Exception $exception) { - $this->logger->error($exception->getMessage(), [ - 'context' => [ - 'headers' => $this->getHeaders(), - ], - 'request' => $params, - 'response' => json_decode($response->raw_body), - 'exceptions' => ExceptionUtility::getExceptions($exception), - ]); + if ($response === null) { + $this->logger->error($exception->getMessage(), [ + 'context' => [ + 'headers' => $this->getHeaders(), + ], + 'request' => $params, + 'response' => null, + 'exceptions' => ExceptionUtility::getExceptions($exception), + ]); + } throw $exception; } @@ -170,13 +172,73 @@ public function getWithCredentials($url, $username, $password, $baseUrl, $params return json_decode($response->raw_body); } catch (Exception $exception) { - $this->logger->error($exception->getMessage(), [ - 'context' => [], + if ($response === null) { + $this->logger->error($exception->getMessage(), [ + 'context' => [], + 'request' => $params, + 'response' => null, + 'exceptions' => ExceptionUtility::getExceptions($exception), + ]); + } + + throw $exception; + } + } + + /** + * API Request Post Method with explicit credentials. + * + * @param string $url + * @param string $username + * @param string $password + * @param string $baseUrl + * @param array|null $params + * @return mixed + * @throws Exception + */ + public function postWithCredentials($url, $username, $password, $baseUrl, $params = null) + { + $response = null; + + try { + $credentials = base64_encode("$username:$password"); + $headers = [ + 'Accept' => 'application/json', + 'Content-Type' => 'application/json', + 'Saferpay-ApiVersion' => SaferPayConfig::API_VERSION, + 'Saferpay-RequestId' => 'false', + 'Authorization' => "Basic $credentials", + ]; + + $body = $params !== null ? json_encode($params) : '{}'; + + $response = Request::post( + $baseUrl . $url, + $headers, + $body + ); + + $this->logger->debug(sprintf('%s - POST (credentials) response: %d', self::FILE_NAME, $response->code), [ + 'context' => [ + 'uri' => $baseUrl . $url, + ], 'request' => $params, - 'response' => $response ? json_decode($response->raw_body) : null, - 'exceptions' => ExceptionUtility::getExceptions($exception), + 'response' => $response->body, ]); + $this->isValidResponse($response); + + return json_decode($response->raw_body); + } catch (Exception $exception) { + if ($response === null) { + $this->logger->error($exception->getMessage(), [ + 'context' => [], + 'request' => $params, + 'response' => null, + 'exceptions' => ExceptionUtility::getExceptions($exception), + ]); + } + throw $exception; } } diff --git a/src/Api/Request/GenerateFieldAccessTokenService.php b/src/Api/Request/GenerateFieldAccessTokenService.php new file mode 100644 index 000000000..3ef46f9a9 --- /dev/null +++ b/src/Api/Request/GenerateFieldAccessTokenService.php @@ -0,0 +1,61 @@ + + *@copyright SIX Payment Services + *@license SIX Payment Services + */ + +namespace Invertus\SaferPay\Api\Request; + +use Invertus\SaferPay\Api\ApiRequest; +use Invertus\SaferPay\DTO\Request\GenerateFieldAccessToken\GenerateFieldAccessTokenRequest; + +if (!defined('_PS_VERSION_')) { + exit; +} + +class GenerateFieldAccessTokenService +{ + /** @var ApiRequest */ + private $apiRequest; + + public function __construct(ApiRequest $apiRequest) + { + $this->apiRequest = $apiRequest; + } + + /** + * @param GenerateFieldAccessTokenRequest $request + * @param string $username + * @param string $password + * @param string $baseUrl + * @param array|null $params + * @return mixed + */ + public function generateToken(GenerateFieldAccessTokenRequest $request, $username, $password, $baseUrl, $params = null) + { + return $this->apiRequest->postWithCredentials( + $request->generateRequestUrl(), + $username, + $password, + $baseUrl, + $params + ); + } +} diff --git a/src/Api/Request/GetLicenseService.php b/src/Api/Request/GetLicenseService.php new file mode 100644 index 000000000..0299875fb --- /dev/null +++ b/src/Api/Request/GetLicenseService.php @@ -0,0 +1,76 @@ + + *@copyright SIX Payment Services + *@license SIX Payment Services + */ + +namespace Invertus\SaferPay\Api\Request; + +use Invertus\SaferPay\Api\ApiRequest; +use Invertus\SaferPay\DTO\Request\GetLicense\GetLicenseRequest; + +if (!defined('_PS_VERSION_')) { + exit; +} + +class GetLicenseService +{ + /** @var ApiRequest */ + private $apiRequest; + + public function __construct(ApiRequest $apiRequest) + { + $this->apiRequest = $apiRequest; + } + + /** + * @param GetLicenseRequest $request + * @param string $username + * @param string $password + * @param string $baseUrl + * @return mixed + */ + public function getLicense(GetLicenseRequest $request, $username, $password, $baseUrl) + { + return $this->apiRequest->getWithCredentials( + $request->generateRequestUrl(), + $username, + $password, + $baseUrl + ); + } + + /** + * @param GetLicenseRequest $request + * @param string $username + * @param string $password + * @param string $baseUrl + * @return mixed + */ + public function getLicenseFallback(GetLicenseRequest $request, $username, $password, $baseUrl) + { + return $this->apiRequest->getWithCredentials( + $request->generateFallbackRequestUrl(), + $username, + $password, + $baseUrl + ); + } +} diff --git a/src/Config/SaferPayConfig.php b/src/Config/SaferPayConfig.php index ae7879997..793d0b8e1 100755 --- a/src/Config/SaferPayConfig.php +++ b/src/Config/SaferPayConfig.php @@ -276,6 +276,9 @@ class SaferPayConfig const PAYMENT_BEHAVIOR_WITHOUT_3D_CANCEL = 0; const PAYMENT_BEHAVIOR_WITHOUT_3D_AUTHORIZE = 1; + const PAYMENT_BEHAVIOR_WITHOUT_3D_CAPTURE = 2; + + const SAFERPAY_ORDER_ID_OPTION = 'SAFERPAY_ORDER_ID_OPTION'; const SAFERPAY_CARDFORM_HOLDERNAME_REQUIRENCE = 'MANDATORY'; const SAFERPAY_DEBUG_MODE = 'SAFERPAY_DEBUG_MODE'; @@ -433,12 +436,13 @@ public static function getDefaultConfiguration() RequestHeader::SPEC_REFUND_VERSION => SaferPayConfig::API_VERSION, RequestHeader::RETRY_INDICATOR => 0, SaferPayConfig::PAYMENT_BEHAVIOR => 1, - SaferPayConfig::PAYMENT_BEHAVIOR_WITHOUT_3D => 1, + SaferPayConfig::PAYMENT_BEHAVIOR_WITHOUT_3D => 0, SaferPayConfig::SAFERPAY_ALLOW_SAFERPAY_SEND_CUSTOMER_MAIL => 1, SaferPayConfig::SAFERPAY_PAYMENT_DESCRIPTION => self::SAFERPAY_PAYMENT_DESCRIPTION_DEFAULT_VALUE, SaferPayConfig::FIELDS_LIBRARY => self::FIELDS_LIBRARY_DEFAULT_VALUE, SaferPayConfig::FIELDS_LIBRARY . SaferPayConfig::TEST_SUFFIX => self::FIELDS_LIBRARY_TEST_DEFAULT_VALUE, self::SAFERPAY_ORDER_CREATION_AFTER_AUTHORIZATION => 0, + self::SAFERPAY_ORDER_ID_OPTION => 0, self::TEST_MODE => 1, self::HOSTED_FIELDS_TEMPLATE => self::HOSTED_FIELDS_TEMPLATE_DEFAULT, self::SAFERPAY_ORDER_STATE_CHOICE_AWAITING_PAYMENT => (int) Configuration::get( @@ -479,6 +483,7 @@ public static function getUninstallConfiguration() self::FIELDS_LIBRARY, self::FIELDS_LIBRARY . self::TEST_SUFFIX, self::SAFERPAY_ORDER_CREATION_AFTER_AUTHORIZATION, + self::SAFERPAY_ORDER_ID_OPTION, self::SAFERPAY_SEND_ORDER_CONF_MAIL, self::SAFERPAY_GROUP_CARDS, ]; diff --git a/src/DTO/Request/GenerateFieldAccessToken/GenerateFieldAccessTokenRequest.php b/src/DTO/Request/GenerateFieldAccessToken/GenerateFieldAccessTokenRequest.php new file mode 100644 index 000000000..5eeb4a490 --- /dev/null +++ b/src/DTO/Request/GenerateFieldAccessToken/GenerateFieldAccessTokenRequest.php @@ -0,0 +1,67 @@ + + *@copyright SIX Payment Services + *@license SIX Payment Services + */ + +namespace Invertus\SaferPay\DTO\Request\GenerateFieldAccessToken; + +if (!defined('_PS_VERSION_')) { + exit; +} + +class GenerateFieldAccessTokenRequest +{ + /** @var string */ + private $customerId; + + /** @var string */ + private $terminalId; + + /** + * @param string $customerId + * @param string $terminalId + */ + public function __construct($customerId, $terminalId) + { + if (!preg_match('/^[a-zA-Z0-9\-_]+$/', $customerId)) { + throw new \InvalidArgumentException('Invalid customer ID format'); + } + + if (!preg_match('/^[a-zA-Z0-9\-_]+$/', $terminalId)) { + throw new \InvalidArgumentException('Invalid terminal ID format'); + } + + $this->customerId = $customerId; + $this->terminalId = $terminalId; + } + + /** + * @return string + */ + public function generateRequestUrl() + { + return sprintf( + 'rest/customers/%s/terminals/%s/fields-access-tokens', + $this->customerId, + $this->terminalId + ); + } +} diff --git a/views/css/admin/saferpay_fields.css b/src/DTO/Request/GetLicense/GetLicenseRequest.php old mode 100755 new mode 100644 similarity index 50% rename from views/css/admin/saferpay_fields.css rename to src/DTO/Request/GetLicense/GetLicenseRequest.php index 064c2c333..0cf139810 --- a/views/css/admin/saferpay_fields.css +++ b/src/DTO/Request/GetLicense/GetLicenseRequest.php @@ -1,3 +1,4 @@ +customerId = $customerId; + } + + /** + * @return string + */ + public function generateRequestUrl() + { + return sprintf('rest/customers/%s/license', $this->customerId); + } -.field-container { - display: flex; - flex-wrap: wrap; + /** + * @return string + */ + public function generateFallbackRequestUrl() + { + return sprintf('rest/customers/%s/license-configuration', $this->customerId); + } } diff --git a/src/Entity/index.php b/src/Entity/index.php deleted file mode 100755 index ee6227264..000000000 --- a/src/Entity/index.php +++ /dev/null @@ -1,31 +0,0 @@ - - *@copyright SIX Payment Services - *@license SIX Payment Services - */ -header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); -header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); - -header('Cache-Control: no-store, no-cache, must-revalidate'); -header('Cache-Control: post-check=0, pre-check=0', false); -header('Pragma: no-cache'); - -header('Location: ../'); -exit; diff --git a/src/Install/AbstractInstaller.php b/src/Install/AbstractInstaller.php index cbe8d897d..2188841f0 100755 --- a/src/Install/AbstractInstaller.php +++ b/src/Install/AbstractInstaller.php @@ -63,12 +63,6 @@ public function tabs() 'module_tab' => true, 'visible' => false, ], - [ - 'name' => $this->module->l('Fields'), - 'class_name' => SaferPayOfficial::ADMIN_FIELDS_CONTROLLER, - 'parent_class_name' => SaferPayOfficial::ADMIN_SAFERPAY_MODULE_CONTROLLER, - 'module_tab' => true, - ], [ 'name' => $this->module->l('Order'), 'class_name' => SaferPayOfficial::ADMIN_ORDER_CONTROLLER, diff --git a/src/Service/Request/RequestObjectCreator.php b/src/Service/Request/RequestObjectCreator.php index 6e961489f..21fc39835 100755 --- a/src/Service/Request/RequestObjectCreator.php +++ b/src/Service/Request/RequestObjectCreator.php @@ -118,14 +118,19 @@ public function createPayment(Cart $cart, $totalPrice) $payment = new Payment(); $payment->setValue($totalPrice); $payment->setCurrencyCode($currency['iso_code']); - $payment->setDescription((string) Configuration::get(SaferPayConfig::SAFERPAY_PAYMENT_DESCRIPTION)); - if ((int) \Configuration::get(SaferPayConfig::SAFERPAY_ORDER_CREATION_AFTER_AUTHORIZATION) && empty($order)) { - return $payment; + $description = (string) Configuration::get(SaferPayConfig::SAFERPAY_PAYMENT_DESCRIPTION); + $orderIdOption = (int) Configuration::get(SaferPayConfig::SAFERPAY_ORDER_ID_OPTION); + + if ($orderIdOption === 0 && !empty($order)) { + $payment->setDescription($order->reference); + } else { + $payment->setDescription($description); } - /** This param is not mandatory, but recommended **/ - $payment->setOrderReference($order->reference); + if (!empty($order)) { + $payment->setOrderReference($order->reference); + } return $payment; } diff --git a/src/Service/SaferPayGenerateFieldAccessToken.php b/src/Service/SaferPayGenerateFieldAccessToken.php new file mode 100644 index 000000000..1609cdd37 --- /dev/null +++ b/src/Service/SaferPayGenerateFieldAccessToken.php @@ -0,0 +1,81 @@ + + *@copyright SIX Payment Services + *@license SIX Payment Services + */ + +namespace Invertus\SaferPay\Service; + +use Exception; +use Invertus\SaferPay\Api\Request\GenerateFieldAccessTokenService; +use Invertus\SaferPay\Config\SaferPayConfig; +use Invertus\SaferPay\DTO\Request\GenerateFieldAccessToken\GenerateFieldAccessTokenRequest; + +if (!defined('_PS_VERSION_')) { + exit; +} + +class SaferPayGenerateFieldAccessToken +{ + /** @var GenerateFieldAccessTokenService */ + private $generateFieldAccessTokenService; + + public function __construct(GenerateFieldAccessTokenService $generateFieldAccessTokenService) + { + $this->generateFieldAccessTokenService = $generateFieldAccessTokenService; + } + + /** + * @param string $username + * @param string $password + * @param string $customerId + * @param string $terminalId + * @param bool $isTestMode + * @param string $shopUrl + * + * @return string + * + * @throws Exception + */ + public function generateWithCredentials($username, $password, $customerId, $terminalId, $isTestMode, $shopUrl) + { + $baseUrl = $isTestMode ? SaferPayConfig::TEST_API : SaferPayConfig::API; + $request = new GenerateFieldAccessTokenRequest($customerId, $terminalId); + + $params = [ + 'Description' => 'PrestaShop Module', + 'SourceUrls' => [$shopUrl], + ]; + + $response = $this->generateFieldAccessTokenService->generateToken( + $request, + $username, + $password, + $baseUrl, + $params + ); + + if (!isset($response->AccessToken)) { + throw new Exception('Unexpected API response: no access token returned'); + } + + return $response->AccessToken; + } +} diff --git a/src/Service/SaferPayGetLicense.php b/src/Service/SaferPayGetLicense.php new file mode 100644 index 000000000..a88e410a6 --- /dev/null +++ b/src/Service/SaferPayGetLicense.php @@ -0,0 +1,106 @@ + + *@copyright SIX Payment Services + *@license SIX Payment Services + */ + +namespace Invertus\SaferPay\Service; + +use Exception; +use Invertus\SaferPay\Api\Request\GetLicenseService; +use Invertus\SaferPay\Config\SaferPayConfig; +use Invertus\SaferPay\DTO\Request\GetLicense\GetLicenseRequest; + +if (!defined('_PS_VERSION_')) { + exit; +} + +class SaferPayGetLicense +{ + const FEATURE_HOSTED_ENTRY_FORM = 'HOSTED_ENTRY_FORM'; + + /** @var GetLicenseService */ + private $getLicenseService; + + public function __construct(GetLicenseService $getLicenseService) + { + $this->getLicenseService = $getLicenseService; + } + + /** + * @param string $username + * @param string $password + * @param string $customerId + * @param bool $isTestMode + * + * @return array{hasBusinessLicense: bool, packageName: string, features: array} + * + * @throws Exception + */ + public function fetchLicenseWithCredentials($username, $password, $customerId, $isTestMode) + { + $baseUrl = $isTestMode ? SaferPayConfig::TEST_API : SaferPayConfig::API; + $request = new GetLicenseRequest($customerId); + + $response = $this->fetchWithFallback($request, $username, $password, $baseUrl); + + $packageName = ''; + if (isset($response->Package->DisplayName)) { + $packageName = $response->Package->DisplayName; + } + + $features = []; + $featureList = isset($response->Features) ? $response->Features : []; + if (is_array($featureList)) { + foreach ($featureList as $feature) { + if (isset($feature->Id)) { + $features[] = $feature->Id; + } + } + } + + $hasBusinessLicense = in_array(self::FEATURE_HOSTED_ENTRY_FORM, $features, true); + + return [ + 'hasBusinessLicense' => $hasBusinessLicense, + 'packageName' => $packageName, + 'features' => $features, + ]; + } + + /** + * @param GetLicenseRequest $request + * @param string $username + * @param string $password + * @param string $baseUrl + * + * @return mixed + * + * @throws Exception + */ + private function fetchWithFallback(GetLicenseRequest $request, $username, $password, $baseUrl) + { + try { + return $this->getLicenseService->getLicense($request, $username, $password, $baseUrl); + } catch (Exception $e) { + return $this->getLicenseService->getLicenseFallback($request, $username, $password, $baseUrl); + } + } +} diff --git a/src/Service/SettingsTranslationService.php b/src/Service/SettingsTranslationService.php index b5589198e..ae2de9990 100644 --- a/src/Service/SettingsTranslationService.php +++ b/src/Service/SettingsTranslationService.php @@ -85,6 +85,7 @@ private function getCommonTranslations() { return [ 'saveChanges' => $this->module->l('Save Changes', self::FILE_NAME), + 'saving' => $this->module->l('Saving...', self::FILE_NAME), 'enable' => $this->module->l('Enable', self::FILE_NAME), 'disable' => $this->module->l('Disable', self::FILE_NAME), 'search' => $this->module->l('Search...', self::FILE_NAME), @@ -120,16 +121,28 @@ private function getApiCredentialsTranslations() 'fetchTerminalsFromApi' => $this->module->l('Fetch terminals from API', self::FILE_NAME), 'merchantEmails' => $this->module->l('Merchant Emails', self::FILE_NAME), 'enterMerchantEmails' => $this->module->l('Enter merchant email addresses (comma-separated)', self::FILE_NAME), - 'separateEmails' => $this->module->l('Separate multiple email addresses with commas.', self::FILE_NAME), + 'separateEmails' => $this->module->l('These email addresses receive payment notification emails directly from SaferPay. Separate multiple email addresses with commas.', self::FILE_NAME), + 'invalidMerchantEmails' => $this->module->l('Invalid email address', self::FILE_NAME), 'saferpayFields' => $this->module->l('Saferpay Fields', self::FILE_NAME), 'saferpayFieldsDescription' => $this->module->l('Configure Saferpay Fields for inline payment form integration.', self::FILE_NAME), 'fieldAccessTokenInfo' => $this->module->l('Saferpay Field Access Token can be found in Saferpay Backoffice, navigate to', self::FILE_NAME), - 'fieldAccessTokenPath' => $this->module->l('Settings > Saferpay Fields Access Tokens', self::FILE_NAME), + 'fieldAccessTokenPath' => $this->module->l('Settings', self::FILE_NAME) . ' > ' . $this->module->l('Saferpay Fields Access Tokens', self::FILE_NAME), 'fieldAccessToken' => $this->module->l('Field Access Token', self::FILE_NAME), - 'enterFieldAccessToken' => $this->module->l('Enter field access token', self::FILE_NAME), + 'enterFieldAccessToken' => $this->module->l('Enter or generate token', self::FILE_NAME), + 'generate' => $this->module->l('Generate', self::FILE_NAME), + 'enterCredentialsToGenerateToken' => $this->module->l('Enter your API credentials first to generate a token.', self::FILE_NAME), + 'moreInformation' => $this->module->l('More information', self::FILE_NAME), 'fieldJsUrl' => $this->module->l('Field Javascript Library URL', self::FILE_NAME), - 'businessLicense' => $this->module->l('I have Business license', self::FILE_NAME), - 'businessLicenseDescription' => $this->module->l('Enable if you have a Saferpay Business license for advanced features.', self::FILE_NAME), + 'findLibraryUrlHere' => $this->module->l('Find the library URL here', self::FILE_NAME), + 'enterCredentialsFirst' => $this->module->l('Enter credentials first', self::FILE_NAME), + 'enterCredentialsToLoadTerminals' => $this->module->l('Enter your API username and password first to load available terminals.', self::FILE_NAME), + 'validatingCredentials' => $this->module->l('Validating credentials...', self::FILE_NAME), + 'credentialsValid' => $this->module->l('Credentials verified successfully.', self::FILE_NAME), + 'invalidCredentials' => $this->module->l('Invalid credentials. Please check your username and password.', self::FILE_NAME), + 'saferpayFieldsIncluded' => $this->module->l('Saferpay Fields is included in your license', self::FILE_NAME), + 'saferpayFieldsIncludedDescription' => $this->module->l('You can use hosted payment fields for a seamless checkout experience.', self::FILE_NAME), + 'tokenGeneratedSuccessfully' => $this->module->l('Access token generated successfully.', self::FILE_NAME), + 'failedToGenerateToken' => $this->module->l('Failed to generate access token.', self::FILE_NAME), ]; } @@ -163,11 +176,12 @@ private function getPaymentProcessingTranslations() 'chargeImmediately' => $this->module->l('Charge immediately', self::FILE_NAME), 'authorize' => $this->module->l('Authorize', self::FILE_NAME), 'reserveAndCaptureLater' => $this->module->l('Reserve and capture later', self::FILE_NAME), - 'behaviourWhen3dsFails' => $this->module->l('Behaviour when 3D Secure fails', self::FILE_NAME), + 'behaviourWhen3dsFails' => $this->module->l('Behavior when 3D Secure Payer Authentication was not successful and liability shift was not granted', self::FILE_NAME), 'behaviourWhen3dsDescription' => $this->module->l('Default payment behavior for payment without 3-D Secure.', self::FILE_NAME), 'cancel' => $this->module->l('Cancel', self::FILE_NAME), 'rejectPayment' => $this->module->l('Reject the payment', self::FILE_NAME), 'continueWithout3ds' => $this->module->l('Continue without 3DS', self::FILE_NAME), + 'captureWithout3ds' => $this->module->l('Charge immediately', self::FILE_NAME), 'restrictRefundAmount' => $this->module->l('Restrict RefundAmount to Captured Amount', self::FILE_NAME), 'restrictRefundDescription' => $this->module->l('If set to true, the refund will be rejected if the sum of authorized refunds exceeds the capture value.', self::FILE_NAME), 'orderCreationRule' => $this->module->l('Order creation rule', self::FILE_NAME), @@ -176,7 +190,7 @@ private function getPaymentProcessingTranslations() 'createWhenAuthorized' => $this->module->l('Create when authorized', self::FILE_NAME), 'beforeAuthorization' => $this->module->l('Before authorization', self::FILE_NAME), 'createBeforePayment' => $this->module->l('Create before payment', self::FILE_NAME), - 'cardDisplaySaving' => $this->module->l('Card Display & Saving', self::FILE_NAME), + 'cardDisplaySaving' => html_entity_decode($this->module->l('Card Display & Saving', self::FILE_NAME), ENT_QUOTES, 'UTF-8'), 'cardDisplayDescription' => $this->module->l('Configure how cards appear at checkout and whether customers can save them.', self::FILE_NAME), 'groupCardsLabel' => $this->module->l('Group debit/credit cards as \'Cards\' in checkout', self::FILE_NAME), 'groupCardsDescription' => $this->module->l('If enabled, all supported card brands will be grouped and shown as a single \'Cards\' payment method at checkout.', self::FILE_NAME), @@ -215,12 +229,22 @@ private function getGeneralSettingsTranslations() 'stylingDescription' => $this->module->l('Customize the appearance of the payment page.', self::FILE_NAME), 'configName' => $this->module->l('Payment Page configurations name', self::FILE_NAME), 'enterConfigName' => $this->module->l('Enter configuration name', self::FILE_NAME), - 'configNameDescription' => $this->module->l('This name is visible in payment page and also in payment confirmation email.', self::FILE_NAME), + 'configNameDescription' => html_entity_decode($this->module->l('Name of the Payment Page Configuration created in Saferpay Backoffice (Settings > Payment Page Configuration). Max 20 characters. Allowed: letters, numbers, dots, colons, hyphens, underscores.', self::FILE_NAME), ENT_QUOTES, 'UTF-8'), + 'hostedFieldInfo' => html_entity_decode($this->module->l('This style applies only to payment methods with "Custom form" enabled in the Payment Methods list. Methods without Custom form or paid with saved cards use the Saferpay-hosted payment page, whose appearance is controlled by "Payment Page configurations name".', self::FILE_NAME), ENT_QUOTES, 'UTF-8'), + 'hostedFieldStyle' => $this->module->l('Hosted field style', self::FILE_NAME), + 'hostedFieldStyleDescription' => $this->module->l('Select the card input form layout for the payment page.', self::FILE_NAME), + 'classicLayout' => $this->module->l('Classic Layout', self::FILE_NAME), + 'labeledLayout' => $this->module->l('Labeled Layout', self::FILE_NAME), + 'inlineLayoutWithCard' => $this->module->l('Inline Layout with Card', self::FILE_NAME), 'configuration' => $this->module->l('Configuration', self::FILE_NAME), 'configurationDescription' => $this->module->l('General module configuration settings.', self::FILE_NAME), 'description' => $this->module->l('Description', self::FILE_NAME), 'enterDescription' => $this->module->l('Enter description', self::FILE_NAME), 'descriptionHelp' => $this->module->l('This description is visible in payment page also in payment confirmation email.', self::FILE_NAME), + 'orderReferenceOnPaymentPage' => $this->module->l('Order reference on payment page', self::FILE_NAME), + 'usePrestaShopOrderReference' => $this->module->l('Use PrestaShop Order reference (default)', self::FILE_NAME), + 'useDescriptionFieldValue' => $this->module->l('Use Description field value', self::FILE_NAME), + 'orderReferenceFallbackInfo' => html_entity_decode($this->module->l('When "Use PrestaShop Order reference" is selected and the order is not yet created (e.g. order creation after authorization), the Description field value is used as fallback.', self::FILE_NAME), ENT_QUOTES, 'UTF-8'), 'debugMode' => $this->module->l('Debug mode', self::FILE_NAME), 'debugModeDescription' => $this->module->l('Enable debug mode to see more information in logs.', self::FILE_NAME), ]; diff --git a/translations/en.php b/translations/en.php new file mode 100644 index 000000000..e69de29bb diff --git a/upgrade/install-1.0.3.php b/upgrade/install-1.0.3.php index 2574c76e5..b4938c7f3 100755 --- a/upgrade/install-1.0.3.php +++ b/upgrade/install-1.0.3.php @@ -57,12 +57,5 @@ function upgrade_module_1_0_3($module) ADD COLUMN `authorized` TINYINT(1) DEFAULT 0' ); - $installer = new \Invertus\SaferPay\Install\Installer($module); - $installer->installTab( - SaferPayOfficial::ADMIN_FIELDS_CONTROLLER, - SaferPayOfficial::ADMIN_SAFERPAY_MODULE_CONTROLLER, - $module->l('Fields') - ); - return $result; } diff --git a/views/templates/admin/partials/field-hosted-field-template-desc.tpl b/upgrade/install-2.1.1.php old mode 100755 new mode 100644 similarity index 75% rename from views/templates/admin/partials/field-hosted-field-template-desc.tpl rename to upgrade/install-2.1.1.php index 8314405ef..b7b39ae00 --- a/views/templates/admin/partials/field-hosted-field-template-desc.tpl +++ b/upgrade/install-2.1.1.php @@ -1,4 +1,5 @@ -{** + *@copyright SIX Payment Services *@license SIX Payment Services - *} -
+ {t('enterCredentialsToLoadTerminals')} +
+ )} ++ {t('invalidMerchantEmails')}: {invalidEmails.join(', ')} +
+ )}{t('separateEmails')}
@@ -232,34 +265,74 @@ export function ApiCredentials() { - {/* Saferpay Fields Configuration */} -+ {t('saferpayFieldsIncluded')} +
++ {t('saferpayFieldsIncludedDescription')} +
+{t('fieldAccessTokenInfo')}{' '} - {t('fieldAccessTokenPath')}. + {t('fieldAccessTokenPath')}.{' '} + {t('moreInformation')}
+ {t('enterCredentialsToGenerateToken')} +
- {t('businessLicenseDescription')} -
-{t('configNameDescription')}
+ {t('hostedFieldInfo')} +
++ {t('hostedFieldStyleDescription')} +
+
+ - {t('descriptionHelp')} + {/* Order reference on payment page */} +
+ {t('descriptionHelp')} +
++ {t('orderReferenceFallbackInfo')}
| + | + {l s='Card type' mod='saferpayofficial'} | -+ | {l s='Credit card' mod='saferpayofficial'} | -+ | {l s='Added date' mod='saferpayofficial'} | -+ | {l s='Valid till' mod='saferpayofficial'} | -- {l s='Card' mod='saferpayofficial'} + | + {l s='Card number' mod='saferpayofficial'} | -+ | {l s='Action' mod='saferpayofficial'} |
|---|