Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions controllers/admin/AdminSaferPayOfficialSettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,64 @@ public function __construct()

public function initContent()
{
if (Tools::getValue('ajax') && Tools::getValue('action') === 'getTerminals') {
$this->displayAjaxGetTerminals();
return;
}

parent::initContent();
}

public function displayAjaxGetTerminals()
{
$environment = Tools::getValue('environment', 'live');
$customerId = Tools::getValue('customer_id');
$username = Tools::getValue('username');
$password = Tools::getValue('password');

if (empty($customerId) || empty($username) || empty($password)) {
$this->ajaxRender(json_encode([
'success' => false,
'error' => $this->module->l('Customer ID, Username, and Password are required', self::FILE_NAME),
'terminals' => [],
]));
die();
}

$isTestMode = $environment === 'test';

try {
/** @var SaferPayTerminalService $terminalService */
$terminalService = $this->module->getService(SaferPayTerminalService::class);

$terminals = $terminalService->getAvailableTerminals($customerId, $username, $password, $isTestMode);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The getAvailableTerminals method is called with four arguments ($customerId, $username, $password, $isTestMode), but its definition in SaferPayTerminalService only accepts one optional argument ($customerId). This will cause a fatal error.

The SaferPayTerminalService::getAvailableTerminals method needs to be updated to accept these new parameters and use them to make the API request with the correct credentials and environment, instead of relying on the stored configuration values.


$this->ajaxRender(json_encode([
'success' => true,
'terminals' => $terminals,
'count' => count($terminals),
]));
die();
} catch (Exception $e) {
/** @var LoggerInterface $logger */
$logger = $this->module->getService(LoggerInterface::class);
$logger->error(sprintf('%s - AJAX failed to get terminals: %s', self::FILE_NAME, $e->getMessage()), [
'context' => [
'environment' => $environment,
'customer_id' => $customerId,
],
'exception' => $e,
]);

$this->ajaxRender(json_encode([
'success' => false,
'error' => $this->module->l('Failed to fetch terminals. Please check your credentials and try again.', self::FILE_NAME),
'terminals' => [],
]));
die();
}
}

public function postProcess()
{
parent::postProcess();
Expand Down
6 changes: 3 additions & 3 deletions src/Service/SaferPayTerminalService.php
Original file line number Diff line number Diff line change
Expand Up @@ -145,17 +145,17 @@ private function parseTerminalsResponse($responseBody)
$description = $terminal['Description'] ?? null;

if ($terminalId) {
$terminals[] = [
$terminals[$terminalId] = [
'TerminalId' => $terminalId,
'Description' => $description ?: $terminalId,
'Description' => $description,
];
}
}
}

$this->logger->debug(sprintf('%s - Parsed %d terminals', self::FILE_NAME, count($terminals)));

return $terminals;
return array_values($terminals);
}

/**
Expand Down
215 changes: 212 additions & 3 deletions views/js/admin/saferpay_settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,224 @@
*/

$(document).ready(function (e) {
// #region agent log
console.log('[DEBUG] saferpay_settings.js loaded');
// #endregion
Comment on lines +24 to +26
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This file contains multiple debugging regions with console.log and console.error statements (e.g., lines 24-26, 41-43, 53-55, 111-113, 124-126, 136-138, 170-172). These should be removed from the production code to avoid cluttering the browser console and for cleaner code.


$("input[name='SAFERPAY_CONFIGURATION_NAME']").keypress(function (e) {
//disable symbols
var txt = String.fromCharCode(e.which);
if (!txt.match(/[A-Za-z0-9&. ]/)) {
return false;
}
// disable space
if (e.keyCode === 32) {
return false;
}
});
});

var terminalFetchTimeout = null;

