diff --git a/backend/bot/cron_empty_results_to_db.php b/backend/bot/cron_empty_results_to_db.php index a4764554..a9f47a60 100644 --- a/backend/bot/cron_empty_results_to_db.php +++ b/backend/bot/cron_empty_results_to_db.php @@ -85,9 +85,6 @@ foreach ($data as $row) { } $distanceKm = (float)($resultData['distance_km'] ?? 1); - if ($distanceKm <= 0) $distanceKm = 1; // Prevent division by zero - $pricePerKm = $amount / $distanceKm; - $durationMin = isset($resultData['duration_min']) ? (int)$resultData['duration_min'] : null; $startLat = $resultData['start_lat'] ?? null; @@ -95,6 +92,20 @@ foreach ($data as $row) { $endLat = $resultData['end_lat'] ?? null; $endLng = $resultData['end_lng'] ?? null; + // --- OSRM Integration (Dynamic Distance & Duration) --- + require_once __DIR__ . '/../core/osrm_routing.php'; + if ($startLat && $endLat && (!$durationMin || $durationMin <= 0)) { + $osrmResult = getOsrmRouteDetails($startLat, $startLng, $endLat, $endLng); + if ($osrmResult) { + $distanceKm = $osrmResult['distance_km']; // Override with accurate OSRM distance + $durationMin = $osrmResult['duration_min']; + } + } + // ------------------------------------------------------ + + if ($distanceKm <= 0) $distanceKm = 1; // Prevent division by zero + $pricePerKm = $amount / $distanceKm; + $countryCode = $row['country_code'] ?? 'JO'; // Default if ($stmt->execute([ diff --git a/backend/bot/worker.php b/backend/bot/worker.php index 913a5bac..4984cb73 100644 --- a/backend/bot/worker.php +++ b/backend/bot/worker.php @@ -131,15 +131,28 @@ if ($method === 'GET') { $pricePerKm = $distance_km > 0 ? ($price / $distance_km) : 0.0; $country_code = $result_data['country_code'] ?? 'SY'; + // --- OSRM Integration (Dynamic Distance & Duration) --- + require_once __DIR__ . '/../core/osrm_routing.php'; + $duration_min = 0; + if ($start_lat != 0 && $end_lat != 0) { + $osrmResult = getOsrmRouteDetails($start_lat, $start_lng, $end_lat, $end_lng); + if ($osrmResult) { + $distance_km = $osrmResult['distance_km']; // Override with accurate OSRM distance + $duration_min = $osrmResult['duration_min']; + $pricePerKm = $distance_km > 0 ? ($price / $distance_km) : 0.0; // Recalculate based on OSRM + } + } + // ------------------------------------------------------ + // 1. Save to MySQL $stmt = $con->prepare(" INSERT INTO scraped_competitor_prices - (task_id, app_name, competitor_name, start_location, end_location, start_lat, start_lng, end_lat, end_lng, price_amount, price_per_km, currency, country_code) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + (task_id, app_name, competitor_name, start_location, end_location, start_lat, start_lng, end_lat, end_lng, price_amount, price_per_km, distance_km, duration_min, currency, country_code) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) "); $start_loc = "Lat: $start_lat, Lng: $start_lng"; $end_loc = "Lat: $end_lat, Lng: $end_lng"; - $stmt->execute([$task_id, $app_name, $app_name, $start_loc, $end_loc, (string)$start_lat, (string)$start_lng, (string)$end_lat, (string)$end_lng, $price, $pricePerKm, 'JOD', $country_code]); + $stmt->execute([$task_id, $app_name, $app_name, $start_loc, $end_loc, (string)$start_lat, (string)$start_lng, (string)$end_lat, (string)$end_lng, $price, $pricePerKm, $distance_km, $duration_min, 'JOD', $country_code]); // 2. Save to Redis (Calculate Price Per KM) if ($distance_km > 0 && $price > 0) { diff --git a/backend/core/osrm_routing.php b/backend/core/osrm_routing.php new file mode 100644 index 00000000..9d380510 --- /dev/null +++ b/backend/core/osrm_routing.php @@ -0,0 +1,54 @@ + float, 'duration_min' => float] or null on failure. + */ +function getOsrmRouteDetails($startLat, $startLng, $endLat, $endLng) { + // Format: longitude,latitude + $coordinates = "{$startLng},{$startLat};{$endLng},{$endLat}"; + + // Using public demo server. Note: Has rate limits. + // For production, highly recommended to use: "http://your-own-osrm-server.com:5000" + $osrmBaseUrl = "http://router.project-osrm.org"; + $url = "{$osrmBaseUrl}/route/v1/driving/{$coordinates}?overview=false"; + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_TIMEOUT, 5); // 5 seconds timeout + + // Add a User-Agent to avoid being blocked by the public demo server + curl_setopt($ch, CURLOPT_USERAGENT, "SiroRideHailingApp/1.0"); + + $response = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + + if ($httpCode === 200 && $response) { + $data = json_decode($response, true); + if (isset($data['code']) && $data['code'] === 'Ok' && isset($data['routes'][0])) { + $route = $data['routes'][0]; + $distanceMeters = isset($route['distance']) ? (float)$route['distance'] : 0; + $durationSeconds = isset($route['duration']) ? (float)$route['duration'] : 0; + + return [ + 'distance_km' => round($distanceMeters / 1000, 2), + 'duration_min' => round($durationSeconds / 60, 2) + ]; + } + } + + return null; +} diff --git a/backend/ride/pricing/get.php b/backend/ride/pricing/get.php index 3fe659da..25aaf97b 100644 --- a/backend/ride/pricing/get.php +++ b/backend/ride/pricing/get.php @@ -184,19 +184,12 @@ function calculateDynamicPrice($country, $minFare, $distance, $duration, $kazanR break; } - $longSpeedThresholdKm = 40.0; - $longSpeedPerKm = 26.0; - $mediumDistThresholdKm = 25.0; $longDistThresholdKm = 35.0; - $longTripPerMin = 6.0; $minuteCapMedium = 60; $minuteCapLong = 80; $freeMinutesLong = 10; - $extraReduction100 = 0.07; - $maxReductionCap = 0.35; - $totalMinutes = floor($duration / 60); $airportCtx = (stripos($startNameAddress, 'airport') !== false || stripos($startNameAddress, 'مطار') !== false || stripos($endNameAddress, 'airport') !== false || stripos($endNameAddress, 'مطار') !== false); @@ -210,23 +203,15 @@ function calculateDynamicPrice($country, $minFare, $distance, $duration, $kazanR $isInDamascusAirportBoundCtx = ($passengerLat <= $northLat && $passengerLat >= $southLat && $passengerLng <= $eastLng && $passengerLng >= $westLng); $billableDistance = ($distance < $minBillableKm) ? $minBillableKm : $distance; - $isLongSpeed = $billableDistance > $longSpeedThresholdKm; $perKmSpeedBaseFromServer = getPerKmRate($carType, $kazanRow); - $perKmSpeed = $isLongSpeed ? $longSpeedPerKm : $perKmSpeedBaseFromServer; - - $reductionPct40 = 0.0; - if ($perKmSpeedBaseFromServer > 0) { - $r = 1.0 - ($longSpeedPerKm / $perKmSpeedBaseFromServer); - $reductionPct40 = max(0.0, min($maxReductionCap, $r)); - } - $reductionPct100 = max(0.0, min($maxReductionCap, $reductionPct40 + $extraReduction100)); - - $distanceReduction = 0.0; - if ($billableDistance > 100.0) { - $distanceReduction = $reductionPct100; - } else if ($billableDistance > 40.0) { - $distanceReduction = $reductionPct40; + + // Apply dynamic percentage discount instead of hardcoded currencies for long trips + $perKmSpeed = $perKmSpeedBaseFromServer; + if ($billableDistance > $longDistThresholdKm) { + $perKmSpeed = $perKmSpeedBaseFromServer * 0.85; // 15% discount per km + } else if ($billableDistance > $mediumDistThresholdKm) { + $perKmSpeed = $perKmSpeedBaseFromServer * 0.90; // 10% discount per km } date_default_timezone_set('Asia/Damascus'); @@ -242,11 +227,10 @@ function calculateDynamicPrice($country, $minFare, $distance, $duration, $kazanR $billableMinutes = $totalMinutes; if ($billableDistance > $longDistThresholdKm) { - $effectivePerMin = $longTripPerMin; + // Prevent absurd time billing for very long trips $capped = ($billableMinutes > $minuteCapLong) ? $minuteCapLong : $billableMinutes; $billableMinutes = max(0, $capped - $freeMinutesLong); } else if ($billableDistance > $mediumDistThresholdKm) { - $effectivePerMin = $longTripPerMin; $billableMinutes = ($billableMinutes > $minuteCapMedium) ? $minuteCapMedium : $billableMinutes; }