'RS256', 'typ' => 'JWT']); $now = time(); $payload = json_encode([ 'iss' => $clientEmail, 'scope' => 'https://www.googleapis.com/auth/firebase.messaging', 'aud' => 'https://oauth2.googleapis.com/token', 'exp' => $now + 3600, 'iat' => $now, ]); $base64UrlHeader = self::base64UrlEncode($header); $base64UrlPayload = self::base64UrlEncode($payload); $signatureInput = $base64UrlHeader . '.' . $base64UrlPayload; $signature = ''; if (!openssl_sign($signatureInput, $signature, $privateKey, OPENSSL_ALGO_SHA256)) { Log::error("Failed to sign JWT with private key using openssl_sign."); return null; } $base64UrlSignature = self::base64UrlEncode($signature); $jwtToken = $signatureInput . '.' . $base64UrlSignature; $response = Http::asForm()->post('https://oauth2.googleapis.com/token', [ 'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer', 'assertion' => $jwtToken, ]); if ($response->failed()) { Log::error("FCM OAuth2 request failed: " . $response->body()); return null; } $data = $response->json(); return $data['access_token'] ?? null; } catch (\Exception $e) { Log::error("FCM Access Token Generation Error: " . $e->getMessage()); return null; } }); } /** * Send a Push Notification to a specific FCM token. * * @param string $token * @param string $title * @param string $body * @param array $data * @return bool */ public static function sendPushNotification($token, $title, $body, array $data = []) { if (empty($token)) { return false; } try { $accessToken = self::getAccessToken(); if (!$accessToken) { Log::error("Cannot send push notification: FCM Access Token is null."); return false; } // Load credentials to fetch the correct project_id $credentialsPath = base_path('migrant-fe5a7-firebase-adminsdk-fbsvc-f8b365ecd0.json'); $credentials = json_decode(file_get_contents($credentialsPath), true); $projectId = $credentials['project_id'] ?? 'migrant-fe5a7'; // Convert all data array values to strings as required by FCM v1 $formattedData = []; foreach ($data as $key => $value) { $formattedData[(string)$key] = (string)$value; } $payload = [ 'message' => [ 'token' => $token, 'notification' => [ 'title' => $title, 'body' => $body, ], ] ]; if (!empty($formattedData)) { $payload['message']['data'] = $formattedData; } $response = Http::withToken($accessToken) ->post("https://fcm.googleapis.com/v1/projects/{$projectId}/messages:send", $payload); if ($response->failed()) { Log::error("FCM Push Notification failed: " . $response->body()); return false; } return true; } catch (\Exception $e) { Log::error("FCM Notification Dispatch Failure: " . $e->getMessage()); return false; } } /** * Base64 Url Encode helper. * * @param string $data * @return string */ private static function base64UrlEncode($data) { return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($data)); } }