This commit is contained in:
2026-03-25 21:29:11 +01:00
parent 6a2bfeb4db
commit 29fc4d6edb
3 changed files with 149 additions and 29 deletions

View File

@@ -5,6 +5,7 @@ class ShopBasketController
{
private const ORDER_SUBMIT_TOKEN_SESSION_KEY = 'order-submit-token';
private const ORDER_SUBMIT_LAST_ORDER_ID_SESSION_KEY = 'order-submit-last-order-id';
private const ORDER_SUBMIT_TOKEN_TTL = 1800; // 30 minutes
public static $title = [
'mainView' => 'Koszyk'
@@ -276,19 +277,6 @@ class ShopBasketController
exit;
}
$existingOrderId = isset( $_SESSION[ self::ORDER_SUBMIT_LAST_ORDER_ID_SESSION_KEY ] )
? (int)$_SESSION[ self::ORDER_SUBMIT_LAST_ORDER_ID_SESSION_KEY ]
: 0;
if ( $existingOrderId > 0 )
{
$existingOrderHash = $this->orderRepository->findHashById( $existingOrderId );
if ( $existingOrderHash )
{
header( 'Location: /zamowienie/' . $existingOrderHash );
exit;
}
}
$client = \Shared\Helpers\Helpers::get_session( 'client' );
$orderSubmitToken = $this->createOrderSubmitToken();
@@ -308,23 +296,26 @@ class ShopBasketController
public function basketSave()
{
$orderSubmitToken = (string)\Shared\Helpers\Helpers::get( 'order_submit_token', true );
$basket = \Shared\Helpers\Helpers::get_session( 'basket' );
$existingOrderId = isset( $_SESSION[ self::ORDER_SUBMIT_LAST_ORDER_ID_SESSION_KEY ] ) ? (int)$_SESSION[ self::ORDER_SUBMIT_LAST_ORDER_ID_SESSION_KEY ] : 0;
if ( ( !is_array( $basket ) || empty( $basket ) ) && $existingOrderId > 0 )
{
$existingOrderHash = $this->orderRepository->findHashById( $existingOrderId );
if ( $existingOrderHash )
{
$this->logOrder( 'Double-submit detected, redirecting to existing order id=' . $existingOrderId );
header( 'Location: /zamowienie/' . $existingOrderHash );
exit;
}
}
$orderSubmitToken = (string)\Shared\Helpers\Helpers::get( 'order_submit_token', true );
if ( !$this->isValidOrderSubmitToken( $orderSubmitToken ) )
{
if ( $existingOrderId > 0 )
{
$existingOrderHash = $this->orderRepository->findHashById( $existingOrderId );
if ( $existingOrderHash )
{
header( 'Location: /zamowienie/' . $existingOrderHash );
exit;
}
}
$this->logOrder( 'Token validation failed. formToken=' . ($orderSubmitToken ?: '(empty)') );
\Shared\Helpers\Helpers::error( \Shared\Helpers\Helpers::lang( 'zamowienie-zostalo-zlozone-komunikat-blad' ) );
header( 'Location: /koszyk' );
header( 'Location: /koszyk-podsumowanie' );
exit;
}
@@ -367,7 +358,7 @@ class ShopBasketController
}
catch ( \Exception $e )
{
error_log( '[basketSave] createFromBasket exception: ' . $e->getMessage() );
$this->logOrder( 'createFromBasket exception: ' . $e->getMessage() );
\Shared\Helpers\Helpers::error( \Shared\Helpers\Helpers::lang( 'zamowienie-zostalo-zlozone-komunikat-blad' ) );
header( 'Location: /koszyk' );
exit;
@@ -400,6 +391,7 @@ class ShopBasketController
}
else
{
$this->logOrder( 'createFromBasket returned falsy order_id. client_id=' . ($client['id'] ?? 'guest') . ' email=' . \Shared\Helpers\Helpers::get( 'email', true ) );
\Shared\Helpers\Helpers::error( \Shared\Helpers\Helpers::lang( 'zamowienie-zostalo-zlozone-komunikat-blad' ) );
header( 'Location: /koszyk' );
exit;
@@ -471,8 +463,23 @@ class ShopBasketController
private function createOrderSubmitToken()
{
$existingTokenData = isset( $_SESSION[ self::ORDER_SUBMIT_TOKEN_SESSION_KEY ] ) ? $_SESSION[ self::ORDER_SUBMIT_TOKEN_SESSION_KEY ] : null;
if ( is_array( $existingTokenData ) && !empty( $existingTokenData['token'] ) && !empty( $existingTokenData['created_at'] ) )
{
$age = time() - (int)$existingTokenData['created_at'];
if ( $age < self::ORDER_SUBMIT_TOKEN_TTL )
{
\Shared\Helpers\Helpers::delete_session( self::ORDER_SUBMIT_LAST_ORDER_ID_SESSION_KEY );
return $existingTokenData['token'];
}
}
$token = $this->generateOrderSubmitToken();
\Shared\Helpers\Helpers::set_session( self::ORDER_SUBMIT_TOKEN_SESSION_KEY, $token );
\Shared\Helpers\Helpers::set_session( self::ORDER_SUBMIT_TOKEN_SESSION_KEY, [
'token' => $token,
'created_at' => time()
] );
\Shared\Helpers\Helpers::delete_session( self::ORDER_SUBMIT_LAST_ORDER_ID_SESSION_KEY );
return $token;
@@ -495,10 +502,26 @@ class ShopBasketController
if ( !$token )
return false;
$sessionToken = isset( $_SESSION[ self::ORDER_SUBMIT_TOKEN_SESSION_KEY ] ) ? (string)$_SESSION[ self::ORDER_SUBMIT_TOKEN_SESSION_KEY ] : '';
if ( !$sessionToken )
$tokenData = isset( $_SESSION[ self::ORDER_SUBMIT_TOKEN_SESSION_KEY ] ) ? $_SESSION[ self::ORDER_SUBMIT_TOKEN_SESSION_KEY ] : null;
if ( is_string( $tokenData ) )
{
$sessionToken = $tokenData;
if ( !$sessionToken )
return false;
return function_exists( 'hash_equals' ) ? hash_equals( $sessionToken, $token ) : $sessionToken === $token;
}
if ( !is_array( $tokenData ) || empty( $tokenData['token'] ) || empty( $tokenData['created_at'] ) )
return false;
$age = time() - (int)$tokenData['created_at'];
if ( $age > self::ORDER_SUBMIT_TOKEN_TTL )
return false;
$sessionToken = (string)$tokenData['token'];
if ( function_exists( 'hash_equals' ) )
return hash_equals( $sessionToken, $token );
@@ -509,4 +532,12 @@ class ShopBasketController
{
\Shared\Helpers\Helpers::delete_session( self::ORDER_SUBMIT_TOKEN_SESSION_KEY );
}
private function logOrder( $message )
{
$logDir = $_SERVER['DOCUMENT_ROOT'] . '/logs';
$logFile = $logDir . '/logs-order-' . date( 'Y-m-d' ) . '.log';
$line = '[' . date( 'Y-m-d H:i:s' ) . '] ' . $message . PHP_EOL;
@file_put_contents( $logFile, $line, FILE_APPEND | LOCK_EX );
}
}