From 638d801a8e6d6330edfbeb39d0e60ffc01b14c89 Mon Sep 17 00:00:00 2001 From: O K Date: Mon, 2 Jun 2025 11:24:18 +0300 Subject: [PATCH] added order message logging --- cb.jsonl | 3 + controllers/front/callback.php | 81 +++---- controllers/front/redirect.php | 3 +- hutko.php | 218 +++++++++++++----- .../templates/admin/order_payment_refund.tpl | 28 ++- 5 files changed, 216 insertions(+), 117 deletions(-) create mode 100644 cb.jsonl diff --git a/cb.jsonl b/cb.jsonl new file mode 100644 index 0000000..79ffef4 --- /dev/null +++ b/cb.jsonl @@ -0,0 +1,3 @@ +{"rrn":"029650316530","masked_card":"444455XXXXXX1111","sender_cell_phone":"","sender_account":"","currency":"UAH","fee":"","reversal_amount":"0","settlement_amount":"0","actual_amount":"7133","response_description":"","sender_email":"panariga@gmail.com","order_status":"approved","response_status":"success","order_time":"02.06.2025 08:55:25","actual_currency":"UAH","order_id":"15#1748843725","tran_type":"purchase","eci":"5","settlement_date":"","payment_system":"card","approval_code":"013197","merchant_id":1700002,"settlement_currency":"","payment_id":100006586,"card_bin":444455,"response_code":"","card_type":"VISA","amount":"7133","signature":"b206a00794701fbc120c6f8b8433a4d9b6a09da2","product_id":"","merchant_data":"","rectoken":"","rectoken_lifetime":"","verification_status":"","parent_order_id":"","additional_info":"{\"capture_status\": null, \"capture_amount\": null, \"reservation_data\": \"{\\\"cms_name\\\": \\\"Prestashop\\\", \\\"cms_version\\\": \\\"8.2.1\\\", \\\"shop_domain\\\": \\\"ps8.panariga.com\\\", \\\"path\\\": \\\"https:\/\/ps8.panariga.com\/module\/hutko\/redirect\\\", \\\"phonemobile\\\": \\\"0670670670\\\", \\\"customer_country\\\": \\\"Ukrayina\\\", \\\"customer_state\\\": \\\"\\\", \\\"customer_name\\\": \\\"ok ok\\\", \\\"customer_city\\\": \\\"Kyiv\\\", \\\"customer_zip\\\": \\\"01011\\\", \\\"account\\\": \\\"3\\\", \\\"uuid\\\": \\\"61424bf8fa3296d92b199ca86a4b6536bdbfafb55ae59d0ac12262c6dae3e9e9\\\", \\\"products\\\": [{\\\"id\\\": \\\"2_9_0\\\", \\\"name\\\": \\\"Hummingbird printed sweater (\\\\u0420\\\\u043e\\\\u0437\\\\u043c\\\\u0456\\\\u0440: S)\\\", \\\"price\\\": 34.46, \\\"total_amount\\\": 68.93, \\\"quantity\\\": 2}, {\\\"id\\\": \\\"\\\\u041a\\\\u043e\\\\u0434 \\\\u0434\\\\u043e\\\\u0441\\\\u0442\\\\u0430\\\\u0432\\\\u043a\\\\u0438\\\", \\\"name\\\": \\\"\\\\u041d\\\\u0430\\\\u0437\\\\u0432\\\\u0430 \\\\u0434\\\\u043b\\\\u044f \\\\u0434\\\\u043e\\\\u0441\\\\u0442\\\\u0430\\\\u0432\\\\u043a\\\\u0438\\\", \\\"price\\\": 2.4, \\\"total_amount\\\": 2.4, \\\"quantity\\\": 1}]}\", \"transaction_id\": 500006502, \"bank_response_code\": null, \"bank_response_description\": null, \"client_fee\": 0.0, \"settlement_fee\": 0.0, \"bank_name\": null, \"bank_country\": null, \"card_type\": \"VISA\", \"card_product\": \"empty_visa\", \"card_category\": \"\", \"timeend\": \"02.06.2025 08:55:31\", \"ipaddress_v4\": \"178.159.232.12\", \"payment_method\": \"card\", \"is_test\": true}","response_signature_string":"**********|7133|UAH|{\"capture_status\": null, \"capture_amount\": null, \"reservation_data\": \"{\\\"cms_name\\\": \\\"Prestashop\\\", \\\"cms_version\\\": \\\"8.2.1\\\", \\\"shop_domain\\\": \\\"ps8.panariga.com\\\", \\\"path\\\": \\\"https:\/\/ps8.panariga.com\/module\/hutko\/redirect\\\", \\\"phonemobile\\\": \\\"0670670670\\\", \\\"customer_country\\\": \\\"Ukrayina\\\", \\\"customer_state\\\": \\\"\\\", \\\"customer_name\\\": \\\"ok ok\\\", \\\"customer_city\\\": \\\"Kyiv\\\", \\\"customer_zip\\\": \\\"01011\\\", \\\"account\\\": \\\"3\\\", \\\"uuid\\\": \\\"61424bf8fa3296d92b199ca86a4b6536bdbfafb55ae59d0ac12262c6dae3e9e9\\\", \\\"products\\\": [{\\\"id\\\": \\\"2_9_0\\\", \\\"name\\\": \\\"Hummingbird printed sweater (\\\\u0420\\\\u043e\\\\u0437\\\\u043c\\\\u0456\\\\u0440: S)\\\", \\\"price\\\": 34.46, \\\"total_amount\\\": 68.93, \\\"quantity\\\": 2}, {\\\"id\\\": \\\"\\\\u041a\\\\u043e\\\\u0434 \\\\u0434\\\\u043e\\\\u0441\\\\u0442\\\\u0430\\\\u0432\\\\u043a\\\\u0438\\\", \\\"name\\\": \\\"\\\\u041d\\\\u0430\\\\u0437\\\\u0432\\\\u0430 \\\\u0434\\\\u043b\\\\u044f \\\\u0434\\\\u043e\\\\u0441\\\\u0442\\\\u0430\\\\u0432\\\\u043a\\\\u0438\\\", \\\"price\\\": 2.4, \\\"total_amount\\\": 2.4, \\\"quantity\\\": 1}]}\", \"transaction_id\": 500006502, \"bank_response_code\": null, \"bank_response_description\": null, \"client_fee\": 0.0, \"settlement_fee\": 0.0, \"bank_name\": null, \"bank_country\": null, \"card_type\": \"VISA\", \"card_product\": \"empty_visa\", \"card_category\": \"\", \"timeend\": \"02.06.2025 08:55:31\", \"ipaddress_v4\": \"178.159.232.12\", \"payment_method\": \"card\", \"is_**********\": true}|7133|013197|444455|VISA|UAH|5|444455XXXXXX1111|1700002|15#1748843725|approved|02.06.2025 08:55:25|100006586|card|success|0|029650316530|panariga@gmail.com|0|purchase"} +{"rrn":"029650316530","masked_card":"444455XXXXXX1111","sender_cell_phone":"","sender_account":"","currency":"UAH","fee":"","reversal_amount":"1000","settlement_amount":"0","actual_amount":"7133","response_description":"","sender_email":"panariga@gmail.com","order_status":"approved","response_status":"success","order_time":"02.06.2025 08:55:25","actual_currency":"UAH","order_id":"15#1748843725","tran_type":"purchase","eci":"5","settlement_date":"","payment_system":"card","approval_code":"013197","merchant_id":1700002,"settlement_currency":"","payment_id":100006586,"card_bin":444455,"response_code":"","card_type":"VISA","amount":"7133","signature":"e0b1f119d57de3542f632b84e41e2c35c526ba05","product_id":"","merchant_data":"","rectoken":"","rectoken_lifetime":"","verification_status":"","parent_order_id":"","additional_info":"{\"capture_status\": null, \"capture_amount\": null, \"reservation_data\": \"{\\\"cms_name\\\": \\\"Prestashop\\\", \\\"cms_version\\\": \\\"8.2.1\\\", \\\"shop_domain\\\": \\\"ps8.panariga.com\\\", \\\"path\\\": \\\"https:\/\/ps8.panariga.com\/module\/hutko\/redirect\\\", \\\"phonemobile\\\": \\\"0670670670\\\", \\\"customer_country\\\": \\\"Ukrayina\\\", \\\"customer_state\\\": \\\"\\\", \\\"customer_name\\\": \\\"ok ok\\\", \\\"customer_city\\\": \\\"Kyiv\\\", \\\"customer_zip\\\": \\\"01011\\\", \\\"account\\\": \\\"3\\\", \\\"uuid\\\": \\\"61424bf8fa3296d92b199ca86a4b6536bdbfafb55ae59d0ac12262c6dae3e9e9\\\", \\\"products\\\": [{\\\"id\\\": \\\"2_9_0\\\", \\\"name\\\": \\\"Hummingbird printed sweater (\\\\u0420\\\\u043e\\\\u0437\\\\u043c\\\\u0456\\\\u0440: S)\\\", \\\"price\\\": 34.46, \\\"total_amount\\\": 68.93, \\\"quantity\\\": 2}, {\\\"id\\\": \\\"\\\\u041a\\\\u043e\\\\u0434 \\\\u0434\\\\u043e\\\\u0441\\\\u0442\\\\u0430\\\\u0432\\\\u043a\\\\u0438\\\", \\\"name\\\": \\\"\\\\u041d\\\\u0430\\\\u0437\\\\u0432\\\\u0430 \\\\u0434\\\\u043b\\\\u044f \\\\u0434\\\\u043e\\\\u0441\\\\u0442\\\\u0430\\\\u0432\\\\u043a\\\\u0438\\\", \\\"price\\\": 2.4, \\\"total_amount\\\": 2.4, \\\"quantity\\\": 1}]}\", \"transaction_id\": 500006504, \"bank_response_code\": null, \"bank_response_description\": null, \"client_fee\": 0.0, \"settlement_fee\": 0.0, \"bank_name\": null, \"bank_country\": null, \"card_type\": \"VISA\", \"card_product\": \"empty_visa\", \"card_category\": \"\", \"timeend\": \"02.06.2025 08:55:31\", \"ipaddress_v4\": \"178.159.232.12\", \"payment_method\": \"card\", \"is_test\": true}","response_signature_string":"**********|7133|UAH|{\"capture_status\": null, \"capture_amount\": null, \"reservation_data\": \"{\\\"cms_name\\\": \\\"Prestashop\\\", \\\"cms_version\\\": \\\"8.2.1\\\", \\\"shop_domain\\\": \\\"ps8.panariga.com\\\", \\\"path\\\": \\\"https:\/\/ps8.panariga.com\/module\/hutko\/redirect\\\", \\\"phonemobile\\\": \\\"0670670670\\\", \\\"customer_country\\\": \\\"Ukrayina\\\", \\\"customer_state\\\": \\\"\\\", \\\"customer_name\\\": \\\"ok ok\\\", \\\"customer_city\\\": \\\"Kyiv\\\", \\\"customer_zip\\\": \\\"01011\\\", \\\"account\\\": \\\"3\\\", \\\"uuid\\\": \\\"61424bf8fa3296d92b199ca86a4b6536bdbfafb55ae59d0ac12262c6dae3e9e9\\\", \\\"products\\\": [{\\\"id\\\": \\\"2_9_0\\\", \\\"name\\\": \\\"Hummingbird printed sweater (\\\\u0420\\\\u043e\\\\u0437\\\\u043c\\\\u0456\\\\u0440: S)\\\", \\\"price\\\": 34.46, \\\"total_amount\\\": 68.93, \\\"quantity\\\": 2}, {\\\"id\\\": \\\"\\\\u041a\\\\u043e\\\\u0434 \\\\u0434\\\\u043e\\\\u0441\\\\u0442\\\\u0430\\\\u0432\\\\u043a\\\\u0438\\\", \\\"name\\\": \\\"\\\\u041d\\\\u0430\\\\u0437\\\\u0432\\\\u0430 \\\\u0434\\\\u043b\\\\u044f \\\\u0434\\\\u043e\\\\u0441\\\\u0442\\\\u0430\\\\u0432\\\\u043a\\\\u0438\\\", \\\"price\\\": 2.4, \\\"total_amount\\\": 2.4, \\\"quantity\\\": 1}]}\", \"transaction_id\": 500006504, \"bank_response_code\": null, \"bank_response_description\": null, \"client_fee\": 0.0, \"settlement_fee\": 0.0, \"bank_name\": null, \"bank_country\": null, \"card_type\": \"VISA\", \"card_product\": \"empty_visa\", \"card_category\": \"\", \"timeend\": \"02.06.2025 08:55:31\", \"ipaddress_v4\": \"178.159.232.12\", \"payment_method\": \"card\", \"is_**********\": true}|7133|013197|444455|VISA|UAH|5|444455XXXXXX1111|1700002|15#1748843725|approved|02.06.2025 08:55:25|100006586|card|success|1000|029650316530|panariga@gmail.com|0|purchase"} +{"rrn":"029650316530","masked_card":"444455XXXXXX1111","sender_cell_phone":"","sender_account":"","currency":"UAH","fee":"","reversal_amount":"1500","settlement_amount":"0","actual_amount":"7133","response_description":"","sender_email":"panariga@gmail.com","order_status":"approved","response_status":"success","order_time":"02.06.2025 08:55:25","actual_currency":"UAH","order_id":"15#1748843725","tran_type":"purchase","eci":"5","settlement_date":"","payment_system":"card","approval_code":"013197","merchant_id":1700002,"settlement_currency":"","payment_id":100006586,"card_bin":444455,"response_code":"","card_type":"VISA","amount":"7133","signature":"321a0ba0c767b2951869cca1264a8acf5937b139","product_id":"","merchant_data":"","rectoken":"","rectoken_lifetime":"","verification_status":"","parent_order_id":"","additional_info":"{\"capture_status\": null, \"capture_amount\": null, \"reservation_data\": \"{\\\"cms_name\\\": \\\"Prestashop\\\", \\\"cms_version\\\": \\\"8.2.1\\\", \\\"shop_domain\\\": \\\"ps8.panariga.com\\\", \\\"path\\\": \\\"https:\/\/ps8.panariga.com\/module\/hutko\/redirect\\\", \\\"phonemobile\\\": \\\"0670670670\\\", \\\"customer_country\\\": \\\"Ukrayina\\\", \\\"customer_state\\\": \\\"\\\", \\\"customer_name\\\": \\\"ok ok\\\", \\\"customer_city\\\": \\\"Kyiv\\\", \\\"customer_zip\\\": \\\"01011\\\", \\\"account\\\": \\\"3\\\", \\\"uuid\\\": \\\"61424bf8fa3296d92b199ca86a4b6536bdbfafb55ae59d0ac12262c6dae3e9e9\\\", \\\"products\\\": [{\\\"id\\\": \\\"2_9_0\\\", \\\"name\\\": \\\"Hummingbird printed sweater (\\\\u0420\\\\u043e\\\\u0437\\\\u043c\\\\u0456\\\\u0440: S)\\\", \\\"price\\\": 34.46, \\\"total_amount\\\": 68.93, \\\"quantity\\\": 2}, {\\\"id\\\": \\\"\\\\u041a\\\\u043e\\\\u0434 \\\\u0434\\\\u043e\\\\u0441\\\\u0442\\\\u0430\\\\u0432\\\\u043a\\\\u0438\\\", \\\"name\\\": \\\"\\\\u041d\\\\u0430\\\\u0437\\\\u0432\\\\u0430 \\\\u0434\\\\u043b\\\\u044f \\\\u0434\\\\u043e\\\\u0441\\\\u0442\\\\u0430\\\\u0432\\\\u043a\\\\u0438\\\", \\\"price\\\": 2.4, \\\"total_amount\\\": 2.4, \\\"quantity\\\": 1}]}\", \"transaction_id\": 500006506, \"bank_response_code\": null, \"bank_response_description\": null, \"client_fee\": 0.0, \"settlement_fee\": 0.0, \"bank_name\": null, \"bank_country\": null, \"card_type\": \"VISA\", \"card_product\": \"empty_visa\", \"card_category\": \"\", \"timeend\": \"02.06.2025 08:55:31\", \"ipaddress_v4\": \"178.159.232.12\", \"payment_method\": \"card\", \"is_test\": true}","response_signature_string":"**********|7133|UAH|{\"capture_status\": null, \"capture_amount\": null, \"reservation_data\": \"{\\\"cms_name\\\": \\\"Prestashop\\\", \\\"cms_version\\\": \\\"8.2.1\\\", \\\"shop_domain\\\": \\\"ps8.panariga.com\\\", \\\"path\\\": \\\"https:\/\/ps8.panariga.com\/module\/hutko\/redirect\\\", \\\"phonemobile\\\": \\\"0670670670\\\", \\\"customer_country\\\": \\\"Ukrayina\\\", \\\"customer_state\\\": \\\"\\\", \\\"customer_name\\\": \\\"ok ok\\\", \\\"customer_city\\\": \\\"Kyiv\\\", \\\"customer_zip\\\": \\\"01011\\\", \\\"account\\\": \\\"3\\\", \\\"uuid\\\": \\\"61424bf8fa3296d92b199ca86a4b6536bdbfafb55ae59d0ac12262c6dae3e9e9\\\", \\\"products\\\": [{\\\"id\\\": \\\"2_9_0\\\", \\\"name\\\": \\\"Hummingbird printed sweater (\\\\u0420\\\\u043e\\\\u0437\\\\u043c\\\\u0456\\\\u0440: S)\\\", \\\"price\\\": 34.46, \\\"total_amount\\\": 68.93, \\\"quantity\\\": 2}, {\\\"id\\\": \\\"\\\\u041a\\\\u043e\\\\u0434 \\\\u0434\\\\u043e\\\\u0441\\\\u0442\\\\u0430\\\\u0432\\\\u043a\\\\u0438\\\", \\\"name\\\": \\\"\\\\u041d\\\\u0430\\\\u0437\\\\u0432\\\\u0430 \\\\u0434\\\\u043b\\\\u044f \\\\u0434\\\\u043e\\\\u0441\\\\u0442\\\\u0430\\\\u0432\\\\u043a\\\\u0438\\\", \\\"price\\\": 2.4, \\\"total_amount\\\": 2.4, \\\"quantity\\\": 1}]}\", \"transaction_id\": 500006506, \"bank_response_code\": null, \"bank_response_description\": null, \"client_fee\": 0.0, \"settlement_fee\": 0.0, \"bank_name\": null, \"bank_country\": null, \"card_type\": \"VISA\", \"card_product\": \"empty_visa\", \"card_category\": \"\", \"timeend\": \"02.06.2025 08:55:31\", \"ipaddress_v4\": \"178.159.232.12\", \"payment_method\": \"card\", \"is_**********\": true}|7133|013197|444455|VISA|UAH|5|444455XXXXXX1111|1700002|15#1748843725|approved|02.06.2025 08:55:25|100006586|card|success|1500|029650316530|panariga@gmail.com|0|purchase"} diff --git a/controllers/front/callback.php b/controllers/front/callback.php index 0d2c5a1..eadbdc1 100644 --- a/controllers/front/callback.php +++ b/controllers/front/callback.php @@ -37,50 +37,41 @@ class HutkoCallbackModuleFrontController extends ModuleFrontController } try { // 1. Parse the incoming request body. - $requestBody = $this->getRequestBody(); - - // If request body is empty, log and exit. - if (empty($requestBody)) { - PrestaShopLogger::addLog('Hutko Callback: Empty request body received.', 2, null, 'Cart', null, true); - exit('Empty request'); - } - // 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); + $calbackContent = $this->getCallbackContent(); - $transaction_id = $requestBody['order_id']; - $orderIdParamParts = explode($this->module->order_separator, $transaction_id); - $orderId = (int)$orderIdParamParts[0]; // Ensure it's an integer + + + $id_order_parts = explode($this->module->order_separator, $calbackContent['order_id']); + $id_order = (int)$id_order_parts[0]; // Ensure it's an integer // If we reached here, an order should exist. Load it. - $order = new Order($orderId); + $order = new Order($id_order); if (!Validate::isLoadedObject($order)) { - PrestaShopLogger::addLog('Hutko Callback: Order could not be loaded for ID: ' . $orderId, 3, null, 'Order', $orderId, true); - exit('Order not found after validation'); + PrestaShopLogger::addLog('Hutko Callback: Order could not be loaded for ID: ' . $id_order, 3, null, 'Order', $id_order, true); + throw new Exception('Order not found after validation'); } + $this->context->currency = new Currency($order->id_currency); + $this->context->customer = new Customer($order->id_customer); + $this->context->language = new Language($order->id_lang); // 7. Handle payment status from the callback. - $orderStatusCallback = $requestBody['order_status']; + $orderStatusCallback = $calbackContent['order_status']; $currentOrderState = (int)$order->getCurrentState(); switch ($orderStatusCallback) { case 'approved': // Only success state if no refunds was done. - if ($requestBody['response_status'] == 'success' && (int)$requestBody['reversal_amount'] === 0) { + if ($calbackContent['response_status'] == 'success' && (int)$calbackContent['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); + $callbackAmount = $calbackContent['actual_amount'] ?? $calbackContent['amount']; + $amountFloat = round($callbackAmount / 100, 2); + $order->addOrderPayment($amountFloat, $this->module->displayName, $calbackContent['order_id'], $this->context->currency); $order->setCurrentState($expectedState); } - } else { - PrestaShopLogger::addLog('Hutko Callback: Unhandled response_status: ' . $requestBody['response_status']); } exit('OK'); break; @@ -103,42 +94,44 @@ class HutkoCallbackModuleFrontController extends ModuleFrontController break; case 'processing': - // If the order is still processing, we might want to update its status - // to a specific 'processing' state if available, or just acknowledge. - // 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')) { - $order->setCurrentState($processingState); - } - exit('Processing'); + //no need to change status + exit('Order ' . $orderStatusCallback); break; default: // Log unexpected status and exit with an error. - PrestaShopLogger::addLog('Hutko Callback: Unexpected order status received: ' . $orderStatusCallback . ' for order ID: ' . $orderId, 3, null, 'Order', $orderId, true); - exit('Unexpected status'); + PrestaShopLogger::addLog('Hutko Callback: Unexpected order status received: ' . $orderStatusCallback . ' for order ID: ' . $id_order, 3, null, 'Order', $id_order, true); + throw new Exception('Unexpected status'); break; } } catch (Exception $e) { // Log any uncaught exceptions and exit with the error message. PrestaShopLogger::addLog('Hutko Callback Error: ' . $e->getMessage(), 3, null, 'HutkoCallbackModuleFrontController', null, true); - exit($e->getMessage()); + throw new Exception('Unknown error'); } } /** - * Helper method to parse the request body from POST or raw input. + * Helper method to parse the request body from raw input. * * @return array The parsed request body. */ - private function getRequestBody(): array + private function getCallbackContent(): array { - - $jsonBody = json_decode(file_get_contents("php://input"), true); - if (is_array($jsonBody)) { - return $jsonBody; + $calbackContent = json_decode(file_get_contents("php://input"), true); + if (!is_array($calbackContent) || !count($calbackContent)) { + PrestaShopLogger::addLog('Hutko Callback: Empty request body received.', 2, null, 'Cart', null, true); + throw new Exception('Empty request'); } - - return []; + // Assuming validateResponse returns true on success, or a string error message on failure. + $isSignatureValid = $this->module->validateResponse($calbackContent); + if ($isSignatureValid !== true) { + PrestaShopLogger::addLog('Hutko Callback: Invalid signature. Error: ' . $isSignatureValid, 2, null, 'Cart', null, true); + throw new Exception('Invalid signature'); + } + if (Configuration::get('HUTKO_SAVE_LOGS')) { + $this->module->log('CalbackContent: ' .json_encode($calbackContent)); + } + return $calbackContent; } } diff --git a/controllers/front/redirect.php b/controllers/front/redirect.php index 0a757f5..8fd4001 100644 --- a/controllers/front/redirect.php +++ b/controllers/front/redirect.php @@ -10,6 +10,7 @@ * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) */ +use Symfony\Component\Serializer\Encoder\JsonEncode; if (!defined('_PS_VERSION_')) { exit; @@ -59,5 +60,5 @@ class HutkoRedirectModuleFrontController extends ModuleFrontController 'hutko_response' => $responseData['response'], // The URL of the Hutko payment gateway. ]); } - } + } } diff --git a/hutko.php b/hutko.php index 1a9bc04..e67a686 100644 --- a/hutko.php +++ b/hutko.php @@ -35,7 +35,8 @@ class Hutko extends PaymentModule 'HUTKO_SHIPPING_PRODUCT_CODE', 'HUTKO_NEW_ORDER_STATUS_ID', 'HUTKO_SUCCESS_STATUS_ID', - 'HUTKO_SHOW_CARDS_LOGO' + 'HUTKO_SHOW_CARDS_LOGO', + 'HUTKO_SAVE_LOGS' ]; public $postErrors = []; @@ -260,6 +261,24 @@ class Hutko extends PaymentModule ) ), ), + array( + 'type' => 'radio', + 'label' => $this->trans('Save Logs', array(), 'Modules.Hutko.Admin'), + 'name' => 'HUTKO_SAVE_LOGS', + 'is_bool' => true, + 'values' => array( + array( + 'id' => 'save_logs', + 'value' => 1, + 'label' => $this->trans('Yes', array(), 'Modules.Hutko.Admin') + ), + array( + 'id' => 'discard_logs', + 'value' => 0, + 'label' => $this->trans('No', array(), 'Modules.Hutko.Admin') + ) + ), + ), ), 'submit' => array( @@ -405,7 +424,7 @@ class Hutko extends PaymentModule 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; + $amount = $order->total_products_wt; } } @@ -727,13 +746,7 @@ class Hutko extends PaymentModule $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); - } /** @@ -789,16 +802,8 @@ class Hutko extends PaymentModule */ public function displayAdminOrderContent(array $params): string { - // Check if a refund form has been submitted - if (Tools::isSubmit('hutkoRefundsubmit')) { - // Process the refund logic. - $this->processRefundForm(); - } - - // Check payment status - if (Tools::getValue('hutkoOrderPaymentStatus')) { - // Process the requested order status check. - $this->processOrderPaymentStatus(Tools::getValue('hutkoOrderPaymentStatus')); + if (!Configuration::get('HUTKO_MERCHANT') || empty(Configuration::get('HUTKO_MERCHANT') || !Configuration::get('HUTKO_SECRET_KEY') || empty(Configuration::get('HUTKO_SECRET_KEY')))) { + return ''; } // Ensure the 'order' object is present in the parameters @@ -816,6 +821,19 @@ class Hutko extends PaymentModule return ''; } + // Check if a refund form has been submitted + if (Tools::isSubmit('hutkoRefundsubmit')) { + // Process the refund logic. + $this->processRefundForm(); + } + + // Check payment status + if (Tools::getValue('hutkoOrderPaymentStatus')) { + + // Process the requested order status check. + $this->renderOrderPaymentStatus(Tools::getValue('hutkoOrderPaymentStatus')); + } + // Get the Order object from the parameters $order = $params['order']; @@ -837,31 +855,37 @@ class Hutko extends PaymentModule // Pass the order ID to the template 'id_order' => $order->id, 'currency' => new Currency($order->id_currency), + 'hutkoOrderPaymentStatus' => $this->context->session->get('hutkoOrderPaymentStatus'), ]); - // 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 renderOrderPaymentStatus(string $order_id) + { - public function processOrderPaymentStatus(string $order_id): void + $data = $this->getOrderPaymentStatus($order_id); + + $this->context->session->set('hutkoOrderPaymentStatus', $this->displayArrayInNotification($data['response'])); + Tools::redirectAdmin($this->context->controller->currentIndex); + } + public function getOrderPaymentStatus(string $order_id): array { $data = [ 'order_id' => $order_id, - 'merchant_id' => Configuration::get('HUTKO_MERCHANT', null), + 'merchant_id' => Configuration::get('HUTKO_MERCHANT'), 'version' => '1.0', ]; $data['signature'] = $this->getSignature($data); - $response = $this->sendAPICall($this->status_url, $data); - $this->context->controller->informations[] = $this->displayArrayInNotification($response['response']); + return $this->sendAPICall($this->status_url, $data); } public function processRefundForm() { $orderPaymentId = (int) Tools::getValue('orderPaymentId'); $amount = (float) Tools::getValue('refund_amount'); - $comment = mb_substr(Tools::getValue('orderPaymentId', ''), 0, 1024); + $comment = mb_substr(Tools::getValue('comment', ''), 0, 1024); $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]); @@ -871,7 +895,7 @@ class Hutko extends PaymentModule if ($result->success) { $this->context->controller->informations[] = $result->description; } - // Tools::redirectAdmin($link); + Tools::redirectAdmin($link); } /** @@ -882,17 +906,18 @@ class Hutko extends PaymentModule * updates the order history, and logs the action. * * @param int $orderPaymentId The ID of the OrderPayment record to refund. - * @param int $orderId The ID of the Order to refund. + * @param int $id_order The ID of the Order to refund. * @param float $amount The amount to refund. * @param string $comment A comment or reason for the refund. * @return stdClass Result description. * @throws Exception If the OrderPayment is not found, invalid, or refund fails. */ - public function processRefund(int $orderPaymentId, int $orderId, float $amount, string $comment = ''): stdClass + public function processRefund(int $orderPaymentId, int $id_order, float $amount, string $comment = ''): stdClass { $result = new stdClass(); $result->error = false; - // 1. Load the OrderPayment object. + + $order = new Order($id_order); $orderPayment = new OrderPayment($orderPaymentId); $currency = new Currency($orderPayment->id_currency); if (!Validate::isLoadedObject($orderPayment)) { @@ -907,8 +932,8 @@ class Hutko extends PaymentModule throw new Exception($this->trans('Order payment not found.', [], 'Modules.Hutko.Admin')); } - // 2. Validate the transaction_id format and extract cart ID. - // Assuming transaction_id is in the format "cartID|timestamp" or "cartID-timestamp" + // Validate the transaction_id format and extract cart ID. + // Assuming transaction_id is in the format "orderID#timestamp" $transactionIdParts = explode($this->order_separator, $orderPayment->transaction_id); $cartId = (int)$transactionIdParts[0]; @@ -926,41 +951,90 @@ class Hutko extends PaymentModule $response = $this->refundAPICall($orderPayment->transaction_id, $amount, $currency->iso_code, $comment); - - - - if ($response['response']['response_status'] === 'failure') { + if ($response['response']['reverse_status'] === 'approved' && $response['response']['response_status'] === 'success') { + $result->success = true; + $result->description = $this->trans('Refund success.', [], 'Modules.Hutko.Admin'); + } else { + PrestaShopLogger::addLog( + 'Hutko Refund: refund failure response: ' . json_encode($response), + 3, // Info + null, + 'OrderPayment', + $orderPaymentId, + true + ); + $this->addOrderMessage($order, $this->trans('Refund Failed. Please check actual amount in Hutko account page.', [], 'Modules.Hutko.Admin')); $result->error = true; $result->description = $response['response']['error_message']; return $result; } - if ($response['response']['response_status'] === 'success') { - $result->success = true; - $result->description = $this->trans('Refund success.', [], 'Modules.Hutko.Admin'); - } + $amountFloat = round((int)$response['response']['reversal_amount'] / 100, 2); - $order = new Order($orderId); + $this->addOrderMessage($order, $this->trans('Refund success.', [], 'Modules.Hutko.Admin') . ' ' . $currency->iso_code . $amountFloat); + $order->addOrderPayment($amountFloat, $this->displayName, $orderPayment->transaction_id . '_refund', $currency); + $order->setCurrentState((int)Configuration::get('PS_OS_REFUND')); - // Add a private message to the order for tracking. - $order->addOrderPayment( - -$amount, // Negative amount for refund - $this->displayName, - $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 - null, - 'OrderPayment', - $orderPaymentId, - true - ); return $result; } + + public function addOrderMessage(Order $order, string $message) + { + try { + $customer = new Customer($order->id_customer); + + $thread = CustomerThread::getIdCustomerThreadByEmailAndIdOrder($customer->email, $order->id); + + if (is_int((int) $thread) && $thread != 0) { + $thread = (int) $thread; + } else { + $customer_thread = new CustomerThread(); + $customer_thread->id_contact = 0; + $customer_thread->id_customer = (int) $order->id_customer; + $customer_thread->id_shop = (int) $order->id_shop; + $customer_thread->id_order = (int) $order->id; + $customer_thread->id_lang = (int) $this->context->language->id; + $customer_thread->email = $customer->email; + $customer_thread->status = 'open'; + $customer_thread->token = Tools::passwdGen(12); + $customer_thread->add(); + $thread = $customer_thread->id; + } + + + + $customer_message = new CustomerMessage(); + $customer_message->id_customer_thread = $thread; + $customer_message->id_employee = 1; + $customer_message->message = $message; + $customer_message->private = 1; + if (false === $customer_message->validateField('message', $customer_message->message)) { + throw new Exception('Invalid reply message'); + } + + if (false === $customer_message->add()) { + throw new Exception('Failed to add customer message'); + } + } catch (Throwable $e) { + PrestaShopLogger::addLog('failed add message to order. ' . $e->getMessage() . ' | ' . $message, 3); + } + } + + + public function addRefundPayment(Order $order, OrderPayment $purchaseOrderPayment, float $amount) + + { + $order_payment = new OrderPayment(); + $order_payment->order_reference = $order->reference; + $order_payment->id_currency = $purchaseOrderPayment->id_currency; + $order_payment->payment_method = $purchaseOrderPayment->payment_method; + $order_payment->transaction_id = $purchaseOrderPayment->transaction_id . $this->order_separator . 'refund'; + $order_payment->amount = $amount; + $order_payment->id_employee = 0; + $order_payment->add(); + $order->total_paid_real = $order->total_paid_real - $amount; + $order->update(); + } /** * Initiates a refund (reverse) request via Hutko API. * @@ -970,17 +1044,17 @@ class Hutko extends PaymentModule * @param string $comment Optional comment for the refund. * @return array Decoded API response array. Returns an error structure on failure. */ - public function refundAPICall(string $order_id, float $amount, string $currency, string $comment = ''): array + public function refundAPICall(string $order_id, float $amount, string $currencyISO, string $comment = ''): array { // 1. Prepare the data payload $data = [ 'order_id' => $order_id, // Assuming Configuration::get is available to fetch the merchant ID - 'merchant_id' => Configuration::get('HUTKO_MERCHANT', null), + 'merchant_id' => Configuration::get('HUTKO_MERCHANT'), 'version' => '1.0', // Amount should be in minor units (cents, kopecks) and converted to string as per API example - 'amount' => (string)round($amount * 100), - 'currency' => $currency, + 'amount' => round($amount * 100), + 'currency' => $currencyISO, ]; if (!empty($comment)) { @@ -1001,7 +1075,9 @@ class Hutko extends PaymentModule */ public function sendAPICall(string $url, array $data, int $timeout = 60): array { - + if (Configuration::get('HUTKO_SAVE_LOGS')) { + $this->log($url . ' <= ' . json_encode($data)); + } // Wrap the prepared data inside the 'request' key as required by the API $requestPayload = ['request' => $data]; @@ -1062,7 +1138,9 @@ class Hutko extends PaymentModule // Close CURL handle curl_close($ch); - + if (Configuration::get('HUTKO_SAVE_LOGS')) { + $this->log($url . ' => ' . $response); + } // Process the response // Decode the JSON response into a PHP array $responseData = json_decode($response, true); @@ -1127,7 +1205,21 @@ class Hutko extends PaymentModule */ public function isUsingNewTranslationSystem() { - return true; } + + + public function log($data) + { + if (!is_string($data)) { + $data = json_encode($data, JSON_UNESCAPED_UNICODE); + } + $logdirectory = _PS_ROOT_DIR_ . '/var/modules/' . $this->name . '/logs/' . date("Y") . '/' . date("m") . '/' . date("d") . '/'; + if (!is_dir($logdirectory)) { + mkdir($logdirectory, 0750, true); + } + $logger = new \FileLogger(0); //0 == debug level, logDebug() won’t work without this. + $logger->setFilename($logdirectory . 'dayly.log'); + $logger->logInfo($data); + } } diff --git a/views/templates/admin/order_payment_refund.tpl b/views/templates/admin/order_payment_refund.tpl index 31c73bc..374119c 100644 --- a/views/templates/admin/order_payment_refund.tpl +++ b/views/templates/admin/order_payment_refund.tpl @@ -16,6 +16,15 @@
+ + {* {if !empty($hutkoOrderPaymentStatus)} + + {/if} *} +
@@ -28,7 +37,7 @@ {foreach from=$hutkoPayments item='payment'} - +
{$payment->transaction_id} {displayPrice price=Tools::ps_round($payment->amount, 2) currency=$currency->id} @@ -43,10 +52,12 @@ {/if} - -