Refactor(Auth): Complete auth folder restructuring, security patches, and Flutter endpoint updates
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/../connect.php';
|
||||
|
||||
// لا نستقبل id أو email من التطبيق بل نأخذهم من التوكن (JWT) لزيادة الأمان
|
||||
$platform = filterRequest("platform") ?: 'unknown';
|
||||
$appName = filterRequest("appName") ?: 'unknown';
|
||||
|
||||
// الاعتماد كلياً على الـ ID المستخرج من JWT داخل connect.php
|
||||
$id = $user_id;
|
||||
if ($id === 'new') {
|
||||
if (isset($decoded->sub) && $decoded->sub !== 'new') {
|
||||
$id = $decoded->sub;
|
||||
} else {
|
||||
$id = filterRequest("passengerID") ?: filterRequest("passengerId");
|
||||
}
|
||||
}
|
||||
|
||||
// تجهيز الاستعلام
|
||||
$sql = "SELECT
|
||||
p.`id`,
|
||||
p.`phone`,
|
||||
p.`email`,
|
||||
p.`gender`,
|
||||
p.`status`,
|
||||
p.`birthdate`,
|
||||
p.`site`,
|
||||
p.`first_name`,
|
||||
p.`last_name`,
|
||||
p.`sosPhone`,
|
||||
p.`education`,
|
||||
p.`employmentType`,
|
||||
p.`maritalStatus`,
|
||||
p.`created_at`,
|
||||
p.`updated_at`,
|
||||
phone_verification_passenger.verified,
|
||||
invitesToPassengers.isInstall,
|
||||
invitesToPassengers.inviteCode,
|
||||
invitesToPassengers.isGiftToken,
|
||||
(SELECT `version` FROM `packageInfo` WHERE platform = :platform AND appName = :appName) AS package,
|
||||
promos.promo_code AS promo,
|
||||
promos.amount AS discount,
|
||||
promos.validity_end_date AS validity,
|
||||
t.token AS fcm_token,
|
||||
t.fingerPrint AS fcm_fingerprint
|
||||
FROM passengers p
|
||||
LEFT JOIN phone_verification_passenger
|
||||
ON phone_verification_passenger.phone_number = p.phone
|
||||
LEFT JOIN invitesToPassengers
|
||||
ON invitesToPassengers.inviterPassengerPhone = p.phone
|
||||
LEFT JOIN promos
|
||||
ON promos.passengerID = p.id
|
||||
LEFT JOIN tokens t
|
||||
ON t.passengerID = p.id
|
||||
WHERE p.id = :id
|
||||
LIMIT 1";
|
||||
|
||||
// تنفيذ الاستعلام
|
||||
$stmt = $con->prepare($sql);
|
||||
$stmt->bindParam(':id', $id);
|
||||
$stmt->bindParam(':appName', $appName);
|
||||
$stmt->bindParam(':platform', $platform);
|
||||
$stmt->execute();
|
||||
|
||||
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
$count = $stmt->rowCount();
|
||||
|
||||
// تجهيز الرد
|
||||
header('Content-Type: application/json');
|
||||
|
||||
if ($count > 0) {
|
||||
foreach ($data as &$row) {
|
||||
// فك تشفير الحقول الحساسة
|
||||
$row['phone'] = $encryptionHelper->decryptData($row['phone']);
|
||||
$row['email'] = $encryptionHelper->decryptData($row['email']);
|
||||
$row['gender'] = $encryptionHelper->decryptData($row['gender']);
|
||||
$row['birthdate'] = $encryptionHelper->decryptData($row['birthdate']);
|
||||
$row['site'] = $encryptionHelper->decryptData($row['site']);
|
||||
$row['first_name'] = $encryptionHelper->decryptData($row['first_name']);
|
||||
$row['last_name'] = $encryptionHelper->decryptData($row['last_name']);
|
||||
$row['sosPhone'] = $encryptionHelper->decryptData($row['sosPhone']);
|
||||
$row['education'] = $encryptionHelper->decryptData($row['education']);
|
||||
$row['employmentType'] = $encryptionHelper->decryptData($row['employmentType']);
|
||||
$row['maritalStatus'] = $encryptionHelper->decryptData($row['maritalStatus']);
|
||||
|
||||
// فك تشفير توكن FCM إذا وجد
|
||||
if (!empty($row['fcm_token'])) {
|
||||
$row['fcm_token'] = $encryptionHelper->decryptData($row['fcm_token']);
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
"status" => "success",
|
||||
"count" => $count,
|
||||
"data" => $data
|
||||
]);
|
||||
} else {
|
||||
error_log("User does not exist: " . $email);
|
||||
echo json_encode([
|
||||
"status" => "Failure",
|
||||
"data" => "User does not exist."
|
||||
]);
|
||||
}
|
||||
|
||||
// تنظيف الموارد
|
||||
$stmt = null;
|
||||
$con = null;
|
||||
exit();
|
||||
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
// loginUsingCredentialsWithoutGooglePassenger.php
|
||||
// مسار مخصص لفاحصي التطبيق (الركاب) يعمل بدون JWT Interceptors
|
||||
|
||||
require_once __DIR__ . '/../core/bootstrap.php';
|
||||
|
||||
$email = filterRequest("email");
|
||||
$password = filterRequest("password");
|
||||
$fingerprint = filterRequest('fingerPrint') ?? filterRequest('fingerprint');
|
||||
$audience = filterRequest('aud') ?: 'siro_passenger';
|
||||
|
||||
// 1. تطبيق حد معدل الطلبات (Rate Limiting) للفاحصين: 3 محاولات بالدقيقة لكل IP
|
||||
$rateLimiter = new RateLimiter($redis);
|
||||
$rateLimiter->enforce(RateLimiter::identifier(), 'tester_login');
|
||||
|
||||
if (!$email || !$password) {
|
||||
echo json_encode(["status" => "failure", "message" => "Email and password are required"]);
|
||||
exit();
|
||||
}
|
||||
|
||||
// 2. التحقق من أن الحساب مخصص للفحص فقط (isTest check)
|
||||
$allowedTesterEmailsEnv = getenv('ALLOWED_TESTER_EMAILS') ?: '';
|
||||
$allowedEmails = array_filter(array_map('trim', explode(',', $allowedTesterEmailsEnv)));
|
||||
if (empty($allowedEmails)) {
|
||||
$allowedEmails = [
|
||||
'driver_tester@siromove.com',
|
||||
'passenger_tester@siromove.com',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
$cleanEmail = strtolower(trim($email));
|
||||
$isTester = in_array($cleanEmail, $allowedEmails) ||
|
||||
substr($cleanEmail, -13) === '@siromove.com' ||
|
||||
str_contains($cleanEmail, 'tester') ||
|
||||
str_contains($cleanEmail, 'reviewer');
|
||||
|
||||
try {
|
||||
$con = Database::get('main');
|
||||
|
||||
// تشفير الإيميل للبحث في قاعدة البيانات
|
||||
$encryptedEmail = $encryptionHelper->encryptData($email);
|
||||
|
||||
// Auto-seed/create tester passenger if it doesn't exist
|
||||
if ($cleanEmail === 'passenger_tester@siromove.com') {
|
||||
$stmtCheck = $con->prepare("SELECT id FROM passengers WHERE email = :email LIMIT 1");
|
||||
$stmtCheck->bindParam(':email', $encryptedEmail);
|
||||
$stmtCheck->execute();
|
||||
if (!$stmtCheck->fetch()) {
|
||||
$passengerId = 'tester_passenger_id_2026';
|
||||
$phone = '+962790000003';
|
||||
$hashedPassword = password_hash('SiroPassenger2026!', PASSWORD_DEFAULT);
|
||||
|
||||
$encryptedPhone = $encryptionHelper->encryptData($phone);
|
||||
$encryptedFirstName = $encryptionHelper->encryptData('Passenger');
|
||||
$encryptedLastName = $encryptionHelper->encryptData('Tester');
|
||||
$encryptedGender = $encryptionHelper->encryptData('Male');
|
||||
$encryptedBirthdate = $encryptionHelper->encryptData('1990-01-01');
|
||||
$encryptedSite = $encryptionHelper->encryptData('Jordan');
|
||||
|
||||
// Insert passenger with verified = 1 so app doesn't reject
|
||||
$insert = $con->prepare("INSERT INTO passengers (id, phone, email, password, gender, birthdate, site, first_name, last_name, is_test, verified)
|
||||
VALUES (:id, :phone, :email, :password, :gender, :birthdate, :site, :first_name, :last_name, 1, 1)");
|
||||
$insert->execute([
|
||||
':id' => $passengerId,
|
||||
':phone' => $encryptedPhone,
|
||||
':email' => $encryptedEmail,
|
||||
':password' => $hashedPassword,
|
||||
':gender' => $encryptedGender,
|
||||
':birthdate' => $encryptedBirthdate,
|
||||
':site' => $encryptedSite,
|
||||
':first_name' => $encryptedFirstName,
|
||||
':last_name' => $encryptedLastName
|
||||
]);
|
||||
|
||||
// Ensure phone_verification_passenger row exists
|
||||
$stmtPhone = $con->prepare("SELECT * FROM phone_verification_passenger WHERE phone_number = :phone LIMIT 1");
|
||||
$stmtPhone->bindParam(':phone', $encryptedPhone);
|
||||
$stmtPhone->execute();
|
||||
if (!$stmtPhone->fetch()) {
|
||||
$insertPhone = $con->prepare("INSERT INTO phone_verification_passenger (phone_number, verified) VALUES (:phone, 1)");
|
||||
$insertPhone->bindParam(':phone', $encryptedPhone);
|
||||
$insertPhone->execute();
|
||||
} else {
|
||||
$updatePhone = $con->prepare("UPDATE phone_verification_passenger SET verified = 1 WHERE phone_number = :phone");
|
||||
$updatePhone->bindParam(':phone', $encryptedPhone);
|
||||
$updatePhone->execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$sql = "SELECT
|
||||
p.*,
|
||||
phone_verification_passenger.verified,
|
||||
invitesToPassengers.isInstall,
|
||||
invitesToPassengers.inviteCode,
|
||||
invitesToPassengers.isGiftToken
|
||||
FROM passengers p
|
||||
LEFT JOIN phone_verification_passenger
|
||||
ON phone_verification_passenger.phone_number = p.phone
|
||||
LEFT JOIN invitesToPassengers
|
||||
ON invitesToPassengers.inviterPassengerPhone = p.phone
|
||||
WHERE p.email = :email
|
||||
LIMIT 1";
|
||||
|
||||
$stmt = $con->prepare($sql);
|
||||
$stmt->bindParam(':email', $encryptedEmail);
|
||||
$stmt->execute();
|
||||
|
||||
$data = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($data) {
|
||||
// فحص الباسورد
|
||||
if (password_verify($password, $data['password']) || $password === $data['password']) {
|
||||
// التحقق من أن الحساب معلم كحساب فحص في قاعدة البيانات أو البيئة
|
||||
$isTestInDb = (isset($data['is_test']) && $data['is_test'] == 1) || (isset($data['isTest']) && $data['isTest'] == 1);
|
||||
if (!$isTestInDb && !$isTester) {
|
||||
jsonError("Access denied. Not a tester account.");
|
||||
exit();
|
||||
}
|
||||
// فك تشفير البيانات للرد
|
||||
if(isset($data['phone'])) $data['phone'] = $encryptionHelper->decryptData($data['phone']);
|
||||
if(isset($data['email'])) $data['email'] = $encryptionHelper->decryptData($data['email']);
|
||||
if(isset($data['gender'])) $data['gender'] = $encryptionHelper->decryptData($data['gender']);
|
||||
if(isset($data['birthdate'])) $data['birthdate'] = $encryptionHelper->decryptData($data['birthdate']);
|
||||
if(isset($data['site'])) $data['site'] = $encryptionHelper->decryptData($data['site']);
|
||||
if(isset($data['first_name'])) $data['first_name'] = $encryptionHelper->decryptData($data['first_name']);
|
||||
if(isset($data['last_name'])) $data['last_name'] = $encryptionHelper->decryptData($data['last_name']);
|
||||
if(isset($data['sosPhone'])) $data['sosPhone'] = $encryptionHelper->decryptData($data['sosPhone']);
|
||||
if(isset($data['education'])) $data['education'] = $encryptionHelper->decryptData($data['education']);
|
||||
if(isset($data['employmentType'])) $data['employmentType'] = $encryptionHelper->decryptData($data['employmentType']);
|
||||
if(isset($data['maritalStatus'])) $data['maritalStatus'] = $encryptionHelper->decryptData($data['maritalStatus']);
|
||||
|
||||
// Force verified = 1 for the test user so the Rider app doesn't reject the login
|
||||
if (isset($data['is_test']) && $data['is_test'] == 1) {
|
||||
$data['verified'] = 1;
|
||||
}
|
||||
|
||||
// توليد الـ JWT بصلاحية (tester) لتميزهم عن المستخدمين الفعليين
|
||||
$jwtService = new JwtService($redis);
|
||||
$jwt = $jwtService->generateAccessToken($data['id'], 'tester', $audience, $fingerprint);
|
||||
|
||||
echo json_encode([
|
||||
"status" => "success",
|
||||
"jwt" => $jwt,
|
||||
"data" => [$data] // مطابق لنسق التطبيق الذي يتوقع مصفوفة
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
|
||||
} else {
|
||||
echo json_encode([
|
||||
"status" => "failure",
|
||||
"message" => "Invalid credentials"
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
echo json_encode([
|
||||
"status" => "failure",
|
||||
"message" => "Invalid credentials"
|
||||
]);
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("Error in loginUsingCredentialsWithoutGooglePassenger: " . $e->getMessage());
|
||||
echo json_encode([
|
||||
"status" => "failure",
|
||||
"message" => "Server error"
|
||||
]);
|
||||
}
|
||||
exit();
|
||||
@@ -0,0 +1,182 @@
|
||||
<?php
|
||||
// File: register_passenger.php
|
||||
|
||||
// إعدادات إظهار الأخطاء
|
||||
ini_set('display_errors', 0);
|
||||
error_reporting(E_ALL);
|
||||
$allowRegistration = true;
|
||||
require_once __DIR__ . '/../../connect.php';
|
||||
|
||||
// Rate Limiting: الحماية من البرمجيات الخبيثة والتسجيل العشوائي
|
||||
$rateLimiter = new RateLimiter($redis);
|
||||
$rateLimiter->enforce(RateLimiter::identifier(), 'register_passenger');
|
||||
|
||||
|
||||
// تعريف بادئة للوج (Tag) لسهولة البحث عنها في ملف الأخطاء
|
||||
$logTag = "[Register_Debug_passenger]";
|
||||
|
||||
$step = 0;
|
||||
|
||||
try {
|
||||
// ======================================================
|
||||
// Step 1: استقبال البيانات
|
||||
// ======================================================
|
||||
$step = 1;
|
||||
$phoneNumber = filterRequest("phone_number");
|
||||
$firstName = filterRequest("first_name");
|
||||
$lastName = filterRequest("last_name");
|
||||
$email = filterRequest("email");
|
||||
|
||||
// طباعة وصول البيانات (مع إخفاء جزء من الرقم)
|
||||
error_log("$logTag Step 1: Received request. Phone: " . substr($phoneNumber, 0, 7) . "*****");
|
||||
|
||||
// ======================================================
|
||||
// Step 2: التحقق من المدخلات
|
||||
// ======================================================
|
||||
$step = 2;
|
||||
if (empty($phoneNumber) || empty($firstName) || empty($lastName)) {
|
||||
error_log("$logTag Step 2 Error: Missing required fields.");
|
||||
jsonError("Required fields are missing.");
|
||||
exit();
|
||||
}
|
||||
|
||||
// ======================================================
|
||||
// Step 3: معالجة الإيميل
|
||||
// ======================================================
|
||||
$step = 3;
|
||||
if (empty($email)) {
|
||||
$email = $phoneNumber . '@intaleqapp.com';
|
||||
error_log("$logTag Step 3: Email was empty, generated default: " . substr($email, 0, 5) . "***");
|
||||
}
|
||||
|
||||
// ======================================================
|
||||
// Step 4: تشفير البيانات
|
||||
// ======================================================
|
||||
$step = 4;
|
||||
error_log("$logTag Step 4: Encrypting data...");
|
||||
|
||||
if (!isset($encryptionHelper)) {
|
||||
throw new Exception("Encryption Helper class is missing.");
|
||||
}
|
||||
|
||||
$phoneNumber_encrypted = $encryptionHelper->encryptData($phoneNumber);
|
||||
$firstName_encrypted = $encryptionHelper->encryptData($firstName);
|
||||
$lastName_encrypted = $encryptionHelper->encryptData($lastName);
|
||||
$email_encrypted = $encryptionHelper->encryptData($email);
|
||||
$password_hashed = password_hash($email, PASSWORD_DEFAULT);
|
||||
$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;
|
||||
// $uniqueId = substr(md5(uniqid(mt_rand(), true)), 0, 20);
|
||||
|
||||
$uniqueId = substr(md5($phoneNumber_encrypted), 0, 20);
|
||||
|
||||
error_log("$logTag Step 5: Generated Unique ID: $uniqueId");
|
||||
|
||||
// ======================================================
|
||||
// Step 6: التحقق من وجود المستخدم (Database Check)
|
||||
// ======================================================
|
||||
$step = 6;
|
||||
$checkStmt = $con->prepare("SELECT id FROM passengers WHERE phone = ?");
|
||||
$checkStmt->execute([$phoneNumber_encrypted]);
|
||||
|
||||
if ($checkStmt->rowCount() > 0) {
|
||||
error_log("$logTag Step 6 Error: User already exists.");
|
||||
jsonError("User with this phone number or email already exists.");
|
||||
exit();
|
||||
}
|
||||
|
||||
// ======================================================
|
||||
// Step 7: الإضافة (Insert User)
|
||||
// ======================================================
|
||||
$step = 7;
|
||||
error_log("$logTag Step 7: Inserting into passengers table...");
|
||||
|
||||
$insertStmt = $con->prepare("
|
||||
INSERT INTO passengers (id, first_name, last_name, email, phone, password, gender, birthdate, site, sosPhone, education, employmentType, maritalStatus, status, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'active', NOW(), NOW())
|
||||
");
|
||||
$success = $insertStmt->execute([
|
||||
$uniqueId,
|
||||
$firstName_encrypted,
|
||||
$lastName_encrypted,
|
||||
$email_encrypted,
|
||||
$phoneNumber_encrypted,
|
||||
$password_hashed,
|
||||
$unknown_encrypted,
|
||||
$unknown_encrypted,
|
||||
$unknown_encrypted,
|
||||
$unknown_encrypted,
|
||||
$unknown_encrypted,
|
||||
$unknown_encrypted,
|
||||
$unknown_encrypted
|
||||
]);
|
||||
|
||||
if (!$success) {
|
||||
$errorInfo = $insertStmt->errorInfo();
|
||||
// طباعة تفاصيل خطأ الـ SQL في اللوج
|
||||
error_log("$logTag Step 7 Error: SQL Insert Failed. Details: " . json_encode($errorInfo));
|
||||
jsonError("Failed to create user account.");
|
||||
exit();
|
||||
}
|
||||
|
||||
|
||||
// ======================================================
|
||||
// Step 9: جلب البيانات لإعادتها
|
||||
// ======================================================
|
||||
$step = 9;
|
||||
$userStmt = $con->prepare("SELECT * FROM passengers WHERE id = ?");
|
||||
$userStmt->execute([$uniqueId]);
|
||||
$newUser = $userStmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
// ======================================================
|
||||
// Step 10: فك التشفير وإرسال الرد
|
||||
// ======================================================
|
||||
$step = 10;
|
||||
if ($newUser) {
|
||||
unset($newUser['password']);
|
||||
foreach ($newUser as $key => &$value) {
|
||||
if ($key !== 'id' && $key !== 'status' && $key !== 'created_at' && $key !== 'updated_at' && !is_null($value)) {
|
||||
$value = $encryptionHelper->decryptData($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
error_log("$logTag Success: User registered successfully.");
|
||||
jsonSuccess(["status" => "registration_success", "data" => $newUser]);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
// طباعة خطأ قاعدة البيانات في اللوج
|
||||
error_log("$logTag PDO Exception at Step $step: " . $e->getMessage());
|
||||
jsonError("Database Error.");
|
||||
} catch (Exception $e) {
|
||||
// طباعة الأخطاء العامة في اللوج
|
||||
error_log("$logTag General Exception at Step $step: " . $e->getMessage());
|
||||
jsonError("General Error.");
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../connect.php';
|
||||
|
||||
$email = filterRequest("email");
|
||||
|
||||
$headers = "MIME-Version: 1.0" . "\r\n";
|
||||
$headers .= "Content-type: text/html; charset=UTF-8" . "\r\n";
|
||||
$headers .= "From: SEFER Team" . "\r\n";
|
||||
|
||||
// Create the email subject and body
|
||||
$subject = 'Your SEFER account has been deleted';
|
||||
$body = '
|
||||
|
||||
Dear passenger,
|
||||
|
||||
We are sorry to see you go, but we respect your decision to delete your SEFER account.
|
||||
|
||||
We would like to thank you for using our platform and for being a part of the SEFER community. We hope that you had a positive experience and that we were able to make your travels easier and more enjoyable.
|
||||
|
||||
If you have any questions or concerns, please do not hesitate to contact us.
|
||||
|
||||
Sincerely,
|
||||
|
||||
The SEFER Team
|
||||
';
|
||||
|
||||
// Send the email
|
||||
mail($email, $subject, $body);
|
||||
|
||||
?>
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../connect.php';
|
||||
|
||||
// Ensure the caller has a valid user_id
|
||||
if (empty($user_id)) {
|
||||
jsonError("Unauthorized", 401);
|
||||
exit;
|
||||
}
|
||||
|
||||
$latitude = filterRequest("latitude");
|
||||
$longitude = filterRequest("longitude");
|
||||
|
||||
// Validate inputs
|
||||
if ($latitude === '' || $longitude === '') {
|
||||
jsonError("Latitude and longitude are required", 400);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
// Insert location log
|
||||
$sql = "INSERT INTO `passenger_opening_locations` (`passenger_id`, `latitude`, `longitude`)
|
||||
VALUES (:passenger_id, :latitude, :longitude)";
|
||||
|
||||
$stmt = $con->prepare($sql);
|
||||
$stmt->bindParam(':passenger_id', $user_id);
|
||||
$stmt->bindParam(':latitude', $latitude);
|
||||
$stmt->bindParam(':longitude', $longitude);
|
||||
|
||||
if ($stmt->execute()) {
|
||||
jsonSuccess(null, "Location logged successfully");
|
||||
} else {
|
||||
jsonError("Failed to log location", 500);
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
error_log("Database Error in save_passenger_location.php: " . $e->getMessage());
|
||||
jsonError("An error occurred while logging location", 500);
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
// File: send_otp.php (بديل عن النسخة المعتمدة على RaseelPlus)
|
||||
require_once __DIR__ . '/../../connect.php';
|
||||
|
||||
/* 1) توليد رمز التحقق (3 خانات) */
|
||||
$otp = (string)random_int(100, 999);
|
||||
$receiver = filterRequest("receiver");
|
||||
|
||||
if (empty($receiver)) {
|
||||
jsonError('Phone number is required.');
|
||||
exit();
|
||||
}
|
||||
|
||||
/* 2) إرسال عبر بوابة الفلاش كول / واتساب */
|
||||
$nabehUrl = 'https://otp.intaleqapp.com/api/request-otp.php';
|
||||
$appKey = getenv('NABEH_OTP_APP_KEY');
|
||||
|
||||
$payload = [
|
||||
'phone' => $receiver,
|
||||
'device_type' => 'android',
|
||||
'method' => 'whatsapp',
|
||||
'code' => $otp
|
||||
];
|
||||
|
||||
$ch = curl_init($nabehUrl);
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_POSTFIELDS => json_encode($payload),
|
||||
CURLOPT_HTTPHEADER => [
|
||||
'Content-Type: application/json',
|
||||
"X-App-Key: $appKey"
|
||||
],
|
||||
CURLOPT_TIMEOUT => 15,
|
||||
CURLOPT_CONNECTTIMEOUT => 5
|
||||
]);
|
||||
|
||||
$res = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($error) {
|
||||
error_log("⚠️ [Flash Call OTP Token Passenger] Curl Error: $error");
|
||||
jsonError('Failed to connect to OTP service');
|
||||
exit;
|
||||
}
|
||||
|
||||
$decoded = json_decode((string)$res, true);
|
||||
$sentOK = ($httpCode === 200 && ($decoded['success'] ?? false));
|
||||
|
||||
if ($sentOK) {
|
||||
/* 3) حفظ الرمز في Redis + قاعدة البيانات */
|
||||
$receiver_enc = $encryptionHelper->encryptData($receiver);
|
||||
$otp_enc = $encryptionHelper->encryptData($otp);
|
||||
|
||||
$exp = date('Y-m-d H:i:s', strtotime('+5 minutes'));
|
||||
$now = date('Y-m-d H:i:s');
|
||||
|
||||
try {
|
||||
// Save to MySQL
|
||||
$con->prepare("DELETE FROM token_verification WHERE phone_number = ?")
|
||||
->execute([$receiver_enc]);
|
||||
|
||||
$stmt = $con->prepare("
|
||||
INSERT INTO token_verification
|
||||
(phone_number, token, expiration_time, verified, created_at)
|
||||
VALUES (?, ?, ?, 0, ?)
|
||||
");
|
||||
$stmt->execute([$receiver_enc, $otp_enc, $exp, $now]);
|
||||
|
||||
// Also save to Redis for verify_otp.php compatibility
|
||||
if ($redis) {
|
||||
$redis->setex("otp:passenger:$receiver", 300, $otp);
|
||||
}
|
||||
|
||||
jsonSuccess(null, 'OTP sent and saved successfully');
|
||||
|
||||
} catch (PDOException $e) {
|
||||
error_log("[send_otp.php] " . $e->getMessage());
|
||||
jsonError('OTP sent but failed to save to database');
|
||||
}
|
||||
|
||||
} else {
|
||||
$errMsg = $decoded['message'] ?? 'Unknown error';
|
||||
jsonError('Failed to send OTP');
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
// File: verify_otp.php (with enhanced logging)
|
||||
// siro_v1/auth/token_passenger
|
||||
require_once __DIR__ . '/../../connect.php';
|
||||
|
||||
// --- Start of Script Execution ---
|
||||
error_log("--- [verify_otp.php] Script execution started. ---");
|
||||
|
||||
$phoneNumber = filterRequest("phone_number");
|
||||
$otp = filterRequest("otp");
|
||||
|
||||
// Log received data for debugging. Be mindful of logging sensitive data in production.
|
||||
error_log("[verify_otp.php] Received phone_number: $phoneNumber | Received otp: $otp");
|
||||
|
||||
if (empty($phoneNumber) || empty($otp)) {
|
||||
error_log("[verify_otp.php] Error: Phone number or OTP is empty.");
|
||||
jsonError("Phone number and OTP are required.");
|
||||
exit();
|
||||
}
|
||||
|
||||
$phoneNumber_encrypted = $encryptionHelper->encryptData($phoneNumber);
|
||||
$otp_encrypted = $encryptionHelper->encryptData($otp);
|
||||
|
||||
try {
|
||||
// 1. التحقق من Redis بدلاً من MySQL
|
||||
if (!$redis) {
|
||||
jsonError("Security service unavailable");
|
||||
exit;
|
||||
}
|
||||
|
||||
$cachedOtp = $redis->get("otp:passenger:$phoneNumber");
|
||||
|
||||
if ($cachedOtp && $cachedOtp === $otp) {
|
||||
// ننجح في التحقق ونحذف المفتاح من Redis لمنع استخدامه مرة أخرى (One-time use)
|
||||
$redis->del("otp:passenger:$phoneNumber");
|
||||
|
||||
error_log("[verify_otp.php] OTP verified via Redis for phone: $phoneNumber");
|
||||
|
||||
// 2. التحقق من وجود الراكب في قاعدة البيانات
|
||||
$passengerStmt = $con->prepare("SELECT id FROM passengers WHERE phone = ?");
|
||||
$passengerStmt->execute([$phoneNumber_encrypted]);
|
||||
$passenger = $passengerStmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($passenger) {
|
||||
$passengerID = $passenger['id'];
|
||||
|
||||
// تحديث التوكن والبصمة إن وجدا
|
||||
$newToken = filterRequest("token");
|
||||
$fingerPrint = filterRequest("fingerPrint");
|
||||
|
||||
if ($newToken && $fingerPrint) {
|
||||
$tokenEncrypted = $encryptionHelper->encryptData($newToken);
|
||||
$updateTokenStmt = $con->prepare("UPDATE tokens SET token = ?, fingerPrint = ? WHERE passengerID = ?");
|
||||
$updateTokenStmt->execute([$tokenEncrypted, $fingerPrint, $passengerID]);
|
||||
}
|
||||
|
||||
printSuccess([
|
||||
"message" => "Token verified and updated.",
|
||||
"isRegistered" => true,
|
||||
"passengerID" => $passengerID
|
||||
]);
|
||||
|
||||
} else {
|
||||
printSuccess([
|
||||
"message" => "Phone verified, passenger not found.",
|
||||
"isRegistered" => false
|
||||
]);
|
||||
}
|
||||
|
||||
} else {
|
||||
error_log("[verify_otp.php] Invalid or expired OTP for phone: $phoneNumber");
|
||||
jsonError("Invalid or expired OTP.");
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
// Log the detailed database error message for debugging.
|
||||
error_log("[verify_otp.php] FATAL DATABASE ERROR: " . $e->getMessage());
|
||||
jsonError("Database error");
|
||||
}
|
||||
?>
|
||||
Reference in New Issue
Block a user