diff --git a/config_uk.xml b/config_uk.xml
new file mode 100644
index 0000000..96d98a5
--- /dev/null
+++ b/config_uk.xml
@@ -0,0 +1,11 @@
+
+
+ hutko
+
+
+
+
+
+ 1
+ 1
+
\ No newline at end of file
diff --git a/controllers/front/callback.php b/controllers/front/callback.php
index f299b4e..0d2c5a1 100644
--- a/controllers/front/callback.php
+++ b/controllers/front/callback.php
@@ -29,24 +29,7 @@ if (!defined('_PS_VERSION_')) {
*/
class HutkoCallbackModuleFrontController extends ModuleFrontController
{
- /**
- * Handles the post-processing of the payment gateway callback.
- *
- * This method is the entry point for Hutko's server-to-server notifications.
- * It performs the following steps:
- * 1. Parses the incoming request body (from POST or raw input).
- * 2. Validates the integrity of the request using the module's signature validation.
- * 3. Extracts the cart ID and loads the corresponding cart.
- * 4. Checks if the order already exists for the cart. If not, it attempts to validate
- * and create the order, using `postponeCallback` to manage potential race conditions.
- * 5. Based on the `order_status` received in the callback, it updates the PrestaShop
- * order's status (e.g., to success, error, or processing).
- * 6. Logs all significant events and errors using PrestaShopLogger.
- * 7. Exits with a simple string response ('OK' or an error message) as expected by
- * payment gateways.
- *
- * @return void
- */
+
public function postProcess(): void
{
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
@@ -61,72 +44,18 @@ class HutkoCallbackModuleFrontController extends ModuleFrontController
PrestaShopLogger::addLog('Hutko Callback: Empty request body received.', 2, null, 'Cart', null, true);
exit('Empty request');
}
-
- // 2. Validate the request signature and required fields.
- // Ensure all expected fields are present before proceeding with validation.
- $requiredFields = ['order_id', 'amount', 'order_status', 'signature', 'merchant_id'];
- foreach ($requiredFields as $field) {
- if (!isset($requestBody[$field])) {
- PrestaShopLogger::addLog('Hutko Callback: Missing required field in request: ' . $field, 2, null, 'Cart', null, true);
- exit('Missing parameter: ' . $field);
- }
- }
-
// Assuming validateResponse returns true on success, or a string error message on failure.
$isSignatureValid = $this->module->validateResponse($requestBody);
if ($isSignatureValid !== true) {
PrestaShopLogger::addLog('Hutko Callback: Invalid signature. Error: ' . $isSignatureValid, 2, null, 'Cart', null, true);
exit('Invalid signature');
}
+ // PrestaShopLogger::addLog('Hutko Callback: ' . json_encode($requestBody), 1);
+
- // 3. Extract cart ID and load the cart.
- // The order_id is expected to be in the format "cartID|timestamp".
$transaction_id = $requestBody['order_id'];
$orderIdParamParts = explode($this->module->order_separator, $transaction_id);
- $cartId = (int)$orderIdParamParts[0]; // Ensure it's an integer
-
- $cart = new Cart($cartId);
-
- // Validate cart object.
- if (!Validate::isLoadedObject($cart)) {
- PrestaShopLogger::addLog('Hutko Callback: Cart not found for ID: ' . $cartId, 3, null, 'Cart', $cartId, true);
- exit('Cart not found');
- }
-
- // 4. Determine the amount received from the callback.
- $amountReceived = round((float)$requestBody['amount'] / 100, 2);
-
- // 5. Check if the order already exists for this cart.
- $orderId = Order::getIdByCartId($cart->id);
- $orderExists = (bool)$orderId;
-
- // 6. If the order doesn't exist, attempt to validate it using postponeCallback.
- // This handles the scenario where the callback arrives before the customer returns to the site.
- if (!$orderExists) {
- // The callback function will check for order existence again right before validation
- // to handle potential race conditions.
- $validationCallback = function () use ($cart, $amountReceived, $transaction_id) {
- // Re-check if the order exists right before validation in case the result controller
- // created it in the interim while we were waiting for the second digit.
- if (Order::getIdByCartId($cart->id)) {
- return true; // Order already exists, no need to validate again.
- }
- // If order still doesn't exist, proceed with validation.
- $idState = (int)Configuration::get('HUTKO_SUCCESS_STATUS_ID');
- return $this->module->validateOrderFromCart((int)$cart->id, $amountReceived, $transaction_id, $idState, true);
- };
-
- // Postpone validation to seconds ending in 8 to avoid collision with result controller (ending in 3).
- $validationResult = $this->module->postponeCallback($validationCallback, 8);
-
- // Re-fetch order ID after potential validation.
- $orderId = Order::getIdByCartId($cart->id);
-
- if (!$orderId || !$validationResult) {
- PrestaShopLogger::addLog('Hutko Callback: Order validation failed for cart ID: ' . $cart->id, 2, null, 'Cart', $cart->id, true);
- exit('Order validation failed');
- }
- }
+ $orderId = (int)$orderIdParamParts[0]; // Ensure it's an integer
// If we reached here, an order should exist. Load it.
$order = new Order($orderId);
@@ -141,11 +70,17 @@ class HutkoCallbackModuleFrontController extends ModuleFrontController
switch ($orderStatusCallback) {
case 'approved':
- $expectedState = (int)Configuration::get('HUTKO_SUCCESS_STATUS_ID');
- // Only change state if it's not already the success state or "Payment accepted".
- // "Payment accepted" (PS_OS_PAYMENT) might be set by validateOrderFromCart.
- if ($currentOrderState !== $expectedState && $currentOrderState !== (int)Configuration::get('PS_OS_PAYMENT')) {
- $this->module->updateOrderStatus($orderId, $expectedState, 'Payment approved by Hutko.');
+ // Only success state if no refunds was done.
+ if ($requestBody['response_status'] == 'success' && (int)$requestBody['reversal_amount'] === 0) {
+ $expectedState = (int)Configuration::get('HUTKO_SUCCESS_STATUS_ID', null, null, null, Configuration::get('PS_OS_PAYMENT'));
+ // Only change state if it's not already the success state or "Payment accepted".
+
+ if ($currentOrderState !== $expectedState) {
+ $this->module->addPayment($requestBody, $order);
+ $order->setCurrentState($expectedState);
+ }
+ } else {
+ PrestaShopLogger::addLog('Hutko Callback: Unhandled response_status: ' . $requestBody['response_status']);
}
exit('OK');
break;
@@ -154,7 +89,7 @@ class HutkoCallbackModuleFrontController extends ModuleFrontController
$expectedState = (int)Configuration::get('PS_OS_ERROR');
// Only change state if it's not already the error state.
if ($currentOrderState !== $expectedState) {
- $this->module->updateOrderStatus($orderId, $expectedState, 'Payment ' . $orderStatusCallback . ' by Hutko.');
+ $order->setCurrentState($expectedState);
}
exit('Order ' . $orderStatusCallback);
break;
@@ -162,7 +97,7 @@ class HutkoCallbackModuleFrontController extends ModuleFrontController
$expectedState = (int)Configuration::get('PS_OS_ERROR');
// Only change state if it's not already the error state.
if ($currentOrderState !== $expectedState) {
- $this->module->updateOrderStatus($orderId, $expectedState, 'Payment ' . $orderStatusCallback . ' by Hutko.');
+ $order->setCurrentState($expectedState);
}
exit('Order ' . $orderStatusCallback);
break;
@@ -173,7 +108,7 @@ class HutkoCallbackModuleFrontController extends ModuleFrontController
// For now, if it's not already in a success/error state, set it to 'processing'.
$processingState = (int)Configuration::get('PS_OS_PAYMENT'); // Or a custom 'processing' state
if ($currentOrderState !== $processingState && $currentOrderState !== (int)Configuration::get('HUTKO_SUCCESS_STATUS_ID') && $currentOrderState !== (int)Configuration::get('PS_OS_ERROR')) {
- $this->module->updateOrderStatus($orderId, $processingState, 'Payment processing by Hutko.');
+ $order->setCurrentState($processingState);
}
exit('Processing');
break;
@@ -198,13 +133,8 @@ class HutkoCallbackModuleFrontController extends ModuleFrontController
*/
private function getRequestBody(): array
{
- // Prioritize $_POST for form data.
- if (!empty($_POST)) {
- return $_POST;
- }
- // Fallback to raw input for JSON payloads, common for callbacks.
- $jsonBody = json_decode(Tools::file_get_contents("php://input"), true);
+ $jsonBody = json_decode(file_get_contents("php://input"), true);
if (is_array($jsonBody)) {
return $jsonBody;
}
diff --git a/controllers/front/redirect.php b/controllers/front/redirect.php
index a49fba1..0a757f5 100644
--- a/controllers/front/redirect.php
+++ b/controllers/front/redirect.php
@@ -32,16 +32,32 @@ class HutkoRedirectModuleFrontController extends ModuleFrontController
*/
public function initContent()
{
- // Call the parent class's initContent method to perform default initializations.
- parent::initContent();
-
- // Assign Smarty variables to be used in the redirect template.
- $this->context->smarty->assign([
- 'hutko_url' => $this->module->checkout_url, // The URL of the Hutko payment gateway.
- 'hutko_inputs' => $this->module->buildInputs(), // An array of input parameters required by Hutko.
- ]);
-
// Set the template to be used for displaying the redirection form.
$this->setTemplate('module:' . $this->module->name . '/views/templates/front/redirect.tpl');
+ // Call the parent class's initContent method to perform default initializations.
+ parent::initContent();
+ $idState = (int) Configuration::get('HUTKO_NEW_ORDER_STATUS_ID', null, null, null, Configuration::get('PS_OS_PREPARATION'));
+ $validationResult = $this->module->validateOrder($this->context->cart->id, $idState, 0, $this->module->displayName);
+
+ if ($validationResult) {
+ $order = new Order((int)$this->module->currentOrder);
+ $requestData = $this->module->buildPaymentRequestData($order, null, null, null);
+ $responseData = $this->module->sendAPICall($this->module->checkout_url, $requestData);
+ if (isset($responseData['response']['response_status']) && $responseData['response']['response_status'] === 'success') {
+ $orderPayment = new OrderPayment();
+ $orderPayment->order_reference = $order->reference;
+ $orderPayment->id_currency = $order->id_currency;
+ $orderPayment->amount = 0;
+ $orderPayment->payment_method = $this->module->displayName;
+ $orderPayment->transaction_id = $requestData['order_id'];
+ $orderPayment->card_holder = $responseData['response']['checkout_url'];
+ $orderPayment->add();
+ Tools::redirect($responseData['response']['checkout_url']);
+ return;
+ }
+ $this->context->smarty->assign([
+ 'hutko_response' => $responseData['response'], // The URL of the Hutko payment gateway.
+ ]);
+ }
}
}
diff --git a/controllers/front/result.php b/controllers/front/result.php
deleted file mode 100644
index 20dc940..0000000
--- a/controllers/front/result.php
+++ /dev/null
@@ -1,185 +0,0 @@
-module->order_separator, $transaction_id);
- $cartId = (int)$cartIdParts[0];
-
- // Validate extracted cart ID. It must be a numeric value.
- if (!is_numeric($cartId)) {
- $this->errors[] = Tools::displayError($this->trans('Invalid cart ID received.', [], 'Modules.Hutko.Shop'));
- $this->redirectWithNotifications($this->context->link->getPageLink('order', true, $this->context->language->id));
- return; // Stop execution after redirection
- }
-
- // Load the cart object.
- $cart = new Cart($cartId);
-
- // Verify that the cart belongs to the current customer to prevent unauthorized access.
- if (!Validate::isLoadedObject($cart) || $cart->id_customer != $this->context->customer->id) {
- $this->errors[] = Tools::displayError($this->trans('Access denied to this order.', [], 'Modules.Hutko.Shop'));
- Tools::redirect('/'); // Redirect to home or a more appropriate error page
- }
-
- // Handle different payment statuses.
- switch ($orderStatus) {
- case 'declined':
- $this->errors[] = Tools::displayError($this->trans('Your payment was declined. Please try again or use a different payment method.', [], 'Modules.Hutko.Shop'));
- $this->redirectWithNotifications($this->context->link->getPageLink('order', true, $this->context->language->id));
- break;
-
- case 'expired':
- $this->errors[] = Tools::displayError($this->trans('Your payment has expired. Please try again.', [], 'Modules.Hutko.Shop'));
- $this->redirectWithNotifications($this->context->link->getPageLink('order', true, $this->context->language->id));
- break;
-
- case 'processing':
- // For 'processing' status, we need to poll for order creation.
- // This loop will try to find the order for a limited time to avoid
- // exceeding PHP execution limits.
- $maxAttempts = 10; // Max 10 attempts
- $sleepTime = 5; // Sleep 5 seconds between attempts (total max 50 seconds)
- $orderFound = false;
- $orderId = 0;
-
- for ($i = 0; $i < $maxAttempts; $i++) {
- $orderId = Order::getIdByCartId($cart->id);
- if ($orderId) {
- $orderFound = true;
- break; // Order found, exit loop
- }
- // If not found, wait for a few seconds before retrying.
- sleep($sleepTime);
- }
-
- if ($orderFound) {
- // Order found, redirect to confirmation page.
- Tools::redirect($this->context->link->getPageLink('order-confirmation', true, $this->context->language->id, [
- 'id_cart' => $cart->id,
- 'id_module' => $this->module->id,
- 'id_order' => $orderId,
- 'key' => $this->context->customer->secure_key,
- ]));
- } else {
- // Order not found after multiple attempts, assume it's still processing or failed silently.
- $this->errors[] = Tools::displayError($this->trans('Your payment is still processing. Please check your order history later.', [], 'Modules.Hutko.Shop'));
- $this->redirectWithNotifications($this->context->link->getPageLink('order-history', true, $this->context->language->id));
- }
- break;
-
- case 'approved':
- $orderId = Order::getIdByCartId($cart->id);
-
- // If the order doesn't exist yet, validate it.
- // The postponeCallback is used here to avoid race conditions with the callback controller
- // (which might be trying to validate the order on seconds ending in 8, while this
- // controller tries on seconds ending in 3).
- if (!$orderId) {
- // Define the validation logic to be executed by postponeCallback.
- // This callback will first check if the order exists, and only
- // validate if it doesn't, to avoid race conditions.
- $validationCallback = function () use ($cart, $amountReceived, $transaction_id) {
- // Re-check if the order exists right before validation in case the callback
- // controller created it in the interim while we were waiting for the second digit.
- if (Order::getIdByCartId($cart->id)) {
- return true; // Order already exists, no need to validate again.
- }
- $idState = (int)Configuration::get('HUTKO_SUCCESS_STATUS_ID');
- // If order still doesn't exist, proceed with validation.
- return $this->module->validateOrderFromCart((int)$cart->id, $amountReceived, $transaction_id, $idState);
- };
-
- // Postpone the execution of the validation callback until the second ends in 3.
- $validationResult = $this->module->postponeCallback($validationCallback, 3);
-
- // After the postponed callback has run, try to get the order ID again.
- $orderId = Order::getIdByCartId($cart->id);
-
- // If validation failed or order still not found, add an error.
- if (!$orderId || !$validationResult) {
- $this->errors[] = Tools::displayError($this->trans('Payment approved but order could not be created. Please contact support.', [], 'Modules.Hutko.Shop'));
- $this->redirectWithNotifications($this->context->link->getPageLink('order', true, $this->context->language->id));
- break;
- }
- }
-
- // If order exists (either found initially or created by validation), redirect to confirmation.
- Tools::redirect($this->context->link->getPageLink('order-confirmation', true, $this->context->language->id, [
- 'id_cart' => $cart->id,
- 'id_module' => $this->module->id,
- 'id_order' => $orderId,
- 'key' => $this->context->customer->secure_key,
- ]));
- break;
-
- default:
- // For any unexpected status, redirect to order history with a generic error.
- $this->errors[] = Tools::displayError($this->trans('An unexpected payment status was received. Please check your order history.', [], 'Modules.Hutko.Shop'));
- $this->redirectWithNotifications($this->context->link->getPageLink('order-history', true, $this->context->language->id));
- break;
- }
-
- // This part should ideally not be reached if all cases are handled with redirects.
- // However, as a fallback, if any errors were accumulated without a specific redirect,
- // redirect to the order page.
- if (count($this->errors)) {
- $this->redirectWithNotifications($this->context->link->getPageLink('order', true, $this->context->language->id));
- }
- }
-}
diff --git a/hutko.php b/hutko.php
index e317d17..1a9bc04 100644
--- a/hutko.php
+++ b/hutko.php
@@ -21,26 +21,29 @@ class Hutko extends PaymentModule
{
public $order_separator = '#';
-
- public $checkout_url = 'https://pay.hutko.org/api/checkout/redirect/';
+ public $redirect_url = 'https://pay.hutko.org/api/checkout/redirect/';
+ public $checkout_url = 'https://pay.hutko.org/api/checkout/url/';
public $refund_url = 'https://pay.hutko.org/api/reverse/order_id';
public $status_url = 'https://pay.hutko.org/api/status/order_id';
- private $settingsList = [
+ public $settingsList = [
'HUTKO_MERCHANT',
'HUTKO_SECRET_KEY',
- 'HUTKO_BACK_REF',
+ 'HUTKO_SHIPPING_INCLUDE',
+ 'HUTKO_SHIPPING_PRODUCT_NAME',
+ 'HUTKO_SHIPPING_PRODUCT_CODE',
+ 'HUTKO_NEW_ORDER_STATUS_ID',
'HUTKO_SUCCESS_STATUS_ID',
'HUTKO_SHOW_CARDS_LOGO'
];
- private $postErrors = [];
+ public $postErrors = [];
public function __construct()
{
$this->name = 'hutko';
$this->tab = 'payments_gateways';
- $this->version = '1.1.1';
+ $this->version = '1.2.0';
$this->author = 'Hutko';
$this->bootstrap = true;
parent::__construct();
@@ -50,12 +53,36 @@ class Hutko extends PaymentModule
$this->description = $this->trans('Hutko is a payment platform whose main function is to provide internet acquiring.', array(), 'Modules.Hutko.Admin');
}
- public function install()
+ public function install(): bool
{
- return parent::install()
+ $success = parent::install()
&& $this->registerHook('paymentOptions')
- && $this->registerHook('displayAdminOrderContentOrder')
&& $this->registerHook('actionAdminControllerSetMedia');
+
+ // If the initial mandatory hooks failed, stop here.
+ if (!$success) {
+ return false;
+ }
+
+ // Now, conditionally register the order content/tab hook based on PS version.
+ // We only need to check if the *required* hook for this version was successfully registered.
+ $conditionalHookSuccess = true; // Assume success until we try to register one and it fails
+
+ // Check if PrestaShop version is 1.7.x (>= 1.7 and < 8.0)
+ if (version_compare(_PS_VERSION_, '1.7', '>=') && version_compare(_PS_VERSION_, '8.0', '<')) {
+ // Register the 1.7 hook
+ $conditionalHookSuccess = $this->registerHook('displayAdminOrderContentOrder');
+ }
+ // Check if PrestaShop version is 8.x or 9.x (>= 8.0 and < 10.0)
+ elseif (version_compare(_PS_VERSION_, '8.0', '>=') && version_compare(_PS_VERSION_, '10.0', '<')) {
+ // Register the 8.x/9.x hook
+ $conditionalHookSuccess = $this->registerHook('displayAdminOrderTabContent');
+ }
+ // Note: If it's a different version (e.g., 1.6, 10.0+), neither of these specific hooks will be registered,
+ // and $conditionalHookSuccess remains true, which is the desired behavior based on the requirement.
+
+ // The module installation is successful only if the initial hooks passed AND the conditional hook (if attempted) passed.
+ return $success && $conditionalHookSuccess;
}
public function uninstall()
@@ -98,7 +125,7 @@ class Hutko extends PaymentModule
/**
* Create the form that will be displayed in the configuration of your module.
*/
- protected function renderForm()
+ public function renderForm()
{
$helper = new HelperForm();
@@ -126,13 +153,13 @@ class Hutko extends PaymentModule
/**
* Create the structure of your form.
*/
- protected function getConfigForm()
+ public function getConfigForm()
{
- global $cookie;
+
$options = [];
- foreach (OrderState::getOrderStates($cookie->id_lang) as $state) { // getting all Prestashop statuses
+ foreach (OrderState::getOrderStates($this->context->language->id) as $state) { // getting all Prestashop statuses
if (empty($state['module_name'])) {
$options[] = ['status_id' => $state['id_order_state'], 'name' => $state['name'] . " [ID: $state[id_order_state]]"];
}
@@ -149,16 +176,16 @@ class Hutko extends PaymentModule
'col' => 4,
'type' => 'text',
'prefix' => '',
- 'desc' => $this->trans('Enter a merchant id', array(), 'Modules.Hutko.Admin'),
+ 'desc' => $this->trans('Enter a merchant id. Use 1700002 for test setup.', array(), 'Modules.Hutko.Admin'),
'name' => 'HUTKO_MERCHANT',
- 'label' => $this->trans('Merchant ID', array(), 'Modules.Hutko.Admin'),
+ 'label' => $this->trans('Merchant ID.', array(), 'Modules.Hutko.Admin'),
),
array(
'col' => 4,
'type' => 'text',
'prefix' => '',
'name' => 'HUTKO_SECRET_KEY',
- 'desc' => $this->trans('Enter a secret key', array(), 'Modules.Hutko.Admin'),
+ 'desc' => $this->trans('Enter a secret key. Use "test" for test setup', array(), 'Modules.Hutko.Admin'),
'label' => $this->trans('Secret key', array(), 'Modules.Hutko.Admin'),
),
array(
@@ -172,6 +199,49 @@ class Hutko extends PaymentModule
'name' => 'name'
)
),
+ array(
+ 'type' => 'select',
+ 'prefix' => '',
+ 'name' => 'HUTKO_NEW_ORDER_STATUS_ID',
+ 'label' => $this->trans('Status for new orders before payment', array(), 'Modules.Hutko.Admin'),
+ 'options' => array(
+ 'query' => $options,
+ 'id' => 'status_id',
+ 'name' => 'name'
+ )
+ ),
+ array(
+ 'type' => 'radio',
+ 'name' => 'HUTKO_SHIPPING_INCLUDE',
+ 'label' => $this->trans('Include shipping cost to payment', array(), 'Modules.Hutko.Admin'),
+ 'is_bool' => true,
+ 'values' => array(
+ array(
+ 'id' => 'show_cards',
+ 'value' => 1,
+ 'label' => $this->trans('Yes', array(), 'Modules.Hutko.Admin')
+ ),
+ array(
+ 'id' => 'hide_cards',
+ 'value' => 0,
+ 'label' => $this->trans('No', array(), 'Modules.Hutko.Admin')
+ )
+ ),
+ ),
+ array(
+ 'type' => 'text',
+ 'name' => 'HUTKO_SHIPPING_PRODUCT_NAME',
+ 'label' => $this->trans('Shipping Name', array(), 'Modules.Hutko.Admin'),
+ 'desc' => $this->trans('Name of product/service to use in fiscalization for shipping amount', array(), 'Modules.Hutko.Admin'),
+
+ ),
+ array(
+ 'type' => 'text',
+ 'name' => 'HUTKO_SHIPPING_PRODUCT_CODE',
+ 'label' => $this->trans('Shipping Code', array(), 'Modules.Hutko.Admin'),
+ 'desc' => $this->trans('Code of product/service to use in fiscalization for shipping amount', array(), 'Modules.Hutko.Admin'),
+
+ ),
array(
'type' => 'radio',
'label' => $this->trans('Show Visa/MasterCard logo', array(), 'Modules.Hutko.Admin'),
@@ -191,6 +261,7 @@ class Hutko extends PaymentModule
),
),
),
+
'submit' => array(
'title' => $this->trans('Save', array(), 'Modules.Hutko.Admin'),
'class' => 'btn btn-default pull-right'
@@ -202,25 +273,21 @@ class Hutko extends PaymentModule
/**
* Set values for the inputs.
*/
- protected function getConfigFormValues()
+ public function getConfigFormValues(): array
{
- return array(
- 'HUTKO_MERCHANT' => Configuration::get('HUTKO_MERCHANT', null),
- 'HUTKO_SECRET_KEY' => Configuration::get('HUTKO_SECRET_KEY', null),
- 'HUTKO_SUCCESS_STATUS_ID' => Configuration::get('HUTKO_SUCCESS_STATUS_ID', null),
- 'HUTKO_SHOW_CARDS_LOGO' => Configuration::get('HUTKO_SHOW_CARDS_LOGO', null),
- );
+ foreach ($this->settingsList as $settingName) {
+ $list[$settingName] = Configuration::get($settingName);
+ }
+ return $list;
}
/**
* Save form data.
*/
- protected function postProcess()
+ public function postProcess()
{
-
- $form_values = $this->getConfigFormValues();
- foreach (array_keys($form_values) as $key) {
- Configuration::updateValue($key, Tools::getValue($key));
+ foreach ($this->settingsList as $settingName) {
+ Configuration::updateValue($settingName, Tools::getValue($settingName));
}
}
@@ -231,7 +298,7 @@ class Hutko extends PaymentModule
* Merchant ID and Secret Key provided by the user. It adds error messages
* to the `$this->postErrors` array if any of the validation rules fail.
*/
- private function postValidation(): void
+ public function postValidation(): void
{
// Check if the module's configuration form has been submitted.
if (Tools::isSubmit('submitHutkoModule')) {
@@ -323,41 +390,60 @@ class Hutko extends PaymentModule
* @return array An associative array containing the input parameters for the
* payment gateway. This array includes the generated signature.
*/
- public function buildInputs(): array
+ public function buildPaymentRequestData(Order $order, ?float $amount, ?Currency $currency, ?Customer $customer): array
{
// 1. Generate a unique order ID combining the cart ID and current timestamp.
- $orderId = $this->context->cart->id . $this->order_separator . time();
+ $orderId = $order->id . $this->order_separator . time();
// 2. Retrieve the merchant ID from the module's configuration.
$merchantId = Configuration::get('HUTKO_MERCHANT');
// 3. Create a description for the order.
- $orderDescription = $this->trans('Cart pay №', [], 'Modules.Hutko.Admin') . $this->context->cart->id;
+ $orderDescription = $this->trans('Order payment #', [], 'Modules.Hutko.Admin') . $order->reference;
// 4. Calculate the order amount in the smallest currency unit.
- $amount = round($this->context->cart->getOrderTotal(true, CART::ONLY_PRODUCTS) * 100);
+ if (!$amount) {
+ if (Configuration::get('HUTKO_SHIPPING_INCLUDE') && $order->total_shipping_tax_incl > 0) {
+ $amount = $order->total_products_wt + $order->total_shipping_tax_incl;
+ } else {
+ $amount = $order->total_products;
+ }
+ }
+ $amountInt = round($amount * 100);
// 5. Get the currency ISO code of the current cart.
- $currency = $this->context->currency->iso_code;
+ if (!$currency) {
+ $currency = new Currency($order->id_currency);
+ }
+ $currencyISO = $currency->iso_code;
// 6. Generate the server callback URL.
$serverCallbackUrl = $this->context->link->getModuleLink($this->name, 'callback', [], true);
+ // 7. Retrieve the customer's email address.
+ if (!$customer) {
+ $customer = new Customer($order->id_customer);
+ }
+ $customerEmail = $customer->email;
+
+ // 8. Generate the customer redirection URL after payment.
+ $responseUrl = $this->context->link->getPageLink('order-confirmation', true, $order->id_lang, [
+ 'id_cart' => $order->id_cart,
+ 'id_module' => $this->id,
+ 'id_order' => $order->id,
+ 'key' => $customer->secure_key,
+ ]);
- // 7. Generate the customer redirection URL after payment.
- $responseUrl = $this->context->link->getModuleLink($this->name, 'result', [], true);
- // 8. Retrieve the customer's email address.
- $customerEmail = $this->context->customer->email;
// 9. Build the reservation data as a base64 encoded JSON string.
- $reservationData = $this->buildReservationData();
+ $reservationData = $this->buildReservationData($order);
// 10. Construct the data array with all the collected parameters.
$data = [
'order_id' => $orderId,
'merchant_id' => $merchantId,
'order_desc' => $orderDescription,
- 'amount' => $amount,
- 'currency' => $currency,
+ 'amount' => $amountInt,
+ 'currency' => $currencyISO,
'server_callback_url' => $serverCallbackUrl,
'response_url' => $responseUrl,
'sender_email' => $customerEmail,
@@ -384,15 +470,15 @@ class Hutko extends PaymentModule
*
* @return string A base64 encoded JSON string containing the reservation data.
*/
- public function buildReservationData(): string
+ public function buildReservationData(Order $order): string
{
// 1. Retrieve the delivery address for the current cart.
- $address = new Address((int)$this->context->cart->id_address_delivery, $this->context->language->id);
+ $address = new Address((int)$order->id_address_delivery, $order->id_lang);
// 2. Fetch the customer's state name, if available.
$customerState = '';
if ($address->id_state) {
- $state = new State((int) $address->id_state, $this->context->language->id);
+ $state = new State((int) $address->id_state, $order->id_lang);
$customerState = $state->name;
}
@@ -409,16 +495,15 @@ class Hutko extends PaymentModule
"customer_name" => $this->getSlug($address->lastname . ' ' . $address->firstname),
"customer_city" => $this->getSlug($address->city),
"customer_zip" => $address->postcode,
- "account" => $this->context->customer->id,
+ "account" => $order->id_customer,
"uuid" => hash('sha256', _COOKIE_KEY_ . Tools::getShopDomainSsl()),
- "products" => $this->getProducts(),
+ "products" => $this->getProducts($order),
];
- // 4. Encode the data array as a JSON string.
- $jsonData = json_encode($data);
- // 5. Base64 encode the JSON string.
- return base64_encode($jsonData);
+
+
+ return base64_encode(json_encode($data));
}
@@ -438,16 +523,26 @@ class Hutko extends PaymentModule
* - 'total_amount': The total price of the product in the cart (price * quantity), rounded to two decimal places.
* - 'quantity': The quantity of the product in the cart.
*/
- public function getProducts(): array
+ public function getProducts(Order $order): array
{
$products = [];
- foreach ($this->context->cart->getProducts() as $cartProduct) {
+ foreach ($order->getProductsDetail() as $productDetail) {
$products[] = [
- "id" => (int)$cartProduct['id_product'],
- "name" => $cartProduct['name'],
- "price" => round((float)$cartProduct['price_with_reduction'], 2),
- "total_amount" => round((float) $cartProduct['price'] * (int)$cartProduct['quantity'], 2),
- "quantity" => (int)$cartProduct['quantity'],
+ "id" => $productDetail['product_id'] . '_' . $productDetail['product_attribute_id'] . '_' . $productDetail['id_customization'],
+ "name" => $productDetail['product_name'],
+ "price" => round((float)$productDetail['unit_price_tax_incl'], 2),
+ "total_amount" => round((float) $productDetail['total_price_tax_incl'], 2),
+ "quantity" => (int)$productDetail['product_quantity'],
+ ];
+ }
+
+ if (Configuration::get('HUTKO_SHIPPING_INCLUDE') && $order->total_shipping_tax_incl > 0) {
+ $products[] = [
+ "id" => Configuration::get('HUTKO_SHIPPING_PRODUCT_CODE', null, null, null, '0_0_1'),
+ "name" => Configuration::get('HUTKO_SHIPPING_PRODUCT_NAME', null, null, null, 'Service Fee'),
+ "price" => round((float)$order->total_shipping_tax_incl, 2),
+ "total_amount" => round((float) $order->total_shipping_tax_incl, 2),
+ "quantity" => 1,
];
}
return $products;
@@ -455,31 +550,6 @@ class Hutko extends PaymentModule
- /**
- * Validates an order based on the provided cart ID and expected amount,
- * setting the order status to "preparation".
- *
- * This method serves as a convenience wrapper around the `validateOrder` method,
- * pre-filling the order status with the configured "preparation" status.
- *
- * @param int $id_cart The ID of the cart associated with the order to be validated.
- * @param float $amount The expected total amount of the order. This value will be
- * compared against the cart's total.
- * @return bool True if the order validation was successful, false otherwise.
- * @see PaymentModule::validateOrder()
- */
- public function validateOrderFromCart(int $id_cart, float $amount, string $transaction_id = '', int $idState = 0, bool $fromCallBack = false): bool
- {
- if (!$idState) {
- $idState = (int) Configuration::get('PS_OS_PREPARATION');
- }
- if ($fromCallBack) {
- $this->context->cart = new Cart($id_cart);
- $this->context->customer = new Customer($this->context->cart->id_customer);
- }
- // Call the parent validateOrder method with the "preparation" status.
- return $this->validateOrder($id_cart, $idState, $amount, $this->displayName, null, ['transaction_id' => $transaction_id], null, false, $this->context->customer->secure_key);
- }
/**
* Generates a URL-friendly slug from a given text.
@@ -618,7 +688,7 @@ class Hutko extends PaymentModule
public function validateResponse(array $response): bool
{
// 1. Verify the Merchant ID
- if (Configuration::get('HUTKO_MERCHANT') !== $response['merchant_id']) {
+ if ((string)Configuration::get('HUTKO_MERCHANT') != (string)$response['merchant_id']) {
return false;
}
@@ -636,43 +706,6 @@ class Hutko extends PaymentModule
}
- /**
- * Postpones the execution of a callback function until the last digit of the current second
- * matches a specified target digit, and returns the result of the callback.
- *
- * @param callable $callback The callback function to execute.
- * @param int $targetDigit An integer from 0 to 9, representing the desired last digit of the second.
- * return the result of the callback function execution.
- * @throws InvalidArgumentException If $targetDigit is not an integer between 0 and 9.
- */
- function postponeCallback(callable $callback, int $targetDigit)
- {
- // Validate the target digit to ensure it's within the valid range (0-9)
- if ($targetDigit < 0 || $targetDigit > 9) {
- throw new InvalidArgumentException("The target digit must be an integer between 0 and 9.");
- }
-
- // Loop indefinitely until the condition is met
- while (true) {
- // Get the current second as a two-digit string (e.g., '05', '12', '59')
- $currentSecond = (int)date('s');
-
- // Extract the last digit of the current second
- $lastDigitOfSecond = $currentSecond % 10;
-
- // Check if the last digit matches the target digit
- if ($lastDigitOfSecond === $targetDigit) {
- echo "Condition met! Current second is {$currentSecond}, last digit is {$lastDigitOfSecond}.\n";
- // If the condition is met, execute the callback and return its result
- return $callback(); // Capture and return the callback's result
- } else {
- // If the condition is not met, print the current status and wait for a short period
- echo "Current second: {$currentSecond}, last digit: {$lastDigitOfSecond}. Still waiting...\n";
- // Wait for 100 milliseconds (0.1 seconds) to avoid busy-waiting and reduce CPU usage
- usleep(100000); // 100000 microseconds = 100 milliseconds
- }
- }
- }
/**
@@ -687,49 +720,131 @@ class Hutko extends PaymentModule
{
$order = new Order($orderId);
// Only update if the order is loaded and the current state is different from the new state.
- if (Validate::isLoadedObject($order) && (int)$order->getCurrentState() !== $newStateId) {
+ if (Validate::isLoadedObject($order) && (int)$order->getCurrentState() != $newStateId) {
$history = new OrderHistory();
$history->id_order = $orderId;
$history->changeIdOrderState($newStateId, $orderId);
$history->addWithemail();
}
}
+ public function addPayment(array $callback, Order $order): void
+ {
+ $callbackAmount = $callback['actual_amount'] ?? $callback['amount'];
+ $amountFloat = round($callbackAmount / 100, 2);
+
+ $order->addOrderPayment($amountFloat, $this->displayName, $callback['order_id'], $order->id_currency);
+ }
/**
- * Hook to display content in the admin order page tabs.
- * This will add a new tab for "Hutko Payments" or similar.
+ * Hook implementation for PrestaShop 1.7.x to display content in the admin order page.
*
- * @param array $params Contains Order 'order'
- * @return string
+ * This hook is typically used to add content *below* the main order details
+ * but before the tabbed section. It's often used for specific sections
+ * rather than entire tabs in 1.7. However, in this case, it's likely
+ * being used as a fallback or alternative for displaying the payment content
+ * depending on the module's design or compatibility needs for 1.7.
+ *
+ * @param array $params Contains context information, including the 'order' object.
+ * @return string The HTML content to be displayed in the admin order page.
*/
- public function hookdisplayAdminOrderContentOrder(array $params): string
+ public function hookdisplayAdminOrderContentOrder(array $params)
{
+ // Delegate the actual content generation to a shared function
+ // to avoid code duplication.
+ return $this->displayAdminOrderContent($params);
+ }
+
+
+ /**
+ * Hook implementation for PrestaShop 8.x and 9.x to display content
+ * within a specific tab on the admin order page.
+ *
+ * This hook is the standard way in newer PS versions to add a custom tab
+ * and populate its content on the order detail page.
+ *
+ * @param array $params Contains context information, including the 'order' object.
+ * @return string The HTML content to be displayed within the module's custom tab.
+ */
+ public function hookdisplayAdminOrderTabContent(array $params)
+ {
+ $params['order'] = new Order((int) $params['id_order']);
+ // Delegate the actual content generation to a shared function
+ // to avoid code duplication.
+ return $this->displayAdminOrderContent($params);
+ }
+
+
+ /**
+ * Common function to display content related to Hutko payments on the admin order page.
+ *
+ * This function handles the logic for processing potential form submissions (like refunds
+ * or status updates) and preparing data to be displayed in a template.
+ *
+ * It is called by different hooks depending on the PrestaShop version
+ * (displayAdminOrderContentOrder for 1.7.x, displayAdminOrderTabContent for 8.x/9.x).
+ *
+ * @param array $params Contains context information from the hook, typically including the 'order' object.
+ * @return string The rendered HTML content from the template.
+ */
+ public function displayAdminOrderContent(array $params): string
+ {
+ // Check if a refund form has been submitted
if (Tools::isSubmit('hutkoRefundsubmit')) {
+ // Process the refund logic.
$this->processRefundForm();
}
- if (Tools::getValue('hutkoOrderStatus')) {
- $this->processOrderStatus(Tools::getValue('hutkoOrderStatus'));
- }
- // This hook is used to render the content of the new tab on the order page.
- // We will fetch the payments for this order and pass them to the template.
+ // Check payment status
+ if (Tools::getValue('hutkoOrderPaymentStatus')) {
+ // Process the requested order status check.
+ $this->processOrderPaymentStatus(Tools::getValue('hutkoOrderPaymentStatus'));
+ }
+
+ // Ensure the 'order' object is present in the parameters
+ if (!isset($params['order']) || !$params['order'] instanceof Order) {
+ // Log an error or return an empty string if the order object is missing
+ // depending on how critical it is. Returning empty string is safer
+ // to avoid crashing the admin page.
+ PrestaShopLogger::addLog(
+ 'Hutko Module: Order object missing in displayAdminOrderContent hook.',
+ 1, // Error level
+ null,
+ 'Module',
+ (int)$this->id
+ );
+ return '';
+ }
+
+ // Get the Order object from the parameters
$order = $params['order'];
-
- // Fetch payments made via Hutko for this order
+ // Fetch all OrderPayment records associated with this order
+ // that were processed specifically by this payment module (based on display name)
+ // and have a transaction ID matching a specific pattern (order ID prefix).
+ // The transaction_id pattern suggests it's linked to the order ID for easy lookup.
$hutkoPayments = new PrestaShopCollection('OrderPayment');
- $hutkoPayments->where('order_reference', '=', $order->reference);
- $hutkoPayments->where('payment_method', '=', $this->displayName);
+ $hutkoPayments->where('order_reference', '=', $order->reference); // Filter by order reference
+ $hutkoPayments->where('payment_method', '=', $this->displayName); // Filter by this module's payment method name
+ // Filter by transaction ID pattern: Starts with the order ID followed by the configured separator.
+ // This assumes transaction IDs generated by this module follow this format.
+ $hutkoPayments->where('transaction_id', 'like', '' . $order->id . $this->order_separator . '%');
+ // Assign data to Smarty to be used in the template
$this->context->smarty->assign([
- 'hutkoPayments' => $hutkoPayments->getAll(),
+ // Pass the fetched Hutko payment records to the template as an array
+ 'hutkoPayments' => $hutkoPayments,
+ // Pass the order ID to the template
'id_order' => $order->id,
+ 'currency' => new Currency($order->id_currency),
]);
+ // Render the template located at 'views/templates/admin/order_payment_refund.tpl'
+ // This template will display the fetched payment information and potentially refund/status forms.
return $this->display(__FILE__, 'views/templates/admin/order_payment_refund.tpl');
}
- public function processOrderStatus(string $order_id): void
+
+ public function processOrderPaymentStatus(string $order_id): void
{
$data = [
'order_id' => $order_id,
@@ -741,32 +856,22 @@ class Hutko extends PaymentModule
$response = $this->sendAPICall($this->status_url, $data);
$this->context->controller->informations[] = $this->displayArrayInNotification($response['response']);
}
- /**
- * Hook to set media (JS/CSS) for admin controllers.
- * Used to load our custom JavaScript for the refund modal.
- *
- * @param array $params
- * @return void
- */
- public function hookActionAdminControllerSetMedia(array $params): void
- {
- // Only load our JS on the AdminOrders controller page
- if ($this->context->controller->controller_name === 'AdminOrders') {
- }
- }
+
public function processRefundForm()
{
$orderPaymentId = (int) Tools::getValue('orderPaymentId');
$amount = (float) Tools::getValue('refund_amount');
$comment = mb_substr(Tools::getValue('orderPaymentId', ''), 0, 1024);
- $orderId = (int) Tools::getValue('id_order');
- $result = $this->processRefund($orderPaymentId, $orderId, $amount, $comment);
+ $id_order = (int) Tools::getValue('id_order');
+ $result = $this->processRefund($orderPaymentId, $id_order, $amount, $comment);
+ $link = $this->context->link->getAdminLink('AdminOrders', true, [], ['id_order' => $id_order, 'vieworder' => true]);
if ($result->error) {
$this->context->controller->errors[] = $result->description;
}
if ($result->success) {
$this->context->controller->informations[] = $result->description;
}
+ // Tools::redirectAdmin($link);
}
/**
@@ -836,9 +941,6 @@ class Hutko extends PaymentModule
$order = new Order($orderId);
- // Add a note to the order history.
- $this->updateOrderStatus($order->id, (int)Configuration::get('PS_OS_REFUND'));
-
// Add a private message to the order for tracking.
$order->addOrderPayment(
-$amount, // Negative amount for refund
@@ -846,6 +948,8 @@ class Hutko extends PaymentModule
$orderPayment->transaction_id
);
+ $order->setCurrentState((int)Configuration::get('PS_OS_REFUND'), $this->context->employee->id);
+
PrestaShopLogger::addLog(
'Hutko Refund: Successfully processed refund for Order: ' . $orderId . ', Amount: ' . $amount . ', Comment: ' . $comment,
1, // Info
@@ -1014,4 +1118,16 @@ class Hutko extends PaymentModule
$retStr .= '';
return $retStr;
}
+
+
+ /**
+ * Check if the module uses the new translation system.
+ *
+ * @return bool
+ */
+ public function isUsingNewTranslationSystem()
+ {
+
+ return true;
+ }
}
diff --git a/translations/ru-RU/ModulesHutkoAdmin.ru-RU.xlf b/translations/ru-RU/ModulesHutkoAdmin.ru-RU.xlf
new file mode 100644
index 0000000..1385a61
--- /dev/null
+++ b/translations/ru-RU/ModulesHutkoAdmin.ru-RU.xlf
@@ -0,0 +1,212 @@
+
+
+
+
+
+Hutko is a payment platform whose main function is to provide internet acquiring.
+Hutko — це платіжна платформа, основною функцією якої є забезпечення інтернет-еквайрингу.
+Line:
+
+
+Довідка specify the Hutko account details for customers
+Пожалуйста, уточніть реквізити акаунту Hutko для клієнтів
+Line:
+
+
+Enter merchant id. Use 1700002 for test setup.
+Введіть ідентифікатор продавця. Використовуйте 1700002 для тестового налаштування.
+Line:
+
+
+Merchant ID.
+Merchant ID.
+Line:
+
+
+Enter a secret key. use "test" for test setup
+Введіть секретний ключ. Використовуйте тест для налаштування тесту.
+Line:
+
+
+Secret key
+Секретний ключ
+Line:
+
+
+Status after success payment
+Статус після успішної оплати
+Line:
+
+
+Status for new orders before payment
+Статус нових замовлень до оплати
+Line:
+
+
+Include shipping cost to payment
+Включити вартість доставки до оплати
+Line:
+
+
+Yes
+Так
+Line:
+
+
+No
+Ні
+Line:
+
+
+Shipping Name
+Назва для доставки
+Line:
+
+
+Назва продукту/сервісу для використання в fiscalization for shipping amount
+Назва продукту/послуги для використання у фіскалізації для суми доставки
+Line:
+
+
+Shipping Code
+Код доставки
+Line:
+
+
+Code product/service для використання в fiscalization for shipping amount
+Код товару/послуги для використання у фіскалізації для суми доставки
+Line:
+
+
+Show Visa/MasterCard logo
+Показати логотип Visa/MasterCard
+Line:
+
+
+Save
+Зберегти
+Line:
+
+
+Merchant ID is required.
+Потрібен ідентифікатор продавця (Merchant ID).
+Line:
+
+
+Merchant ID must be numeric.
+Ідентифікатор продавця має бути числовим.
+Line:
+
+
+Secret key is required.
+Потрібен секретний ключ.
+Line:
+
+
+Секрет key must be at least 10 characters long and cannot be entirely numeric.
+Секретний ключ повинен містити щонайменше 10 символів і не може бути повністю числовим.
+Line:
+
+
+Pay via payment system Hutko
+Оплата через платіжну систему Hutko
+Line:
+
+
+Pay via Hutko
+Оплата через Hutko
+Line:
+
+
+Order payment #
+Оплата замовлення #
+Line:
+
+
+Order payment not found.
+Оплата замовлення не знайдена.
+Line:
+
+
+Invalid transaction ID format.
+Недійсний формат ідентифікатора транзакції.
+Line:
+
+
+Refund success.
+Повернення коштів успішне.
+Line:
+
+
+
+Платежі та повернення коштів Hutko
+Line:
+
+
+Transaction ID
+Transaction ID
+Line:
+
+
+Amount
+Сума
+Line:
+
+
+Payment Date
+Дата платежу
+Line:
+
+
+Actions
+Дії
+Line:
+
+
+Refund
+Повернення коштів
+Line:
+
+
+Status
+Статус
+Line:
+
+
+Initiate Refund
+Ініціювати повернення коштів
+Line:
+
+
+Refund Amount
+Сума відшкодування
+Line:
+
+
+Виберіть гроші для того, щоб скористатися цим платежом.
+Введіть суму для повернення коштів за цей платіж.
+Line:
+
+
+Refund Reason/Comment
+Причина/коментар повернення коштів
+Line:
+
+
+Optional: A brief reason for the refund.
+Необов'язково: Коротка причина повернення коштів.
+Line:
+
+
+Cancel
+Скасувати
+Line:
+
+
+Process Refund
+Виконати повернення коштів
+Line:
+
+
+
+
diff --git a/translations/ru-RU/ModulesHutkoShop.ru-RU.xlf b/translations/ru-RU/ModulesHutkoShop.ru-RU.xlf
new file mode 100644
index 0000000..0e5d0e4
--- /dev/null
+++ b/translations/ru-RU/ModulesHutkoShop.ru-RU.xlf
@@ -0,0 +1,22 @@
+
+
+
+
+
+Payment failure.
+Сбой оплаты.
+Line:
+
+
+Допустить попытку
+Попробуйте еще раз
+Line:
+
+
+Order validation failure
+Ошибка проверки заказа
+Line:
+
+
+
+
diff --git a/translations/uk-UA/ModulesHutkoAdmin.uk-UA.xlf b/translations/uk-UA/ModulesHutkoAdmin.uk-UA.xlf
new file mode 100644
index 0000000..02f7a41
--- /dev/null
+++ b/translations/uk-UA/ModulesHutkoAdmin.uk-UA.xlf
@@ -0,0 +1,212 @@
+
+
+
+
+
+Hutko is a payment platform whose main function is to provide internet acquiring.
+Hutko – это платежная платформа, основной функцией которой является обеспечение интернет-эквайринга.
+Line:
+
+
+Связать specify the Hutko account details for customers
+Пожалуйста, уточните реквизиты учетной записи Hutko для клиентов
+Line:
+
+
+Enter для merchant id.
+Введите идентификатор продавца. Используйте 1700002 для тестовой настройки.
+Line:
+
+
+Merchant ID.
+Merchant ID.
+Line:
+
+
+Enter a secret key.
+Введите секретный ключ.
+Line:
+
+
+Secret key
+Секретный ключ
+Line:
+
+
+Status after success payment
+Статус после успешной оплаты
+Line:
+
+
+Status for new orders before payment
+Статус новых заказов к оплате
+Line:
+
+
+Включая плату за выплату
+Включить стоимость доставки в оплату
+Line:
+
+
+Yes
+Да
+Line:
+
+
+No
+Нет
+Line:
+
+
+Shipping Name
+Название для доставки
+Line:
+
+
+Имя продукта/сервиса для использования в fiscalization for shipping amount
+Имя продукта/услуги для использования в фискализации для суммы доставки
+Line:
+
+
+Shipping Code
+Код доставки
+Line:
+
+
+Code Product/Service для использования в fiscalization for shipping amount
+Код товара/услуги для использования в фискализации для суммы доставки
+Line:
+
+
+Show Visa/MasterCard logo
+Показать логотип Visa/MasterCard
+Line:
+
+
+Save
+Сохранить
+Line:
+
+
+Информационный идентификатор не требуется.
+Требуется идентификатор продавца (Merchant ID).
+Line:
+
+
+Merchant ID должен быть нумерен.
+Идентификатор продавца должен быть числовым.
+Line:
+
+
+Неверный key не требуется.
+Требуется секретный ключ.
+Line:
+
+
+Секрет key должен быть в пределах 10 characters long and cannot be entirely numeric.
+Секретный ключ должен содержать не менее 10 символов и не может быть полностью числовым.
+Line:
+
+
+Pay via payment system Hutko
+Оплата через платежную систему Hutko
+Line:
+
+
+Pay via Hutko
+Оплата через Hutko
+Line:
+
+
+Order payment #
+Оплата заказа #
+Line:
+
+
+Order payment not found.
+Оплата заказа не найдена.
+Line:
+
+
+Invalid transaction ID format.
+Недействительный формат идентификатора транзакции.
+Line:
+
+
+Refund success.
+Восстановление средств успешно.
+Line:
+
+
+
+Платежи и возврат средств Hutko
+Line:
+
+
+Transaction ID
+Transaction ID
+Line:
+
+
+Amount
+Сумма
+Line:
+
+
+Payment Date
+Дата платежа
+Line:
+
+
+Actions
+Действия
+Line:
+
+
+Refund
+Возврат средств
+Line:
+
+
+Status
+Статус
+Line:
+
+
+Initiate Refund
+Инициировать возврат средств
+Line:
+
+
+Refund Amount
+Сумма возмещения
+Line:
+
+
+Воспользуйтесь темой для оплаты этого платежа.
+Введите сумму для возврата средств за этот платеж.
+Line:
+
+
+Refund Reason/Comment
+Причина/комментарий возврата средств
+Line:
+
+
+Optional: A brief reason for the refund.
+Необязательно: краткая причина возврата средств.
+Line:
+
+
+Cancel
+Отменить
+Line:
+
+
+Process Refund
+Выполнить возврат средств
+Line:
+
+
+
+
\ No newline at end of file
diff --git a/translations/uk-UA/ModulesHutkoShop.uk-UA.xlf b/translations/uk-UA/ModulesHutkoShop.uk-UA.xlf
new file mode 100644
index 0000000..8bf02c1
--- /dev/null
+++ b/translations/uk-UA/ModulesHutkoShop.uk-UA.xlf
@@ -0,0 +1,22 @@
+
+
+
+
+
+ Payment failure.
+ Збій оплати.
+ Line:
+
+
+ Please try again
+ Будь ласка, спробуйте ще раз
+ Line:
+
+
+ Order validation failure
+ Помилка перевірки замовлення
+ Line:
+
+
+
+
diff --git a/views/templates/admin/order_payment_refund.tpl b/views/templates/admin/order_payment_refund.tpl
index b6c669a..31c73bc 100644
--- a/views/templates/admin/order_payment_refund.tpl
+++ b/views/templates/admin/order_payment_refund.tpl
@@ -11,7 +11,7 @@