fixed cart clearance
This commit is contained in:
@@ -1,12 +1,15 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Opencart\Admin\Controller\Extension\Hutko\Payment;
|
namespace Opencart\Admin\Controller\Extension\Hutko\Payment;
|
||||||
|
|
||||||
class Hutko extends \Opencart\System\Engine\Controller {
|
class Hutko extends \Opencart\System\Engine\Controller
|
||||||
|
{
|
||||||
private $checkout_url = 'https://pay.hutko.org/api/checkout/url/';
|
private $checkout_url = 'https://pay.hutko.org/api/checkout/url/';
|
||||||
private $refund_url = 'https://pay.hutko.org/api/reverse/order_id';
|
private $refund_url = 'https://pay.hutko.org/api/reverse/order_id';
|
||||||
private $status_url = 'https://pay.hutko.org/api/status/order_id';
|
private $status_url = 'https://pay.hutko.org/api/status/order_id';
|
||||||
|
|
||||||
public function index(): void {
|
public function index(): void
|
||||||
|
{
|
||||||
$this->load->language('extension/hutko/payment/hutko');
|
$this->load->language('extension/hutko/payment/hutko');
|
||||||
$this->document->setTitle($this->language->get('heading_title'));
|
$this->document->setTitle($this->language->get('heading_title'));
|
||||||
|
|
||||||
@@ -29,11 +32,22 @@ class Hutko extends \Opencart\System\Engine\Controller {
|
|||||||
|
|
||||||
// Config fields
|
// Config fields
|
||||||
$fields = [
|
$fields = [
|
||||||
'payment_hutko_merchant_id', 'payment_hutko_secret_key', 'payment_hutko_shipping_include',
|
'payment_hutko_merchant_id',
|
||||||
'payment_hutko_shipping_product_name', 'payment_hutko_shipping_product_code',
|
'payment_hutko_secret_key',
|
||||||
'payment_hutko_new_order_status_id', 'payment_hutko_success_status_id', 'payment_hutko_declined_status_id',
|
'payment_hutko_shipping_include',
|
||||||
'payment_hutko_expired_status_id', 'payment_hutko_refunded_status_id', 'payment_hutko_include_discount_to_total',
|
'payment_hutko_shipping_product_name',
|
||||||
'payment_hutko_status', 'payment_hutko_sort_order', 'payment_hutko_geo_zone_id', 'payment_hutko_total', 'payment_hutko_save_logs'
|
'payment_hutko_shipping_product_code',
|
||||||
|
'payment_hutko_new_order_status_id',
|
||||||
|
'payment_hutko_success_status_id',
|
||||||
|
'payment_hutko_declined_status_id',
|
||||||
|
'payment_hutko_expired_status_id',
|
||||||
|
'payment_hutko_refunded_status_id',
|
||||||
|
'payment_hutko_include_discount_to_total',
|
||||||
|
'payment_hutko_status',
|
||||||
|
'payment_hutko_sort_order',
|
||||||
|
'payment_hutko_geo_zone_id',
|
||||||
|
'payment_hutko_total',
|
||||||
|
'payment_hutko_save_logs'
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($fields as $field) {
|
foreach ($fields as $field) {
|
||||||
@@ -62,7 +76,8 @@ class Hutko extends \Opencart\System\Engine\Controller {
|
|||||||
$this->response->setOutput($this->load->view('extension/hutko/payment/hutko', $data));
|
$this->response->setOutput($this->load->view('extension/hutko/payment/hutko', $data));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function save(): void {
|
public function save(): void
|
||||||
|
{
|
||||||
$this->load->language('extension/hutko/payment/hutko');
|
$this->load->language('extension/hutko/payment/hutko');
|
||||||
$json = [];
|
$json = [];
|
||||||
|
|
||||||
@@ -89,13 +104,15 @@ class Hutko extends \Opencart\System\Engine\Controller {
|
|||||||
$this->response->setOutput(json_encode($json));
|
$this->response->setOutput(json_encode($json));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function install(): void {
|
public function install(): void
|
||||||
|
{
|
||||||
$this->load->model('extension/hutko/payment/hutko');
|
$this->load->model('extension/hutko/payment/hutko');
|
||||||
$this->model_extension_hutko_payment_hutko->install();
|
$this->model_extension_hutko_payment_hutko->install();
|
||||||
// No event registration needed - we use the native 'order()' method now
|
// No event registration needed - we use the native 'order()' method now
|
||||||
}
|
}
|
||||||
|
|
||||||
public function uninstall(): void {
|
public function uninstall(): void
|
||||||
|
{
|
||||||
$this->load->model('setting/event');
|
$this->load->model('setting/event');
|
||||||
$this->model_setting_event->deleteEventByCode('hutko_order_info'); // Cleanup old events if any
|
$this->model_setting_event->deleteEventByCode('hutko_order_info'); // Cleanup old events if any
|
||||||
}
|
}
|
||||||
@@ -105,7 +122,8 @@ class Hutko extends \Opencart\System\Engine\Controller {
|
|||||||
* OC4 calls this method automatically if the payment method is 'hutko'.
|
* OC4 calls this method automatically if the payment method is 'hutko'.
|
||||||
* It renders the content into a Tab in the Order Info page.
|
* It renders the content into a Tab in the Order Info page.
|
||||||
*/
|
*/
|
||||||
public function order(): string {
|
public function order(): string
|
||||||
|
{
|
||||||
$this->load->language('extension/hutko/payment/hutko');
|
$this->load->language('extension/hutko/payment/hutko');
|
||||||
|
|
||||||
// In OC4, load->controller calls for order info don't always pass arguments,
|
// In OC4, load->controller calls for order info don't always pass arguments,
|
||||||
@@ -115,17 +133,26 @@ class Hutko extends \Opencart\System\Engine\Controller {
|
|||||||
|
|
||||||
$this->load->model('extension/hutko/payment/hutko');
|
$this->load->model('extension/hutko/payment/hutko');
|
||||||
$transactions = $this->model_extension_hutko_payment_hutko->getTransactions($order_id);
|
$transactions = $this->model_extension_hutko_payment_hutko->getTransactions($order_id);
|
||||||
|
|
||||||
$data['transactions'] = [];
|
$data['transactions'] = [];
|
||||||
foreach ($transactions as $t) {
|
foreach ($transactions as $t) {
|
||||||
$payload_arr = json_decode($t['payload'], true);
|
$payload_arr = json_decode($t['payload'], true);
|
||||||
if (isset($payload_arr['request_data']['reservation_data'])) {
|
if (isset($payload_arr['request_data']['reservation_data'])) {
|
||||||
$inner = json_decode(base64_decode($payload_arr['request_data']['reservation_data']), true);
|
$inner = json_decode(base64_decode($payload_arr['request_data']['reservation_data']), true);
|
||||||
if ($inner) $payload_arr['request_data']['reservation_data'] = $inner;
|
if ($inner) $payload_arr['request_data']['reservation_data'] = $inner;
|
||||||
}
|
}
|
||||||
|
if (isset($payload_arr['additional_info'])) {
|
||||||
// FIX: Pretty print with unescaped characters for better readability
|
$inner = json_decode($payload_arr['additional_info'], true);
|
||||||
$pretty_payload = $payload_arr ? json_encode($payload_arr, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : $t['payload'];
|
if ($inner) $payload_arr['additional_info'] = $inner;
|
||||||
|
if (isset($payload_arr['additional_info']['reservation_data'])) {
|
||||||
|
$inner = json_decode($payload_arr['additional_info']['reservation_data'], true);
|
||||||
|
if ($inner) $payload_arr['additional_info']['reservation_data'] = $inner;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isset($payload_arr['response_signature_string'])) {
|
||||||
|
unset($payload_arr['response_signature_string']);
|
||||||
|
}
|
||||||
|
$pretty_payload = $payload_arr ? json_encode($payload_arr, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : $t['payload'];
|
||||||
$data['transactions'][] = [
|
$data['transactions'][] = [
|
||||||
'date' => date($this->language->get('datetime_format'), strtotime($t['date_added'])),
|
'date' => date($this->language->get('datetime_format'), strtotime($t['date_added'])),
|
||||||
'ref' => $t['hutko_ref'],
|
'ref' => $t['hutko_ref'],
|
||||||
@@ -133,13 +160,13 @@ class Hutko extends \Opencart\System\Engine\Controller {
|
|||||||
'status' => $t['status'],
|
'status' => $t['status'],
|
||||||
'amount' => $t['amount'] . ' ' . $t['currency'],
|
'amount' => $t['amount'] . ' ' . $t['currency'],
|
||||||
'payload' => $pretty_payload,
|
'payload' => $pretty_payload,
|
||||||
'can_refund'=> ($t['type'] == 'callback' && $t['status'] == 'success')
|
'can_refund' => ($t['type'] == 'callback' && $t['status'] == 'success')
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
$data['order_id'] = $order_id;
|
$data['order_id'] = $order_id;
|
||||||
$data['user_token'] = $this->session->data['user_token'];
|
$data['user_token'] = $this->session->data['user_token'];
|
||||||
|
|
||||||
$data['refund_url'] = $this->url->link('extension/hutko/payment/hutko.refund', 'user_token=' . $this->session->data['user_token'] . '&order_id=' . $order_id, true);
|
$data['refund_url'] = $this->url->link('extension/hutko/payment/hutko.refund', 'user_token=' . $this->session->data['user_token'] . '&order_id=' . $order_id, true);
|
||||||
$data['status_url'] = $this->url->link('extension/hutko/payment/hutko.status', 'user_token=' . $this->session->data['user_token'], true);
|
$data['status_url'] = $this->url->link('extension/hutko/payment/hutko.status', 'user_token=' . $this->session->data['user_token'], true);
|
||||||
$data['create_link_url'] = $this->url->link('extension/hutko/payment/hutko.create_payment_link', 'user_token=' . $this->session->data['user_token'] . '&order_id=' . $order_id, true);
|
$data['create_link_url'] = $this->url->link('extension/hutko/payment/hutko.create_payment_link', 'user_token=' . $this->session->data['user_token'] . '&order_id=' . $order_id, true);
|
||||||
@@ -154,13 +181,14 @@ class Hutko extends \Opencart\System\Engine\Controller {
|
|||||||
$data['text_no_transactions'] = 'No Hutko transactions recorded.';
|
$data['text_no_transactions'] = 'No Hutko transactions recorded.';
|
||||||
$data['entry_refund_amount'] = $this->language->get('entry_refund_amount');
|
$data['entry_refund_amount'] = $this->language->get('entry_refund_amount');
|
||||||
$data['entry_refund_comment'] = $this->language->get('entry_refund_comment');
|
$data['entry_refund_comment'] = $this->language->get('entry_refund_comment');
|
||||||
|
|
||||||
$data['text_create_link_info'] = 'Create a new payment link using current order totals.';
|
$data['text_create_link_info'] = 'Create a new payment link using current order totals.';
|
||||||
|
|
||||||
return $this->load->view('extension/hutko/payment/hutko_order_info_panel', $data);
|
return $this->load->view('extension/hutko/payment/hutko_order_info_panel', $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function create_payment_link(): void {
|
public function create_payment_link(): void
|
||||||
|
{
|
||||||
$this->load->language('extension/hutko/payment/hutko');
|
$this->load->language('extension/hutko/payment/hutko');
|
||||||
$this->load->model('extension/hutko/payment/hutko');
|
$this->load->model('extension/hutko/payment/hutko');
|
||||||
$this->load->model('sale/order');
|
$this->load->model('sale/order');
|
||||||
@@ -170,8 +198,8 @@ public function create_payment_link(): void {
|
|||||||
$order_info = $this->model_sale_order->getOrder($order_id);
|
$order_info = $this->model_sale_order->getOrder($order_id);
|
||||||
|
|
||||||
if ($order_info) {
|
if ($order_info) {
|
||||||
$hutko_ref = $order_id . '#ADM' . time();
|
$hutko_ref = $order_id . '#ADM' . time();
|
||||||
|
|
||||||
$request_data = $this->buildRequest($order_info, $hutko_ref);
|
$request_data = $this->buildRequest($order_info, $hutko_ref);
|
||||||
|
|
||||||
if (!$request_data) {
|
if (!$request_data) {
|
||||||
@@ -181,7 +209,7 @@ public function create_payment_link(): void {
|
|||||||
|
|
||||||
if (($response['response']['response_status'] ?? '') === 'success' && !empty($response['response']['checkout_url'])) {
|
if (($response['response']['response_status'] ?? '') === 'success' && !empty($response['response']['checkout_url'])) {
|
||||||
$url = $response['response']['checkout_url'];
|
$url = $response['response']['checkout_url'];
|
||||||
|
|
||||||
$this->model_extension_hutko_payment_hutko->logTransaction(
|
$this->model_extension_hutko_payment_hutko->logTransaction(
|
||||||
$order_id,
|
$order_id,
|
||||||
$hutko_ref,
|
$hutko_ref,
|
||||||
@@ -199,7 +227,7 @@ public function create_payment_link(): void {
|
|||||||
if ((int)$order_info['order_status_id'] == 0) {
|
if ((int)$order_info['order_status_id'] == 0) {
|
||||||
$new_status_id = (int)$this->config->get('payment_hutko_new_order_status_id');
|
$new_status_id = (int)$this->config->get('payment_hutko_new_order_status_id');
|
||||||
if ($new_status_id <= 0) $new_status_id = 1; // Default to Pending
|
if ($new_status_id <= 0) $new_status_id = 1; // Default to Pending
|
||||||
|
|
||||||
$this->model_extension_hutko_payment_hutko->addOrderHistory($order_id, $new_status_id, 'Payment Link Created (Admin)', false);
|
$this->model_extension_hutko_payment_hutko->addOrderHistory($order_id, $new_status_id, 'Payment Link Created (Admin)', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,7 +236,7 @@ public function create_payment_link(): void {
|
|||||||
} else {
|
} else {
|
||||||
$err = $response['response']['error_message'] ?? 'API Error';
|
$err = $response['response']['error_message'] ?? 'API Error';
|
||||||
$json['error'] = $err;
|
$json['error'] = $err;
|
||||||
|
|
||||||
$this->model_extension_hutko_payment_hutko->logTransaction(
|
$this->model_extension_hutko_payment_hutko->logTransaction(
|
||||||
$order_id,
|
$order_id,
|
||||||
$hutko_ref,
|
$hutko_ref,
|
||||||
@@ -228,21 +256,22 @@ public function create_payment_link(): void {
|
|||||||
$this->response->setOutput(json_encode($json));
|
$this->response->setOutput(json_encode($json));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function refund(): void {
|
public function refund(): void
|
||||||
|
{
|
||||||
$this->load->language('extension/hutko/payment/hutko');
|
$this->load->language('extension/hutko/payment/hutko');
|
||||||
$this->load->model('extension/hutko/payment/hutko');
|
$this->load->model('extension/hutko/payment/hutko');
|
||||||
$this->load->model('sale/order');
|
$this->load->model('sale/order');
|
||||||
|
|
||||||
$json = [];
|
$json = [];
|
||||||
$order_id = (int)($this->request->post['order_id'] ?? 0);
|
$order_id = (int)($this->request->post['order_id'] ?? 0);
|
||||||
$amount = (float)($this->request->post['refund_amount'] ?? 0);
|
$amount = (float)($this->request->post['refund_amount'] ?? 0);
|
||||||
$comment = (string)($this->request->post['refund_comment'] ?? '');
|
$comment = (string)($this->request->post['refund_comment'] ?? '');
|
||||||
$hutko_ref = (string)($this->request->post['hutko_ref'] ?? '');
|
$hutko_ref = (string)($this->request->post['hutko_ref'] ?? '');
|
||||||
|
|
||||||
if (empty($hutko_ref)) {
|
if (empty($hutko_ref)) {
|
||||||
// Find the successful payment if ref not provided
|
// Find the successful payment if ref not provided
|
||||||
$transactions = $this->model_extension_hutko_payment_hutko->getTransactions($order_id);
|
$transactions = $this->model_extension_hutko_payment_hutko->getTransactions($order_id);
|
||||||
foreach($transactions as $t) {
|
foreach ($transactions as $t) {
|
||||||
if ($t['type'] == 'callback' && $t['status'] == 'success') {
|
if ($t['type'] == 'callback' && $t['status'] == 'success') {
|
||||||
$hutko_ref = $t['hutko_ref'];
|
$hutko_ref = $t['hutko_ref'];
|
||||||
break;
|
break;
|
||||||
@@ -262,9 +291,9 @@ public function create_payment_link(): void {
|
|||||||
'comment' => $comment
|
'comment' => $comment
|
||||||
];
|
];
|
||||||
$data['signature'] = $this->sign($data);
|
$data['signature'] = $this->sign($data);
|
||||||
|
|
||||||
$response = $this->api($this->refund_url, $data);
|
$response = $this->api($this->refund_url, $data);
|
||||||
|
|
||||||
$this->model_extension_hutko_payment_hutko->logTransaction(
|
$this->model_extension_hutko_payment_hutko->logTransaction(
|
||||||
$order_id,
|
$order_id,
|
||||||
$hutko_ref,
|
$hutko_ref,
|
||||||
@@ -277,14 +306,15 @@ public function create_payment_link(): void {
|
|||||||
|
|
||||||
if (($response['response']['reverse_status'] ?? '') === 'approved') {
|
if (($response['response']['reverse_status'] ?? '') === 'approved') {
|
||||||
$json['success'] = $this->language->get('text_refund_success');
|
$json['success'] = $this->language->get('text_refund_success');
|
||||||
$rev_amt = isset($response['response']['reversal_amount']) ? $response['response']['reversal_amount']/100 : $amount;
|
$rev_amt = isset($response['response']['reversal_amount']) ? $response['response']['reversal_amount'] / 100 : $amount;
|
||||||
|
|
||||||
$msg = sprintf($this->language->get('text_refund_success_comment'),
|
$msg = sprintf(
|
||||||
$hutko_ref,
|
$this->language->get('text_refund_success_comment'),
|
||||||
$this->currency->format($rev_amt, $order_info['currency_code'], $order_info['currency_value']),
|
$hutko_ref,
|
||||||
|
$this->currency->format($rev_amt, $order_info['currency_code'], $order_info['currency_value']),
|
||||||
$comment
|
$comment
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->model_extension_hutko_payment_hutko->addOrderHistory($order_id, $this->config->get('payment_hutko_refunded_status_id'), $msg, true);
|
$this->model_extension_hutko_payment_hutko->addOrderHistory($order_id, $this->config->get('payment_hutko_refunded_status_id'), $msg, true);
|
||||||
} else {
|
} else {
|
||||||
$err = $response['response']['error_message'] ?? 'Unknown Error';
|
$err = $response['response']['error_message'] ?? 'Unknown Error';
|
||||||
@@ -298,7 +328,8 @@ public function create_payment_link(): void {
|
|||||||
$this->response->setOutput(json_encode($json));
|
$this->response->setOutput(json_encode($json));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function status(): void {
|
public function status(): void
|
||||||
|
{
|
||||||
$this->load->language('extension/hutko/payment/hutko');
|
$this->load->language('extension/hutko/payment/hutko');
|
||||||
$json = [];
|
$json = [];
|
||||||
$ref = $this->request->post['hutko_transaction_ref'] ?? '';
|
$ref = $this->request->post['hutko_transaction_ref'] ?? '';
|
||||||
@@ -310,7 +341,7 @@ public function create_payment_link(): void {
|
|||||||
];
|
];
|
||||||
$data['signature'] = $this->sign($data);
|
$data['signature'] = $this->sign($data);
|
||||||
$response = $this->api($this->status_url, $data);
|
$response = $this->api($this->status_url, $data);
|
||||||
|
|
||||||
if (($response['response']['response_status'] ?? '') === 'success') {
|
if (($response['response']['response_status'] ?? '') === 'success') {
|
||||||
$json['success'] = $this->language->get('text_status_success');
|
$json['success'] = $this->language->get('text_status_success');
|
||||||
unset($response['response']['response_signature_string'], $response['response']['signature']);
|
unset($response['response']['response_signature_string'], $response['response']['signature']);
|
||||||
@@ -326,7 +357,8 @@ public function create_payment_link(): void {
|
|||||||
$this->response->setOutput(json_encode($json));
|
$this->response->setOutput(json_encode($json));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function displayLastDayLog() {
|
private function displayLastDayLog()
|
||||||
|
{
|
||||||
if (!$this->config->get('payment_hutko_save_logs')) return $this->language->get('text_logs_disabled');
|
if (!$this->config->get('payment_hutko_save_logs')) return $this->language->get('text_logs_disabled');
|
||||||
$file = DIR_LOGS . 'error.log';
|
$file = DIR_LOGS . 'error.log';
|
||||||
if (!file_exists($file)) return sprintf($this->language->get('text_log_file_not_found'), 'error.log');
|
if (!file_exists($file)) return sprintf($this->language->get('text_log_file_not_found'), 'error.log');
|
||||||
@@ -337,16 +369,17 @@ public function create_payment_link(): void {
|
|||||||
}
|
}
|
||||||
return empty($output) ? $this->language->get('text_no_logs_found') : implode('<br>', $output);
|
return empty($output) ? $this->language->get('text_no_logs_found') : implode('<br>', $output);
|
||||||
}
|
}
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// SHARED LOGIC START
|
// SHARED LOGIC START
|
||||||
// MAINTENANCE WARNING: The following functions (buildRequest, getProducts,
|
// MAINTENANCE WARNING: The following functions (buildRequest, getProducts,
|
||||||
// sign, api, logOC) must remain identical in Admin and Catalog controllers.
|
// sign, api, logOC) must remain identical in Admin and Catalog controllers.
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
|
|
||||||
private function buildRequest($order, $hutko_ref) {
|
private function buildRequest($order, $hutko_ref)
|
||||||
|
{
|
||||||
$products_data = $this->getProducts($order['order_id'], $order);
|
$products_data = $this->getProducts($order['order_id'], $order);
|
||||||
|
|
||||||
$total_products_sum = 0;
|
$total_products_sum = 0;
|
||||||
foreach ($products_data as $p) {
|
foreach ($products_data as $p) {
|
||||||
$total_products_sum += $p['total_amount'];
|
$total_products_sum += $p['total_amount'];
|
||||||
@@ -361,7 +394,7 @@ public function create_payment_link(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$order_total_val = $this->currency->format($order['total'], $order['currency_code'], $order['currency_value'], false);
|
$order_total_val = $this->currency->format($order['total'], $order['currency_code'], $order['currency_value'], false);
|
||||||
|
|
||||||
if ($this->config->get('payment_hutko_include_discount_to_total')) {
|
if ($this->config->get('payment_hutko_include_discount_to_total')) {
|
||||||
$amount_val = $order_total_val;
|
$amount_val = $order_total_val;
|
||||||
if (!$this->config->get('payment_hutko_shipping_include')) {
|
if (!$this->config->get('payment_hutko_shipping_include')) {
|
||||||
@@ -403,12 +436,13 @@ public function create_payment_link(): void {
|
|||||||
'sender_email' => $order['email'],
|
'sender_email' => $order['email'],
|
||||||
'reservation_data' => base64_encode(json_encode($reservation_data))
|
'reservation_data' => base64_encode(json_encode($reservation_data))
|
||||||
];
|
];
|
||||||
|
|
||||||
$data['signature'] = $this->sign($data);
|
$data['signature'] = $this->sign($data);
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getProducts(int $order_id, array $order_info): array {
|
private function getProducts(int $order_id, array $order_info): array
|
||||||
|
{
|
||||||
$products_data = [];
|
$products_data = [];
|
||||||
$query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "order_product` WHERE `order_id` = '" . (int)$order_id . "'");
|
$query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "order_product` WHERE `order_id` = '" . (int)$order_id . "'");
|
||||||
|
|
||||||
@@ -443,16 +477,20 @@ public function create_payment_link(): void {
|
|||||||
return $products_data;
|
return $products_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function sign($data) {
|
private function sign($data)
|
||||||
|
{
|
||||||
$key = $this->config->get('payment_hutko_secret_key');
|
$key = $this->config->get('payment_hutko_secret_key');
|
||||||
$arr = array_filter($data, function($v){ return $v !== '' && $v !== null; });
|
$arr = array_filter($data, function ($v) {
|
||||||
|
return $v !== '' && $v !== null;
|
||||||
|
});
|
||||||
ksort($arr);
|
ksort($arr);
|
||||||
$str = $key;
|
$str = $key;
|
||||||
foreach($arr as $v) $str .= '|' . $v;
|
foreach ($arr as $v) $str .= '|' . $v;
|
||||||
return sha1($str);
|
return sha1($str);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function api($url, $data) {
|
private function api($url, $data)
|
||||||
|
{
|
||||||
if ($this->config->get('payment_hutko_save_logs')) $this->logOC('Req: ' . json_encode($data));
|
if ($this->config->get('payment_hutko_save_logs')) $this->logOC('Req: ' . json_encode($data));
|
||||||
|
|
||||||
$ch = curl_init($url);
|
$ch = curl_init($url);
|
||||||
@@ -462,7 +500,7 @@ public function create_payment_link(): void {
|
|||||||
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
|
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
|
||||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
|
||||||
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
||||||
|
|
||||||
$res = curl_exec($ch);
|
$res = curl_exec($ch);
|
||||||
$error = curl_error($ch);
|
$error = curl_error($ch);
|
||||||
curl_close($ch);
|
curl_close($ch);
|
||||||
@@ -471,14 +509,15 @@ public function create_payment_link(): void {
|
|||||||
$this->logOC('Res: ' . $res);
|
$this->logOC('Res: ' . $res);
|
||||||
if ($error) $this->logOC('Curl Error: ' . $error);
|
if ($error) $this->logOC('Curl Error: ' . $error);
|
||||||
}
|
}
|
||||||
|
|
||||||
return json_decode($res, true) ?: [];
|
return json_decode($res, true) ?: [];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function logOC($msg) {
|
private function logOC($msg)
|
||||||
|
{
|
||||||
$this->log->write("Hutko Payment: " . $msg);
|
$this->log->write("Hutko Payment: " . $msg);
|
||||||
}
|
}
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// SHARED LOGIC END
|
// SHARED LOGIC END
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,22 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Opencart\Catalog\Controller\Extension\Hutko\Payment;
|
namespace Opencart\Catalog\Controller\Extension\Hutko\Payment;
|
||||||
|
|
||||||
class Hutko extends \Opencart\System\Engine\Controller {
|
class Hutko extends \Opencart\System\Engine\Controller
|
||||||
|
{
|
||||||
private $checkout_url = 'https://pay.hutko.org/api/checkout/url/';
|
private $checkout_url = 'https://pay.hutko.org/api/checkout/url/';
|
||||||
|
|
||||||
public function index(): string {
|
public function index(): string
|
||||||
|
{
|
||||||
$this->load->language('extension/hutko/payment/hutko');
|
$this->load->language('extension/hutko/payment/hutko');
|
||||||
return $this->load->view('extension/hutko/payment/hutko', ['language' => $this->config->get('config_language')]);
|
return $this->load->view('extension/hutko/payment/hutko', ['language' => $this->config->get('config_language')]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function confirm(): void {
|
public function confirm(): void
|
||||||
|
{
|
||||||
$this->load->language('extension/hutko/payment/hutko');
|
$this->load->language('extension/hutko/payment/hutko');
|
||||||
$this->load->model('checkout/order');
|
$this->load->model('checkout/order');
|
||||||
|
|
||||||
$json = [];
|
$json = [];
|
||||||
|
|
||||||
if (!isset($this->session->data['order_id'])) {
|
if (!isset($this->session->data['order_id'])) {
|
||||||
@@ -20,7 +24,7 @@ public function confirm(): void {
|
|||||||
$json['redirect'] = $this->url->link('checkout/failure', 'language=' . $this->config->get('config_language'), true);
|
$json['redirect'] = $this->url->link('checkout/failure', 'language=' . $this->config->get('config_language'), true);
|
||||||
} else {
|
} else {
|
||||||
$order_info = $this->model_checkout_order->getOrder($this->session->data['order_id']);
|
$order_info = $this->model_checkout_order->getOrder($this->session->data['order_id']);
|
||||||
|
|
||||||
if (!$order_info) {
|
if (!$order_info) {
|
||||||
$json['error'] = 'Order missing';
|
$json['error'] = 'Order missing';
|
||||||
} else {
|
} else {
|
||||||
@@ -28,7 +32,7 @@ public function confirm(): void {
|
|||||||
|
|
||||||
// Call the shared logic method
|
// Call the shared logic method
|
||||||
$request_data = $this->buildRequest($order_info, $hutko_ref);
|
$request_data = $this->buildRequest($order_info, $hutko_ref);
|
||||||
|
|
||||||
if (!$request_data) {
|
if (!$request_data) {
|
||||||
$json['error'] = $this->language->get('error_payment_data_build');
|
$json['error'] = $this->language->get('error_payment_data_build');
|
||||||
} else {
|
} else {
|
||||||
@@ -58,14 +62,27 @@ public function confirm(): void {
|
|||||||
if ($new_status_id <= 0) {
|
if ($new_status_id <= 0) {
|
||||||
$new_status_id = 1; // Default to Pending
|
$new_status_id = 1; // Default to Pending
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->model_checkout_order->addHistory($order_info['order_id'], $new_status_id, $this->language->get('text_initiated_payment'), false);
|
$this->model_checkout_order->addHistory($order_info['order_id'], $new_status_id, $this->language->get('text_initiated_payment'), false);
|
||||||
}
|
}
|
||||||
|
// Clear Cart and Session Data BEFORE redirecting to Gateway
|
||||||
|
// This ensures the cart is empty regardless of the return path/device.
|
||||||
|
$this->cart->clear();
|
||||||
|
unset($this->session->data['shipping_method']);
|
||||||
|
unset($this->session->data['shipping_methods']);
|
||||||
|
unset($this->session->data['payment_method']);
|
||||||
|
unset($this->session->data['payment_methods']);
|
||||||
|
unset($this->session->data['guest']);
|
||||||
|
unset($this->session->data['comment']);
|
||||||
|
unset($this->session->data['coupon']);
|
||||||
|
unset($this->session->data['reward']);
|
||||||
|
unset($this->session->data['voucher']);
|
||||||
|
unset($this->session->data['vouchers']);
|
||||||
|
unset($this->session->data['totals']);
|
||||||
|
// NOTE: Do NOT unset 'order_id' here, as checkout/success needs it to show the "Order #123 placed" page.
|
||||||
$json['redirect'] = $url;
|
$json['redirect'] = $url;
|
||||||
} else {
|
} else {
|
||||||
// ... error handling (same as before) ...
|
$err = $response['response']['error_message'] ?? $this->language->get('error_api_communication');
|
||||||
$err = $response['response']['error_message'] ?? $this->language->get('error_api_communication');
|
|
||||||
$json['error'] = $err;
|
$json['error'] = $err;
|
||||||
$this->model_extension_hutko_payment_hutko->logTransaction(
|
$this->model_extension_hutko_payment_hutko->logTransaction(
|
||||||
$order_info['order_id'],
|
$order_info['order_id'],
|
||||||
@@ -84,9 +101,10 @@ public function confirm(): void {
|
|||||||
$this->response->addHeader('Content-Type: application/json');
|
$this->response->addHeader('Content-Type: application/json');
|
||||||
$this->response->setOutput(json_encode($json));
|
$this->response->setOutput(json_encode($json));
|
||||||
}
|
}
|
||||||
public function callback(): void {
|
public function callback(): void
|
||||||
|
{
|
||||||
$this->load->language('extension/hutko/payment/hutko');
|
$this->load->language('extension/hutko/payment/hutko');
|
||||||
|
|
||||||
$input = file_get_contents("php://input");
|
$input = file_get_contents("php://input");
|
||||||
$data = json_decode($input, true);
|
$data = json_decode($input, true);
|
||||||
|
|
||||||
@@ -105,7 +123,7 @@ public function confirm(): void {
|
|||||||
$hutko_ref = $data['order_id']; // e.g., 55#17555555
|
$hutko_ref = $data['order_id']; // e.g., 55#17555555
|
||||||
$parts = explode('#', $hutko_ref);
|
$parts = explode('#', $hutko_ref);
|
||||||
$order_id = (int)$parts[0];
|
$order_id = (int)$parts[0];
|
||||||
|
|
||||||
$this->load->model('checkout/order');
|
$this->load->model('checkout/order');
|
||||||
$order_info = $this->model_checkout_order->getOrder($order_id);
|
$order_info = $this->model_checkout_order->getOrder($order_id);
|
||||||
|
|
||||||
@@ -125,12 +143,12 @@ public function confirm(): void {
|
|||||||
);
|
);
|
||||||
|
|
||||||
$current_status_id = (int)$order_info['order_status_id'];
|
$current_status_id = (int)$order_info['order_status_id'];
|
||||||
|
|
||||||
// 2. Update OpenCart History (Clean Messages Only)
|
// 2. Update OpenCart History (Clean Messages Only)
|
||||||
if ($status === 'approved') {
|
if ($status === 'approved') {
|
||||||
if (isset($data['response_status']) && $data['response_status'] == 'success') {
|
if (isset($data['response_status']) && $data['response_status'] == 'success') {
|
||||||
$target_status_id = (int)$this->config->get('payment_hutko_success_status_id');
|
$target_status_id = (int)$this->config->get('payment_hutko_success_status_id');
|
||||||
|
|
||||||
// Avoid duplicates: Only update if status is different
|
// Avoid duplicates: Only update if status is different
|
||||||
if ($current_status_id != $target_status_id) {
|
if ($current_status_id != $target_status_id) {
|
||||||
$this->model_checkout_order->addHistory($order_id, $target_status_id, $this->language->get('text_payment_approved'), true);
|
$this->model_checkout_order->addHistory($order_id, $target_status_id, $this->language->get('text_payment_approved'), true);
|
||||||
@@ -163,9 +181,10 @@ public function confirm(): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private function validate($data) {
|
private function validate($data)
|
||||||
|
{
|
||||||
$sig = $data['signature'] ?? '';
|
$sig = $data['signature'] ?? '';
|
||||||
unset($data['signature'], $data['response_signature_string']);
|
unset($data['signature'], $data['response_signature_string']);
|
||||||
return hash_equals($this->sign($data), $sig);
|
return hash_equals($this->sign($data), $sig);
|
||||||
@@ -177,9 +196,10 @@ public function confirm(): void {
|
|||||||
// MAINTENANCE WARNING: Keep synchronized with Admin Controller
|
// MAINTENANCE WARNING: Keep synchronized with Admin Controller
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
|
|
||||||
private function buildRequest($order, $hutko_ref) {
|
private function buildRequest($order, $hutko_ref)
|
||||||
|
{
|
||||||
$products_data = $this->getProducts($order['order_id'], $order);
|
$products_data = $this->getProducts($order['order_id'], $order);
|
||||||
|
|
||||||
$total_products_sum = 0;
|
$total_products_sum = 0;
|
||||||
foreach ($products_data as $p) {
|
foreach ($products_data as $p) {
|
||||||
$total_products_sum += $p['total_amount'];
|
$total_products_sum += $p['total_amount'];
|
||||||
@@ -194,7 +214,7 @@ public function confirm(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$order_total_val = $this->currency->format($order['total'], $order['currency_code'], $order['currency_value'], false);
|
$order_total_val = $this->currency->format($order['total'], $order['currency_code'], $order['currency_value'], false);
|
||||||
|
|
||||||
if ($this->config->get('payment_hutko_include_discount_to_total')) {
|
if ($this->config->get('payment_hutko_include_discount_to_total')) {
|
||||||
$amount_val = $order_total_val;
|
$amount_val = $order_total_val;
|
||||||
if (!$this->config->get('payment_hutko_shipping_include')) {
|
if (!$this->config->get('payment_hutko_shipping_include')) {
|
||||||
@@ -207,7 +227,8 @@ public function confirm(): void {
|
|||||||
if ($amount_val < 0.01) $amount_val = 0.01;
|
if ($amount_val < 0.01) $amount_val = 0.01;
|
||||||
$total_cents = (int)round($amount_val * 100);
|
$total_cents = (int)round($amount_val * 100);
|
||||||
|
|
||||||
// Catalog side URLs are simple
|
|
||||||
|
|
||||||
$response_url = $this->url->link('checkout/success', 'language=' . $this->config->get('config_language'), true);
|
$response_url = $this->url->link('checkout/success', 'language=' . $this->config->get('config_language'), true);
|
||||||
$callback_url = $this->url->link('extension/hutko/payment/hutko.callback', '', true);
|
$callback_url = $this->url->link('extension/hutko/payment/hutko.callback', '', true);
|
||||||
|
|
||||||
@@ -234,12 +255,25 @@ public function confirm(): void {
|
|||||||
'sender_email' => $order['email'],
|
'sender_email' => $order['email'],
|
||||||
'reservation_data' => base64_encode(json_encode($reservation_data))
|
'reservation_data' => base64_encode(json_encode($reservation_data))
|
||||||
];
|
];
|
||||||
|
|
||||||
$data['signature'] = $this->sign($data);
|
$data['signature'] = $this->sign($data);
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getProducts(int $order_id, array $order_info): array {
|
|
||||||
|
public function response(): void
|
||||||
|
{
|
||||||
|
// Post-Redirect-Get pattern.
|
||||||
|
// Accepts the POST from Gateway, then redirects user via GET to restore Session/Cookies.
|
||||||
|
// This ensures the Cart is cleared and User is not logged out.
|
||||||
|
|
||||||
|
// If the gateway passes specific error flags in POST, you could check them here
|
||||||
|
// and redirect to checkout/failure instead. For now, we assume success flow.
|
||||||
|
$this->response->redirect($this->url->link('checkout/success', 'language=' . $this->config->get('config_language'), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getProducts(int $order_id, array $order_info): array
|
||||||
|
{
|
||||||
$products_data = [];
|
$products_data = [];
|
||||||
$query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "order_product` WHERE `order_id` = '" . (int)$order_id . "'");
|
$query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "order_product` WHERE `order_id` = '" . (int)$order_id . "'");
|
||||||
|
|
||||||
@@ -274,16 +308,20 @@ public function confirm(): void {
|
|||||||
return $products_data;
|
return $products_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function sign($data) {
|
private function sign($data)
|
||||||
|
{
|
||||||
$key = $this->config->get('payment_hutko_secret_key');
|
$key = $this->config->get('payment_hutko_secret_key');
|
||||||
$arr = array_filter($data, function($v){ return $v !== '' && $v !== null; });
|
$arr = array_filter($data, function ($v) {
|
||||||
|
return $v !== '' && $v !== null;
|
||||||
|
});
|
||||||
ksort($arr);
|
ksort($arr);
|
||||||
$str = $key;
|
$str = $key;
|
||||||
foreach($arr as $v) $str .= '|' . $v;
|
foreach ($arr as $v) $str .= '|' . $v;
|
||||||
return sha1($str);
|
return sha1($str);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function api($url, $data) {
|
private function api($url, $data)
|
||||||
|
{
|
||||||
if ($this->config->get('payment_hutko_save_logs')) $this->logOC('Req: ' . json_encode($data));
|
if ($this->config->get('payment_hutko_save_logs')) $this->logOC('Req: ' . json_encode($data));
|
||||||
|
|
||||||
$ch = curl_init($url);
|
$ch = curl_init($url);
|
||||||
@@ -293,7 +331,7 @@ public function confirm(): void {
|
|||||||
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
|
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
|
||||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
|
||||||
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
||||||
|
|
||||||
$res = curl_exec($ch);
|
$res = curl_exec($ch);
|
||||||
$error = curl_error($ch);
|
$error = curl_error($ch);
|
||||||
curl_close($ch);
|
curl_close($ch);
|
||||||
@@ -302,14 +340,15 @@ public function confirm(): void {
|
|||||||
$this->logOC('Res: ' . $res);
|
$this->logOC('Res: ' . $res);
|
||||||
if ($error) $this->logOC('Curl Error: ' . $error);
|
if ($error) $this->logOC('Curl Error: ' . $error);
|
||||||
}
|
}
|
||||||
|
|
||||||
return json_decode($res, true) ?: [];
|
return json_decode($res, true) ?: [];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function logOC($msg) {
|
private function logOC($msg)
|
||||||
|
{
|
||||||
$this->log->write("Hutko Payment: " . $msg);
|
$this->log->write("Hutko Payment: " . $msg);
|
||||||
}
|
}
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// SHARED LOGIC END
|
// SHARED LOGIC END
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user