'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 ] ]); } ?>