Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Fcm Notification Channel Laravel Package

ankurk91/fcm-notification-channel

Laravel notification channel for Firebase Cloud Messaging (FCM). Send push notifications to device tokens, topics, or conditions through Laravel's notification system, with fluent message builders and simple service account configuration.

View on GitHub
Deep Wiki
Context7

Getting Started

Before installing anything, confirm you actually can send a push: the package targets the legacy FCM HTTP API (https://fcm.googleapis.com/fcm/send) using a server key, and that API was turned off in mid-2024. If you are starting fresh today, this package will likely fail in production regardless of how well you wire it up. Treat the first task as "can I still authenticate against FCM at all?" — test a single curl against the endpoint with your server key before writing any app code.

If you have a working legacy key (or you're maintaining a legacy project that still has one), the minimal path is:

  1. Install the package. It was last released in Feb 2022, so it is not tested against Laravel 10/11. Pin your Laravel version or expect to bump composer.json constraints yourself.
    composer require ankurk91/fcm-notification-channel
    
  2. Publish the config. There is a config/fcm.php (server key, endpoint, timeout). Publish and inspect it before touching anything else — this is where the API URL lives, so it is the natural place to point at a proxy or a compatibility shim if you have one.
    php artisan vendor:publish --tag=fcm
    
  3. Put the credentials in .env. The server key typically comes from the Firebase console under Project Settings → Cloud Messaging. Never hardcode it in the config file.
  4. Give the notifiable a token column. Add fcm_token (nullable string, indexed) to your users table, or wherever you store device tokens.
  5. Tell Laravel how to find the token. On the Notifiable model:
    public function routeNotificationForFcm()
    {
        return $this->fcm_token;
    }
    
  6. Send your first notification. A minimal class implementing via() and toFcm():
    public function via($notifiable) { return [FcmChannel::class]; }
    
    public function toFcm($notifiable)
    {
        return FcmMessage::create()
            ->to($notifiable->fcm_token)
            ->data(['title' => 'Hi', 'body' => 'It works']);
    }
    
    Then $user->notify(new WelcomeNotification).

The first real use case is almost always transactional: "order shipped", "your ride is arriving", "new message" — a small payload with a title/body and a deep-link key in data.

If step 6 returns a 401/404 from Google, stop. You are almost certainly hitting the dead legacy endpoint and need a different transport (HTTP v1 via a service account, or a maintained channel such as laravel-notification-channels/fcm v2 / kreait/laravel-firebase).

Implementation Patterns

Single device vs. topic vs. condition. The message builder exposes targeting at the message level, and this is the main lever you'll pull:

// Single device
FcmMessage::create()->to($token);

// Fan-out to a topic (subscribed client-side)
FcmMessage::create()->toTopic('news');

// Boolean expression over topics
FcmMessage::create()->toCondition("'news' in topics && 'sports' in topics");

Use toTopic() for broadcast-style features (everyone who opted into a category) and to() for per-user transactional pushes. Conditions are powerful but painful to debug — reserve them for genuinely combinatorial audience rules.

Data-only vs. notification payloads. Decide this early, because Android and iOS behave very differently:

  • A notification payload is rendered by the OS when the app is backgrounded and the app can't easily suppress it.
  • A data-only payload wakes the app and lets your client decide whether to display anything, but requires more client-side work (and on iOS, content-available handling).

Most teams end up data-only for control, or notification payloads for simplicity. Pick one and make it a lint rule in the notification classes.

Queue the notification, not the channel. This is standard Laravel, but it matters more here because FCM round-trips are slow:

class OrderShipped extends Notification implements ShouldQueue
{
    public $tries = 3;
    public $backoff = [10, 60, 300];
}

Retries interact badly with fan-out sends (you can double-deliver to topics). If a notification targets a topic, keep $tries = 1 or accept duplicates.

One notification, multiple channels. The real value of the notification system is that FCM sits alongside mail/database:

public function via($notifiable)
{
    return $notifiable->wantsPush()
        ? [FcmChannel::class, 'database']
        : ['database'];
}

Guard on user preference and on token presence — sending to a user with fcm_token = null wastes a request and can log noise.

Token lifecycle. Register tokens on login/app start, and clean them up on logout. The package surfaces FCM's "not registered" responses via exceptions on failure; wire this into your token store:

public function failed($notifiable, $exception) { /* remove dead token */ }

For batch sends, wrap the loop and delete tokens that come back as NotRegistered / InvalidRegistration.

Test with fakes. Standard Laravel:

Notification::fake();
$user->notify(new OrderShipped($order));
Notification::assertSentTo($user, OrderShipped::class);

This is the only sane way to test the channel, because actually running the transport requires live credentials.

Custom messages per audience. Extract builders so notification classes stay readable:

FcmMessage::create()
    ->to($token)
    ->priority('high')
    ->timeToLive(3600)
    ->data(['type' => 'order', 'id' => (string) $order->id]);

Gotchas and Tips

  • The API is dead — this is the headline. The package was archived in 2022 and targets the legacy FCM endpoint that Google shut down. Before you write a single notification, verify with curl that your key still works. If it doesn't, the correct move is a migration to the HTTP v1 API (service account + OAuth2), which this package does not implement. A maintained alternative is the right call for new work.
  • Archived means "no fixes." Laravel 10/11/12 compatibility, PSR-4 warnings, and PHP 8.2 deprecations are yours to patch. Fork it if you keep using it.
  • data values must be strings. FCM rejects integers/booleans in data. Cast aggressively: (string) $order->id, never $order->id.
  • 4KB payload limit. This bites people sending base64 thumbnails or long lists. Send an ID and have the client fetch the rest.
  • Server key vs. service account. The legacy "server key" is not the same credential as the HTTP v1 service account JSON. Don't paste one where the other is expected — the error is a generic 401 and easy to misdiagnose.
  • Config caching. If you deploy with php artisan config:cache, changing the key in .env at runtime does nothing. Always config:clear when debugging credentials.
  • Priority and TTL silently affect delivery. priority('high') for time-sensitive pushes; set a TTL on anything where a stale push is worse than no push (e.g. "your table is ready"). Without it FCM may deliver hours late.
  • Topic fan-out is throttled. Subscribing or sending to a very large topic takes time and the call can time out. Raise the HTTP timeout in config or dispatch to a queue with a generous timeout.
  • routeNotificationForFcm returning an array sends to multiple tokens in one call — convenient for multi-device users, but a single bad token can affect the batch, and per-token error reporting gets fuzzy. Prefer one call per live token when you need precise invalidation.
  • iOS/Android payload divergence. notification blocks are rendered by the OS; data blocks are not. The same payload will look different across platforms and across app foreground/background states. Test on real devices, not just via Notification::fake().
  • Extension points are thin. The main overrides are (a) swapping FcmChannel for your own subclass in via(), and (b) the message builder itself. If you need HTTP v1, signing, or per-tenant service accounts, you're writing a parallel channel from scratch rather than extending this one.
  • Logging. The package throws a CouldNotSendNotification-style exception on non-2xx; log the response body in your notification's failed() hook. Google's error strings ("SenderId mismatch", "MismatchSenderId") are the fastest path to the real problem.
  • Deprecation risk on your other dependencies. If you fork or hold this package at the 2022 release, it will pin related packages. Isolate it in a small service or at least a single module so a future migration is a two-file change, not a codebase-wide search.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource