fix: resolve stale price display in driver ratings and remove misleading discount UI in rider ratings
This commit is contained in:
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user