diff --git a/catalog/controller/payment/hutko.php b/catalog/controller/payment/hutko.php index a3f0941..b7eb520 100644 --- a/catalog/controller/payment/hutko.php +++ b/catalog/controller/payment/hutko.php @@ -10,7 +10,6 @@ class Hutko extends \Opencart\System\Engine\Controller { } public function confirm(): void { - // Load language here so 'text_redirecting_comment' is available $this->load->language('extension/hutko/payment/hutko'); $this->load->model('checkout/order'); @@ -25,26 +24,59 @@ class Hutko extends \Opencart\System\Engine\Controller { if (!$order_info) { $json['error'] = 'Order missing'; } else { - $request_data = $this->buildRequest($order_info); + // 1. Generate unique Ref for THIS attempt + $timestamp = time(); + $hutko_ref = $order_info['order_id'] . '#' . $timestamp; + + $request_data = $this->buildRequest($order_info, $hutko_ref); if (!$request_data) { $json['error'] = $this->language->get('error_payment_data_build'); } else { $this->load->model('extension/hutko/payment/hutko'); - $this->model_extension_hutko_payment_hutko->addHutkoOrder($order_info['order_id'], $request_data['order_id']); + // 2. Call API $response = $this->api($this->checkout_url, $request_data); if (($response['response']['response_status'] ?? '') === 'success' && !empty($response['response']['checkout_url'])) { - // Language keys are loaded now, so this will contain actual text - $comment = sprintf($this->language->get('text_redirecting_comment'), $request_data['order_id'], $response['response']['checkout_url']); - $this->model_checkout_order->addHistory($order_info['order_id'], $this->config->get('payment_hutko_new_order_status_id'), $comment, false); + $url = $response['response']['checkout_url']; + + // 3. Log to INTERNAL DB (Not Customer History) + $this->model_extension_hutko_payment_hutko->logTransaction( + $order_info['order_id'], + $hutko_ref, + 'payment_request', + 'created', + $request_data['amount'] / 100, + $request_data['currency'], + [ + 'request_data' => $request_data, // Store sent data (except signature if needed) + 'checkout_url' => $url, + 'user_agent' => $this->request->server['HTTP_USER_AGENT'] ?? '' + ] + ); + + // 4. Update Order Status to "Pending" (or configured status) if not already + // Only add history if it's a fresh order, don't spam. + if ($order_info['order_status_id'] == 0) { + $this->model_checkout_order->addHistory($order_info['order_id'], $this->config->get('payment_hutko_new_order_status_id'), $this->language->get('text_initiated_payment'), false); + } - $json['redirect'] = $response['response']['checkout_url']; + $json['redirect'] = $url; } else { $err = $response['response']['error_message'] ?? $this->language->get('error_api_communication'); $json['error'] = $err; - $this->logOC('Checkout Error: ' . $err); + + // Log Failure + $this->model_extension_hutko_payment_hutko->logTransaction( + $order_info['order_id'], + $hutko_ref, + 'payment_request', + 'failed', + $request_data['amount'] / 100, + $request_data['currency'], + ['error' => $err, 'api_response' => $response] + ); } } } @@ -55,56 +87,73 @@ class Hutko extends \Opencart\System\Engine\Controller { } public function callback(): void { - // IMPORTANT: Load language for status translations (e.g. text_payment_approved) $this->load->language('extension/hutko/payment/hutko'); $input = file_get_contents("php://input"); $data = json_decode($input, true); - if ($this->config->get('payment_hutko_save_logs')) { - $this->logOC('Callback: ' . $input); - } - - if (!$data || !$this->validate($data)) { + // Basic Validation + if (!$data || !isset($data['order_id'])) { http_response_code(400); exit('Invalid Request'); } - $parts = explode('#', $data['order_id']); + // Verify Signature first + if (!$this->validate($data)) { + http_response_code(400); + exit('Invalid Signature'); + } + + $hutko_ref = $data['order_id']; // e.g., 55#17555555 + $parts = explode('#', $hutko_ref); $order_id = (int)$parts[0]; $this->load->model('checkout/order'); $order_info = $this->model_checkout_order->getOrder($order_id); if ($order_info) { + $this->load->model('extension/hutko/payment/hutko'); $status = $data['order_status'] ?? ''; - $comment_details = "Hutko Order ID: " . $data['order_id'] . ". Status: " . $status . ". "; - - $current_status_id = $order_info['order_status_id']; + + // 1. Log the RAW callback to our internal table + $this->model_extension_hutko_payment_hutko->logTransaction( + $order_id, + $hutko_ref, + 'callback', + ($status === 'approved') ? 'success' : 'failed', + isset($data['amount']) ? $data['amount'] / 100 : 0, + $data['currency'] ?? '', + $data // Save full payload + ); + + $current_status_id = (int)$order_info['order_status_id']; + // 2. Update OpenCart History (Clean Messages Only) if ($status === 'approved') { - if (isset($data['response_status']) && $data['response_status'] == 'success' && (!isset($data['reversal_amount']) || (int)$data['reversal_amount'] === 0)) { - $target = (int)$this->config->get('payment_hutko_success_status_id'); - if ($current_status_id != $target) { - $msg = $this->language->get('text_payment_approved') . ' ' . $comment_details; - $this->model_checkout_order->addHistory($order_id, $target, $msg, true); + if (isset($data['response_status']) && $data['response_status'] == 'success') { + $target_status_id = (int)$this->config->get('payment_hutko_success_status_id'); + + // Avoid duplicates: Only update if status is different + if ($current_status_id != $target_status_id) { + $this->model_checkout_order->addHistory($order_id, $target_status_id, $this->language->get('text_payment_approved'), true); } echo "OK"; } else { echo "Approved but invalid details"; } } elseif ($status === 'declined') { - $target = (int)$this->config->get('payment_hutko_declined_status_id'); - if ($current_status_id != $target) { - $msg = $this->language->get('text_payment_declined') . ' ' . $comment_details; - $this->model_checkout_order->addHistory($order_id, $target, $msg, true); + $target_status_id = (int)$this->config->get('payment_hutko_declined_status_id'); + if ($current_status_id != $target_status_id) { + // Don't notify customer on decline to avoid spam if they retry + $this->model_checkout_order->addHistory($order_id, $target_status_id, $this->language->get('text_payment_declined'), false); } echo "Order declined"; } elseif ($status === 'expired') { - $target = (int)$this->config->get('payment_hutko_expired_status_id'); - if ($current_status_id != $target) { - $msg = $this->language->get('text_payment_expired') . ' ' . $comment_details; - $this->model_checkout_order->addHistory($order_id, $target, $msg, true); + // Often better not to change order status to "Expired" if you want to allow retries, + // but if you do, don't notify customer. + $target_status_id = (int)$this->config->get('payment_hutko_expired_status_id'); + if ($current_status_id != $target_status_id) { + $this->model_checkout_order->addHistory($order_id, $target_status_id, $this->language->get('text_payment_expired'), false); } echo "Order expired"; } else { @@ -116,9 +165,8 @@ class Hutko extends \Opencart\System\Engine\Controller { } } - private function buildRequest($order) { - $ref = $order['order_id'] . '#' . time(); - + private function buildRequest($order, $hutko_ref) { + // Logic same as before, but using passed $hutko_ref $products_data = $this->getProducts($order['order_id'], $order); $total_products_sum = 0; @@ -161,7 +209,7 @@ class Hutko extends \Opencart\System\Engine\Controller { ]; $data = [ - 'order_id' => $ref, + 'order_id' => $hutko_ref, 'merchant_id' => $this->config->get('payment_hutko_merchant_id'), 'amount' => $total_cents, 'currency' => $order['currency_code'], @@ -176,6 +224,8 @@ class Hutko extends \Opencart\System\Engine\Controller { return $data; } + // Helper functions (getProducts, sign, validate, api, logOC) remain the same... + // Just ensure logOC uses file log for debug, not DB log. private function getProducts(int $order_id, array $order_info): array { $products_data = []; $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "order_product` WHERE `order_id` = '" . (int)$order_id . "'");