Update: 2026-08-07 00:36:27
This commit is contained in:
@@ -0,0 +1,22 @@
|
|||||||
|
-- 2026_08_07_kazan_cancel_penalty_policy.sql
|
||||||
|
--
|
||||||
|
-- سبب الترحيل: كان مبلغ غرامة الإلغاء في ride/rides/cancel_ride_by_driver.php
|
||||||
|
-- يُقرأ من طلب العميل مباشرة (filterRequest("penalty_fee")) ويُخصم من محفظة
|
||||||
|
-- الراكب بلا سقف ولا تحقق — أي أن طلباً مصاغاً يدوياً كان يقدر يخصم أي مبلغ
|
||||||
|
-- من أي راكب. صار المبلغ يُقرأ من هنا فقط، ولا يقبل الخادم أي قيمة من العميل.
|
||||||
|
--
|
||||||
|
-- القيمة الافتراضية '0' تحفظ السلوك الحالي حرفياً: لا عميل يرسل penalty_fee
|
||||||
|
-- اليوم، فالغرامة لا تُطبَّق فعلياً. تفعيلها قرار إداري صريح بتحديث الصف.
|
||||||
|
--
|
||||||
|
-- القاعدة المستهدفة: primary فقط — الكود يقرأ kazan عبر $con، وجدول kazan في
|
||||||
|
-- قاعدتي ride/tracking بنية أقدم ومختلفة الأعمدة (لا يُستخدم في هذا المسار).
|
||||||
|
|
||||||
|
ALTER TABLE `kazan`
|
||||||
|
ADD COLUMN `cancelPenaltyFee` VARCHAR(10)
|
||||||
|
CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci
|
||||||
|
NOT NULL DEFAULT '0'
|
||||||
|
COMMENT 'غرامة إلغاء السائق بعد القبول، تُخصم من محفظة الراكب. 0 = معطّلة'
|
||||||
|
AFTER `startPrice`;
|
||||||
|
|
||||||
|
-- للتراجع:
|
||||||
|
-- ALTER TABLE `kazan` DROP COLUMN `cancelPenaltyFee`;
|
||||||
@@ -13,11 +13,22 @@ require_once __DIR__ . '/streak_helper.php';
|
|||||||
$rideId = filterRequest("ride_id");
|
$rideId = filterRequest("ride_id");
|
||||||
$driverId = filterRequest("driver_id");
|
$driverId = filterRequest("driver_id");
|
||||||
$reason = filterRequest("reason");
|
$reason = filterRequest("reason");
|
||||||
$passengerToken = filterRequest("passenger_token");
|
$passengerToken = filterRequest("passenger_token");
|
||||||
$penaltyFee = (float) filterRequest("penalty_fee");
|
|
||||||
|
// مبلغ الغرامة لا يُقرأ من الطلب إطلاقاً. كان سابقاً filterRequest("penalty_fee")
|
||||||
|
// فيُخصم من محفظة الراكب بلا سقف ولا تحقق. صار يُحسم من سياسة الخادم أدناه.
|
||||||
|
// إن وصل المفتاح من عميل فهو إما نسخة قديمة أو محاولة عبث — نسجّله ونتجاهله.
|
||||||
|
if (filterRequest("penalty_fee") !== null && filterRequest("penalty_fee") !== '') {
|
||||||
|
error_log("[cancel_ride_by_driver] SECURITY: تجاهل penalty_fee من العميل"
|
||||||
|
. " (ride=$rideId driver=$driverId) — السياسة تُقرأ من kazan فقط");
|
||||||
|
}
|
||||||
|
|
||||||
|
// سقف صلب فوق أي قيمة في قاعدة البيانات — يمنع خطأ إدارياً أو صفاً تالفاً
|
||||||
|
// من تحويل الغرامة إلى خصم كارثي.
|
||||||
|
const CANCEL_PENALTY_ABSOLUTE_CAP = 50000.0;
|
||||||
|
|
||||||
// تثبيت الحالة
|
// تثبيت الحالة
|
||||||
$statusText = "CancelFromDriverAfterApply";
|
$statusText = "CancelFromDriverAfterApply";
|
||||||
|
|
||||||
if (!$rideId || !$driverId) {
|
if (!$rideId || !$driverId) {
|
||||||
jsonError("Missing parameters");
|
jsonError("Missing parameters");
|
||||||
@@ -27,6 +38,28 @@ if (!$rideId || !$driverId) {
|
|||||||
try {
|
try {
|
||||||
$con->beginTransaction();
|
$con->beginTransaction();
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// 0. التحقق من الملكية — الرحلة يجب أن تكون مُسندة لهذا السائق
|
||||||
|
// بدون هذا الفحص كان أي driver_id يقدر يلغي أي رحلة ويغرّم راكبها.
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
$stmtOwn = $con->prepare("SELECT driver_id FROM ride WHERE id = ? FOR UPDATE");
|
||||||
|
$stmtOwn->execute([$rideId]);
|
||||||
|
$assignedDriver = $stmtOwn->fetchColumn();
|
||||||
|
|
||||||
|
if ($assignedDriver === false) {
|
||||||
|
$con->rollBack();
|
||||||
|
jsonError("Ride not found");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((string) $assignedDriver !== (string) $driverId) {
|
||||||
|
$con->rollBack();
|
||||||
|
error_log("[cancel_ride_by_driver] SECURITY: رفض إلغاء رحلة غير مُسندة"
|
||||||
|
. " (ride=$rideId caller=$driverId assigned=$assignedDriver)");
|
||||||
|
jsonError("Ride is not assigned to this driver");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------
|
// ---------------------------------------------------------
|
||||||
// 1. معالجة driver_orders (Insert or Update)
|
// 1. معالجة driver_orders (Insert or Update)
|
||||||
// ---------------------------------------------------------
|
// ---------------------------------------------------------
|
||||||
@@ -99,17 +132,35 @@ try {
|
|||||||
notifyPassengerOnRideServer($passenger_id, $socketPayload);
|
notifyPassengerOnRideServer($passenger_id, $socketPayload);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4.1. إضافة غرامة الإلغاء على الراكب (الدين) إذا كانت موجودة
|
// 4.1. غرامة الإلغاء على الراكب — المبلغ من سياسة الخادم لا من العميل
|
||||||
|
// Resolve country + penalty policy (kazan هو مصدر الحقيقة الوحيد)
|
||||||
|
// قراءة متسامحة: لو نزل الكود قبل ترحيل cancelPenaltyFee، لا نُسقط
|
||||||
|
// الإلغاء كله — نرجع لقراءة الدولة وحدها ونعتبر الغرامة صفراً.
|
||||||
|
try {
|
||||||
|
$stmtKazan = $con->prepare("SELECT country, cancelPenaltyFee FROM kazan LIMIT 1");
|
||||||
|
$stmtKazan->execute();
|
||||||
|
$kazan = $stmtKazan->fetch(PDO::FETCH_ASSOC) ?: [];
|
||||||
|
} catch (PDOException $eKazan) {
|
||||||
|
error_log("[cancel_ride_by_driver] cancelPenaltyFee غير موجود بعد"
|
||||||
|
. " (شغّل migrations/2026_08_07_kazan_cancel_penalty_policy.sql): "
|
||||||
|
. $eKazan->getMessage());
|
||||||
|
$stmtKazan = $con->prepare("SELECT country FROM kazan LIMIT 1");
|
||||||
|
$stmtKazan->execute();
|
||||||
|
$kazan = $stmtKazan->fetch(PDO::FETCH_ASSOC) ?: [];
|
||||||
|
}
|
||||||
|
$country = $kazan['country'] ?? 'Jordan';
|
||||||
|
|
||||||
|
$penaltyFee = (float) ($kazan['cancelPenaltyFee'] ?? 0);
|
||||||
|
if ($penaltyFee > CANCEL_PENALTY_ABSOLUTE_CAP) {
|
||||||
|
error_log("[cancel_ride_by_driver] kazan.cancelPenaltyFee=$penaltyFee"
|
||||||
|
. " تجاوز السقف الصلب — قُصّ إلى " . CANCEL_PENALTY_ABSOLUTE_CAP);
|
||||||
|
$penaltyFee = CANCEL_PENALTY_ABSOLUTE_CAP;
|
||||||
|
}
|
||||||
|
|
||||||
if ($penaltyFee > 0) {
|
if ($penaltyFee > 0) {
|
||||||
// إضافة القيمة كدين سالب في المحفظة
|
// إضافة القيمة كدين سالب في المحفظة
|
||||||
$negativeDebt = -$penaltyFee;
|
$negativeDebt = -$penaltyFee;
|
||||||
|
|
||||||
// Resolve country and wallet server
|
|
||||||
$stmtKazan = $con->prepare("SELECT country FROM kazan LIMIT 1");
|
|
||||||
$stmtKazan->execute();
|
|
||||||
$kazan = $stmtKazan->fetch(PDO::FETCH_ASSOC) ?: ["country" => "Jordan"];
|
|
||||||
$country = $kazan['country'] ?? 'Jordan';
|
|
||||||
|
|
||||||
$walletServer = "https://walletintaleq.intaleq.xyz";
|
$walletServer = "https://walletintaleq.intaleq.xyz";
|
||||||
if (strtolower($country) == 'jordan') {
|
if (strtolower($country) == 'jordan') {
|
||||||
$walletServer = getenv('WALLET_SERVER_JORDAN') ?: "https://walletintaleq.intaleq.xyz";
|
$walletServer = getenv('WALLET_SERVER_JORDAN') ?: "https://walletintaleq.intaleq.xyz";
|
||||||
|
|||||||
@@ -839,6 +839,7 @@ CREATE TABLE `kazan` (
|
|||||||
`fuelPrice` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'سعر الوقود المرجعي',
|
`fuelPrice` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'سعر الوقود المرجعي',
|
||||||
`currency` varchar(5) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'SYP' COMMENT 'رمز العملة: SYP, EGP, JOD, IQD, ...',
|
`currency` varchar(5) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'SYP' COMMENT 'رمز العملة: SYP, EGP, JOD, IQD, ...',
|
||||||
`startPrice` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '0' COMMENT 'فتحة العداد',
|
`startPrice` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '0' COMMENT 'فتحة العداد',
|
||||||
|
`cancelPenaltyFee` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '0' COMMENT 'غرامة إلغاء السائق بعد القبول، تُخصم من محفظة الراكب. 0 = معطّلة',
|
||||||
|
|
||||||
-- ========== 🚗 أسعار الكيلومتر لكل نوع سيارة ==========
|
-- ========== 🚗 أسعار الكيلومتر لكل نوع سيارة ==========
|
||||||
`speedPrice` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'سعر الكيلومتر - Speed',
|
`speedPrice` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'سعر الكيلومتر - Speed',
|
||||||
|
|||||||
@@ -209,7 +209,10 @@ services:
|
|||||||
PMA_PORT: 3306
|
PMA_PORT: 3306
|
||||||
UPLOAD_LIMIT: 300M
|
UPLOAD_LIMIT: 300M
|
||||||
ports:
|
ports:
|
||||||
- "8082:80" # ادخل عليه عبر http://your-domain:8082 أو افتح بورت 8082 في CloudPanel
|
# مقيَّد على loopback عمداً: الربط السابق "8082:80" كان يكشف phpMyAdmin
|
||||||
|
# للإنترنت بلا TLS (docker-proxy يلتفّ على UFW). الوصول عبر نفق SSH فقط:
|
||||||
|
# ssh -N -L 8082:127.0.0.1:8082 USER@SERVER
|
||||||
|
- "127.0.0.1:8082:80"
|
||||||
depends_on:
|
depends_on:
|
||||||
- mysql
|
- mysql
|
||||||
mem_limit: 256m
|
mem_limit: 256m
|
||||||
|
|||||||
+728
-1024
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user