"failure", "message" => "Country parameter is required"]); exit; } $price = 0.0; $price_for_driver = 0.0; $withCommission = 0.0; $kazan = 0.0; $isNightFare = false; // Common variables date_default_timezone_set('Asia/Damascus'); $currentTime = new DateTime(); $hour = (int)$currentTime->format('H'); switch ($country) { case 'Syria': $minFare = 150.0; break; case 'Egypt': $minFare = 20.0; break; case 'Jordan': $minFare = 1.0; break; default: $minFare = 0.0; break; } // Fetch kazan from DB for the specified country $sql = "SELECT * FROM `kazan` WHERE country = :country LIMIT 1"; $stmt = $con->prepare($sql); $stmt->execute([':country' => $country]); $kazanRow = $stmt->fetch(PDO::FETCH_ASSOC); if (!$kazanRow) { echo json_encode(["status" => "failure", "message" => "No pricing available for this country"]); exit; } $result = calculateDynamicPrice($country, $minFare, $distance, $duration, $kazanRow, $startNameAddress, $endNameAddress, $destLat, $destLng, $passengerLat, $passengerLng); $price = $result['price']; $price_for_driver = $result['price_for_driver']; $withCommission = $result['withCommission']; $kazan = $result['kazan']; $isNightFare = $result['isNightFare']; // ---------------------------------------------------------------------- // Helper Functions for Countries // ---------------------------------------------------------------------- function calculateDynamicPrice($country, $minFare, $distance, $duration, $kazanRow, $startNameAddress, $endNameAddress, $destLat, $destLng, $passengerLat, $passengerLng) { $comfortPrice = (float) $kazanRow['comfortPrice']; $speedPrice = (float) $kazanRow['speedPrice']; $familyPrice = (float) $kazanRow['familyPrice']; $deliveryPrice = (float) $kazanRow['deliveryPrice']; $naturePrice = (float) $kazanRow['naturePrice']; $heavyPrice = (float) $kazanRow['heavyPrice']; $latePrice = (float) $kazanRow['latePrice']; $kazanPercent = (float) $kazanRow['kazan']; // === General Settings === $minBillableKm = 0.2; $airportAddon = 0.0; $damascusAirportBoundAddon = 0.0; switch ($country) { case 'Egypt': $airportAddon = 35.0; break; case 'Jordan': $airportAddon = 5.0; break; default: // Syria $airportAddon = 200.0; $damascusAirportBoundAddon = 1400.0; 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); $clubCtx = (stripos($startNameAddress, 'club') !== false || stripos($startNameAddress, 'ديسكو') !== false || stripos($endNameAddress, 'club') !== false || stripos($endNameAddress, 'ديسكو') !== false); $northLat = 33.415313; $southLat = 33.400265; $eastLng = 36.531505; $westLng = 36.499687; $damascusAirportBoundCtx = ($destLat <= $northLat && $destLat >= $southLat && $destLng <= $eastLng && $destLng >= $westLng); $isInDamascusAirportBoundCtx = ($passengerLat <= $northLat && $passengerLat >= $southLat && $passengerLng <= $eastLng && $passengerLng >= $westLng); $billableDistance = ($distance < $minBillableKm) ? $minBillableKm : $distance; $isLongSpeed = $billableDistance > $longSpeedThresholdKm; $perKmSpeedBaseFromServer = $speedPrice; $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; } date_default_timezone_set('Asia/Damascus'); $hour = (int)date('H'); $effectivePerMin = $naturePrice; if ($hour >= 21 || $hour < 1) { $effectivePerMin = $latePrice; } else if ($hour >= 1 && $hour < 5) { $effectivePerMin = $clubCtx ? ($latePrice * 2) : $latePrice; } else if ($hour >= 14 && $hour <= 17) { $effectivePerMin = $heavyPrice; } $billableMinutes = $totalMinutes; if ($billableDistance > $longDistThresholdKm) { $effectivePerMin = $longTripPerMin; $capped = ($billableMinutes > $minuteCapLong) ? $minuteCapLong : $billableMinutes; $billableMinutes = max(0, $capped - $freeMinutesLong); } else if ($billableDistance > $mediumDistThresholdKm) { $effectivePerMin = $longTripPerMin; $billableMinutes = ($billableMinutes > $minuteCapMedium) ? $minuteCapMedium : $billableMinutes; } $fare = $billableDistance * $perKmSpeed; $fare += $billableMinutes * $effectivePerMin; if ($airportCtx) $fare += $airportAddon; if ($damascusAirportBoundCtx || $isInDamascusAirportBoundCtx) { $fare += $damascusAirportBoundAddon; } $price = max($fare, $minFare); // Apply kazan (e.g. 11%) $withCommission = ceil($price * (1 + $kazanPercent / 100)); $kazan = $withCommission - $price; $price_for_driver = $price; return [ 'price' => $price, 'price_for_driver' => $price_for_driver, 'withCommission' => $withCommission, 'kazan' => $kazan, 'isNightFare' => false ]; } // 2. Validate Promo Code $discount = 0; if (!empty($promo_code)) { $sqlPromo = "SELECT amount FROM `promos` WHERE promo_code = :promo_code AND (passengerID = :passenger_id OR passengerID LIKE '%all%') AND validity_start_date <= CURDATE() AND validity_end_date >= CURDATE()"; $stmtPromo = $con->prepare($sqlPromo); $stmtPromo->execute([ ':promo_code' => $promo_code, ':passenger_id' => $passenger_id ]); if ($stmtPromo->rowCount() > 0) { $promoData = $stmtPromo->fetch(PDO::FETCH_ASSOC); $discount = (float) $promoData['amount']; } } // 3. Fetch Passenger Wallet (Negative Balance / Debt) // Using Redis for ultra-fast reads, with a fallback to the Payment Server API. $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; } else { // Fallback: If not in Redis, call the Payment Server Endpoint // TODO: Replace with the actual Payment Server Endpoint URL and API Key /* $paymentApiUrl = "https://payment.siroapp.com/api/get_debt?passenger_id=" . urlencode($passenger_id); $options = [ "http" => [ "header" => "Authorization: Bearer YOUR_API_KEY\r\n" ] ]; $context = stream_context_create($options); $response = file_get_contents($paymentApiUrl, false, $context); if ($response !== false) { $data = json_decode($response, true); if (isset($data['debt'])) { $negativeBalance = (float) $data['debt']; // Cache the result in Redis for future pricing calls (e.g. 1 hour) $redis->setex($redisKey, 3600, $negativeBalance); } } */ } } catch (Exception $e) { // If Redis fails, gracefully default to 0 or fallback API $negativeBalance = 0; } } $prices = ['total' => $withCommission]; // 4. Apply Discount and Negative Balance foreach ($prices as $key => $price) { // Apply discount (Assuming percentage discount if amount <= 100, else fixed amount) if ($discount > 0 && $discount <= 100) { $prices[$key] = max(0, $price - ($price * ($discount / 100))); } else { $prices[$key] = max(0, $price - $discount); } // Add negative balance $prices[$key] += $negativeBalance; } echo json_encode([ 'status' => 'success', 'data' => $prices, 'applied_discount' => $discount, 'added_negative_balance' => $negativeBalance ]); ?>