278 lines
9.5 KiB
PHP
Executable File
278 lines
9.5 KiB
PHP
Executable File
<?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
|
|
]
|
|
]);
|
|
}
|
|
?>
|