Files
hutko/controllers/front/callback.php
2025-06-01 20:19:08 +03:00

145 lines
6.4 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.
$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);
$transaction_id = $requestBody['order_id'];
$orderIdParamParts = explode($this->module->order_separator, $transaction_id);
$orderId = (int)$orderIdParamParts[0]; // Ensure it's an integer
// If we reached here, an order should exist. Load it.
$order = new Order($orderId);
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');
}
// 7. Handle payment status from the callback.
$orderStatusCallback = $requestBody['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) {
$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;
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':
// 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');
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');
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());
}
}
/**
* Helper method to parse the request body from POST or raw input.
*
* @return array The parsed request body.
*/
private function getRequestBody(): array
{
$jsonBody = json_decode(file_get_contents("php://input"), true);
if (is_array($jsonBody)) {
return $jsonBody;
}
return [];
}
}