PHPackages                             swanflutter/notification-master - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Mail &amp; Notifications](/categories/mail)
4. /
5. swanflutter/notification-master

ActiveLibrary[Mail &amp; Notifications](/categories/mail)

swanflutter/notification-master
===============================

A modern PHP 8.2+ / Laravel push notification package supporting Firebase FCM HTTP v1 and polling mode.

1.0.3(1mo ago)136MITPHPPHP ^8.2CI passing

Since Jul 13Pushed 1mo agoCompare

[ Source](https://github.com/SwanFlutter/notification-master)[ Packagist](https://packagist.org/packages/swanflutter/notification-master)[ Docs](https://github.com/swanflutter/notification-master)[ RSS](/packages/swanflutter-notification-master/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (4)Dependencies (12)Versions (5)Used By (0)

swanflutter/notification-master
===============================

[](#swanflutternotification-master)

A modern, production-ready **PHP 8.2+ / Laravel** push notification package built on the **Firebase Cloud Messaging (FCM) HTTP v1 API**.

Supports real-time FCM delivery **and** a polling-mode fallback — all with a clean fluent API, typed exceptions, and first-class Laravel integration.

[![Latest Version on Packagist](https://camo.githubusercontent.com/afb6ba4b67a1952330791c9030b46ebd06bde80e5e402284d6819d80d2178bf1/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f7377616e666c75747465722f6e6f74696669636174696f6e2d6d61737465722e737667)](https://packagist.org/packages/swanflutter/notification-master)[![PHP Version](https://camo.githubusercontent.com/0f16581d1180dbfd4c0e13166ec1267d4ad2f2fab8281ea6d6b284cf5c65d921/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e322532422d626c75652e737667)](https://www.php.net/)[![License: MIT](https://camo.githubusercontent.com/784362b26e4b3546254f1893e778ba64616e362bd6ac791991d2c9e880a3a64e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d677265656e2e737667)](LICENSE)

---

Table of Contents
-----------------

[](#table-of-contents)

- [Requirements](#requirements)
- [Installation](#installation)
    - [Laravel Setup](#laravel-setup)
    - [Standalone PHP Setup](#standalone-php-setup)
- [Configuration](#configuration)
- [Usage — FCM Push](#usage--fcm-push)
    - [Send to a Device Token](#send-to-a-device-token)
    - [Send to a Topic](#send-to-a-topic)
    - [Send to a Condition](#send-to-a-condition)
    - [Multicast (Multiple Tokens)](#multicast-multiple-tokens)
    - [Advanced Message Builder](#advanced-message-builder)
    - [Raw Payload](#raw-payload)
    - [Dry-Run Mode](#dry-run-mode)
    - [Retry Configuration](#retry-configuration)
- [Usage — Laravel Notification Channel](#usage--laravel-notification-channel)
- [Usage — Laravel Facade](#usage--laravel-facade)
- [Usage — Polling Mode](#usage--polling-mode)
    - [How Polling Works](#how-polling-works)
    - [Enqueue a Notification](#enqueue-a-notification)
    - [Client Polling Endpoint](#client-polling-endpoint)
    - [Flush &amp; Forward via FCM](#flush--forward-via-fcm)
    - [Custom Poll Store](#custom-poll-store)
- [Exception Handling](#exception-handling)
- [Testing](#testing)
- [Changelog](#changelog)
- [License](#license)

---

Requirements
------------

[](#requirements)

DependencyVersionPHP`^8.2``guzzlehttp/guzzle``^7.9``swanflutter/native-jwt``^1.2`Laravel (optional)`^10.0 | ^11.0 | ^12.0`> **Note:** This package uses [`swanflutter/native-jwt`](https://packagist.org/packages/swanflutter/native-jwt) for JWT signing (RS256). It is a drop-in, dependency-free replacement for `firebase/php-jwt` with built-in `alg=none` and algorithm-confusion protection. The old `firebase/php-jwt` dependency has been removed.

---

Installation
------------

[](#installation)

Install the package via Composer:

```
composer require swanflutter/notification-master
```

To install a specific version:

```
composer require "swanflutter/notification-master:^1.0.2"
```

#### Resolving a JWT library conflict

[](#resolving-a-jwt-library-conflict)

This package depends on [`swanflutter/native-jwt`](https://packagist.org/packages/swanflutter/native-jwt)for RS256 JWT signing. If your application still requires the deprecated `firebase/php-jwt`, you can migrate to `swanflutter/native-jwt`, which is a drop-in replacement exposing the same `JWT::encode()` / `JWT::decode()` API:

```
composer remove firebase/php-jwt
composer require "swanflutter/native-jwt:^1.2"
```

If your application or another dependency still pins `firebase/php-jwt`, run `composer why firebase/php-jwt` to find which package holds it back.

### Laravel Setup

[](#laravel-setup)

The package is **auto-discovered**. After installing, publish the config file:

```
php artisan vendor:publish --tag=push-notification-config
```

This creates `config/push-notification.php` in your application.

Set the path to your Firebase service-account JSON file in `.env`:

```
FIREBASE_CREDENTIALS=/absolute/path/to/service-account.json
```

> The service-account JSON is generated in the [Firebase Console](https://console.firebase.google.com/) under **Project Settings → Service Accounts → Generate New Private Key**.

### Standalone PHP Setup

[](#standalone-php-setup)

No framework integration required — just instantiate `FirebasePush` directly:

```
use SwanFlutter\NotificationMaster\FirebasePush;

$push = new FirebasePush('/path/to/service-account.json');

// or pass the decoded array directly:
$push = new FirebasePush([
    'type'         => 'service_account',
    'project_id'   => 'my-project',
    'private_key'  => '-----BEGIN RSA PRIVATE KEY-----...',
    'client_email' => 'firebase-adminsdk@my-project.iam.gserviceaccount.com',
]);
```

---

Configuration
-------------

[](#configuration)

`config/push-notification.php` (after publishing):

```
return [
    // Path to your Firebase service-account JSON (or the decoded array).
    'credentials' => env('FIREBASE_CREDENTIALS', storage_path('app/firebase/service-account.json')),

    // Validate payload without sending to devices (staging/testing).
    'dry_run' => env('PUSH_DRY_RUN', false),

    // Automatic retries on transient errors (429 / 5xx). Set 0 to disable.
    'retries'        => env('PUSH_RETRIES', 1),
    'retry_delay_ms' => env('PUSH_RETRY_DELAY_MS', 500),

    // Polling mode store driver: 'cache' (default) or 'array' (in-memory).
    'poll_store' => env('PUSH_POLL_STORE', 'cache'),

    // TTL in seconds for the cache-backed poll store.
    'poll_ttl' => env('PUSH_POLL_TTL', 86400),
];
```

---

Usage — FCM Push
----------------

[](#usage--fcm-push)

### Send to a Device Token

[](#send-to-a-device-token)

```
use SwanFlutter\NotificationMaster\FirebasePush;
use SwanFlutter\NotificationMaster\Message;

$push = new FirebasePush(config('push-notification.credentials'));

$push->sendToToken(token: 'device-registration-token', title: 'Hello', body: 'World');
```

### Send to a Topic

[](#send-to-a-topic)

```
$push->sendToTopic(topic: 'news', title: 'Breaking News', body: 'Something just happened.');
```

### Send to a Condition

[](#send-to-a-condition)

```
use SwanFlutter\NotificationMaster\Message;

$push->send(
    Message::create()
        ->toCondition("'sports' in topics || 'tech' in topics")
        ->title('Multi-topic alert')
        ->body('This goes to sports and tech subscribers.')
);
```

### Multicast (Multiple Tokens)

[](#multicast-multiple-tokens)

```
$tokens = ['token-1', 'token-2', 'token-3'];

$results = $push->sendToMany($tokens, Message::create()
    ->title('Broadcast')
    ->body('Hello everyone!')
);

foreach ($results as $token => $result) {
    if ($result['success']) {
        echo "Sent to {$token}\n";
    } else {
        echo "Failed for {$token}: {$result['error']}\n";
    }
}
```

### Advanced Message Builder

[](#advanced-message-builder)

```
use SwanFlutter\NotificationMaster\Message;

$message = Message::create()
    ->toToken('device-token')
    ->title('Flash Sale!')
    ->body('50% off — today only.')
    ->image('https://cdn.example.com/banner.jpg')
    ->data([
        'route'      => 'shop',
        'product_id' => '42',
    ])
    ->ttl(3600)
    ->android([
        'priority'     => 'high',
        'notification' => [
            'channel_id' => 'promotions',
            'sound'      => 'default',
        ],
    ])
    ->apns([
        'payload' => [
            'aps' => [
                'sound' => 'default',
                'badge' => 1,
            ],
        ],
    ])
    ->webpush([
        'notification' => ['icon' => '/icon.png'],
    ])
    ->analyticsLabel('flash_sale_july');

$push->send($message);
```

### Raw Payload

[](#raw-payload)

Send any FCM-compatible payload directly, bypassing the builder:

```
$push->sendRaw([
    'message' => [
        'token'        => 'device-token',
        'notification' => ['title' => 'Raw', 'body' => 'Payload'],
    ],
]);
```

### Dry-Run Mode

[](#dry-run-mode)

```
// Messages are validated but NOT delivered.
$push->dryRun()->send($message);
```

### Retry Configuration

[](#retry-configuration)

```
// 3 retries, 1 second base delay (doubled each attempt).
$push->withRetries(3, 1000)->send($message);
```

---

Usage — Laravel Notification Channel
------------------------------------

[](#usage--laravel-notification-channel)

Add `toFcm()` to your Laravel notification class:

```
use Illuminate\Notifications\Notification;
use SwanFlutter\NotificationMaster\Message;

class OrderShipped extends Notification
{
    public function via(object $notifiable): array
    {
        return ['fcm'];
    }

    public function toFcm(object $notifiable): Message
    {
        return Message::create()
            ->title('Your order has shipped!')
            ->body("Order #{$this->order->id} is on its way.")
            ->data(['order_id' => (string) $this->order->id]);
    }
}
```

Your notifiable model must implement `routeNotificationForFcm()` if no target is set on the Message:

```
// In your User model:
public function routeNotificationForFcm(): string
{
    return $this->fcm_token;
}
```

Then send it like any other Laravel notification:

```
$user->notify(new OrderShipped($order));
```

---

Usage — Laravel Facade
----------------------

[](#usage--laravel-facade)

```
use SwanFlutter\NotificationMaster\Laravel\Facades\PushNotification;
use SwanFlutter\NotificationMaster\Message;

PushNotification::sendToToken(token: 'device-token', title: 'Hi', body: 'Hello from facade!');

PushNotification::send(
    Message::create()->toTopic('alerts')->title('Alert')->body('Pay attention.')
);
```

---

Usage — Polling Mode
--------------------

[](#usage--polling-mode)

Polling mode stores notifications server-side so that client devices can retrieve them on demand via a regular HTTP call. No native push infrastructure required.

### How Polling Works

[](#how-polling-works)

```
Backend                       Client Device
  │                                │
  │── enqueue(userId, message) ──▶ │  (notification stored)
  │                                │
  │◀── GET /api/notifications ──── │  (device polls every N seconds)
  │── poll(userId) ──────────────▶ │  (returns & marks as delivered)

```

### Enqueue a Notification

[](#enqueue-a-notification)

```
use SwanFlutter\NotificationMaster\Polling\NotificationPoller;
use SwanFlutter\NotificationMaster\Message;

$poller = app(NotificationPoller::class);

$id = $poller->enqueue(
    recipientId: (string) $user->id,
    message: Message::create()
        ->title('New message')
        ->body('You have a new message from Alice.')
        ->data(['chat_id' => '99']),
);
```

Schedule delivery for a future time:

```
$poller->enqueue(
    recipientId: (string) $user->id,
    message: $message,
    deliverAt: new \DateTimeImmutable('+30 minutes'),
);
```

### Client Polling Endpoint

[](#client-polling-endpoint)

```
// routes/api.php
Route::middleware('auth:sanctum')->get('/notifications/poll', function (Request $request) {
    $poller        = app(\SwanFlutter\NotificationMaster\Polling\NotificationPoller::class);
    $notifications = $poller->poll((string) $request->user()->id);

    return response()->json(['notifications' => $notifications]);
});
```

### Flush &amp; Forward via FCM

[](#flush--forward-via-fcm)

Deliver all queued notifications immediately via FCM and mark them delivered:

```
use SwanFlutter\NotificationMaster\Contracts\PushNotificationInterface;
use SwanFlutter\NotificationMaster\Polling\NotificationPoller;

$results = app(NotificationPoller::class)->flushAndSend(
    deviceToken: $user->fcm_token,
    recipientId: (string) $user->id,
    sender:      app(PushNotificationInterface::class),
);
```

### Custom Poll Store

[](#custom-poll-store)

Implement `PollStoreInterface` to use your own storage backend (Eloquent, Redis, SQS, …):

```
use SwanFlutter\NotificationMaster\Polling\Store\PollStoreInterface;

class DatabasePollStore implements PollStoreInterface
{
    public function push(string $recipientId, array $notification): void
    {
        DB::table('push_notifications')->insert([
            'recipient_id' => $recipientId,
            'payload'      => json_encode($notification),
            'delivered'    => false,
            'created_at'   => now(),
        ]);
    }

    public function pending(string $recipientId): array
    {
        return DB::table('push_notifications')
            ->where('recipient_id', $recipientId)
            ->where('delivered', false)
            ->get()
            ->map(fn($row) => json_decode($row->payload, true))
            ->toArray();
    }

    public function markDelivered(string $recipientId, array $ids): void
    {
        DB::table('push_notifications')
            ->where('recipient_id', $recipientId)
            ->whereIn(DB::raw("JSON_UNQUOTE(JSON_EXTRACT(payload, '$.id'))"), $ids)
            ->update(['delivered' => true]);
    }

    public function purge(string $recipientId): void
    {
        DB::table('push_notifications')
            ->where('recipient_id', $recipientId)
            ->delete();
    }
}
```

Bind it in your `AppServiceProvider`:

```
use SwanFlutter\NotificationMaster\Polling\Store\PollStoreInterface;

$this->app->bind(PollStoreInterface::class, DatabasePollStore::class);
```

---

Exception Handling
------------------

[](#exception-handling)

All exceptions extend `SwanFlutter\NotificationMaster\Exceptions\PushNotificationException`.

ExceptionWhen thrown`InvalidCredentialsException`Service-account file missing, unreadable, or malformed`AuthenticationException`JWT signing or Google OAuth token fetch failed`InvalidMessageException`Message missing a target or content`SendingFailedException`FCM HTTP request failed (network / FCM error); includes `getFcmResponse()````
use SwanFlutter\NotificationMaster\Exceptions\SendingFailedException;
use SwanFlutter\NotificationMaster\Exceptions\PushNotificationException;

try {
    $push->send($message);
} catch (SendingFailedException $e) {
    // FCM-level error
    logger()->error('FCM error', [
        'message' => $e->getMessage(),
        'fcm'     => $e->getFcmResponse(),
    ]);
} catch (PushNotificationException $e) {
    // Any other package error (credentials, auth, validation)
    logger()->error('Push error: ' . $e->getMessage());
}
```

---

Testing
-------

[](#testing)

```
composer test
```

For unit tests, use `ArrayPollStore` as a lightweight in-memory poll store:

```
use SwanFlutter\NotificationMaster\Polling\NotificationPoller;
use SwanFlutter\NotificationMaster\Polling\Store\ArrayPollStore;
use SwanFlutter\NotificationMaster\Message;

$store  = new ArrayPollStore();
$poller = new NotificationPoller($store);

$poller->enqueue('user-1', Message::create()->toToken('t')->title('Hi')->body('Test'));

$pending = $poller->poll('user-1');
assert(count($pending) === 1);
assert($poller->count('user-1') === 0); // marked delivered
```

---

Changelog
---------

[](#changelog)

See [CHANGELOG.md](CHANGELOG.md) for a full history of changes.

---

License
-------

[](#license)

This package is open-source software licensed under the [MIT License](LICENSE).

###  Health Score

42

—

FairBetter than 88% of packages

Maintenance91

Actively maintained with recent releases

Popularity12

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity49

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

4

Last Release

44d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/a80f10ad30fd4667dbe9edb01324f6c8014dfc8fe2ff0daa03474de5b2ddf18c?d=identicon)[SwanFlutter](/maintainers/SwanFlutter)

---

Top Contributors

[![SwanFlutter](https://avatars.githubusercontent.com/u/151648897?v=4)](https://github.com/SwanFlutter "SwanFlutter (19 commits)")

---

Tags

phplaravelnotificationfirebaseFCMpush notificationpolling

###  Code Quality

TestsPHPUnit

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/swanflutter-notification-master/health.svg)

```
[![Health](https://phpackages.com/badges/swanflutter-notification-master/health.svg)](https://phpackages.com/packages/swanflutter-notification-master)
```

###  Alternatives

[laravel/socialite

Laravel wrapper around OAuth 1 &amp; OAuth 2 libraries.

5.7k118.2M1.0k](/packages/laravel-socialite)[ellaisys/aws-cognito

Laravel authentication with AWS Cognito, supporting web, API, SSO, MFA, WebAuthn, passkeys, and passwordless authentication.

122284.0k1](/packages/ellaisys-aws-cognito)[civicrm/civicrm-core

Open source constituent relationship management for non-profits, NGOs and advocacy organizations.

769306.5k56](/packages/civicrm-civicrm-core)[xeroapi/xero-php-oauth2

Xero official PHP SDK for oAuth2 generated with OpenAPI spec 3

1075.1M21](/packages/xeroapi-xero-php-oauth2)[eslazarev/wildberries-sdk

Wildberries OpenAPI clients (generated).

353.6k](/packages/eslazarev-wildberries-sdk)[kedniko/firebase-cloud-messaging-http-v1-php

Firebase cloud messaging http v1 php

126.4k](/packages/kedniko-firebase-cloud-messaging-http-v1-php)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
