Files
hutko/controllers/front/callback.php
2025-06-02 11:24:18 +03:00

138 lines
6.0 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* Hutko - Платіжний сервіс, який рухає бізнеси вперед.
*
* Запускайтесь, набирайте темп, масштабуйтесь ми підстрахуємо всюди.
*
* @author panariga
* @copyright 2025 Hutko
* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
*/
if (!defined('_PS_VERSION_')) {
exit;
}
/**
* Class HutkoCallbackModuleFrontController
*
* This front controller handles the asynchronous callback notifications from the Hutko payment gateway.
* It is responsible for validating the payment response, updating the order status in PrestaShop,
* and handling various payment statuses (approved, declined, expired, processing).
* It also incorporates logic to mitigate race conditions with the customer's return to the result page.
*
* @property \Hutko $module An instance of the Hutko module.
*/
class HutkoCallbackModuleFrontController extends ModuleFrontController
{
public function postProcess(): void
{
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
exit;
}
try {
// 1. Parse the incoming request body.
$calbackContent = $this->getCallbackContent();
$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($id_order);
if (!Validate::isLoadedObject($order)) {
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 = $calbackContent['order_status'];
$currentOrderState = (int)$order->getCurrentState();
switch ($orderStatusCallback) {
case 'approved':
// Only success state if no refunds was done.
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) {
$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);
}
}
exit('OK');
break;
case 'declined':
$expectedState = (int)Configuration::get('PS_OS_ERROR');
// Only change state if it's not already the error state.
if ($currentOrderState !== $expectedState) {
$order->setCurrentState($expectedState);
}
exit('Order ' . $orderStatusCallback);
break;
case 'expired':
$expectedState = (int)Configuration::get('PS_OS_ERROR');
// Only change state if it's not already the error state.
if ($currentOrderState !== $expectedState) {
$order->setCurrentState($expectedState);
}
exit('Order ' . $orderStatusCallback);
break;
case '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: ' . $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);
throw new Exception('Unknown error');
}
}
/**
* Helper method to parse the request body from raw input.
*
* @return array The parsed request body.
*/
private function getCallbackContent(): array
{
$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');
}
// 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;
}
}