fix: resolve stale price display in driver ratings and remove misleading discount UI in rider ratings

This commit is contained in:
Hamza-Ayed
2026-07-09 04:48:19 +03:00
parent 03e9d648a1
commit 2152d34a8e
6 changed files with 76 additions and 62 deletions
@@ -1,6 +1,14 @@
<?php <?php
require_once __DIR__ . '/../../connect.php'; require_once __DIR__ . '/../../connect.php';
// 🔥 [Fix Broken Access Control] كان يتحقق من صلاحية التوكن فقط — أي مستخدم
// مسجّل دخول كان يقدر يغيّر حالة أي سائق (تفعيل/رفض) أو رقم هاتفه.
if ($role !== 'admin' && $role !== 'super_admin') {
http_response_code(403);
echo json_encode(['error' => 'Unauthorized access. Admin role required.']);
exit;
}
$driver_id = filterRequest("id"); $driver_id = filterRequest("id");
$phone = filterRequest("phone"); $phone = filterRequest("phone");
$status = filterRequest("status"); $status = filterRequest("status");
@@ -1,6 +1,15 @@
<?php <?php
require_once __DIR__ . '/../../../connect.php'; require_once __DIR__ . '/../../../connect.php';
// 🔥 [Fix Broken Access Control] كان يتحقق من صلاحية التوكن فقط — أي مستخدم
// مسجّل دخول (راكب/سائق آخر) كان يقدر يجلب بيانات أي سائق مفكوكة التشفير
// (هوية وطنية، هاتف، عنوان...) بالإضافة لروابط وثائقه الشخصية.
if ($role !== 'admin' && $role !== 'super_admin') {
http_response_code(403);
echo json_encode(['error' => 'Unauthorized access. Admin role required.']);
exit;
}
$driverId = filterRequest("id"); $driverId = filterRequest("id");
if (empty($driverId)) { if (empty($driverId)) {
@@ -1,6 +1,15 @@
<?php <?php
require_once __DIR__ . '/../../../connect.php'; require_once __DIR__ . '/../../../connect.php';
// 🔥 [Fix Broken Access Control] كان يتحقق من صلاحية التوكن فقط بدون التحقق
// من الدور — أي توكن صالح (حتى راكب) كان يقدر يسحب قائمة السائقين المعلّقين
// وبياناتهم الشخصية المفكوكة التشفير.
if ($role !== 'admin' && $role !== 'super_admin') {
http_response_code(403);
echo json_encode(['error' => 'Unauthorized access. Admin role required.']);
exit;
}
$limit = isset($_POST['limit']) ? (int)$_POST['limit'] : (isset($_GET['limit']) ? (int)$_GET['limit'] : 10); $limit = isset($_POST['limit']) ? (int)$_POST['limit'] : (isset($_GET['limit']) ? (int)$_GET['limit'] : 10);
$offset = isset($_POST['offset']) ? (int)$_POST['offset'] : (isset($_GET['offset']) ? (int)$_GET['offset'] : 0); $offset = isset($_POST['offset']) ? (int)$_POST['offset'] : (isset($_GET['offset']) ? (int)$_GET['offset'] : 0);
+23 -1
View File
@@ -58,9 +58,31 @@ try {
$firstName_encrypted = $encryptionHelper->encryptData($firstName); $firstName_encrypted = $encryptionHelper->encryptData($firstName);
$lastName_encrypted = $encryptionHelper->encryptData($lastName); $lastName_encrypted = $encryptionHelper->encryptData($lastName);
$email_encrypted = $encryptionHelper->encryptData($email); $email_encrypted = $encryptionHelper->encryptData($email);
$password_hashed = password_hash($email, PASSWORD_DEFAULT); $password_hashed = password_hash($email, PASSWORD_DEFAULT);
$unknown_encrypted = $encryptionHelper->encryptData("unknown yet"); $unknown_encrypted = $encryptionHelper->encryptData("unknown yet");
// ======================================================
// Step 4.5: التحقق الفعلي من ملكية رقم الهاتف (🔥 Fix)
// ======================================================
// كانت هذه النقطة تسمح بإنشاء حساب راكب بأي رقم هاتف بدون إثبات
// ملكيته فعلياً — auth/otp/verify.php يُعلّم الصف verified=1 لكن
// register_passenger.php لم يكن يتحقق من ذلك إطلاقاً. الآن نشترط
// وجود صف تحقق ناجح (verified=1) لنفس رقم الهاتف خلال آخر 30 دقيقة
// (مهلة أوسع من صلاحية الرمز نفسه [5 دقائق] لإعطاء وقت كافٍ لإكمال
// نموذج التسجيل بعد التحقق مباشرة).
$step = 4.5;
$verifyCheckStmt = $con->prepare(
"SELECT id FROM phone_verification_passenger
WHERE phone_number = ? AND verified = 1 AND created_at > DATE_SUB(NOW(), INTERVAL 30 MINUTE)
LIMIT 1"
);
$verifyCheckStmt->execute([$phoneNumber_encrypted]);
if ($verifyCheckStmt->rowCount() === 0) {
error_log("$logTag Step 4.5 Error: Phone number not verified via OTP.");
jsonError("Phone number must be verified before registration.");
exit();
}
// ====================================================== // ======================================================
// Step 5: إنشاء ID فريد // Step 5: إنشاء ID فريد
// ====================================================== // ======================================================
@@ -76,9 +76,14 @@ class RatePassenger extends StatelessWidget {
Widget _buildHeroPriceDisplay(BuildContext context) { Widget _buildHeroPriceDisplay(BuildContext context) {
final MapDriverController mapController = Get.find<MapDriverController>(); final MapDriverController mapController = Get.find<MapDriverController>();
// Parse the string to double to format it correctly // 🔥 [Fix Stale Price] كان يُعرض mapController.paymentAmount — وهو السعر
double amount = // المُقتبَس وقت عرض الرحلة على السائق، ولا يتحدث أبداً بعد ذلك. السعر
double.tryParse(mapController.paymentAmount.toString()) ?? 0.0; // النهائي الصحيح (بعد كل التعديلات من finish_ride_updates.php) موجود
// فعلاً في controller.price لكنه لم يكن يُستخدم هنا. الآن نعتمده أولاً.
final String? finalPriceStr = controller.price;
double amount = (finalPriceStr != null && finalPriceStr.isNotEmpty)
? (double.tryParse(finalPriceStr) ?? 0.0)
: (double.tryParse(mapController.paymentAmount.toString()) ?? 0.0);
String formattedAmount = currencyFormatter.format(amount); String formattedAmount = currencyFormatter.format(amount);
return Container( return Container(
+19 -58
View File
@@ -4,6 +4,7 @@ import 'package:flutter_rating_bar/flutter_rating_bar.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import '../../constant/colors.dart'; import '../../constant/colors.dart';
import '../../constant/currency.dart';
import '../../constant/style.dart'; import '../../constant/style.dart';
import '../../controller/firebase/firbase_messge.dart'; import '../../controller/firebase/firbase_messge.dart';
import '../../controller/payment/payment_controller.dart'; import '../../controller/payment/payment_controller.dart';
@@ -43,69 +44,29 @@ class RateDriverFromPassenger extends StatelessWidget {
'${'Total price to '.tr}${Get.find<RideLifecycleController>().driverName}', '${'Total price to '.tr}${Get.find<RideLifecycleController>().driverName}',
style: AppStyle.title, style: AppStyle.title,
), ),
Row( // 🔥 [Fix Fake Discount] كان هنا سعر "خصم" مُصطنَع
mainAxisAlignment: // بالكامل (السعر الحقيقي × 1.12 + السعر الحقيقي)
MainAxisAlignment.spaceEvenly, // يُعرض بخط مشطوب وكأنه سعر سابق حقيقي — رقم
children: [ // مضلِّل تماماً، لا يعكس أي سعر منافس أو عرض
Container( // حقيقي. الآن نعرض السعر الفعلي فقط مع رمز
decoration: BoxDecoration( // العملة الصحيح حسب دولة الرحلة.
border: Border.all( Container(
width: 2, decoration: BoxDecoration(
color: AppColor.redColor, border: Border.all(
)), width: 2,
child: Padding( color: AppColor.greenColor,
padding: const EdgeInsets.all(4), )),
child: Text( child: Padding(
(double.parse(controller.price padding: const EdgeInsets.all(4),
.toString()) * child: Text(
.12 + '${controller.price ?? '0'} ${CurrencyHelper.currency}',
double.parse(controller.price style: AppStyle.number,
.toString()))
.toStringAsFixed(2),
style: AppStyle.number.copyWith(
color: AppColor.redColor,
textBaseline:
TextBaseline.ideographic,
decoration:
TextDecoration.lineThrough,
decorationColor:
AppColor.redColor),
),
),
), ),
const SizedBox( ),
height: 10,
),
Container(
decoration: BoxDecoration(
border: Border.all(
width: 2,
color: AppColor.greenColor,
)),
child: Padding(
padding: const EdgeInsets.all(4),
child: Text(
controller.price.toString(),
style: AppStyle.number,
),
),
),
],
), ),
const SizedBox( const SizedBox(
height: 10, height: 10,
), ),
Padding(
padding: const EdgeInsets.all(4.0),
child: Text(
'Exclusive offers and discounts always with the Siro app'
.tr,
style: AppStyle.title.copyWith(
color: AppColor.redColor,
),
textAlign: TextAlign.center,
),
),
(Get.find<PaymentController>() (Get.find<PaymentController>()
.isWalletChecked == .isWalletChecked ==
true) true)