diff --git a/backend/.env.example b/backend/.env.example index 70e595b4..66f57fa3 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -129,6 +129,8 @@ APP_ENV=production APP_DEBUG=false APP_NAME=Siro APP_DOMAIN=api-syria.siromove.com +APP_COUNTRY=Jordan +APP_CURRENCY=JOD # ============================================================================= # Nabeh Integration (server-to-server API key) diff --git a/backend/functions.php b/backend/functions.php index e06ec4d1..333813af 100644 --- a/backend/functions.php +++ b/backend/functions.php @@ -539,6 +539,23 @@ function sendFCM_Internal( return $fcm->send($target, $title, $body, is_array($customData) ? $customData : [], $category, $tone); } +// 🆕 دالة ذكية لإرسال وإرساء إشعارات السائقين في قاعدة البيانات (لضمان بقائها حتى لو لم يقرأها) +function sendAndSaveDriverNotification($con, $driver_id, $token, $title, $body, $customData = [], $category = 'System', $tone = 'ding') { + // 1. حفظ في قاعدة البيانات (لتظهر في صفحة الإشعارات داخل التطبيق) + try { + $stmt = $con->prepare("INSERT INTO notificationCaptain (driverID, title, body) VALUES (?, ?, ?)"); + $stmt->execute([$driver_id, $title, $body]); + } catch (Exception $e) { + error_log("[sendAndSaveDriverNotification] DB Save Error: " . $e->getMessage()); + } + + // 2. إرسال Push Notification + if (!empty($token) && $token !== 'all') { + return sendFCM_Internal($token, $title, $body, $customData, $category, false, $tone); + } + return true; +} + function logAudit($con, $adminId, $action, $tableName = null, $recordId = null, $details = null) { diff --git a/backend/ride/gamification/getGamificationDashboard.php b/backend/ride/gamification/getGamificationDashboard.php index e325bf4b..47aa15f9 100644 --- a/backend/ride/gamification/getGamificationDashboard.php +++ b/backend/ride/gamification/getGamificationDashboard.php @@ -8,10 +8,30 @@ if (!$driver_id) { } try { - // 1. Get Country and Currency Info - $stmtKazan = $con->prepare("SELECT country, currency FROM kazan LIMIT 1"); - $stmtKazan->execute(); - $kazan = $stmtKazan->fetch(PDO::FETCH_ASSOC) ?: ["country" => "Jordan", "currency" => "JOD"]; + global $redis; + $redisKey = "gamification_dashboard:{$driver_id}"; + + // 1. Try to read from Redis first + if (isset($redis)) { + try { + $cached = $redis->get($redisKey); + if ($cached) { + $data = json_decode($cached, true); + if ($data) { + jsonSuccess($data); + exit; + } + } + } catch (Exception $e) { + error_log("Redis read error in getGamificationDashboard: " . $e->getMessage()); + } + } + + // 2. Fallback to Database & External API + // Get Country and Currency Info from Environment Variables (to avoid querying kazan table) + $country = getenv('APP_COUNTRY') ?: getenv('COUNTRY') ?: "Jordan"; + $currency = getenv('APP_CURRENCY') ?: getenv('CURRENCY') ?: "JOD"; + $kazan = ["country" => $country, "currency" => $currency]; // 2. Get Total Completed Trips $stmtTrips = $con->prepare("SELECT COUNT(*) as count FROM `ride` WHERE driver_id = :driver_id AND status = 'Finished'"); @@ -94,10 +114,33 @@ try { } // 8. Calculate Normalized Points - // 10 pts per finished trip, 100 pts per referral invite, 2 pts per behavior score point + claimed challenge points $normalizedPoints = ($totalTrips * 10) + ($totalReferrals * 100) + ((int)$behavior['avg_score'] * 2) + $challengePoints; - jsonSuccess([ + // 9. Fetch Available Challenges + $stmtCh = $con->prepare("SELECT * FROM gamification_challenges WHERE is_active = 1 AND country = :country ORDER BY target_count ASC"); + $stmtCh->execute([':country' => $kazan['country']]); + $challengesRaw = $stmtCh->fetchAll(PDO::FETCH_ASSOC); + + $challenges = []; + $tier = 'Bronze'; // Placeholder logic + foreach ($challengesRaw as $chRaw) { + $isCompleted = false; + $isClaimed = false; + + $challenges[] = [ + 'id' => (int)$chRaw['id'], + 'title' => $chRaw['title'], + 'description' => $chRaw['description'], + 'type' => $chRaw['challenge_type'], + 'target' => (int)$chRaw['target_count'], + 'reward_amount' => (float)$chRaw['reward_amount'], + 'reward_type' => $chRaw['reward_type'], + 'is_completed' => $isCompleted, + 'is_claimed' => $isClaimed + ]; + } + + $responseData = [ "country" => $kazan["country"], "currency" => $kazan["currency"], "totalTrips" => $totalTrips, @@ -110,10 +153,23 @@ try { "maxSpeed" => (float)$behavior["max_speed"], "todayTrips" => $todayTrips, "todayEarnings" => $todayEarnings, - "totalPoints" => $normalizedPoints - ]); + "totalPoints" => $normalizedPoints, + "tier" => $tier, + "challenges" => $challenges + ]; -} catch (PDOException $e) { + // 10. Save the compiled data into Redis + if (isset($redis)) { + try { + $redis->setex($redisKey, 900, json_encode($responseData)); + } catch (Exception $e) { + error_log("Redis write error in getGamificationDashboard: " . $e->getMessage()); + } + } + + jsonSuccess($responseData); + +} catch (Exception $e) { error_log("getGamificationDashboard Error: " . $e->getMessage()); jsonError("An internal error occurred. Please try again later."); } diff --git a/backend/ride/gamification/getLeaderboard.php b/backend/ride/gamification/getLeaderboard.php index 3e76f042..29408c66 100644 --- a/backend/ride/gamification/getLeaderboard.php +++ b/backend/ride/gamification/getLeaderboard.php @@ -4,6 +4,26 @@ require_once __DIR__ . '/../../connect.php'; $type = isset($_POST['type']) ? filterRequest("type") : 'trips'; try { + global $redis; + $redisKey = "gamification_leaderboard:{$type}"; + + // 1. Try to read from Redis first + if (isset($redis)) { + try { + $cached = $redis->get($redisKey); + if ($cached) { + $data = json_decode($cached, true); + if ($data !== null) { + jsonSuccess($data); + exit; + } + } + } catch (Exception $e) { + error_log("Redis read error in getLeaderboard: " . $e->getMessage()); + } + } + + // 2. Fallback to Database if ($type === 'earnings') { $sql = " SELECT @@ -53,8 +73,20 @@ if ($stmt->rowCount() > 0) { $row['rank'] = $rank++; } - jsonSuccess($rows); + $responseData = $rows; } else { - jsonSuccess([]); + $responseData = []; } + +// 3. Save the DB result into Redis for future reads +if (isset($redis)) { + try { + // Cache leaderboard for 1 hour (3600 seconds) + $redis->setex($redisKey, 3600, json_encode($responseData)); + } catch (Exception $e) { + error_log("Redis write error in getLeaderboard: " . $e->getMessage()); + } +} + +jsonSuccess($responseData); ?> diff --git a/backend/ride/gamification/get_streak.php b/backend/ride/gamification/get_streak.php new file mode 100644 index 00000000..6354a986 --- /dev/null +++ b/backend/ride/gamification/get_streak.php @@ -0,0 +1,69 @@ +get($redisKey); + if ($cached) { + $data = json_decode($cached, true); + if ($data) { + // Return immediately if found in cache! + jsonSuccess($data); + exit; + } + } + } catch (Exception $e) { + error_log("Redis read error in get_streak: " . $e->getMessage()); + } +} + +// 2. Fallback to Database if not found in Redis +$stmt = $con->prepare("SELECT current_streak, zero_commission_until FROM driver_streaks WHERE driver_id = ?"); +$stmt->execute([$driver_id]); +$streak = $stmt->fetch(PDO::FETCH_ASSOC); + +if ($streak) { + $now = new DateTime(); + $until = new DateTime($streak['zero_commission_until']); + + $isActive = false; + if ($streak['zero_commission_until'] !== '2000-01-01 00:00:00' && $now < $until) { + $isActive = true; + } + + $responseData = [ + 'streak_count' => intval($streak['current_streak']), + 'is_zero_commission_active' => $isActive, + 'zero_commission_until' => $streak['zero_commission_until'] + ]; +} else { + // Default if no record yet + $responseData = [ + 'streak_count' => 0, + 'is_zero_commission_active' => false, + 'zero_commission_until' => '2000-01-01 00:00:00' + ]; +} + +// 3. Save the DB result into Redis for future reads +if (isset($redis)) { + try { + // Cache it for 2 hours (7200 seconds) + $redis->setex($redisKey, 7200, json_encode($responseData)); + } catch (Exception $e) { + error_log("Redis write error in get_streak: " . $e->getMessage()); + } +} + +jsonSuccess($responseData); +?> diff --git a/backend/ride/notificationCaptain/get.php b/backend/ride/notificationCaptain/get.php index 8f5bf1d8..86de1d0b 100644 --- a/backend/ride/notificationCaptain/get.php +++ b/backend/ride/notificationCaptain/get.php @@ -4,10 +4,10 @@ require_once __DIR__ . '/../../connect.php'; $driverID = filterRequest("driverID"); $sql = "SELECT * FROM `notificationCaptain` - WHERE `driverID` = :driverID - AND `dateCreated` > DATE_SUB(NOW(), INTERVAL 2 DAY) + WHERE (`driverID` = :driverID OR `driverID` = 'all') + AND `dateCreated` > DATE_SUB(NOW(), INTERVAL 7 DAY) ORDER BY `dateCreated` DESC - LIMIT 10"; + LIMIT 50"; $stmt = $con->prepare($sql); $stmt->bindParam(':driverID', $driverID, PDO::PARAM_STR); diff --git a/backend/ride/rides/cancelRideFromDriver.php b/backend/ride/rides/cancelRideFromDriver.php index 5ce604d6..6003bfba 100644 --- a/backend/ride/rides/cancelRideFromDriver.php +++ b/backend/ride/rides/cancelRideFromDriver.php @@ -3,6 +3,7 @@ // تأكد أن هذا الملف يحتوي على دوال الإشعارات (notifyPassengerOnRideServer) require_once __DIR__ . '/../../connect.php'; +require_once __DIR__ . '/streak_helper.php'; // 🚀 تسجيل بداية العملية error_log("🚀 [cancelRide.php] Request Started to Cancel Ride From Driver."); @@ -55,6 +56,14 @@ try { // تحديث جدول driver_orders أيضاً لتوحيد الحالة (اختياري ولكنه مفضل) $stmtDriverOrder = $con->prepare("UPDATE driver_orders SET status = ? WHERE order_id = ?"); $stmtDriverOrder->execute([$newStatus, $id]); + + // 🆕 تصفير التتابع لأن السائق ألغى الرحلة + $stmtGetDriver = $con->prepare("SELECT driver_id FROM ride WHERE id = ?"); + $stmtGetDriver->execute([$id]); + $driver_id_to_reset = $stmtGetDriver->fetchColumn() ?: $user_id; + if ($driver_id_to_reset) { + handleDriverStreak($con, $driver_id_to_reset, 'reset'); + } $con->commit(); } catch (Exception $eLocal) { diff --git a/backend/ride/rides/cancel_ride_by_driver.php b/backend/ride/rides/cancel_ride_by_driver.php index 966cf1a4..c950a23d 100644 --- a/backend/ride/rides/cancel_ride_by_driver.php +++ b/backend/ride/rides/cancel_ride_by_driver.php @@ -8,6 +8,8 @@ try { error_log("[cancel_ride_by_driver] Failed to connect to Ride Database: " . $e->getMessage()); } +require_once __DIR__ . '/streak_helper.php'; + $rideId = filterRequest("ride_id"); $driverId = filterRequest("driver_id"); $reason = filterRequest("reason"); @@ -74,6 +76,9 @@ try { if (isset($con_ride)) { $con_ride->prepare($sqlRide)->execute([$statusText, $rideId]); } + + // 🆕 تصفير التتابع لأن السائق ألغى الرحلة + handleDriverStreak($con, $driverId, 'reset'); // --------------------------------------------------------- // 4. إشعار الراكب diff --git a/backend/ride/rides/finish_ride_updates.php b/backend/ride/rides/finish_ride_updates.php index 9c974669..6927be4f 100644 --- a/backend/ride/rides/finish_ride_updates.php +++ b/backend/ride/rides/finish_ride_updates.php @@ -7,6 +7,8 @@ try { error_log("[finish_ride_updates] Failed to connect to Ride Database: " . $e->getMessage()); } +require_once __DIR__ . '/streak_helper.php'; + // ============================================================ // finish_ride_updates.php — Atomic Server-to-Server // ============================================================ @@ -133,6 +135,16 @@ try { $durationMin = intval(preg_replace('/[^0-9]/', '', $actualDuration)); $calculated = ($distanceKm * $perKmRate) + ($durationMin * $perMinRate); + + // 🆕 تطبيق خصم التتابع (إعفاء من العمولة) - نحدد المتغير فقط لكن لا نغير السعر على الراكب + $is_zero_commission = false; + if (hasZeroCommission($con, $driver_id)) { + $is_zero_commission = true; + error_log("[finish_ride_updates] Driver $driver_id has active 0% commission streak!"); + } + + // السعر النهائي يجب أن يتضمن العمولة دائماً لكي يدفعها الراكب، + // لكن إذا كانت العمولة صفر للسائق، يتم إعطاؤها للسائق بدلاً من الشركة عبر السيرفر المالي. $calculated *= (1 + ($kazanPercent / 100)); $finalPrice = max($quotedPrice, round($calculated, 2)); @@ -198,6 +210,8 @@ try { 'authToken' => $driver_token, 'currency' => $currency, // 🆕 إرسال العملة لمخدم الدفع 'country_code' => $countryCode, // 🆕 إرسال الدولة لمخدم الدفع + 'is_zero_commission' => isset($is_zero_commission) && $is_zero_commission ? 'true' : 'false', // 🆕 إرسال إعفاء العمولة + 'kazanPercent' => $kazanPercent, // 🆕 إرسال نسبة العمولة متغيرة حسب الدولة ]; $ch = curl_init(WALLET_PAYMENT_URL); @@ -244,6 +258,9 @@ try { // ✅ Payment succeeded — COMMIT $con->commit(); + + // 🆕 Update Driver Streak (Increment because ride is finished successfully) + handleDriverStreak($con, $driver_id, 'increment'); // ============================================================ // 5. Notifications (After successful commit) @@ -287,11 +304,29 @@ try { "المبلغ المطلوب: " . $finalPrice . " " . $currency, $fcmData, 'Driver Finish Trip', - false + $passengerId ); } } + // 🆕 c) Driver notification for Zero Commission + if (isset($is_zero_commission) && $is_zero_commission && !empty($driver_token)) { + $fcmDriverData = [ + 'ride_id' => (string)$rideId, + 'status' => 'streak_reward' + ]; + + sendAndSaveDriverNotification( + $con, + $driver_id, + $driver_token, + "🔥 مكافأة التتابع", + "أنت بطل! لم يتم خصم عمولة لهذه الرحلة لأنك حافظت على تتابع قبول الرحلات.", + $fcmDriverData, + 'Zero Commission Reward' + ); + } + // ============================================================ // 6. Return Success with server-calculated price + currency // ============================================================ diff --git a/backend/ride/rides/streak_helper.php b/backend/ride/rides/streak_helper.php new file mode 100644 index 00000000..7ed36a1c --- /dev/null +++ b/backend/ride/rides/streak_helper.php @@ -0,0 +1,76 @@ +prepare(" + INSERT INTO driver_streaks (driver_id, current_streak, zero_commission_until) + VALUES (?, 0, NULL) + ON DUPLICATE KEY UPDATE current_streak = 0 + "); + $stmt->execute([$driver_id]); + } elseif ($action === 'increment') { + $stmt = $con->prepare("SELECT current_streak FROM driver_streaks WHERE driver_id = ?"); + $stmt->execute([$driver_id]); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + + $current = $row ? intval($row['current_streak']) + 1 : 1; + + $zero_until = NULL; + if ($current >= 5) { + // Reward unlocked! 0% commission until end of today + $zero_until = date('Y-m-d 23:59:59'); + $current = 0; // reset for next streak + } + + $stmt2 = $con->prepare(" + INSERT INTO driver_streaks (driver_id, current_streak, best_streak, zero_commission_until) + VALUES (?, ?, ?, ?) + ON DUPLICATE KEY UPDATE + current_streak = ?, + best_streak = GREATEST(best_streak, ?), + zero_commission_until = COALESCE(?, zero_commission_until) + "); + $stmt2->execute([ + $driver_id, + $current, + $current, + $zero_until, + + $current, + $current, + $zero_until + ]); + } + + // 🆕 Invalidate Redis Cache (Cache-Aside pattern) + global $redis; + if (isset($redis)) { + try { + $redis->del("driver_streak:{$driver_id}"); + $redis->del("gamification_dashboard:{$driver_id}"); + } catch (Exception $e) { + error_log("[streak_helper] Redis Cache Invalidation Error: " . $e->getMessage()); + } + } + + } catch (Exception $e) { + error_log("[streak_helper] Error: " . $e->getMessage()); + } +} + +function hasZeroCommission($con, $driver_id) { + try { + $stmt = $con->prepare("SELECT zero_commission_until FROM driver_streaks WHERE driver_id = ?"); + $stmt->execute([$driver_id]); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + if ($row && !empty($row['zero_commission_until'])) { + return (strtotime($row['zero_commission_until']) > time()); + } + } catch (Exception $e) { + error_log("[streak_helper] Error checking commission: " . $e->getMessage()); + } + return false; +} +?> diff --git a/backend/schema_primary.sql b/backend/schema_primary.sql index c6555f34..2fa82ef8 100644 --- a/backend/schema_primary.sql +++ b/backend/schema_primary.sql @@ -1450,6 +1450,7 @@ CREATE TABLE `ride` ( `time` time NOT NULL, `endtime` time NOT NULL, `price` decimal(10,2) NOT NULL DEFAULT '0.00', + `ai_negotiated_bonus` double DEFAULT '0', `passenger_id` varchar(111) NOT NULL, `driver_id` varchar(111) NOT NULL, `status` varchar(200) NOT NULL DEFAULT 'nothing', @@ -2051,3 +2052,20 @@ VALUES ('Mall of Egypt', 29.9705, 30.9856, 2000, 9, 'EG'), ('Cairo Opera House', 30.0427, 31.2238, 1000, 7, 'EG'), ('Al-Azhar Mosque', 30.0457, 31.2627, 1000, 6, 'EG'); + +-- +-- Table structure for table `driver_streaks` +-- + +DROP TABLE IF EXISTS `driver_streaks`; +CREATE TABLE `driver_streaks` ( + `id` int NOT NULL AUTO_INCREMENT, + `driver_id` varchar(255) NOT NULL, + `current_streak` int DEFAULT 0, + `best_streak` int DEFAULT 0, + `zero_commission_until` datetime DEFAULT NULL, + `last_updated` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `driver_id` (`driver_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + diff --git a/backend/schema_ride.sql b/backend/schema_ride.sql index 7c50c26b..6057379e 100644 --- a/backend/schema_ride.sql +++ b/backend/schema_ride.sql @@ -1353,6 +1353,7 @@ CREATE TABLE `ride` ( `time` time NOT NULL, `endtime` time NOT NULL, `price` decimal(10,2) NOT NULL DEFAULT '0.00', + `ai_negotiated_bonus` double DEFAULT '0', `passenger_id` varchar(111) NOT NULL, `driver_id` varchar(111) NOT NULL, `status` varchar(200) NOT NULL DEFAULT 'nothing', diff --git a/siro_driver/lib/constant/links.dart b/siro_driver/lib/constant/links.dart index f9675a7d..9f1424e3 100755 --- a/siro_driver/lib/constant/links.dart +++ b/siro_driver/lib/constant/links.dart @@ -249,6 +249,12 @@ class AppLink { "$endPoint/ride/gamification/getGamificationDashboard.php"; static String get getLeaderboard => "$endPoint/ride/gamification/getLeaderboard.php"; + static String get getSubAdminForDriver => + "$serverName/Admin/gamification/getSubAdminForDriver.php"; + + // 🆕 مسار جلب حالة تتابع الرحلات (Gamification Streak) + static String get getDriverStreak => + "$serverName/ride/gamification/get_streak.php"; static String get claimChallengeReward => "$endPoint/ride/gamification/claimChallengeReward.php"; static String get getReferralStats => diff --git a/siro_driver/lib/controller/home/captin/home_captain_controller.dart b/siro_driver/lib/controller/home/captin/home_captain_controller.dart index f8b3449c..c1a5b1e8 100755 --- a/siro_driver/lib/controller/home/captin/home_captain_controller.dart +++ b/siro_driver/lib/controller/home/captin/home_captain_controller.dart @@ -802,6 +802,9 @@ class HomeCaptainController extends GetxController { calculateConsumptionFuel() { mpg = fuelPrice / 12; //todo in register car add mpg in box } + // 🆕 Gamification Streak State + int streakCount = 0; + bool isZeroCommissionActive = false; getCountRideToday() async { var res = await CRUD().get( @@ -811,6 +814,24 @@ class HomeCaptainController extends GetxController { countRideToday = data['message'][0]['count'].toString(); update(); + + // Fetch Gamification Streak as well + getDriverStreak(); + } + + // 🆕 Fetch Driver Streak + getDriverStreak() async { + var res = await CRUD().get( + link: AppLink.getDriverStreak, + payload: {'driver_id': box.read(BoxName.driverID).toString()}); + if (res != 'failure') { + var decode = jsonDecode(res); + if (decode['status'] == 'success') { + streakCount = decode['message']['streak_count'] ?? 0; + isZeroCommissionActive = decode['message']['is_zero_commission_active'] ?? false; + update(); + } + } } getDriverRate() async { diff --git a/siro_driver/lib/views/home/Captin/home_captain/home_captin.dart b/siro_driver/lib/views/home/Captin/home_captain/home_captin.dart index 4a0555f0..f30fd2e9 100755 --- a/siro_driver/lib/views/home/Captin/home_captain/home_captin.dart +++ b/siro_driver/lib/views/home/Captin/home_captain/home_captin.dart @@ -222,6 +222,15 @@ class _HomeAppBar extends StatelessWidget implements PreferredSizeWidget { ), ), actions: [ + // ── Gamification Streak Badge ──────── + GetBuilder( + builder: (c) => _PillBadge( + icon: Icons.local_fire_department_rounded, + label: c.isZeroCommissionActive ? 'Free!' : '${c.streakCount}/5', + color: c.isZeroCommissionActive ? _Token.success : Colors.orange, + ), + ), + const SizedBox(width: 6), // ── Refuse Counter ─────────────────── GetBuilder( builder: (c) => _PillBadge( @@ -310,7 +319,7 @@ class _AppBarControls extends StatelessWidget { // Heatmap GetBuilder( builder: (c) => _IconBtn( - icon: Icons.local_fire_department_rounded, + icon: Icons.layers_rounded, color: c.isHeatmapVisible ? Colors.orange : Colors.grey.shade600, tooltip: 'Heatmap'.tr, diff --git a/siro_rider/lib/views/widgets/competitor_price_badge.dart b/siro_rider/lib/views/widgets/competitor_price_badge.dart index 594cfd96..6b715133 100644 --- a/siro_rider/lib/views/widgets/competitor_price_badge.dart +++ b/siro_rider/lib/views/widgets/competitor_price_badge.dart @@ -20,6 +20,7 @@ class CompetitorPriceBadge extends StatelessWidget { this.savingsPercent, this.savingsLabel, this.topCompetitor, + String? badgeText, }); @override @@ -79,7 +80,9 @@ class CompetitorPriceBadge extends StatelessWidget { shape: BoxShape.circle, ), child: Icon( - isSignificant ? Icons.trending_down_rounded : Icons.price_check_rounded, + isSignificant + ? Icons.trending_down_rounded + : Icons.price_check_rounded, color: textColor, size: 18, ), diff --git a/walletintaleq.intaleq.xyz/v2/main/ride/payment/process_ride_payments.php b/walletintaleq.intaleq.xyz/v2/main/ride/payment/process_ride_payments.php index 113809e0..d0a3e680 100755 --- a/walletintaleq.intaleq.xyz/v2/main/ride/payment/process_ride_payments.php +++ b/walletintaleq.intaleq.xyz/v2/main/ride/payment/process_ride_payments.php @@ -42,6 +42,8 @@ $paymentAmount = filterRequest("paymentAmount"); $paymentMethod = filterRequest("paymentMethod"); $walletChecked = filterRequest("walletChecked"); // 'true' or 'false' $authToken = filterRequest("authToken"); // kept for logging/audit, not used for auth +$is_zero_commission = filterRequest("is_zero_commission"); // 'true' or 'false' +$kazanPercent = filterRequest("kazanPercent"); // dynamic commission rate // --- Validate required fields --- if (empty($rideId) || empty($driverId) || empty($passengerId) || @@ -134,8 +136,17 @@ try { } } - // 3d. Deduct driver points (8% of payment amount) - $pointsSubtraction = floatval($paymentAmount) * (-0.08); + // 3d. Deduct driver points (dynamic commission rate) + $commissionRate = 0.08; // default fallback 8% + if (isset($kazanPercent) && is_numeric($kazanPercent)) { + $commissionRate = floatval($kazanPercent) / 100; + } + + $pointsSubtraction = floatval($paymentAmount) * (-$commissionRate); + if ($is_zero_commission === 'true') { + $pointsSubtraction = 0; // 🆕 لا نخصم عمولة أبداً لأن السائق لديه تتابع نشط + } + $stmtDriverPoints = $con->prepare( "INSERT INTO `driverWallet` (`driverID`, `paymentID`, `amount`, `paymentMethod`) VALUES (:driverID, :paymentID, :amount, :paymentMethod)"