Update: 2026-06-21 02:07:00

This commit is contained in:
Hamza-Ayed
2026-06-21 02:07:00 +03:00
parent af3dcae5b7
commit b2fae9ec66
23 changed files with 1412 additions and 210 deletions

View File

@@ -60,14 +60,16 @@ require_once __DIR__ . '/helpers.php';
$envFile = getenv('ENV_FILE_PATH') ?: (__DIR__ . '/../.env');
loadEnvironment($envFile);
// 4. Redis Connection (Singleton)
// 4. Redis Connections (Dual Architecture)
$redis = null;
$redisLocation = null;
try {
if (extension_loaded('redis')) {
// --- Main Server Redis ---
$redis = new Redis();
$redisHost = getenv('REDIS_HOST') ?: '127.0.0.1';
$redisPort = (int)(getenv('REDIS_PORT') ?: 6379);
$redisPass = getenv('REDIS_PASSWORD');
$redisHost = getenv('REDIS_MAIN_HOST') ?: getenv('REDIS_HOST') ?: '127.0.0.1';
$redisPort = (int)(getenv('REDIS_MAIN_PORT') ?: getenv('REDIS_PORT') ?: 6379);
$redisPass = getenv('REDIS_MAIN_PASSWORD') ?: getenv('REDIS_MAIN_AUTH') ?: getenv('REDIS_PASSWORD') ?: getenv('REDIS_AUTH');
if ($redis->connect($redisHost, $redisPort, 1.5)) {
if ($redisPass) $redis->auth($redisPass);
@@ -75,10 +77,24 @@ try {
} else {
$redis = null;
}
// --- Location Server Redis ---
$redisLocation = new Redis();
$locHost = getenv('REDIS_LOCATION_HOST') ?: $redisHost;
$locPort = (int)(getenv('REDIS_LOCATION_PORT') ?: $redisPort);
$locPass = getenv('REDIS_LOCATION_PASSWORD') ?: $redisPass;
if ($redisLocation->connect($locHost, $locPort, 1.5)) {
if ($locPass) $redisLocation->auth($locPass);
// No prefix for location server
} else {
$redisLocation = null;
}
}
} catch (Exception $e) {
error_log("[REDIS] Connection failed: " . $e->getMessage());
$redis = null;
$redisLocation = null;
}
// 5. تحميل الـ Services الأساسية

View File

@@ -13,6 +13,11 @@ if ($isDriverCallPassenger === null || $isDriverCallPassenger === "") {
$isDriverCallPassenger = "0";
}
if (!$driverID || !$passengerID || !$rideID) {
jsonError("Missing required fields");
exit();
}
// استخدام التاريخ الحالي
$dateCreated = date("Y-m-d H:i:s");
@@ -42,6 +47,16 @@ $stmt->bindParam(":dateCreated", $dateCreated);
$stmt->execute();
if ($stmt->rowCount() > 0) {
// Invalidate Redis cache key for this driver
if (isset($redis) && $redis !== null && $driverID) {
try {
$today = date("Y-m-d");
$redisKey = "driver:scam_count:" . $driverID . ":" . $today;
$redis->del($redisKey);
} catch (Exception $e) {
error_log("[add.php] Redis cache invalidation failed: " . $e->getMessage());
}
}
jsonSuccess(null, "Driver ride scam data saved successfully");
} else {
jsonError("Failed to save driver ride scam data");

View File

@@ -9,22 +9,44 @@ if (!$driverID) {
exit();
}
$today = date("Y-m-d");
$redisKey = "driver:scam_count:" . $driverID . ":" . $today;
$cachedData = null;
// 1. Try to read from Redis
if (isset($redis) && $redis !== null) {
try {
$cachedData = $redis->get($redisKey);
if ($cachedData !== false && $cachedData !== null) {
$rows = json_decode($cachedData, true);
if (!empty($rows)) {
echo json_encode(array("status" => "success", "message" => $rows));
exit();
} else {
jsonError("No ride scam record found");
exit();
}
}
} catch (Exception $e) {
error_log("[get.php] Redis read failed: " . $e->getMessage());
}
}
// 2. Fallback to SQL Database
$sql = "SELECT
DATE(driver_ride_scam.dateCreated) AS date,
CAST(COUNT(driver_ride_scam.id) AS CHAR) AS count
FROM
driver_ride_scam
LEFT JOIN
INNER JOIN
ride ON ride.id = driver_ride_scam.rideID
AND ride.status = 'Cancel'
AND (ride.status LIKE 'Cancel%' OR ride.status LIKE 'cancel%' OR ride.status = 'cancelled_no_driver_found')
WHERE
driver_ride_scam.driverID = :driverID
AND driver_ride_scam.dateCreated >= CURDATE()
AND driver_ride_scam.dateCreated < DATE_ADD(CURDATE(), INTERVAL 1 DAY)
GROUP BY
DATE(driver_ride_scam.dateCreated)
ORDER BY
date DESC";
DATE(driver_ride_scam.dateCreated)";
try {
$stmt = $con->prepare($sql);
@@ -33,6 +55,15 @@ try {
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 3. Cache the results in Redis (TTL of 60 seconds)
if (isset($redis) && $redis !== null) {
try {
$redis->set($redisKey, json_encode($rows), 60);
} catch (Exception $e) {
error_log("[get.php] Redis write failed: " . $e->getMessage());
}
}
if (!empty($rows)) {
// --- FIX IS HERE ---
// Your Flutter app looks for d['message'].

View File

@@ -0,0 +1,72 @@
<?php
require_once __DIR__ . '/../connect.php';
// If Main Redis is not available, return empty array
if (!isset($redis) || $redis === null) {
echo json_encode([]);
exit();
}
$grid_size = 0.0135;
$keys = [];
try {
// Prefix 'siro:' is automatically applied by $redis
$keys = $redis->keys("demand:grid:*");
} catch (Exception $e) {
error_log("[heatmap_live.php] Redis keys error: " . $e->getMessage());
echo json_encode([]);
exit();
}
$heatmap_data = [];
foreach ($keys as $key) {
// The keys returned by $redis->keys() will actually contain the 'siro:' prefix
// e.g. siro:demand:grid:33.5135_36.2735
$parts = explode(":", $key);
$coords = explode("_", end($parts));
if (count($coords) == 2) {
$lat = (float)$coords[0];
$lng = (float)$coords[1];
// We must strip 'siro:' to use $redis->get() because $redis auto-prefixes everything!
// Actually, $redis->keys() returns the physical key "siro:demand:grid:X"
// But $redis->get("demand:grid:X") automatically prepends "siro:".
// So we must strip the "siro:" part before passing to get()
$clean_key = str_replace("siro:", "", $key);
$count = (int)$redis->get($clean_key);
// Fetch active drivers using Location Redis
$available_drivers = 0;
try {
global $redisLocation;
if (isset($redisLocation) && $redisLocation !== null) {
$drivers = $redisLocation->georadius('geo:drivers:available', $lng, $lat, 0.75, 'km');
$availableDrivers = count($drivers);
}
} catch (Exception $e) {}
$intensity = 'low';
$surge_ratio = ($available_drivers > 0) ? ($count / $available_drivers) : $count;
if ($surge_ratio > 2.0 || $count >= 5) {
$intensity = 'high';
} else if ($surge_ratio > 1.2 || $count >= 3) {
$intensity = 'medium';
}
$heatmap_data[] = [
"lat" => $lat,
"lng" => $lng,
"count" => $count,
"intensity" => $intensity
];
}
}
// Output the JSON array as expected by home_captain_controller.dart
header('Content-Type: application/json');
echo json_encode($heatmap_data);
?>

View File

@@ -0,0 +1,43 @@
<?php
require_once __DIR__ . '/../connect.php';
$lat = filterRequest("lat");
$lng = filterRequest("lng");
if (!$lat || !$lng) {
jsonError("Missing coordinates");
exit();
}
if (!isset($redis) || $redis === null) {
// If Redis is not available, we fail gracefully.
jsonSuccess(null, "Demand logged (fallback)");
exit();
}
// Create a 1.5 km grid cell
// 1 degree latitude is approximately 111 km.
// 1.5 km / 111 km ≈ 0.0135 degrees.
$grid_size = 0.0135;
$grid_lat = round((float)$lat / $grid_size) * $grid_size;
$grid_lng = round((float)$lng / $grid_size) * $grid_size;
$grid_id = $grid_lat . "_" . $grid_lng;
$redisKey = "demand:grid:" . $grid_id;
try {
// Increment the demand count for this grid
$currentCount = $redis->incr($redisKey);
// If this is the first request, set the expiry to 60 seconds
if ($currentCount == 1) {
$redis->expire($redisKey, 60);
}
jsonSuccess(["grid_id" => $grid_id, "count" => $currentCount], "Demand logged successfully");
} catch (Exception $e) {
error_log("[log_demand.php] Redis error: " . $e->getMessage());
jsonError("Error logging demand");
}
?>

View File

@@ -1,6 +1,7 @@
<?php
require_once __DIR__ . '/../../connect.php'; // يفترض أن هذا الملف ينشئ $con و $con_tracking
//getSpeed.php
require_once __DIR__ . '/../../connect.php'; // Provides $con, $redisLocation, $encryptionHelper, jsonSuccess/jsonError
// getSpeed.php (Redis-Optimized Version)
try {
// 1) قراءة والتحقق من الإحداثيات
$southwestLat = filterRequest("southwestLat");
@@ -13,89 +14,107 @@ try {
exit;
}
$freshSeconds = 180; // 3 دقائق
// =================================================================
// الخطوة 1: جلب المواقع والمعرفات من قاعدة بيانات التتبع
// الخطوة 1: البحث في Redis باستخدام تقنية GeoRadius (أسرع 100 مرة من MySQL)
// =================================================================
$boundingBoxWKT = sprintf(
'POLYGON((%f %f, %f %f, %f %f, %f %f, %f %f))',
$southwestLon, $southwestLat,
$northeastLon, $southwestLat,
$northeastLon, $northeastLat,
$southwestLon, $northeastLat,
$southwestLon, $southwestLat
);
$centerLat = ($southwestLat + $northeastLat) / 2.0;
$centerLon = ($southwestLon + $northeastLon) / 2.0;
// نجلب مجموعة من المرشحين المحتملين للفلترة والترتيب لاحقاً
$sql_locations = "
SELECT driver_id, latitude, longitude, heading, speed, status, updated_at
FROM car_locations
WHERE
ST_CONTAINS(ST_GeomFromText(:boundingBox, 4326), location_point)
AND status = 'off'
AND updated_at >= NOW() - INTERVAL :freshSeconds SECOND
ORDER BY updated_at DESC
LIMIT 100; -- نجلب 100 مرشح محتمل
";
// حساب تقريبي لنصف القطر بالكيلومترات بناءً على الصندوق (Bounding Box)
$earth_radius = 6371;
$dLat = deg2rad($southwestLat - $centerLat);
$dLon = deg2rad($southwestLon - $centerLon);
$a = sin($dLat/2) * sin($dLat/2) + cos(deg2rad($centerLat)) * cos(deg2rad($southwestLat)) * sin($dLon/2) * sin($dLon/2);
$c = 2 * asin(sqrt($a));
$radiusKm = max(1, ($earth_radius * $c) + 1);
$stmt_locations = $con_tracking->prepare($sql_locations);
$stmt_locations->bindValue(':boundingBox', $boundingBoxWKT);
$stmt_locations->bindValue(':freshSeconds', $freshSeconds, PDO::PARAM_INT);
$stmt_locations->execute();
$locations = $stmt_locations->fetchAll(PDO::FETCH_ASSOC);
// سحب معرفات السائقين المتاحين حول الراكب من سيرفر المواقع (Dual Redis)
$driver_ids = [];
if (isset($redisLocation)) {
$redisResults = $redisLocation->geoRadius('geo:drivers:available', $centerLon, $centerLat, $radiusKm, 'km');
if ($redisResults) {
foreach ($redisResults as $res) {
// قد يرجع Redis مصفوفة داخلية إذا تم تمرير خيارات، ولكن بالوضع الافتراضي يرجع سلاسل نصية
$driver_ids[] = is_array($res) ? $res[0] : $res;
}
}
}
if (!$locations) {
if (empty($driver_ids)) {
jsonError("No car locations found in the specified area.");
exit;
}
// =================================================================
// الخطوة 2: تجميع معرفات السائقين (driver_id)
// الخطوة 2: جلب تفاصيل الموقع الدقيقة والسرعة لكل سائق من Redis Pipeline
// =================================================================
$driver_ids = array_column($locations, 'driver_id');
$pipe = $redisLocation->pipeline();
foreach ($driver_ids as $id) {
$pipe->hGetAll("driver:profile:$id");
}
$profiles = $pipe->exec();
// =================================================================
// الخطوة 3: جلب البيانات الثابتة من القاعدة الأساسية وتطبيق الفلاتر الإضافية
// =================================================================
$drivers_info = [];
if (!empty($driver_ids)) {
$placeholders = implode(',', array_fill(0, count($driver_ids), '?'));
$locations = [];
foreach ($driver_ids as $index => $id) {
$profile = $profiles[$index];
if (!$profile || empty($profile['lat'])) continue;
// هنا نطبق الشروط الخاصة بهذا السكريبت (موديل السيارة > 2000)
$sql_drivers_info = "
SELECT
d.id AS driver_id, d.phone, d.email, d.birthdate, d.first_name, d.last_name, d.gender, d.maritalStatus,
cr.make, cr.model, cr.color, cr.color_hex, cr.year,
dt.token,
COALESCE(rdAvg.ratingDriver, 0) AS ratingDriver,
COALESCE(rdAvg.ratingCount, 0) AS ratingCount
FROM driver d
LEFT JOIN CarRegistration cr ON cr.driverID = d.id
LEFT JOIN driverToken dt ON dt.captain_id = d.id
LEFT JOIN (
SELECT driver_id, AVG(rating) AS ratingDriver, COUNT(id) AS ratingCount
FROM ratingDriver
GROUP BY driver_id
) rdAvg ON rdAvg.driver_id = d.id
WHERE d.id IN ($placeholders)
-- AND COALESCE(cr.year, 0) > 2000 -- ⭐ الشرط الخاص بهذا السكريبت
-- AND (cr.make NOT LIKE '%دراج%' AND cr.model NOT LIKE '%دراج%')
AND (cr.model NOT LIKE '%Van%' AND cr.make NOT LIKE '%Van%')
";
// تجاهل المواقع القديمة (أكثر من 3 دقائق)
$updatedAt = $profile['updated_at'] ?? 0;
if (time() - $updatedAt > 180) continue;
$stmt_drivers_info = $con->prepare($sql_drivers_info);
$stmt_drivers_info->execute($driver_ids);
$drivers_info_raw = $stmt_drivers_info->fetchAll(PDO::FETCH_ASSOC);
$locations[] = [
'driver_id' => $id,
'latitude' => $profile['lat'],
'longitude' => $profile['lng'],
'heading' => $profile['heading'] ?? 0,
'speed' => $profile['speed'] ?? 0,
'status' => 'off', // متواجدون في geo:drivers:available
'updated_at' => date('Y-m-d H:i:s', $updatedAt)
];
}
// تحويل المصفوفة لتسهيل عملية الدمج لاحقاً
foreach ($drivers_info_raw as $driver) {
$drivers_info[$driver['driver_id']] = $driver;
}
if (empty($locations)) {
jsonError("No fresh car locations found in the specified area.");
exit;
}
// =================================================================
// الخطوة 4: دمج النتائج في PHP
// الخطوة 3: جلب البيانات الثابتة (السيارة، الموديل، التقييم) من MySQL
// =================================================================
$drivers_info = [];
$valid_driver_ids = array_column($locations, 'driver_id');
$placeholders = implode(',', array_fill(0, count($valid_driver_ids), '?'));
$sql_drivers_info = "
SELECT
d.id AS driver_id, d.phone, d.email, d.birthdate, d.first_name, d.last_name, d.gender, d.maritalStatus,
cr.make, cr.model, cr.color, cr.color_hex, cr.year,
dt.token,
COALESCE(rdAvg.ratingDriver, 0) AS ratingDriver,
COALESCE(rdAvg.ratingCount, 0) AS ratingCount
FROM driver d
LEFT JOIN CarRegistration cr ON cr.driverID = d.id
LEFT JOIN driverToken dt ON dt.captain_id = d.id
LEFT JOIN (
SELECT driver_id, AVG(rating) AS ratingDriver, COUNT(id) AS ratingCount
FROM ratingDriver
GROUP BY driver_id
) rdAvg ON rdAvg.driver_id = d.id
WHERE d.id IN ($placeholders)
AND (cr.model NOT LIKE '%Van%' AND cr.make NOT LIKE '%Van%')
";
$stmt_drivers_info = $con->prepare($sql_drivers_info);
$stmt_drivers_info->execute($valid_driver_ids);
$drivers_info_raw = $stmt_drivers_info->fetchAll(PDO::FETCH_ASSOC);
foreach ($drivers_info_raw as $driver) {
$drivers_info[$driver['driver_id']] = $driver;
}
// =================================================================
// الخطوة 4: دمج النتائج والترتيب
// =================================================================
$final_results = [];
foreach ($locations as $location) {
@@ -105,9 +124,6 @@ try {
}
}
// =================================================================
// الخطوة 5: تطبيق الترتيب والحد النهائي في PHP
// =================================================================
usort($final_results, function ($a, $b) {
if ($a['ratingDriver'] != $b['ratingDriver']) {
return $b['ratingDriver'] <=> $a['ratingDriver'];
@@ -121,14 +137,14 @@ try {
$limited_results = array_slice($final_results, 0, 10);
if (empty($limited_results)) {
jsonError("No cars matching the specific criteria (year > 2000) found.");
jsonError("No cars matching the specific criteria found.");
exit;
}
// =================================================================
// الخطوة 6: فك التشفير وحساب العمر (بدون تغيير)
// الخطوة 5: فك التشفير وحساب العمر
// =================================================================
$fieldsToDecrypt = [ 'phone','email','gender','birthdate', 'first_name','last_name', 'token','car_plate','vin' ];
$fieldsToDecrypt = ['phone','email','gender','birthdate', 'first_name','last_name', 'token','car_plate','vin'];
foreach ($limited_results as &$row) {
foreach ($fieldsToDecrypt as $field) {
if (isset($row[$field]) && !empty($row[$field])) {
@@ -151,7 +167,7 @@ try {
jsonSuccess($limited_results);
} catch (PDOException $e) {
error_log("[getSpeed.php] " . $e->getMessage());
error_log("[getSpeed.php PDO] " . $e->getMessage());
jsonError("An internal error occurred. Please try again later.");
} catch (Throwable $e) {
error_log("[getSpeed.php] " . $e->getMessage());

View File

@@ -109,6 +109,38 @@ function getPerKmRate($carType, $kazanRow) {
}
function calculateDynamicPrice($country, $minFare, $distance, $duration, $kazanRow, $startNameAddress, $endNameAddress, $destLat, $destLng, $passengerLat, $passengerLng, $carType = 'Speed') {
global $redis, $redisLocation;
$surgeMultiplier = 1.0;
if (isset($redis) && $redis !== null) {
try {
$grid_size = 0.0135;
$grid_lat = round((float)$passengerLat / $grid_size) * $grid_size;
$grid_lng = round((float)$passengerLng / $grid_size) * $grid_size;
$grid_id = $grid_lat . "_" . $grid_lng;
// Demand is handled by Main Redis (prefix automatically applied)
$demandCount = (int)$redis->get("demand:grid:" . $grid_id);
$availableDrivers = 0;
// Driver locations are handled by Location Redis (no prefix)
try {
if (isset($redisLocation) && $redisLocation !== null) {
$drivers = $redisLocation->georadius('geo:drivers:available', $grid_lng, $grid_lat, 0.75, 'km');
$availableDrivers = count($drivers);
}
} catch (Exception $e) {}
if ($demandCount > 0) {
$surgeRatio = ($availableDrivers > 0) ? ($demandCount / $availableDrivers) : $demandCount;
if ($surgeRatio > 1.2) {
$surgeMultiplier = 1.0 + ($surgeRatio - 1.2) * 0.5;
$surgeMultiplier = min(3.0, $surgeMultiplier); // Cap at 3.0
}
}
} catch (Exception $e) {}
}
$naturePrice = (float) ($kazanRow['naturePrice'] ?? 0);
$heavyPrice = (float) ($kazanRow['heavyPrice'] ?? 0);
$latePrice = (float) ($kazanRow['latePrice'] ?? 0);
@@ -200,6 +232,9 @@ function calculateDynamicPrice($country, $minFare, $distance, $duration, $kazanR
$fare = $billableDistance * $perKmSpeed;
$fare += $billableMinutes * $effectivePerMin;
// Apply Redis Geohash Surge Multiplier
$fare *= $surgeMultiplier;
if ($airportCtx) $fare += $airportAddon;
if ($damascusAirportBoundCtx || $isInDamascusAirportBoundCtx) {
$fare += $damascusAirportBoundAddon;
@@ -244,13 +279,27 @@ if (!empty($promo_code)) {
$negativeBalance = 0;
if (!empty($passenger_id)) {
try {
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$redisKey = "passenger_debt_" . $passenger_id;
$redisDebt = $redis->get($redisKey);
if ($redisDebt !== false) {
$negativeBalance = (float) $redisDebt;
$redisInstance = null;
if (isset($redis) && $redis !== null) {
$redisInstance = $redis;
} else if (extension_loaded('redis')) {
$localRedis = new Redis();
$redisHost = getenv('REDIS_MAIN_HOST') ?: getenv('REDIS_HOST') ?: '127.0.0.1';
$redisPort = (int)(getenv('REDIS_MAIN_PORT') ?: getenv('REDIS_PORT') ?: 6379);
$redisPass = getenv('REDIS_MAIN_PASSWORD') ?: getenv('REDIS_MAIN_AUTH') ?: getenv('REDIS_PASSWORD') ?: getenv('REDIS_AUTH');
if ($localRedis->connect($redisHost, $redisPort, 1.5)) {
if ($redisPass) $localRedis->auth($redisPass);
$localRedis->setOption(Redis::OPT_PREFIX, 'siro:');
$redisInstance = $localRedis;
}
}
if ($redisInstance !== null) {
$redisKey = "passenger_debt_" . $passenger_id;
$redisDebt = $redisInstance->get($redisKey);
if ($redisDebt !== false) {
$negativeBalance = (float) $redisDebt;
}
}
} catch (Exception $e) {
$negativeBalance = 0;

View File

@@ -21,12 +21,22 @@ $rideId = filterRequest("id");
$driverId = $user_id;
$status = filterRequest("status"); // القيمة التي يرسلها التطبيق: 'accepted'
$passengerToken = filterRequest("passengerToken");
$passengerFingerprint = filterRequest("passengerFingerprint");
$passengerIdValue = filterRequest("passenger_id");
if (empty($rideId) || empty($driverId)) {
printFailure("Missing required parameters");
exit;
}
// Self-ride validation
$driverFingerprint = isset($_SERVER['HTTP_X_DEVICE_FP']) ? $_SERVER['HTTP_X_DEVICE_FP'] : '';
if (!empty($driverFingerprint) && $driverFingerprint === $passengerFingerprint) {
error_log("[accept_ride] Self-ride attempt blocked. DriverID=$driverId, Fingerprint=$driverFingerprint");
printFailure("Self-matching is not allowed");
exit;
}
// status whitelist — لا نقبل قيمة عشوائية من التطبيق
$allowedStatuses = ['accepted', 'Apply'];
if (!in_array($status, $allowedStatuses, true)) {
@@ -158,9 +168,11 @@ try {
// ═══════════════════════════════════════════════════════════
// STEP E — جلب passenger_id وإرسال الإشعارات
// ═══════════════════════════════════════════════════════════
$passengerId = $con->prepare("SELECT passenger_id FROM ride WHERE id = ? LIMIT 1");
$passengerId->execute([$rideId]);
$passengerIdValue = $passengerId->fetchColumn();
if (empty($passengerIdValue)) {
$passengerId = $con->prepare("SELECT passenger_id FROM ride WHERE id = ? LIMIT 1");
$passengerId->execute([$rideId]);
$passengerIdValue = $passengerId->fetchColumn();
}
if ($passengerIdValue) {
// Socket — real-time update على خريطة الراكب

View File

@@ -242,6 +242,7 @@ try {
// STEP C — بناء الـ payload وإرسال الرحلة للسائقين
// ═══════════════════════════════════════════════════════════
$kazan = (float) $price - (float) $price_for_driver;
$passengerFp = isset($_SERVER['HTTP_X_DEVICE_FP']) ? $_SERVER['HTTP_X_DEVICE_FP'] : '';
$payload = [
(string) $startLat,
(string) $startLng,
@@ -249,7 +250,7 @@ try {
(string) $endLat,
(string) $endLng,
(string) $distance_text,
"",
(string) $passengerFp,
(string) $passenger_id,
(string) $passenger_name,
(string) $passenger_token,

View File

@@ -140,18 +140,30 @@ try {
// تخزين الدين في الـ Redis لمدة 6 شهور (15552000 ثانية)
try {
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$redisPass = getenv('REDIS_PASSWORD');
if ($redisPass) $redis->auth($redisPass);
$redis->setOption(Redis::OPT_PREFIX, 'siro:');
$redisKey = "passenger_debt_" . $passenger_id;
// إضافة الدين الجديد إلى الدين السابق إن وجد
$currentDebt = (float) $redis->get($redisKey);
$newDebt = $currentDebt + $negativeDebt;
$redis->setex($redisKey, 15552000, $newDebt);
$redisInstance = null;
if (isset($redis) && $redis !== null) {
$redisInstance = $redis;
} else if (extension_loaded('redis')) {
$localRedis = new Redis();
$redisHost = getenv('REDIS_MAIN_HOST') ?: getenv('REDIS_HOST') ?: '127.0.0.1';
$redisPort = (int)(getenv('REDIS_MAIN_PORT') ?: getenv('REDIS_PORT') ?: 6379);
$redisPass = getenv('REDIS_MAIN_PASSWORD') ?: getenv('REDIS_MAIN_AUTH') ?: getenv('REDIS_PASSWORD') ?: getenv('REDIS_AUTH');
if ($localRedis->connect($redisHost, $redisPort, 1.5)) {
if ($redisPass) $localRedis->auth($redisPass);
$localRedis->setOption(Redis::OPT_PREFIX, 'siro:');
$redisInstance = $localRedis;
}
}
if ($redisInstance !== null) {
$redisKey = "passenger_debt_" . $passenger_id;
// إضافة الدين الجديد إلى الدين السابق إن وجد
$currentDebt = (float) $redisInstance->get($redisKey);
$newDebt = $currentDebt + $negativeDebt;
$redisInstance->setex($redisKey, 15552000, $newDebt);
}
} catch (Exception $e) {
error_log("Redis Error: " . $e->getMessage());
error_log("Redis Error in cancel_ride_by_driver: " . $e->getMessage());
}
}
}

View File

@@ -173,17 +173,16 @@ try {
throw new Exception("Ride already finished or not found in local DB.");
}
// 4b. Update driver_orders
$checkStmt = $con->prepare("SELECT order_id FROM driver_orders WHERE order_id = ?");
$checkStmt->execute([$rideId]);
if ($checkStmt->rowCount() > 0) {
$con->prepare("UPDATE driver_orders SET driver_id = ?, status = ?, created_at = NOW() WHERE order_id = ?")
->execute([$driver_id, $newStatus, $rideId]);
} else {
$con->prepare("INSERT INTO driver_orders (driver_id, order_id, created_at, status) VALUES (?, ?, NOW(), ?)")
->execute([$driver_id, $rideId, $newStatus]);
}
// 4b. Update driver_orders (Optimized atomic query)
$stmtOrders = $con->prepare("
INSERT INTO `driver_orders` (`driver_id`, `order_id`, `status`, `created_at`)
VALUES (?, ?, ?, NOW())
ON DUPLICATE KEY UPDATE
`driver_id` = VALUES(`driver_id`),
`status` = VALUES(`status`),
`created_at` = NOW()
");
$stmtOrders->execute([$driver_id, $rideId, $newStatus]);
// ============================================================
// 4c. Server-to-Server Payment Processing (S2S)

View File

@@ -48,6 +48,7 @@ try {
// 3. حساب العمولة (Kazan)
$kazan = (double)$price - (double)$priceForDriver;
$passengerFp = isset($_SERVER['HTTP_X_DEVICE_FP']) ? $_SERVER['HTTP_X_DEVICE_FP'] : '';
// 4. بناء Payload مطابق لـ add_ride.php (0 - 33)
$payloadTemplate = [];
$payloadTemplate[0] = (string)$startLat;
@@ -56,7 +57,7 @@ try {
$payloadTemplate[3] = (string)$endLat;
$payloadTemplate[4] = (string)$endLng;
$payloadTemplate[5] = (string)$distanceText;
$payloadTemplate[6] = ""; // Driver ID placeholder
$payloadTemplate[6] = (string)$passengerFp;
$payloadTemplate[7] = (string)$passengerId;
$payloadTemplate[8] = (string)$passengerName;
$payloadTemplate[9] = (string)$passengerToken;