PHPackages                             badawy24/pushify - 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. badawy24/pushify

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

badawy24/pushify
================

Laravel multi-provider push notification package with Firebase and OneSignal support.

1.0.10(1mo ago)0191MITPHPPHP ^8.2

Since Apr 27Pushed 1mo agoCompare

[ Source](https://github.com/backend-2022/pushify)[ Packagist](https://packagist.org/packages/badawy24/pushify)[ Docs](https://github.com/backend-2022/pushify)[ RSS](/packages/badawy24-pushify/feed)WikiDiscussions main Synced 3w ago

READMEChangelogDependencies (12)Versions (10)Used By (0)

Pushify
=======

[](#pushify)

[![Latest Version on Packagist](https://camo.githubusercontent.com/87a319d4db08a56ea512a1195960f267f25a2796ba83f74f38b9f4c12f118363/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f62616461777932342f707573686966792e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/badawy24/pushify) [![PHP Version](https://camo.githubusercontent.com/0137db7634f2bfb58931a1788223a4a9136100008a2ee84e758b20b9c6ec21a0/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d253545382e322d626c75653f7374796c653d666c61742d737175617265)](https://www.php.net) [![Laravel Version](https://camo.githubusercontent.com/d595a13461fa4fb5ff354efcf503be0ab4b47a805ead05c98e29a1bd428294ae/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c61726176656c2d25354531322e302d7265643f7374796c653d666c61742d737175617265)](https://laravel.com) [![License: MIT](https://camo.githubusercontent.com/422db9fd40f5831c765cf6530b6750c081b696bd18d904cf89554df98c676277/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d677265656e3f7374796c653d666c61742d737175617265)](LICENSE)

> Laravel backend package for sending push notifications through one selected provider at a time, with OneSignal device subscription management built in.

Supported providers:

ProviderSchedulingSubscriptions🔥 Firebase Cloud Messaging (FCM)Via scheduled command—📣 OneSignalNative `send_after`✅ Device register &amp; logout---

Project Structure
-----------------

[](#project-structure)

```
pushify/
├── config/
│   └── pushify.php
├── database/
│   └── migrations/
│       ├── 2026_01_01_000000_create_pushify_notifications_table.php
│       ├── 2026_01_02_000000_create_pushify_subscriptions_table.php
│       └── 2026_01_03_000000_add_pushify_external_id_to_users_table.php
├── routes/
│   └── pushify.php
├── src/
│   ├── Commands/
│   │   └── SendScheduledPushifyNotifications.php
│   ├── Concerns/
│   │   └── HasPushifyExternalId.php
│   ├── Contracts/
│   │   ├── PushifyProviderInterface.php
│   │   ├── PushifyServiceInterface.php
│   │   └── PushifySubscriptionsInterface.php
│   ├── Factories/
│   │   └── PushifyProviderFactory.php
│   ├── Http/
│   │   ├── Controllers/
│   │   │   └── PushifyController.php
│   │   ├── Requests/
│   │   │   └── StorePushifyRequest.php
│   │   └── Resources/
│   │       └── PushifyResource.php
│   ├── Models/
│   │   ├── Pushify.php
│   │   └── PushifySubscription.php
│   ├── Providers/
│   │   ├── FirebaseProvider.php
│   │   └── OneSignalProvider.php
│   ├── Services/
│   │   ├── FirebaseService.php
│   │   ├── OneSignalService.php
│   │   ├── PushifyService.php
│   │   └── PushifySubscriptionsService.php
│   ├── Support/
│   │   └── PushifyExternalIdGenerator.php
│   └── PushifyServiceProvider.php
├── stubs/
│   ├── Http/
│   │   ├── Controllers/
│   │   │   └── PushifyController.stub
│   │   ├── Requests/
│   │   │   └── StorePushifyRequest.stub
│   │   └── Resources/
│   │       └── PushifyResource.stub
│   └── routes/
│       └── pushify.stub
└── composer.json

```

---

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

[](#table-of-contents)

- [Requirements](#requirements)
- [Installation](#installation)
- [Publish](#publish)
- [Migration](#migration)
- [Configuration](#configuration)
- [Routes](#routes)
- [Usage](#usage)
    - [Notifications](#notifications)
    - [Subscriptions (OneSignal)](#subscriptions-onesignal)
- [Notification Statuses](#notification-statuses)
- [Scheduled Command](#scheduled-command)
- [Customizing the HTTP Layer](#customizing-the-http-layer)
- [Adding a Custom Provider](#adding-a-custom-provider)
- [Store Endpoint Payload](#store-endpoint-payload)
- [Response Structure](#response-structure)
- [Authors](#authors)
- [License](#license)

---

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

[](#requirements)

- PHP &gt;= 8.2
- Laravel &gt;= 12.0
- OpenSSL PHP extension *(required for Firebase JWT signing — no external Google SDK needed)*
- A `users` table *(required before running Pushify migrations)*

---

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

[](#installation)

```
composer require badawy24/pushify
```

Then clear the cache:

```
php artisan optimize:clear
```

---

Publish
-------

[](#publish)

### Publish everything at once

[](#publish-everything-at-once)

```
php artisan vendor:publish --tag=pushify
```

This publishes only the files you are expected to edit:

```
config/pushify.php
routes/pushify.php
app/Http/Controllers/Pushify/PushifyController.php
app/Http/Requests/Pushify/StorePushifyRequest.php
app/Http/Resources/Pushify/PushifyResource.php

```

> Core services, providers, factories, models, commands, and contracts stay inside the package and are never published.

### Publish separately

[](#publish-separately)

```
# Config only
php artisan vendor:publish --tag=pushify-config

# Routes only
php artisan vendor:publish --tag=pushify-routes

# Controller, Request, Resource
php artisan vendor:publish --tag=pushify-http
```

---

Migration
---------

[](#migration)

> **Important:** Your `users` table must exist before running migrations. If it does not, the migration will abort with a clear error message.

```
php artisan migrate
```

### Tables created / modified

[](#tables-created--modified)

**`pushify_notifications`** — notification log:

```
id, title, body, image, data, scheduled_at,
status, sent_at, failed_at, error_message,
created_at, updated_at

```

**`pushify_subscriptions`** — registered devices:

```
id, external_id, device_token, subscription_id,
device_type, created_at, updated_at

```

**`users`** — column added:

```
pushify_external_id   (nullable, unique)   e.g. AGFGFFGY_1

```

The `pushify_external_id` is generated automatically on first use: `8 random chars` + `_` + `user_id`.

---

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

[](#configuration)

Published config file: `config/pushify.php`

Only one provider is active at a time, selected from `.env`.

### 🔥 Firebase

[](#-firebase)

```
PUSHIFY_PROVIDER=firebase
FIREBASE_CREDENTIALS=storage/firebase/firebase.json
```

Place your Firebase service account JSON file at `storage/firebase/firebase.json`.

Firebase sends to a topic — default is `all`. Configure it in the published config:

```
'firebase' => [
    'topic' => 'all',
],
```

> Your mobile app must subscribe devices to the same topic.

### 📣 OneSignal

[](#-onesignal)

```
PUSHIFY_PROVIDER=onesignal
ONESIGNAL_APP_ID=your-app-id
ONESIGNAL_API_KEY=your-api-key
ONESIGNAL_API_URL=https://api.onesignal.com/notifications
```

OneSignal `sendToAll` sends to `included_segments => ['All']`.

### Users (external ID)

[](#users-external-id)

```
PUSHIFY_USERS_TABLE=users
PUSHIFY_EXTERNAL_ID_COLUMN=pushify_external_id
```

### Optional

[](#optional)

```
# Log full request payload — disable in production
PUSHIFY_LOG_PAYLOAD=false

# Disable package routes if you prefer to define your own
PUSHIFY_ROUTES_ENABLED=true

# Change the route prefix (default: pushify)
PUSHIFY_ROUTE_PREFIX=pushify
```

---

Routes
------

[](#routes)

The package registers these routes automatically:

```
GET  /pushify                  List all notifications (paginated)
POST /pushify                  Create and send
GET  /pushify/{pushify}        Show one
POST /pushify/{pushify}/send   Send an existing notification

```

Check registered routes:

```
php artisan route:list | grep pushify
```

### Adding Auth Middleware

[](#adding-auth-middleware)

After publishing, open `routes/pushify.php` and update the middleware:

```
Route::prefix(config('pushify.routes.prefix', 'pushify'))
    ->middleware(['api', 'auth:sanctum'])
    ->group(function () {
        // routes...
    });
```

---

Usage
-----

[](#usage)

The package exposes two separate interfaces:

InterfacePurpose`PushifyServiceInterface`Send notifications`PushifySubscriptionsInterface`Register &amp; remove devices *(OneSignal only)*Both are bound automatically — **no manual binding required**.

---

### Notifications

[](#notifications)

#### Inject the service

[](#inject-the-service)

```
use Badawy\Pushify\Contracts\PushifyServiceInterface;
```

#### Send to all

[](#send-to-all)

```
$notification = $push->sendToAll(
    title: 'New offer',
    body: 'Check our latest offers now',
    data: [
        'type'     => 'offer',
        'offer_id' => 15,
    ],
    image: 'https://example.com/image.jpg',
    scheduledAt: null,
);
```

#### Send to a specific user

[](#send-to-a-specific-user)

Use `sendToUserById()` with your local `user_id` — the package resolves `pushify_external_id` from the `users` table automatically:

```
$notification = $push->sendToUserById(
    userIds: $user->id,
    title: 'Order updated',
    body: 'Your order status has changed',
    data: [
        'type'     => 'order_status_updated',
        'order_id' => 15,
    ],
);
```

OneSignal delivers to **all devices** registered under that user's `external_id`.

You can also pass `external_id` strings directly via `sendToUser()`:

```
$notification = $push->sendToUser(
    userIds: [$user->pushifyExternalId()],
    title: 'Order updated',
    body: 'Your order status has changed',
    data: ['type' => 'order_status_updated'],
);
```

#### Schedule a notification

[](#schedule-a-notification)

```
$notification = $push->sendToAll(
    title: 'Upcoming sale',
    body: 'Our sale starts in one hour',
    data: ['type' => 'sale'],
    image: null,
    scheduledAt: now()->addHour()->toDateTimeString(),
);
```

ProviderBehavior🔥 FirebaseSaved as `pending` — sent by the command when `scheduled_at create([
    'title'        => 'Draft notification',
    'body'         => 'Notification body',
    'data'         => ['type' => 'general'],
    'image'        => null,
    'scheduled_at' => null,
]);
```

#### Send an existing notification

[](#send-an-existing-notification)

```
use Badawy\Pushify\Models\Pushify;

$notification = Pushify::findOrFail($id);
$notification = $push->send($notification);
```

#### `PushifyServiceInterface` methods

[](#pushifyserviceinterface-methods)

```
public function create(array $payload): Pushify;

public function sendToAll(
    string $title,
    string $body,
    array $data = [],
    ?string $image = null,
    ?string $scheduledAt = null
): Pushify;

public function sendToUser(
    array|string $userIds,
    string $title,
    string $body,
    array $data = [],
    ?string $image = null,
    ?string $scheduledAt = null
): Pushify;

public function sendToUserById(
    array|int $userIds,
    string $title,
    string $body,
    array $data = [],
    ?string $image = null,
    ?string $scheduledAt = null
): Pushify;

public function send(Pushify $notification): Pushify;

public function markScheduledAsSent(): int;
```

---

### Subscriptions (OneSignal)

[](#subscriptions-onesignal)

> Requires `PUSHIFY_PROVIDER=onesignal`.

#### Setup — add trait to your User model

[](#setup--add-trait-to-your-user-model)

```
use Badawy\Pushify\Concerns\HasPushifyExternalId;

class User extends Authenticatable
{
    use HasPushifyExternalId;
}
```

#### Inject the service

[](#inject-the-service-1)

```
use Badawy\Pushify\Contracts\PushifySubscriptionsInterface;
```

#### Full flow

[](#full-flow)

```
class AuthController extends Controller
{
    public function __construct(
        private readonly PushifySubscriptionsInterface $subscriptions,
        private readonly PushifyServiceInterface $pushify,
    ) {}

    // 1. Login — register device
    public function registerDevice(Request $request)
    {
        $subscription = $this->subscriptions->subscribe(
            userId: $request->user()->id,
            token: $request->input('device_token'),
            data: ['type' => 'Android'],
        );

        return response()->json($subscription);
    }

    // 2. Send notification (from anywhere in your app)
    public function notifyUser(int $userId)
    {
        $this->pushify->sendToUserById(
            userIds: $userId,
            title: 'Hello',
            body: 'You have a new notification',
            data: ['type' => 'general'],
        );
    }

    // 3. Logout — remove device
    public function logout(Request $request)
    {
        $this->subscriptions->unsubscribe($request->input('device_token'));

        // ... your logout logic
    }
}
```

#### What happens internally

[](#what-happens-internally)

StepMethodActionLogin`subscribe($userId, $token)`Generates `pushify_external_id` on `users`, registers device on OneSignal, saves `device_token` + `subscription_id` in `pushify_subscriptions`Notify`sendToUserById($userId, ...)`Resolves `pushify_external_id` from `users`, sends via OneSignal to all user devicesLogout`unsubscribe($deviceToken)`Deletes subscription from OneSignal, removes row from `pushify_subscriptions`#### `PushifySubscriptionsInterface` methods

[](#pushifysubscriptionsinterface-methods)

```
public function subscribe(int $userId, string $token, array $data = []): PushifySubscription;

public function unsubscribe(string $deviceToken): void;
```

#### Optional `$data` fields for `subscribe`

[](#optional-data-fields-for-subscribe)

FieldDescription`type`Device type, e.g. `Android`, `iOS``language`User language, e.g. `ar``timezone_id`e.g. `Africa/Cairo``country`e.g. `EG``tags`Key-value tags array`device_model`e.g. `iPhone 15``device_os`e.g. `iOS 18``app_version`e.g. `1.0.0`---

### Quick test via Tinker

[](#quick-test-via-tinker)

```
php artisan tinker
```

```
// Register device
app(\Badawy\Pushify\Contracts\PushifySubscriptionsInterface::class)
    ->subscribe(1, 'your-fcm-token', ['type' => 'Android']);

// Send notification
app(\Badawy\Pushify\Contracts\PushifyServiceInterface::class)
    ->sendToUserById(1, 'Hello', 'Test from Tinker', ['type' => 'test']);

// Logout device
app(\Badawy\Pushify\Contracts\PushifySubscriptionsInterface::class)
    ->unsubscribe('your-fcm-token');
```

---

Notification Statuses
---------------------

[](#notification-statuses)

StatusMeaning`pending`Stored, not sent yet`processing`Currently being dispatched to the provider`scheduled`Submitted to OneSignal with a future `send_after``sent`Successfully delivered to the provider`failed`Failed — see `error_message` column in the database---

Scheduled Command
-----------------

[](#scheduled-command)

```
php artisan pushify:send-scheduled
```

ProviderWhat it does🔥 FirebaseSends all `pending` notifications where `scheduled_at everyMinute();
```

Or via cron:

```
* * * * * php /path/to/project/artisan pushify:send-scheduled >> /dev/null 2>&1
```

---

Customizing the HTTP Layer
--------------------------

[](#customizing-the-http-layer)

After publishing with `--tag=pushify-http`, you can freely edit:

FilePurpose`app/Http/Controllers/Pushify/PushifyController.php`Request handling &amp; response`app/Http/Requests/Pushify/StorePushifyRequest.php`Validation rules &amp; authorization`app/Http/Resources/Pushify/PushifyResource.php`JSON output shapeThe published controller injects `PushifyServiceInterface` — extend or replace any logic without touching the package internals.

---

Adding a Custom Provider
------------------------

[](#adding-a-custom-provider)

**Step 1 — Create your provider class:**

```
namespace App\Pushify\Providers;

use Badawy\Pushify\Contracts\PushifyProviderInterface;

class CustomProvider implements PushifyProviderInterface
{
    public function sendToAll(
        string $title,
        string $body,
        array $data = [],
        ?string $image = null,
        ?string $scheduledAt = null
    ): array {
        return ['success' => true];
    }

    public function sendToUser(
        array|string $userIds,
        string $title,
        string $body,
        array $data = [],
        ?string $image = null,
        ?string $scheduledAt = null
    ): array {
        return ['success' => true];
    }
}
```

**Step 2 — Register it in `config/pushify.php`:**

```
'providers' => [
    'firebase'  => \Badawy\Pushify\Providers\FirebaseProvider::class,
    'onesignal' => \Badawy\Pushify\Providers\OneSignalProvider::class,
    'custom'    => \App\Pushify\Providers\CustomProvider::class,
],
```

**Step 3 — Activate it in `.env`:**

```
PUSHIFY_PROVIDER=custom
```

---

Store Endpoint Payload
----------------------

[](#store-endpoint-payload)

`POST /pushify`

```
{
    "title": "New offer",
    "body": "Check our latest offers",
    "image": "https://example.com/image.jpg",
    "data": {
        "type": "offer",
        "offer_id": 15
    },
    "scheduled_at": null
}
```

Scheduled example:

```
{
    "title": "Scheduled offer",
    "body": "This will be sent later",
    "image": null,
    "data": { "type": "offer" },
    "scheduled_at": "2026-06-01 09:00:00"
}
```

---

Response Structure
------------------

[](#response-structure)

All endpoints return a consistent JSON envelope:

```
{
    "data": {
        "id": 1,
        "title": "New offer",
        "body": "Check our latest offers",
        "image": "https://example.com/image.jpg",
        "data": { "type": "offer", "offer_id": "15" },
        "scheduled_at": null,
        "status": "sent",
        "sent_at": "2026-01-01T12:00:00+00:00",
        "failed_at": null,
        "created_at": "2026-01-01T11:59:00+00:00",
        "updated_at": "2026-01-01T12:00:00+00:00"
    }
}
```

---

Authors
-------

[](#authors)

[Badawy](https://github.com/Badawy24) · [Hassan](https://github.com/hassanmostfa)

---

License
-------

[](#license)

MIT

###  Health Score

43

—

FairBetter than 89% of packages

Maintenance91

Actively maintained with recent releases

Popularity10

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity52

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 69.2% 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 ~6 days

Recently: every ~0 days

Total

9

Last Release

43d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/88353210?v=4)[Abdelrahman Badawy](/maintainers/badawy24)[@Badawy24](https://github.com/Badawy24)

---

Top Contributors

[![Badawy24](https://avatars.githubusercontent.com/u/88353210?v=4)](https://github.com/Badawy24 "Badawy24 (9 commits)")[![hassanmostfa](https://avatars.githubusercontent.com/u/113209768?v=4)](https://github.com/hassanmostfa "hassanmostfa (2 commits)")[![Kareem154](https://avatars.githubusercontent.com/u/129125061?v=4)](https://github.com/Kareem154 "Kareem154 (2 commits)")

---

Tags

laravelnotificationsfirebaseFCMpush notificationonesignalpush notifications

### Embed Badge

![Health badge](/badges/badawy24-pushify/health.svg)

```
[![Health](https://phpackages.com/badges/badawy24-pushify/health.svg)](https://phpackages.com/packages/badawy24-pushify)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3345.3M347](/packages/psalm-plugin-laravel)[laravel/pulse

Laravel Pulse is a real-time application performance monitoring tool and dashboard for your Laravel application.

1.7k15.1M136](/packages/laravel-pulse)[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

9762.4M133](/packages/roots-acorn)[mike-bronner/laravel-model-caching

Automatic caching for Eloquent models.

2.4k96.5k1](/packages/mike-bronner-laravel-model-caching)[api-platform/laravel

API Platform support for Laravel

58174.6k17](/packages/api-platform-laravel)[aedart/athenaeum

Athenaeum is a mono repository; a collection of various PHP packages

255.2k](/packages/aedart-athenaeum)

PHPackages © 2026

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