first commit
This commit is contained in:
47
backend/ride/firebase/add.php
Normal file
47
backend/ride/firebase/add.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/../../connect.php';
|
||||
|
||||
// استقبال المتغيرات
|
||||
$token = filterRequest("token"); // نص عادي ➜ سيتم تشفيره
|
||||
$passengerID = filterRequest("passengerID"); // ID عادي
|
||||
$fingerPrint = filterRequest("fingerPrint"); // مشفّر مسبقًا من Flutter ➜ لا يتم تشفيره هنا
|
||||
|
||||
// تشفير التوكن فقط
|
||||
$tokenEncrypted = $encryptionHelper->encryptData($token);
|
||||
|
||||
// التحقق مما إذا كان السجل موجودًا
|
||||
$sqlCheck = "SELECT * FROM `tokens` WHERE `passengerID` = :passengerID";
|
||||
$stmtCheck = $con->prepare($sqlCheck);
|
||||
$stmtCheck->bindParam(':passengerID', $passengerID);
|
||||
$stmtCheck->execute();
|
||||
|
||||
$result = $stmtCheck->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($result) {
|
||||
// تحديث السجل الموجود
|
||||
$sqlUpdate = "UPDATE `tokens` SET `token` = :token, `fingerPrint` = :fingerPrint WHERE `passengerID` = :passengerID";
|
||||
$stmtUpdate = $con->prepare($sqlUpdate);
|
||||
$stmtUpdate->bindParam(':token', $tokenEncrypted);
|
||||
$stmtUpdate->bindParam(':fingerPrint', $fingerPrint); // بدون تشفير إضافي
|
||||
$stmtUpdate->bindParam(':passengerID', $passengerID);
|
||||
$stmtUpdate->execute();
|
||||
|
||||
jsonSuccess(null, "Token updated successfully");
|
||||
|
||||
} else {
|
||||
// إدخال سجل جديد
|
||||
$sqlInsert = "INSERT INTO `tokens` (`token`, `passengerID`, `fingerPrint`) VALUES (:token, :passengerID, :fingerPrint)";
|
||||
$stmtInsert = $con->prepare($sqlInsert);
|
||||
$stmtInsert->bindParam(':token', $tokenEncrypted);
|
||||
$stmtInsert->bindParam(':passengerID', $passengerID);
|
||||
$stmtInsert->bindParam(':fingerPrint', $fingerPrint); // بدون تشفير إضافي
|
||||
$stmtInsert->execute();
|
||||
|
||||
if ($stmtInsert->rowCount() > 0) {
|
||||
jsonSuccess(null, "Token inserted successfully");
|
||||
} else {
|
||||
jsonError("Failed to insert token");
|
||||
}
|
||||
}
|
||||
?>
|
||||
46
backend/ride/firebase/addDriver.php
Normal file
46
backend/ride/firebase/addDriver.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/../../connect.php';
|
||||
|
||||
$token = filterRequest("token"); // نص عادي ➜ سيتم تشفيره
|
||||
$captain_id = filterRequest("captain_id"); // ID
|
||||
$fingerPrint = filterRequest("fingerPrint"); // مشفّر مسبقًا من Flutter
|
||||
|
||||
// تشفير التوكن فقط
|
||||
$tokenEncrypted = $encryptionHelper->encryptData($token);
|
||||
|
||||
// التحقق مما إذا كان السجل موجودًا
|
||||
$sqlCheck = "SELECT * FROM `driverToken` WHERE `captain_id` = :captain_id";
|
||||
$stmtCheck = $con->prepare($sqlCheck);
|
||||
$stmtCheck->bindParam(':captain_id', $captain_id);
|
||||
$stmtCheck->execute();
|
||||
|
||||
$result = $stmtCheck->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($result) {
|
||||
// تحديث السجل
|
||||
$sqlUpdate = "UPDATE `driverToken` SET `token` = :token, `fingerPrint` = :fingerPrint WHERE `captain_id` = :captain_id";
|
||||
$stmtUpdate = $con->prepare($sqlUpdate);
|
||||
$stmtUpdate->bindParam(':token', $tokenEncrypted);
|
||||
$stmtUpdate->bindParam(':fingerPrint', $fingerPrint); // بدون إعادة تشفير
|
||||
$stmtUpdate->bindParam(':captain_id', $captain_id);
|
||||
$stmtUpdate->execute();
|
||||
|
||||
jsonSuccess(null, "Token updated successfully");
|
||||
|
||||
} else {
|
||||
// إدخال سجل جديد
|
||||
$sqlInsert = "INSERT INTO `driverToken` (`token`, `captain_id`, `fingerPrint`) VALUES (:token, :captain_id, :fingerPrint)";
|
||||
$stmtInsert = $con->prepare($sqlInsert);
|
||||
$stmtInsert->bindParam(':token', $tokenEncrypted);
|
||||
$stmtInsert->bindParam(':captain_id', $captain_id);
|
||||
$stmtInsert->bindParam(':fingerPrint', $fingerPrint); // بدون إعادة تشفير
|
||||
$stmtInsert->execute();
|
||||
|
||||
if ($stmtInsert->rowCount() > 0) {
|
||||
jsonSuccess(null, "Token inserted successfully");
|
||||
} else {
|
||||
jsonError("Failed to insert token");
|
||||
}
|
||||
}
|
||||
?>
|
||||
47
backend/ride/firebase/addToken.php
Executable file
47
backend/ride/firebase/addToken.php
Executable file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/../../connect.php';
|
||||
|
||||
// استقبال المتغيرات
|
||||
$token = filterRequest("token"); // نص عادي ➜ سيتم تشفيره
|
||||
$passengerID = filterRequest("passengerID"); // ID عادي
|
||||
$fingerPrint = filterRequest("fingerPrint"); // مشفّر مسبقًا من Flutter ➜ لا يتم تشفيره هنا
|
||||
|
||||
// تشفير التوكن فقط
|
||||
$tokenEncrypted = $encryptionHelper->encryptData($token);
|
||||
|
||||
// التحقق مما إذا كان السجل موجودًا
|
||||
$sqlCheck = "SELECT * FROM `tokens` WHERE `passengerID` = :passengerID";
|
||||
$stmtCheck = $con->prepare($sqlCheck);
|
||||
$stmtCheck->bindParam(':passengerID', $passengerID);
|
||||
$stmtCheck->execute();
|
||||
|
||||
$result = $stmtCheck->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($result) {
|
||||
// تحديث السجل الموجود
|
||||
$sqlUpdate = "UPDATE `tokens` SET `token` = :token, `fingerPrint` = :fingerPrint WHERE `passengerID` = :passengerID";
|
||||
$stmtUpdate = $con->prepare($sqlUpdate);
|
||||
$stmtUpdate->bindParam(':token', $tokenEncrypted);
|
||||
$stmtUpdate->bindParam(':fingerPrint', $fingerPrint); // بدون تشفير إضافي
|
||||
$stmtUpdate->bindParam(':passengerID', $passengerID);
|
||||
$stmtUpdate->execute();
|
||||
|
||||
jsonSuccess(null, "Token updated successfully");
|
||||
|
||||
} else {
|
||||
// إدخال سجل جديد
|
||||
$sqlInsert = "INSERT INTO `tokens` (`token`, `passengerID`, `fingerPrint`) VALUES (:token, :passengerID, :fingerPrint)";
|
||||
$stmtInsert = $con->prepare($sqlInsert);
|
||||
$stmtInsert->bindParam(':token', $tokenEncrypted);
|
||||
$stmtInsert->bindParam(':passengerID', $passengerID);
|
||||
$stmtInsert->bindParam(':fingerPrint', $fingerPrint); // بدون تشفير إضافي
|
||||
$stmtInsert->execute();
|
||||
|
||||
if ($stmtInsert->rowCount() > 0) {
|
||||
jsonSuccess(null, "Token inserted successfully");
|
||||
} else {
|
||||
jsonError("Failed to insert token");
|
||||
}
|
||||
}
|
||||
?>
|
||||
17
backend/ride/firebase/delete.php
Normal file
17
backend/ride/firebase/delete.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/../../connect.php';
|
||||
|
||||
$id = filterRequest("id");
|
||||
|
||||
$sql = "DELETE FROM `tokens` WHERE `id` = :id";
|
||||
$stmt = $con->prepare($sql);
|
||||
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
|
||||
$stmt->execute();
|
||||
|
||||
if ($stmt->rowCount() > 0) {
|
||||
jsonSuccess(null, "Token deleted successfully");
|
||||
} else {
|
||||
jsonError("Failed to delete token");
|
||||
}
|
||||
?>
|
||||
277
backend/ride/firebase/fcm_fun.php
Executable file
277
backend/ride/firebase/fcm_fun.php
Executable file
@@ -0,0 +1,277 @@
|
||||
<?php
|
||||
// fcm_functions.php
|
||||
// مكتبة مركزية لإرسال إشعارات FCM (استدعاء داخلي - بدون HTTP)
|
||||
|
||||
// ============================================================================
|
||||
// دالة Base64 URL-Safe Encoding
|
||||
// ============================================================================
|
||||
function base64UrlEncode($data) {
|
||||
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// دالة الحصول على Access Token من Google OAuth2
|
||||
// ============================================================================
|
||||
function getFCMAccessToken($serviceAccountPath = null) {
|
||||
if (!$serviceAccountPath) {
|
||||
$serviceAccountPath = __DIR__ . '/service-account.json';
|
||||
}
|
||||
|
||||
if (!file_exists($serviceAccountPath)) {
|
||||
error_log("❌ FCM: service-account.json not found at: $serviceAccountPath");
|
||||
return null;
|
||||
}
|
||||
|
||||
$credentials = json_decode(file_get_contents($serviceAccountPath), true);
|
||||
$clientEmail = $credentials['client_email'];
|
||||
$privateKey = $credentials['private_key'];
|
||||
|
||||
$now = time();
|
||||
$header = base64UrlEncode(json_encode(['alg' => 'RS256', 'typ' => 'JWT']));
|
||||
$claim = base64UrlEncode(json_encode([
|
||||
'iss' => $clientEmail,
|
||||
'scope' => 'https://www.googleapis.com/auth/firebase.messaging',
|
||||
'aud' => 'https://oauth2.googleapis.com/token',
|
||||
'exp' => $now + 3600,
|
||||
'iat' => $now
|
||||
]));
|
||||
|
||||
$signature = '';
|
||||
openssl_sign("$header.$claim", $signature, $privateKey, 'SHA256');
|
||||
$jwt = "$header.$claim." . base64UrlEncode($signature);
|
||||
|
||||
$ch = curl_init("https://oauth2.googleapis.com/token");
|
||||
curl_setopt($ch, CURLOPT_POST, 1);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
|
||||
'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
|
||||
'assertion' => $jwt
|
||||
]));
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
||||
|
||||
$res = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode != 200) {
|
||||
error_log("❌ FCM OAuth Error ($httpCode): $res");
|
||||
return null;
|
||||
}
|
||||
|
||||
return json_decode($res, true)['access_token'] ?? null;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 🔥 الدالة الرئيسية: إرسال إشعار FCM (داخلي - بدون HTTP)
|
||||
// ============================================================================
|
||||
function sendFCMNotification($params) {
|
||||
// استخراج البارامترات
|
||||
$token = $params['token'] ?? null;
|
||||
$title = $params['title'] ?? '';
|
||||
$body = $params['body'] ?? '';
|
||||
$category = $params['category'] ?? '';
|
||||
$data = $params['data'] ?? [];
|
||||
$tone = $params['tone'] ?? 'default';
|
||||
$isTopic = $params['isTopic'] ?? false;
|
||||
$serviceAccountPath = $params['service_account_path'] ?? __DIR__ . '/service-account.json';
|
||||
|
||||
// التحقق من البيانات الأساسية
|
||||
if (empty($token) || empty($title) || empty($body)) {
|
||||
error_log("❌ FCM: Missing required fields (token, title, or body)");
|
||||
return [
|
||||
'success' => false,
|
||||
'error' => 'Missing required parameters',
|
||||
'http_code' => 400
|
||||
];
|
||||
}
|
||||
|
||||
// الحصول على Access Token
|
||||
$accessToken = getFCMAccessToken($serviceAccountPath);
|
||||
if (!$accessToken) {
|
||||
return [
|
||||
'success' => false,
|
||||
'error' => 'Failed to get Access Token',
|
||||
'http_code' => 500
|
||||
];
|
||||
}
|
||||
|
||||
// جلب Project ID
|
||||
$creds = json_decode(file_get_contents($serviceAccountPath), true);
|
||||
$projectId = $creds['project_id'];
|
||||
$fcmUrl = "https://fcm.googleapis.com/v1/projects/$projectId/messages:send";
|
||||
|
||||
// بناء الـ Payload
|
||||
$messagePayload = [
|
||||
'message' => [
|
||||
'notification' => [
|
||||
'title' => $title,
|
||||
'body' => $body
|
||||
],
|
||||
'android' => [
|
||||
'priority' => 'HIGH',
|
||||
'notification' => [
|
||||
'sound' => $tone,
|
||||
'channel_id' => 'high_importance_channel'
|
||||
]
|
||||
],
|
||||
'apns' => [
|
||||
'headers' => ['apns-priority' => '10'],
|
||||
'payload' => [
|
||||
'aps' => [
|
||||
'sound' => $tone . '.caf',
|
||||
'content-available' => 1
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
// تحديد الهدف
|
||||
if ($isTopic) {
|
||||
$messagePayload['message']['topic'] = $token;
|
||||
} else {
|
||||
$messagePayload['message']['token'] = $token;
|
||||
}
|
||||
|
||||
// إضافة الـ Data Payload
|
||||
$customData = ['category' => (string)$category];
|
||||
if (is_array($data) && !empty($data)) {
|
||||
$customData = array_merge($customData, $data);
|
||||
}
|
||||
|
||||
// تحويل كل القيم إلى String (FCM requirement)
|
||||
$processedData = [];
|
||||
foreach ($customData as $key => $val) {
|
||||
if (is_array($val) || is_object($val)) {
|
||||
$processedData[$key] = json_encode($val, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
} else {
|
||||
$processedData[$key] = (string)$val;
|
||||
}
|
||||
}
|
||||
$messagePayload['message']['data'] = $processedData;
|
||||
|
||||
// الإرسال إلى FCM
|
||||
$ch = curl_init($fcmUrl);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Authorization: Bearer ' . $accessToken,
|
||||
'Content-Type: application/json; charset=UTF-8'
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($messagePayload, JSON_UNESCAPED_UNICODE));
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
||||
|
||||
$result = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$curlError = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
// معالجة النتيجة
|
||||
if ($httpCode == 200) {
|
||||
error_log("✅ FCM Sent: Category=$category, Token=" . substr($token, 0, 15) . "...");
|
||||
return [
|
||||
'success' => true,
|
||||
'http_code' => $httpCode,
|
||||
'response' => json_decode($result, true)
|
||||
];
|
||||
} else {
|
||||
error_log("❌ FCM Error ($httpCode): $result | CURL: $curlError");
|
||||
return [
|
||||
'success' => false,
|
||||
'http_code' => $httpCode,
|
||||
'error' => json_decode($result, true),
|
||||
'curl_error' => $curlError
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 🎯 دوال مُساعدة جاهزة للاستخدام المباشر
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* إرسال إشعار "وصول السائق"
|
||||
*/
|
||||
function notifyDriverArrival($passengerToken, $driverName, $rideId) {
|
||||
return sendFCMNotification([
|
||||
'token' => $passengerToken,
|
||||
'title' => "السائق وصل إليك 📍",
|
||||
'body' => "$driverName في انتظارك الآن.",
|
||||
'category' => 'Arrive Ride',
|
||||
'tone' => 'tone1',
|
||||
'data' => [
|
||||
'ride_id' => (string)$rideId,
|
||||
'timestamp' => date('Y-m-d H:i:s')
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* إرسال إشعار "بدأت الرحلة"
|
||||
*/
|
||||
function notifyTripBegin($passengerToken, $driverName, $rideId) {
|
||||
return sendFCMNotification([
|
||||
'token' => $passengerToken,
|
||||
'title' => "بدأت الرحلة 🚗",
|
||||
'body' => "السائق $driverName بدأ رحلتك الآن.",
|
||||
'category' => 'Trip is Begin',
|
||||
'tone' => 'start',
|
||||
'data' => [
|
||||
'ride_id' => (string)$rideId,
|
||||
'start_time' => date('Y-m-d H:i:s')
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* إرسال إشعار "قبول الطلب"
|
||||
*/
|
||||
function notifyRideAccepted($passengerToken, $driverInfo, $rideId) {
|
||||
return sendFCMNotification([
|
||||
'token' => $passengerToken,
|
||||
'title' => "تم قبول الطلب 🚖",
|
||||
'body' => "الكابتن {$driverInfo['driverName']} قادم إليك.",
|
||||
'category' => 'Accepted Ride',
|
||||
'tone' => 'start',
|
||||
'data' => [
|
||||
'ride_id' => (string)$rideId,
|
||||
'driver_id' => (string)$driverInfo['driverId'],
|
||||
'driver_info' => $driverInfo // سيتم تحويلها لـ JSON تلقائياً
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* إرسال إشعار "إلغاء الرحلة من السائق"
|
||||
*/
|
||||
function notifyRideCancelled($passengerToken, $rideId, $reason = '') {
|
||||
return sendFCMNotification([
|
||||
'token' => $passengerToken,
|
||||
'title' => "تم إلغاء الرحلة ❌",
|
||||
'body' => "السائق اعتذر عن إكمال الرحلة.",
|
||||
'category' => 'Cancel Trip from driver',
|
||||
'tone' => 'cancel',
|
||||
'data' => [
|
||||
'ride_id' => (string)$rideId,
|
||||
'reason' => $reason,
|
||||
'cancelled_at' => date('Y-m-d H:i:s')
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* إرسال إشعار "انتهاء الرحلة"
|
||||
*/
|
||||
function notifyTripFinished($passengerToken, $tripData) {
|
||||
return sendFCMNotification([
|
||||
'token' => $passengerToken,
|
||||
'title' => "انتهت الرحلة 🏁",
|
||||
'body' => "شكرًا لاستخدامك تطبيق Tripz",
|
||||
'category' => 'Driver Finish Trip',
|
||||
'tone' => 'default',
|
||||
'data' => [
|
||||
'DriverList' => $tripData // Array سيتم تحويلها لـ JSON
|
||||
]
|
||||
]);
|
||||
}
|
||||
?>
|
||||
22
backend/ride/firebase/get.php
Normal file
22
backend/ride/firebase/get.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../connect.php';
|
||||
|
||||
$passengerID = filterRequest("passengerID");
|
||||
|
||||
// جلب السجل حسب passengerID
|
||||
$sql = "SELECT * FROM `tokens` WHERE `passengerID` = :passengerID";
|
||||
$stmt = $con->prepare($sql);
|
||||
$stmt->bindParam(':passengerID', $passengerID, PDO::PARAM_STR);
|
||||
$stmt->execute();
|
||||
|
||||
$data = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($data) {
|
||||
// فك تشفير التوكن فقط
|
||||
$data['token'] = $encryptionHelper->decryptData($data['token']);
|
||||
jsonSuccess($data);
|
||||
|
||||
} else {
|
||||
jsonError("No token found for this passenger");
|
||||
}
|
||||
?>
|
||||
23
backend/ride/firebase/getALlTokenDrivers.php
Executable file
23
backend/ride/firebase/getALlTokenDrivers.php
Executable file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../connect.php';
|
||||
|
||||
$sql = "SELECT * FROM `driverToken`";
|
||||
$stmt = $con->prepare($sql);
|
||||
$stmt->execute();
|
||||
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($data) {
|
||||
// فك تشفير token فقط لكل سجل
|
||||
foreach ($data as &$item) {
|
||||
$item['token'] = $encryptionHelper->decryptData($item['token']);
|
||||
// لا يتم فك تشفير fingerPrint لأنه مشفّر من Flutter
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'status' => 'success',
|
||||
'data' => $data
|
||||
]);
|
||||
} else {
|
||||
jsonError("No driver tokens found");
|
||||
}
|
||||
?>
|
||||
23
backend/ride/firebase/getAllTokenPassengers.php
Executable file
23
backend/ride/firebase/getAllTokenPassengers.php
Executable file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../connect.php';
|
||||
|
||||
$sql = "SELECT * FROM `tokens`";
|
||||
$stmt = $con->prepare($sql);
|
||||
$stmt->execute();
|
||||
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($data) {
|
||||
// فك تشفير token فقط
|
||||
foreach ($data as &$item) {
|
||||
$item['token'] = $encryptionHelper->decryptData($item['token']);
|
||||
// fingerPrint يبقى كما هو (مشفّر من التطبيق)
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'status' => 'success',
|
||||
'data' => $data
|
||||
]);
|
||||
} else {
|
||||
jsonError("No token records found");
|
||||
}
|
||||
?>
|
||||
26
backend/ride/firebase/getDriverToken.php
Normal file
26
backend/ride/firebase/getDriverToken.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../connect.php';
|
||||
|
||||
$captain_id = filterRequest("captain_id");
|
||||
|
||||
$sql = "SELECT * FROM `driverToken` WHERE `captain_id` = :captain_id";
|
||||
$stmt = $con->prepare($sql);
|
||||
$stmt->bindParam(':captain_id', $captain_id, PDO::PARAM_STR);
|
||||
$stmt->execute();
|
||||
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($data) {
|
||||
// فك تشفير token فقط
|
||||
foreach ($data as &$item) {
|
||||
$item['token'] = $encryptionHelper->decryptData($item['token']);
|
||||
// fingerPrint يبقى كما هو
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'status' => 'success',
|
||||
'data' => $data
|
||||
]);
|
||||
} else {
|
||||
jsonError("No driver token found");
|
||||
}
|
||||
?>
|
||||
45
backend/ride/firebase/getTokenParent.php
Normal file
45
backend/ride/firebase/getTokenParent.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../connect.php';
|
||||
|
||||
$phone = filterRequest("phone");
|
||||
|
||||
// 🔐 تشفير رقم الهاتف قبل البحث (لأنه مشفّر في قاعدة البيانات)
|
||||
$phoneEncrypted = $encryptionHelper->encryptData($phone);
|
||||
|
||||
// 1️⃣ جلب passengerID بناءً على رقم الهاتف
|
||||
$sql = "SELECT `id` FROM `passengers` WHERE `phone` = :phone";
|
||||
$stmt = $con->prepare($sql);
|
||||
$stmt->bindParam(':phone', $phoneEncrypted);
|
||||
$stmt->execute();
|
||||
$data = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($data) {
|
||||
$passengerID = $data['id'];
|
||||
} else {
|
||||
jsonError("No passenger found for the given phone number");
|
||||
exit;
|
||||
}
|
||||
|
||||
// 2️⃣ جلب التوكنات المرتبطة بـ passengerID
|
||||
$sql1 = "SELECT * FROM `tokens` WHERE `passengerID` = :passengerID";
|
||||
$stmt = $con->prepare($sql1);
|
||||
$stmt->bindParam(':passengerID', $passengerID);
|
||||
$stmt->execute();
|
||||
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($data) {
|
||||
// فك تشفير التوكن فقط
|
||||
foreach ($data as &$row) {
|
||||
$row['token'] = $encryptionHelper->decryptData($row['token']);
|
||||
// fingerPrint يبقى كما هو
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'status' => 'success',
|
||||
'count' => count($data),
|
||||
'data' => $data
|
||||
]);
|
||||
} else {
|
||||
jsonError("No tokens found for the passenger");
|
||||
}
|
||||
?>
|
||||
22
backend/ride/firebase/getTokensPassenger.php
Executable file
22
backend/ride/firebase/getTokensPassenger.php
Executable file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../connect.php';
|
||||
|
||||
$passengerID = filterRequest("passengerID");
|
||||
|
||||
// جلب السجل حسب passengerID
|
||||
$sql = "SELECT * FROM `tokens` WHERE `passengerID` = :passengerID";
|
||||
$stmt = $con->prepare($sql);
|
||||
$stmt->bindParam(':passengerID', $passengerID, PDO::PARAM_STR);
|
||||
$stmt->execute();
|
||||
|
||||
$data = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($data) {
|
||||
// فك تشفير التوكن فقط
|
||||
$data['token'] = $encryptionHelper->decryptData($data['token']);
|
||||
jsonSuccess($data);
|
||||
|
||||
} else {
|
||||
jsonError("No token found for this passenger");
|
||||
}
|
||||
?>
|
||||
41
backend/ride/firebase/notify_driver_arrival.php
Executable file
41
backend/ride/firebase/notify_driver_arrival.php
Executable file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
// simple_fcm_test.php
|
||||
// تجربة إرسال إشعار بسيط جداً (بدون قاعدة بيانات)
|
||||
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
|
||||
// 1. تضمين ملف الدوال (الذي يحتوي على sendFCM_Internal)
|
||||
if (file_exists("../../functions.php")) {
|
||||
include "../../functions.php";
|
||||
} else {
|
||||
die("❌ Error: functions.php not found!");
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// 🟢🟢 ضع التوكن الخاص بك هنا (بين علامات التنصيص) 🟢🟢
|
||||
// =================================================================
|
||||
$myToken = "dd35hHkHSOirLLu8luXGKz:APA91bHiW-HpEFMLAZB8nDKV5cT3IplNOYNSbQeIyieZ1KaEkjM1CjeQ8CY9JQcCNqdC8StoUAQGpn-birQiZz1xSndLObXox6O52bxgsUV_dCzr-7BOoR8";
|
||||
// =================================================================
|
||||
|
||||
|
||||
if ($myToken == "PASTE_YOUR_TOKEN_HERE") {
|
||||
die("⚠️ الرجاء وضع التوكن الخاص بك داخل ملف السكريبت في السطر 15");
|
||||
}
|
||||
|
||||
//echo "🚀 جاري إرسال الإشعار...\n";
|
||||
//echo "إلى: " . substr($myToken, 0, 20) . "...\n\n";
|
||||
|
||||
// 2. استدعاء دالة الإرسال
|
||||
$result = sendFCM_Internal(
|
||||
$myToken, // الهدف
|
||||
"تنبيه تجريبي 🔔", // العنوان
|
||||
"أنا وصلت للموقع 📍", // الرسالة (كما طلبت)
|
||||
['type' => 'test'], // بيانات إضافية بسيطة
|
||||
"General" // التصنيف
|
||||
);
|
||||
|
||||
// 3. طباعة النتيجة
|
||||
//echo "النتيجة:\n";
|
||||
print_r($result);
|
||||
|
||||
?>
|
||||
171
backend/ride/firebase/send_fcm.php
Executable file
171
backend/ride/firebase/send_fcm.php
Executable file
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
// send_fcm.php - FCM HTTP v1 Sender
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
$serviceAccountFile = __DIR__ . '/service-account.json';
|
||||
|
||||
// السماح فقط بـ POST
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(405);
|
||||
echo json_encode(['status' => 'error', 'message' => 'Only POST allowed.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// استقبال البيانات
|
||||
$json_input = file_get_contents('php://input');
|
||||
$requestData = json_decode($json_input, true);
|
||||
|
||||
$target = $requestData['target'] ?? null;
|
||||
$title = $requestData['title'] ?? null;
|
||||
$body = $requestData['body'] ?? null;
|
||||
$isTopic = $requestData['isTopic'] ?? false;
|
||||
$tone = $requestData['tone'] ?? 'default';
|
||||
$customData = $requestData['data'] ?? [];
|
||||
|
||||
if (!$target) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['status' => 'error', 'message' => 'Missing: target, title, or body.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// دالة Base64 URL-Safe Encoding (ضرورية للـ JWT)
|
||||
// ============================================================================
|
||||
function base64UrlEncode($data) {
|
||||
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// دالة المصادقة (Google OAuth2)
|
||||
// ============================================================================
|
||||
function getAccessToken($credentialsPath) {
|
||||
if (!file_exists($credentialsPath)) return null;
|
||||
|
||||
$credentials = json_decode(file_get_contents($credentialsPath), true);
|
||||
$clientEmail = $credentials['client_email'];
|
||||
$privateKey = $credentials['private_key'];
|
||||
|
||||
$now = time();
|
||||
$header = base64UrlEncode(json_encode(['alg' => 'RS256', 'typ' => 'JWT']));
|
||||
$claim = base64UrlEncode(json_encode([
|
||||
'iss' => $clientEmail,
|
||||
'scope' => 'https://www.googleapis.com/auth/firebase.messaging',
|
||||
'aud' => 'https://oauth2.googleapis.com/token',
|
||||
'exp' => $now + 3600,
|
||||
'iat' => $now
|
||||
]));
|
||||
|
||||
$signature = '';
|
||||
openssl_sign("$header.$claim", $signature, $privateKey, 'SHA256');
|
||||
$jwt = "$header.$claim." . base64UrlEncode($signature);
|
||||
|
||||
$ch = curl_init("https://oauth2.googleapis.com/token");
|
||||
curl_setopt($ch, CURLOPT_POST, 1);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
|
||||
'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
|
||||
'assertion' => $jwt
|
||||
]));
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
$res = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
|
||||
return json_decode($res, true)['access_token'] ?? null;
|
||||
}
|
||||
|
||||
// الحصول على Access Token
|
||||
$accessToken = getAccessToken($serviceAccountFile);
|
||||
if (!$accessToken) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['status' => 'error', 'message' => 'Failed to get Access Token.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// جلب Project ID
|
||||
$creds = json_decode(file_get_contents($serviceAccountFile), true);
|
||||
$projectId = $creds['project_id'];
|
||||
$fcmUrl = "https://fcm.googleapis.com/v1/projects/$projectId/messages:send";
|
||||
|
||||
// ============================================================================
|
||||
// بناء هيكل الرسالة
|
||||
// ============================================================================
|
||||
$messagePayload = [
|
||||
'message' => [
|
||||
'notification' => [
|
||||
'title' => $title,
|
||||
'body' => $body
|
||||
],
|
||||
'android' => [
|
||||
'priority' => 'HIGH',
|
||||
'notification' => [
|
||||
'sound' => $tone,
|
||||
'channel_id' => 'high_importance_channel' // تأكد من تطابقه مع Android
|
||||
]
|
||||
],
|
||||
'apns' => [
|
||||
'headers' => ['apns-priority' => '10'],
|
||||
'payload' => [
|
||||
'aps' => [
|
||||
'sound' => $tone . '.caf',
|
||||
'content-available' => 1
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
// تحديد الهدف (Topic أو Token)
|
||||
if ($isTopic) {
|
||||
$messagePayload['message']['topic'] = $target;
|
||||
} else {
|
||||
$messagePayload['message']['token'] = $target;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 🔥 معالجة Data Payload (يجب أن تكون String: String فقط)
|
||||
// ============================================================================
|
||||
if (!empty($customData)) {
|
||||
$processedData = [];
|
||||
foreach ($customData as $key => $val) {
|
||||
if (is_array($val) || is_object($val)) {
|
||||
// تحويل المصفوفات/الكائنات إلى JSON String
|
||||
$processedData[$key] = json_encode($val, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
} else {
|
||||
// تحويل أي قيمة أخرى إلى String
|
||||
$processedData[$key] = (string)$val;
|
||||
}
|
||||
}
|
||||
$messagePayload['message']['data'] = $processedData;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// الإرسال الفعلي إلى FCM
|
||||
// ============================================================================
|
||||
$ch = curl_init($fcmUrl);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Authorization: Bearer ' . $accessToken,
|
||||
'Content-Type: application/json; charset=UTF-8'
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($messagePayload, JSON_UNESCAPED_UNICODE));
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
|
||||
$result = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
// الرد
|
||||
if ($httpCode == 200) {
|
||||
echo json_encode([
|
||||
'status' => 'success',
|
||||
'message' => 'Notification sent successfully',
|
||||
'fcm_response' => json_decode($result)
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
} else {
|
||||
http_response_code($httpCode);
|
||||
echo json_encode([
|
||||
'status' => 'error',
|
||||
'message' => 'FCM request failed',
|
||||
'fcm_response' => json_decode($result)
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
?>
|
||||
Reference in New Issue
Block a user