getMessage()); } $ride_id = filterRequest("id"); $driver_id = filterRequest("driver_id"); $status = filterRequest("status"); // 'Begin' $passengerToken = filterRequest("passengerToken"); if (!$ride_id || !$driver_id || !$status) { jsonError("Missing parameters"); exit; } try { // 1. تحديث سيرفر الرحلات (Remote DB - con_ride) $stmtRemote = $con_ride->prepare("UPDATE ride SET status = ?, rideTimeStart = NOW() WHERE id = ?"); $stmtRemote->execute([$status, $ride_id]); if ($stmtRemote->rowCount() == 0) { // ملاحظة: أحياناً التحديث لا يؤثر بصفوف إذا كانت البيانات نفسها، // لكن هنا نفترض الفشل إذا لم يجد الرحلة. // يمكنك إكمال التنفيذ إذا كنت متأكداً أن الرحلة موجودة. } // 2. تحديث السيرفر المحلي (Local DB) والمعاملات $con->beginTransaction(); // تحديث الرحلة محلياً $stmtMainRide = $con->prepare("UPDATE ride SET status = ?, rideTimeStart = NOW() WHERE id = ?"); $stmtMainRide->execute([$status, $ride_id]); // 🆕 Immutable Fare Lock: Save agreed fixed price in Redis try { $stmtRideInfo = $con->prepare("SELECT price, car_type FROM ride WHERE id = ? LIMIT 1"); $stmtRideInfo->execute([$ride_id]); $rideInfo = $stmtRideInfo->fetch(PDO::FETCH_ASSOC); if ($rideInfo) { $agreedPrice = floatval($rideInfo['price']); $carTypeStr = $rideInfo['car_type'] ?? 'Fixed Price'; $fixedPriceTypes = ['Speed', 'Fixed Price', 'Awfar Car']; if (in_array($carTypeStr, $fixedPriceTypes)) { global $redis; if ($redis) { $redis->setex("ride_locked_price_{$ride_id}", 86400, $agreedPrice); error_log("[start_ride] Locked Fixed Price for ride {$ride_id}: {$agreedPrice}"); } else { error_log("[start_ride] Failed to lock price: Global Redis instance not available."); } } } } catch (Exception $e) { error_log("[start_ride] Redis Error (locking price): " . $e->getMessage()); } // تحديث أو إدخال في جدول Driver Orders $checkSql = "SELECT `order_id` FROM `driver_orders` WHERE `order_id` = ?"; $checkStmt = $con->prepare($checkSql); $checkStmt->execute([$ride_id]); if ($checkStmt->rowCount() > 0) { $updateSql = "UPDATE `driver_orders` SET `driver_id` = ?, `status` = ?, `created_at` = NOW() WHERE `order_id` = ?"; $con->prepare($updateSql)->execute([$driver_id, $status, $ride_id]); } else { $insertSql = "INSERT INTO `driver_orders` (`driver_id`, `order_id`, `created_at`, `status`) VALUES (?, ?, NOW(), ?)"; $con->prepare($insertSql)->execute([$driver_id, $ride_id, $status]); } // ================================================================= // 🔥 الخطوة 3: إشعار الراكب (Socket + FCM) // ================================================================= // جلب بيانات الراكب من قاعدة البيانات لضمان الدقة $stmtPas = $con_ride->prepare("SELECT passenger_id FROM ride WHERE id = ?"); $stmtPas->execute([$ride_id]); $passenger_id = $stmtPas->fetchColumn(); // 2.5 تصفير الدين من Redis عند بدء الرحلة (كما طلبت) if ($passenger_id) { try { global $redis; if ($redis) { $redisKey = "passenger_debt_" . $passenger_id; // قراءة الدين الحالي من Redis قبل الحذف (إن لزم الأمر للتسجيل مستقبلاً) $currentDebt = (float) $redis->get($redisKey); // تصفير / حذف الدين $redis->del($redisKey); // يمكنك هنا أيضاً إدراج حركة معاكسة في جدول passengerWallet إذا أردت تسوية قاعدة البيانات if ($currentDebt < 0) { $positiveOffset = abs($currentDebt); $stmtWallet = $con->prepare("INSERT INTO `passengerWallet` (passenger_id, balance) VALUES (?, ?)"); $stmtWallet->execute([$passenger_id, $positiveOffset]); } } } catch (Exception $e) { error_log("Redis Error (zeroing debt): " . $e->getMessage()); } } if ($passenger_id) { // أ) إرسال السوكيت (Socket) // تم إلغاء التعليق عنه ليكون السيرفر هو المسؤول $socketPayload = [ 'ride_id' => $ride_id, 'status' => 'started', // أو 'Begin' حسب ما يتوقعه التطبيق 'msg' => 'بدأت الرحلة، نتمنى لك سلامة الوصول 🚀' ]; if (function_exists('notifyPassengerOnRideServer')) { notifyPassengerOnRideServer($passenger_id, $socketPayload); } // ب) إرسال FCM (Internal) if (!empty($passengerToken)) { $fcmData = [ 'category' => 'Trip is Begin', 'ride_id' => (string)$ride_id, 'status' => 'started' ]; // 🔥 استخدام sendFCM_Internal كرسالة صامتة sendFCM_Internal( $passengerToken, // الهدف "", // تفريغ العنوان "", // تفريغ النص $fcmData, // البيانات "Trip is Begin", // التصنيف false // ليس Topic ); } } $con->commit(); // 🆕 Cache ride state in Redis — use $status (the exact value just // written to MySQL above), not a hardcoded label, so a cache hit can // never disagree with the row it was seeded from. sendToLocationServer('update_ride_state', [ 'ride_id' => $ride_id, 'status' => $status, 'driver_id' => $driver_id, 'passenger_id' => $passenger_id ?? '', ]); jsonSuccess(null, "Ride started successfully"); } catch (PDOException $e) { if ($con->inTransaction()) { $con->rollBack(); } jsonError("An internal error occurred. Please try again later."); } ?>