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

Firebase Cloud Messaging Bundle Laravel Package

fresh/firebase-cloud-messaging-bundle

Fresh/FirebaseCloudMessagingBundle integrates Firebase Cloud Messaging into Symfony applications, providing configuration and services to send push notifications to Android, iOS, and web clients through FCM.

View on GitHub
Deep Wiki
Context7

Getting Started

Before you type composer require, know what you're holding: this is a Symfony bundle wrapping the legacy FCM HTTP API (https://fcm.googleapis.com/fcm/send with Authorization: key=<server_key>), which Google shut down in June 2024. There is no ServiceProvider, no facade, no config publish tag. In a Laravel app this package cannot be consumed as-is — you either reach in and instantiate its internal sender service manually, or (correctly) read it as reference and write your own client.

Read this first (in order)

  1. Service/FirebaseCloudMessaging.php — the whole transport. Read top to bottom; it's small. Confirm whether it POSTs to the legacy endpoint or HTTP v1.
  2. DependencyInjection/Configuration.php — the only config keys (server_key, maybe api_url/timeout).
  3. Model/Message.php and Model/Notification.php — payload builders.
  4. Resources/config/services.yml — service ids (relevant only if you're on Symfony).
  5. composer.json — PHP constraint, Symfony component majors, Guzzle major. Do this before anything else; if it pins Symfony 3/4, Laravel 10/11 will refuse to resolve.

Same-day go/no-go check

composer require fresh/firebase-cloud-messaging-bundle --dry-run 2>&1 | head -30

If Composer throws a dependency resolution conflict with Laravel's pinned Symfony components, stop. It's not worth the adapter.

First legitmate use case

If you need push today on Laravel, the correct first move is not this package — it's kreait/firebase-php (FCM HTTP v1, OAuth2 service account) behind your own PushSender contract. This package's realistic first use case is: a 30-minute read to understand FCM payload shape, then close the tab.

Implementation Patterns

The only pattern worth writing: PushSender contract

Define your own boundary so the transport is swappable — you'll be swapping it, trust me.

// app/Contracts/PushSender.php
namespace App\Contracts;

interface PushSender
{
    public function send(PushMessage $message): SendResult;
}
// app/Support/Push/FirebaseV1PushSender.php
namespace App\Support\Push;

use App\Contracts\PushSender;
use Kreait\Firebase\Contract\Messaging;
use Kreait\Firebase\Messaging\CloudMessage;

class FirebaseV1PushSender implements PushSender
{
    public function __construct(private Messaging $messaging) {}

    public function send(PushMessage $message): SendResult
    {
        $cloud = CloudMessage::withTarget('token', $message->token)
            ->withNotification(['title' => $message->title, 'body' => $message->body])
            ->withData($message->data)
            ->withAndroidConfig(['priority' => 'high', 'ttl' => '3600s'])
            ->withApnsConfig(['headers' => ['apns-priority' => '10']]);

        $report = $this->messaging->send($cloud);

        return SendResult::fromFirebase($report); // maps UNREGISTERED/INVALID_ARGUMENT
    }
}

Queue every send — never send inline

// app/Jobs/SendPushNotification.php
class SendPushNotification implements ShouldQueue
{
    use Queueable, InteractsWithQueue;

    public int $tries = 5;

    public function __construct(public PushMessage $message) {}

    public function backoff(): array { return [5, 15, 60, 300]; }

    public function handle(PushSender $sender): void
    {
        $sender->send($this->message);
    }

    public function retryUntil() { return now()->addMinutes(30); }
}

Dispatch from notifications or events:

SendPushNotification::dispatch($message)->onQueue('push');

If you insist on adapting the Symfony bundle

The bundle has no Laravel provider. You'd manually bind its service:

// app/Providers/LegacyFcmServiceProvider.php
public function register(): void
{
    $this->app->singleton(\Fresh\FirebaseCloudMessagingBundle\Service\FirebaseCloudMessaging::class, function () {
        $client = new \GuzzleHttp\Client(['timeout' => 10]);
        return new \Fresh\FirebaseCloudMessagingBundle\Service\FirebaseCloudMessaging(
            config('services.fcm.server_key'),
            $client,
            config('services.fcm.api_url', 'https://fcm.googleapis.com/fcm/send'),
        );
    });
}

Then app(PushSender::class) binds an adapter. This will 401 or 410-Gone against current FCM if the bundle targets legacy. Verify before shipping anything.

Token lifecycle (mandatory, not optional)

// On SendResult failure codes
match ($result->errorCode) {
    'UNREGISTERED', 'INVALID_ARGUMENT' => $device->delete(),
    'QUOTA_EXCEEDED', 'UNAVAILABLE' => $this->release(60),
    default => $this->fail($result->exception),
};

Multicast batching

FCM HTTP v1 caps at 500 tokens per request. If you're fanning out to a segment, chunk:

collect($tokens)->chunk(500)->each(function ($chunk) {
    $this->messaging->sendMulticast(
        CloudMessage::new()->withNotification([...]),
        $chunk->all()
    );
});

Integration with Laravel notifications

Prefer a channel over direct job dispatch when the push is user-facing:

public function via($notifiable) { return [FcmChannel::class]; }

public function toFcm($notifiable): PushMessage
{
    return new PushMessage($notifiable->fcm_token, 'Order shipped', '...');
}

Gotchas and Tips

The API is dead — this is the only gotcha that matters

If the bundle POSTs to fcm.googleapis.com/fcm/send with a server key, it produces silent 401s in Google's response — messages vanish with no delivery signal. There is no fallback, no compat layer. Treat "which FCM API does it target?" as the decisive day-one question. Legacy = disqualified.

No Laravel container integration means no config, no facade, no php artisan vendor:publish

You will hand-wire everything. Any doc or AI suggestion that references config/firebase.php or a facade is hallucinated — it does not exist in this package.

Composer will likely fail before install completes

Laravel pins specific Symfony majors (http-foundation, http-kernel, console, process). A bundle from this era typically requires Symfony 3/4/5. composer require will conflict. If it does install, it installs a version of Symfony that may break unrelated packages in your lockfile.

Credential handling

The bundle uses a static server key in config/dotenv. Current FCM requires OAuth2 service-account credentials with token refresh. If you're stuck on legacy during a transition, rotate the server key and be aware it's the "everyone with the key can spam your users" model. Never commit it; never log it.

Payload quirks to know regardless of transport

  • 4KB cap on the whole payload. Fails at send time, not at build time — validate client-side.
  • Notification vs. data-only — data-only messages need high-priority Android config or Doze mode will defer them indefinitely.
  • APNs overrides are separate from Android config. iOS will silently drop messages without apns-priority / apns-push-type if the payload is malformed.
  • No idempotency key — a retried job on timeout can double-deliver. Instrument for it.

Debugging checklist when pushes "don't arrive"

  1. Log the raw response body and status code — 401 vs. 404 vs. 200-with-"results":[{"error":"MismatchSenderId"}] all look different and mean different things.
  2. Verify fcm.googleapis.com vs. fcm.googleapis.com/v1/projects/{id}/messages:send in the bundle source — this is your API generation clue.
  3. Test with a device token you know is live (install a fresh registration) — invalid tokens fail silently.
  4. Confirm OAuth token freshness if you've swapped to v1 — an expired token fails intermittently and is hard to reproduce.

Extension points that don't exist

The bundle is a thin wrapper — there's no middleware, no event dispatch, no retry policy, no rate-limit handling, no logging hooks. If you need observability or backoff you're either forking it or writing it yourself. Given the ~200–300 LOC of transport code involved, writing it yourself is cheaper than maintaining a fork.

Recommended replacement matrix

Need Use
FCM HTTP v1, batteries included kreait/firebase-php
Laravel notification channel laravel-notification-channels/fcm
Raw control, minimal deps google/auth + Guzzle, ~200 LOC
Managed provider (metrics, segmentation) OneSignal / Braze / Airship

Bottom line

Treat this package as reference material for FCM payload structure and HTTP shape, not a dependency. The score of 0, zero dependents, no visible maintainer, and the decommissioned API it almost certainly targets add up to "do not ship." Build a PushSender interface today, back it with a maintained v1 client, and put the boundary in place before you need a second provider.

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.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package