$('.saferpay-terminal-selector').each(function() {
// #region agent log
console.log('[DEBUG] Found terminal selector, count:', $('.saferpay-terminal-selector').length);
// #endregion

var $container = $(this);
var environment = $container.data('environment');
var $select = $container.find('.saferpay-terminal-select');
var $helpBlock = $container.find('.help-block');
var $manualInput = $container.find('.saferpay-terminal-manual-input');

var getFieldValue = function(fieldName) {
var $field = $('input[name="' + fieldName + '"], select[name="' + fieldName + '"]');
// #region agent log
console.log('[DEBUG] getFieldValue:', fieldName, 'found:', $field.length > 0, 'value:', $field.length ? ($field.attr('type') === 'password' ? '***' : $field.val()) : 'not found');
// #endregion
if ($field.length) {
if ($field.attr('type') === 'password') {
return $field.val() || '';
}
return $field.val() || '';
}
return '';
};

var getFieldName = function(baseName) {
return environment === 'test' ? baseName + '_TEST' : baseName;
};

var lastCredentials = {
customerId: getFieldValue(getFieldName('SAFERPAY_CUSTOMER_ID')),
username: getFieldValue(getFieldName('SAFERPAY_USERNAME')),
password: getFieldValue(getFieldName('SAFERPAY_PASSWORD'))
};

var fetchTerminals = function(force) {
var customerId = getFieldValue(getFieldName('SAFERPAY_CUSTOMER_ID'));
var username = getFieldValue(getFieldName('SAFERPAY_USERNAME'));
var password = getFieldValue(getFieldName('SAFERPAY_PASSWORD'));

var credentialsChanged =
customerId !== lastCredentials.customerId ||
username !== lastCredentials.username ||
password !== lastCredentials.password;

if (!force && !credentialsChanged && $select.find('option').length > 1) {
return;
}

// Update last credentials
lastCredentials = {
customerId: customerId,
username: username,
password: password
};

if (!customerId || !username || !password) {
$select.prop('disabled', true).html('<option value="">-- Select Terminal --</option>');
$helpBlock.text($helpBlock.data('empty-text') || 'Please configure Customer ID, Username, and Password to load terminals');
if ($manualInput.length) {
$manualInput.show();
}
return;
}

$select.prop('disabled', true);
$select.html('<option value="">Loading terminals...</option>');
$helpBlock.html('<i class="icon-spinner icon-spin"></i> Loading terminals...');

var ajaxUrl = window.location.href;

// #region agent log
console.log('[DEBUG] AJAX URL:', ajaxUrl, 'Window location:', window.location.href);
// #endregion

var params = {
ajax: 1,
action: 'getTerminals',
environment: environment,
customer_id: customerId,
username: username,
password: password
};

// #region agent log
console.log('[DEBUG] Making AJAX request with params:', Object.assign({}, params, {password: '***'}));
// #endregion

alert('Customer ID before AJAX: ' + customerId); // Temporary debug alert
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

This alert() is clearly for debugging and must be removed. It will block the UI for administrators and provides a poor user experience.


$.ajax({
url: ajaxUrl,
type: 'POST',
data: params,
dataType: 'json',
success: function(response) {
// #region agent log
console.log('[DEBUG] AJAX success response:', {success: response.success, terminalsCount: response.terminals ? response.terminals.length : 0, error: response.error});
// #endregion
if (response.success && response.terminals && response.terminals.length > 0) {
var options = '<option value="">-- Select Terminal --</option>';
var currentValue = $select.data('current-value') || '';

$.each(response.terminals, function(index, terminal) {
var selected = terminal.TerminalId === currentValue ? ' selected' : '';
var label = terminal.TerminalId + (terminal.Description ? ' - ' + terminal.Description : '');
options += '<option value="' + terminal.TerminalId + '"' + selected + '>' + label + '</option>';
});

$select.html(options).prop('disabled', false);

if (response.count === 1 && !$select.val()) {
$select.val(response.terminals[0].TerminalId).trigger('change');
}

$helpBlock.text('Select a terminal from the list');
if ($manualInput.length) {
$manualInput.hide();
$manualInput.val('');
}
} else {
$select.html('<option value="">-- Select Terminal --</option>').prop('disabled', false);
var errorMsg = response.error || 'No terminals found. Please check your credentials.';
$helpBlock.html('<span class="text-danger">' + errorMsg + '</span>');
if ($manualInput.length) {
$manualInput.show();
}
}
},
error: function(xhr, status, error) {
// #region agent log
console.error('[DEBUG] AJAX Error:', {status: xhr.status, statusText: xhr.statusText, responseText: xhr.responseText ? xhr.responseText.substring(0, 200) : 'empty', error: error});
// #endregion
$select.html('<option value="">-- Select Terminal --</option>').prop('disabled', false);
$helpBlock.html('<span class="text-danger">Failed to load terminals. You can enter the Terminal ID manually.</span>');
if ($manualInput.length) {
$manualInput.show();
}
}
});
};

var debouncedFetch = function() {
clearTimeout(terminalFetchTimeout);
terminalFetchTimeout = setTimeout(fetchTerminals, 500);
};

var fieldNames = [
getFieldName('SAFERPAY_CUSTOMER_ID'),
getFieldName('SAFERPAY_USERNAME'),
getFieldName('SAFERPAY_PASSWORD')
];

fieldNames.forEach(function(fieldName) {
$('input[name="' + fieldName + '"], select[name="' + fieldName + '"]').on('input change blur', debouncedFetch);
});

$select.on('change', function() {
if ($(this).val()) {
$manualInput.val('');
}
});

$manualInput.on('input', function() {
if ($(this).val()) {
$select.val('');
}
});

$manualInput.on('blur', function() {
if ($(this).val() && !$select.val()) {
var manualValue = $(this).val();
if (!$select.find('option[value="' + manualValue + '"]').length) {
$select.append('<option value="' + manualValue + '" selected>' + manualValue + '</option>');
} else {
$select.val(manualValue);
}
$(this).val('');
}
});

$container.find('.saferpay-refresh-terminals').on('click', function() {
fetchTerminals(true);
});

$('form').on('submit', function() {
if ($manualInput.val() && !$select.val()) {
var manualValue = $(this).val();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

There is a bug in the form submit handler. $(this).val() is being used to get the value of the manual input, but this refers to the form element, not the input. This will not retrieve the correct value. You should use $manualInput.val() instead.

Suggested change
var manualValue = $(this).val();
var manualValue = $manualInput.val();

if (!$select.find('option[value="' + manualValue + '"]').length) {
$select.append('<option value="' + manualValue + '" selected>' + manualValue + '</option>');
} else {
$select.val(manualValue);
}
}
});

if (getFieldValue(getFieldName('SAFERPAY_CUSTOMER_ID')) &&
getFieldValue(getFieldName('SAFERPAY_USERNAME')) &&
getFieldValue(getFieldName('SAFERPAY_PASSWORD'))) {
if ($select.find('option').length <= 1) {
fetchTerminals();
}
}
});
});
59 changes: 37 additions & 22 deletions views/templates/admin/partials/field-terminal-id.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,43 @@
*@license SIX Payment Services
*}
<div class="saferpay-terminal-selector" data-environment="{$field['environment']|escape:'htmlall':'UTF-8'}">
<select
name="{$key|escape:'htmlall':'UTF-8'}"
id="{$key|escape:'htmlall':'UTF-8'}"
class="saferpay-terminal-select form-control fixed-width-xl"
{if !isset($field['terminals']) || count($field['terminals']) == 0}disabled{/if}>
<option value="">{l s='-- Select Terminal --' mod='saferpayofficial'}</option>
{if isset($field['terminals']) && count($field['terminals']) > 0}
{foreach from=$field['terminals'] item=terminal}
<option value="{$terminal['TerminalId']|escape:'htmlall':'UTF-8'}"
{if $field['value'] == $terminal['TerminalId']}selected{/if}>
{$terminal['TerminalId']|escape:'htmlall':'UTF-8'} - {$terminal['Description']|escape:'htmlall':'UTF-8'}
</option>
{/foreach}
{/if}
</select>
{if !isset($field['terminals']) || count($field['terminals']) == 0}
<p class="help-block text-muted">
<div class="input-group fixed-width-xl">
<select
name="{$key|escape:'htmlall':'UTF-8'}"
id="{$key|escape:'htmlall':'UTF-8'}"
class="saferpay-terminal-select form-control"
data-current-value="{$field['value']|escape:'htmlall':'UTF-8'}"
{if !isset($field['terminals']) || count($field['terminals']) == 0}disabled{/if}>
<option value="">{l s='-- Select Terminal --' mod='saferpayofficial'}</option>
{if isset($field['terminals']) && count($field['terminals']) > 0}
{foreach from=$field['terminals'] item=terminal}
<option value="{$terminal['TerminalId']|escape:'htmlall':'UTF-8'}"
{if $field['value'] == $terminal['TerminalId']}selected{/if}>
{$terminal['TerminalId']|escape:'htmlall':'UTF-8'}{if $terminal['Description']} - {$terminal['Description']|escape:'htmlall':'UTF-8'}{/if}
</option>
{/foreach}
{/if}
</select>
<span class="input-group-btn">
<button type="button" class="btn btn-default saferpay-refresh-terminals" title="{l s='Refresh Terminals' mod='saferpayofficial'}">
<i class="icon-refresh"></i>
</button>
</span>
</div>
<input
type="text"
name="{$key|escape:'htmlall':'UTF-8'}_manual"
id="{$key|escape:'htmlall':'UTF-8'}_manual"
class="saferpay-terminal-manual-input form-control fixed-width-xl"
placeholder="{l s='Or enter Terminal ID manually' mod='saferpayofficial'}"
value="{if (!isset($field['terminals']) || count($field['terminals']) == 0) && $field['value']}{$field['value']|escape:'htmlall':'UTF-8'}{/if}"
style="margin-top: 5px; {if isset($field['terminals']) && count($field['terminals']) > 0}display: none;{/if}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using inline styles for conditional display logic is not ideal as it mixes presentation with structure. It's better to toggle a CSS class. PrestaShop/Bootstrap provides utility classes like hidden or d-none for this purpose. This improves code readability and maintainability.

For example:

class="... {if isset($field['terminals']) && count($field['terminals']) > 0}hidden{/if}"
style="margin-top: 5px;"

/>
<p class="help-block" data-empty-text="{l s='Please configure Customer ID, Username, and Password to load terminals' mod='saferpayofficial'}">
{if !isset($field['terminals']) || count($field['terminals']) == 0}
{l s='Please configure Customer ID, Username, and Password to load terminals' mod='saferpayofficial'}
</p>
{else}
<p class="help-block">
{else}
{l s='Select a terminal from the list' mod='saferpayofficial'}
</p>
{/if}
{/if}
</p>
</div>
Loading