projectId = config('services.firebase.project_id', 'animexeapp'); $this->serverKey = config('services.firebase.server_key'); } /** * Send push notification to a single FCM token. */ public function sendToToken(string $token, string $title, string $body, array $data = []): bool { if (!$this->serverKey) { Log::warning('FCM server key not configured'); return false; } try { $response = Http::withHeaders([ 'Authorization' => 'key=' . $this->serverKey, 'Content-Type' => 'application/json', ])->post('https://fcm.googleapis.com/fcm/send', [ 'to' => $token, 'notification' => [ 'title' => $title, 'body' => $body, 'sound' => 'default', ], 'data' => $data, 'priority' => 'high', ]); return $response->successful(); } catch (\Throwable $e) { Log::error('FCM send error: ' . $e->getMessage()); return false; } } /** * Send to multiple tokens (batch). */ public function sendToTokens(array $tokens, string $title, string $body, array $data = []): int { if (!$this->serverKey || empty($tokens)) return 0; $sent = 0; // FCM supports max 1000 tokens per batch foreach (array_chunk($tokens, 1000) as $chunk) { try { $response = Http::withHeaders([ 'Authorization' => 'key=' . $this->serverKey, 'Content-Type' => 'application/json', ])->post('https://fcm.googleapis.com/fcm/send', [ 'registration_ids' => $chunk, 'notification' => [ 'title' => $title, 'body' => $body, 'sound' => 'default', ], 'data' => $data, 'priority' => 'high', ]); if ($response->successful()) { $sent += count($chunk); } } catch (\Throwable $e) { Log::error('FCM batch send error: ' . $e->getMessage()); } } return $sent; } }