Update: 2026-08-07 15:50:04

This commit is contained in:
Hamza-Ayed
2026-08-07 15:50:04 +03:00
parent b2a95c53aa
commit 9338e76894
11 changed files with 1375 additions and 4 deletions
+36
View File
@@ -391,6 +391,42 @@ try {
'passenger_id' => $passengerIdValue ?? '',
]);
// ═══════════════════════════════════════════════════════════
// ‏الراكب الذي طلب برسالة نصية لا يملك إنترنت: السوكيت لن يصله،
// ‏والإشعار الصامت أعلاه يذهب إلى العدم. الرسالة النصية هي قناته
// ‏الوحيدة لمعرفة أن سائقاً قَبِل، ومن هو، وكيف يعرف سيارته.
// ═══════════════════════════════════════════════════════════
try {
$stmtSms = $con->prepare("
SELECT sender_norm FROM sms_ride_requests
WHERE ride_id = ? AND status = 'dispatched' LIMIT 1
");
$stmtSms->execute([$rideId]);
$smsSender = $stmtSms->fetchColumn();
if ($smsSender) {
require_once __DIR__ . '/../../sms/helpers.php';
$plate = trim((string) ($driverInfo['car_plate'] ?? ''));
$car = trim(($driverInfo['make'] ?? '') . ' ' . ($driverInfo['model'] ?? ''));
$color = trim((string) ($driverInfo['color'] ?? ''));
$lines = ['سيرو: وجدنا لك سائقاً.'];
$lines[] = 'السائق: ' . ($driverInfo['driverName'] ?? '—');
if (!empty($driverInfo['phone'])) $lines[] = 'هاتفه: ' . $driverInfo['phone'];
if ($car !== '' || $color !== '') $lines[] = 'السيارة: ' . trim("$car $color");
if ($plate !== '') $lines[] = 'اللوحة: ' . $plate;
$lines[] = 'سيتصل بك عند وصوله.';
smsEnqueue($con, (string) $smsSender, implode("\n", $lines),
'driver_found', (string) $rideId);
}
} catch (Throwable $eSms) {
// ‏لا نُسقط قبول الرحلة من أجل رسالة: السائق قَبِل فعلاً،
// ‏والرسالة الفائتة أهون من رحلة تُلغى بخطأ.
error_log('[accept_ride] تعذّر تجهيز رسالة SMS للراكب: ' . $eSms->getMessage());
}
error_log("[accept_ride] SUCCESS. RideID=$rideId accepted by DriverID=$driverId");
// ═══════════════════════════════════════════════════════════
+78 -3
View File
@@ -122,6 +122,11 @@ error_log("[add_ride] Request started. passenger_id=" . ($_POST['passenger_id']
$isScheduledRide = filterRequest("source") === 'scheduled';
$scheduledAtLabel = filterRequest("scheduled_at") ?: '';
// ‏وسم الطلب بالرسائل النصية: يمرّره sms/ride_request.php لراكب لا
// ‏يملك إنترنت. تطبيق السائق يقرأ الوسم ليعرف أن التواصل بالاتصال لا
// ‏بالمحادثة — الراكب لن يرى أي إشعار في التطبيق.
$isSmsRide = filterRequest("source") === 'sms';
$start_location = filterRequest("start_location");
$end_location = filterRequest("end_location");
$price = filterRequest("price");
@@ -145,6 +150,11 @@ $price_token = filterRequest("price_token");
// ══════════════════════════════════════════════════════════════
$isInternalScheduled = false;
// ‏يُملأ أحدهما فقط حسب المصدر. التصريح بهما هنا يمنع تحذير «متغيّر
// ‏غير معرّف» في كتلة التسعير الداخلي التي تقرأ الاثنين.
$booking = null;
$smsReq = null;
if ($isScheduledRide) {
$providedKey = $_SERVER['HTTP_X_S2S_API_KEY'] ?? '';
$expectedKey = getenv('S2S_SHARED_KEY') ?: '';
@@ -196,6 +206,62 @@ if ($isScheduledRide) {
$end_location = $booking['end_location'];
$carType = $booking['car_type'];
$distance = $booking['distance'];
} elseif ($isSmsRide) {
// ══════════════════════════════════════════════════════════
// ‏نفس البوّابة المزدوجة بالضبط، على جدول sms_ride_requests.
//
// ‏الفرق عن الحجز المسبق أن مصدر الطلب هنا رسالة نصية من رقم لا
// ‏يحمل JWT إطلاقاً — فالشرط الثاني هو كل الحماية: لا رحلة إلا
// ‏مقابل صف أنشأه الويب‌هوك من رسالة فعلية وقفله للتوّ.
// ══════════════════════════════════════════════════════════
$providedKey = $_SERVER['HTTP_X_S2S_API_KEY'] ?? '';
$expectedKey = getenv('S2S_SHARED_KEY') ?: '';
$smsRequestId = (int) (filterRequest('sms_request_id', 'int') ?: 0);
$claimedPassenger = filterRequest('passenger_id');
if (empty($expectedKey) || !hash_equals($expectedKey, (string) $providedKey)) {
error_log('[add_ride] SECURITY: مسار SMS بمفتاح S2S غير صالح');
printFailure('Unauthorized');
exit;
}
if ($smsRequestId <= 0 || empty($claimedPassenger)) {
printFailure('Missing sms_request_id or passenger_id');
exit;
}
try {
$stmtSms = $con->prepare("
SELECT passenger_id, start_location, end_location, distance, duration
FROM sms_ride_requests
WHERE id = ? AND status = 'dispatching' LIMIT 1
");
$stmtSms->execute([$smsRequestId]);
$smsReq = $stmtSms->fetch(PDO::FETCH_ASSOC);
} catch (PDOException $eS) {
error_log('[add_ride] تعذّرت قراءة طلب SMS: ' . $eS->getMessage());
printFailure('Server error');
exit;
}
if (!$smsReq) {
error_log("[add_ride] SECURITY: طلب SMS #$smsRequestId غير موجود أو ليس قيد الإطلاق");
printFailure('Invalid sms request');
exit;
}
if ((string) $smsReq['passenger_id'] !== (string) $claimedPassenger
|| !coordsMatch($smsReq['start_location'], $start_location)) {
error_log("[add_ride] SECURITY: طلب SMS #$smsRequestId لا يطابق الراكب أو النقطة");
printFailure('SMS request mismatch');
exit;
}
// ‏نثق بالصف المخزَّن لا بما وصل في الطلب.
$isInternalScheduled = true; // ‏نفس الإعفاء من رمز السعر و JWT
$passenger_id = $smsReq['passenger_id'];
$end_location = $smsReq['end_location'];
$distance = $smsReq['distance'];
} else {
// Force passenger_id from JWT — never trust user-supplied passenger_id
$passenger_id = $user_id;
@@ -260,14 +326,19 @@ if ($isInternalScheduled) {
exit;
}
// ‏المدّة من الصف المخزَّن أيّاً كان مصدره — الحجز المسبق أو طلب
// ‏الرسائل. قراءتها من `$booking` وحده كانت تمرّر صفراً لكل رحلة
// ‏قادمة من SMS، فيسقط شقّ الزمن من السعر.
$internalDuration = (int) ($booking['duration'] ?? $smsReq['duration'] ?? 0);
$price = computeInternalRidePrice(
$kazanRow, (string) $carType,
(float) $distance, (int) ($booking['duration'] ?? 0)
$kazanRow, (string) $carType, (float) $distance, $internalDuration
);
$price_for_driver = $price;
$price_for_passenger = $price;
error_log("[add_ride] حجز #$scheduledId سُعِّر داخلياً: $price");
$internalRef = $isSmsRide ? "طلب SMS #$smsRequestId" : "حجز #$scheduledId";
error_log("[add_ride] $internalRef سُعِّر داخلياً: $price");
}
// SECURE PRICE TOKEN VERIFICATION
@@ -515,6 +586,10 @@ try {
// ليست طلباً لحظياً بل موعد مضبوط، وأن الراكب ينتظره في وقت محدد.
$isScheduledRide ? '1' : '',
$isScheduledRide ? (string) $scheduledAtLabel : '',
// 🆕 Index 41: طلب وصل عبر رسالة نصية — الراكب بلا إنترنت.
// السائق يجب أن يعرف أن المحادثة داخل التطبيق لن تصل إليه،
// وأن الاتصال الهاتفي هو وسيلة التواصل الوحيدة معه.
$isSmsRide ? '1' : '',
];
// Direct dispatch للسائقين القريبين