From f66db7db42dfb82d3c40cae8f297ccbb393f349d Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Sun, 26 Jul 2026 16:18:02 +0300 Subject: [PATCH] =?UTF-8?q?=D8=A5=D8=B6=D8=A7=D9=81=D8=A9=20getRideOrderID?= =?UTF-8?q?.php=20=D8=A7=D9=84=D9=85=D9=81=D9=82=D9=88=D8=AF=20=E2=80=94?= =?UTF-8?q?=20=D8=A7=D9=84=D9=85=D8=B3=D8=A7=D8=B1=20=D8=A7=D9=84=D8=A7?= =?UTF-8?q?=D8=AD=D8=AA=D9=8A=D8=A7=D8=B7=D9=8A=20=D9=84=D8=A8=D9=8A=D8=A7?= =?UTF-8?q?=D9=86=D8=A7=D8=AA=20=D8=A7=D9=84=D8=B3=D8=A7=D8=A6=D9=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit تطبيق الراكب يطلب /ride/rides/getRideOrderID.php منذ البداية، والموجود على السيرفر getRideOrderIDNew.php فقط ⇒ 404 صامت في getUpdatedRideForDriverApply. هذا يفسّر عرضين ظنّاهما منفصلين: - الـ polling يسبق الـ FCM فيضبط _isAcceptanceProcessed=true ثم يأخذ 404، فيُرفض بعدها payload الـ FCM الذي يحمل driver_info كاملاً ("Already processed") ⇒ الرحلة مقبولة بلا معلومات سائق. - ولأن driverToken يبقى فارغاً، ترد send_fcm.php بـ 400 Missing: target ⇒ رسائل الراكب للسائق لا تُرسَل إطلاقاً. (الاتجاه المعاكس كان يعمل بعد إصلاح getRideWaiting.php — نفس خطأ التشفير معكوساً.) getRideOrderIDNew.php لا يصلح بديلاً: داخلي عبر get_connect.php → validateInternalKey فلا يستطيع التطبيق مناداته، ولا يرجع ratingCount/completedRides/driverTier. النقطة الجديدة: - connect.php (JWT)، والراكب من الـ JWT فقط لا من الطلب (حماية IDOR). - نفس استعلام acceptRide.php وشكل رده حرفياً حتى يقرأه _fillDriverDataLocally بنفس المفاتيح. - فك تشفير driverToken.token — بلا ذلك يصل التطبيق blob يستخدمه كـ FCM target فيرفضه FCM بـ 400. - تفادي تصادم المفاتيح: getUpdatedRideForDriverApply يقرأ passengerName + last_name كاسم الراكب، فلقب السائق نُقل إلى driver_last_name واسم الراكب الحقيقي يُرجَّع في مكانه. - ride DB هو المرجع مع fallback على primary، ورحلة بلا سائق ترد success لا failure. Co-Authored-By: Claude Opus 5 --- backend/ride/rides/getRideOrderID.php | 202 ++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 backend/ride/rides/getRideOrderID.php diff --git a/backend/ride/rides/getRideOrderID.php b/backend/ride/rides/getRideOrderID.php new file mode 100644 index 00000000..69c207fd --- /dev/null +++ b/backend/ride/rides/getRideOrderID.php @@ -0,0 +1,202 @@ +prepare($sqlRide); + $stmtRide->execute($params); + $rideRow = $stmtRide->fetch(PDO::FETCH_ASSOC); + } catch (Exception $eRideDb) { + error_log("[getRideOrderID] ride DB unavailable: " . $eRideDb->getMessage()); + } + + if (!$rideRow) { + $stmtRide = $con->prepare($sqlRide); + $stmtRide->execute($params); + $rideRow = $stmtRide->fetch(PDO::FETCH_ASSOC); + } + + if (!$rideRow) { + printFailure("No ride found"); + exit; + } + + $driverId = $rideRow['driver_id'] ?? ''; + if (empty($driverId)) { + // رحلة لم يقبلها سائق بعد — ليست حالة خطأ + echo json_encode([ + "status" => "success", + "message" => "No driver assigned yet", + "data" => ["ride_id" => (string)$rideRow['id'], "status" => $rideRow['status']], + ]); + exit; + } + + // ── 2. بيانات السائق — نفس استعلام acceptRide.php ────────────── + $stmtDriver = $con->prepare(" + SELECT + d.id AS driver_id, + d.first_name, + d.last_name, + d.gender, + d.phone, + c.make, + c.model, + c.car_plate, + c.year, + c.color, + c.color_hex, + (SELECT ROUND(AVG(rating), 2) FROM ratingDriver WHERE driver_id = d.id) AS ratingDriver, + (SELECT COUNT(*) FROM ratingDriver WHERE driver_id = d.id) AS ratingCount, + (SELECT COUNT(*) FROM ride WHERE driver_id = d.id AND status IN ('Finished', 'finished')) AS completedRides, + dt.token + FROM driver d + LEFT JOIN CarRegistration c ON c.driverID = d.id + LEFT JOIN driverToken dt ON dt.captain_id = d.id + WHERE d.id = ? + LIMIT 1 + "); + $stmtDriver->execute([$driverId]); + $driverRaw = $stmtDriver->fetch(PDO::FETCH_ASSOC); + + if (!$driverRaw) { + printFailure("Driver not found"); + exit; + } + + // جدول driverToken يخزّن التوكن مشفّراً — بلا فك تشفير يصل التطبيق + // blob يستخدمه كـ FCM target فيرفضه FCM بـ 400 ولا تصل رسائل الراكب. + $driverInfo = []; + $encryptedFields = ['first_name', 'last_name', 'gender', 'phone', 'car_plate', 'token']; + foreach ($driverRaw as $key => $value) { + if (in_array($key, $encryptedFields, true) && !empty($value)) { + $decrypted = false; + try { + $decrypted = $encryptionHelper->decryptData($value); + } catch (\Throwable $e) { + $decrypted = false; + } + $driverInfo[$key] = ($decrypted !== false && $decrypted !== null && $decrypted !== '') + ? $decrypted + : $value; + } else { + $driverInfo[$key] = $value; + } + } + + $driverInfo['driverName'] = trim(($driverInfo['first_name'] ?? '') . ' ' . ($driverInfo['last_name'] ?? '')); + if (empty($driverInfo['driverName']) && !empty($driverInfo['phone'])) { + $driverInfo['driverName'] = $driverInfo['phone']; + } + + $driverInfo['ratingDriver'] = !empty($driverInfo['ratingDriver']) ? (string)$driverInfo['ratingDriver'] : "5.0"; + $ratingValue = (float) $driverInfo['ratingDriver']; + $ratingCount = (int) ($driverInfo['ratingCount'] ?? 0); + $completedRides = (int) ($driverInfo['completedRides'] ?? 0); + if ($ratingValue >= 4.8 && $ratingCount >= 50 && $completedRides >= 100) { + $driverInfo['driverTier'] = 'Professional driver'; + } elseif ($ratingValue >= 4.5 && $ratingCount >= 15 && $completedRides >= 30) { + $driverInfo['driverTier'] = 'Trusted driver'; + } else { + $driverInfo['driverTier'] = 'Verified driver'; + } + + // ── 3. آخر موقع للسائق من Redis (اختياري) ────────────────────── + if (isset($redisLocation) && $redisLocation) { + try { + $driverLoc = $redisLocation->hGetAll("driver:location:$driverId"); + if (!empty($driverLoc)) { + $driverInfo['lat'] = (float)($driverLoc['lat'] ?? 0); + $driverInfo['lng'] = (float)($driverLoc['lng'] ?? 0); + $driverInfo['heading'] = (float)($driverLoc['heading'] ?? 0); + } + } catch (Exception $eLoc) { + // تجاهل — الموقع يصل بالسوكيت أصلاً + } + } + + // ⚠️ تصادم مفاتيح: getUpdatedRideForDriverApply يقرأ passengerName + last_name + // كاسم **الراكب**. لو تركنا last_name للسائق يظهر لقب السائق كاسم الراكب. + // ننقل لقب السائق إلى driver_last_name (وهو مفتاح يفهمه + // _fillDriverDataLocally أصلاً) ونرجّع اسم الراكب الحقيقي في مكانه. + $driverInfo['driver_last_name'] = $driverInfo['last_name'] ?? ''; + unset($driverInfo['last_name']); + + try { + $stmtPas = $con->prepare("SELECT first_name, last_name FROM passengers WHERE id = ? LIMIT 1"); + $stmtPas->execute([$passengerId]); + $pasRow = $stmtPas->fetch(PDO::FETCH_ASSOC); + if ($pasRow) { + foreach (['first_name' => 'passengerName', 'last_name' => 'last_name'] as $src => $dst) { + $val = $pasRow[$src] ?? ''; + if ($val === '' || $val === null) { $driverInfo[$dst] = ''; continue; } + $dec = false; + try { + $dec = $encryptionHelper->decryptData($val); + } catch (\Throwable $e) { + $dec = false; + } + $driverInfo[$dst] = ($dec !== false && $dec !== null && $dec !== '') ? $dec : $val; + } + } + } catch (PDOException $ePas) { + error_log("[getRideOrderID] passenger name lookup WARNING: " . $ePas->getMessage()); + } + + $driverInfo['ride_id'] = (string)$rideRow['id']; + $driverInfo['status'] = $rideRow['status']; + + echo json_encode([ + "status" => "success", + "message" => "Driver info", + "data" => $driverInfo, + ]); + +} catch (PDOException $e) { + error_log("[getRideOrderID] CRITICAL: " . $e->getMessage()); + printFailure("Server error"); +}