badawy24 / pushify
Laravel multi-provider push notification package with Firebase and OneSignal support.
Requires
- php: ^8.2
- illuminate/cache: ^12.0
- illuminate/console: ^12.0
- illuminate/database: ^12.0
- illuminate/http: ^12.0
- illuminate/routing: ^12.0
- illuminate/support: ^12.0
This package is not auto-updated.
Last update: 2026-04-28 10:13:19 UTC
README
Laravel backend package for sending push notifications through one selected provider at a time.
Supported providers:
| Provider | Scheduling |
|---|---|
| ๐ฅ Firebase Cloud Messaging (FCM) | Via scheduled command |
| ๐ฃ OneSignal | Native send_after |
Project Structure
pushify/
โโโ config/
โ โโโ pushify.php
โโโ database/
โ โโโ migrations/
โ โโโ 2026_01_01_000000_create_pushify_notifications_table.php
โโโ routes/
โ โโโ pushify.php
โโโ src/
โ โโโ Commands/
โ โ โโโ SendScheduledPushifyNotifications.php
โ โโโ Contracts/
โ โ โโโ PushifyProviderInterface.php
โ โ โโโ PushifyServiceInterface.php
โ โโโ Factories/
โ โ โโโ PushifyProviderFactory.php
โ โโโ Http/
โ โ โโโ Controllers/
โ โ โ โโโ PushifyController.php
โ โ โโโ Requests/
โ โ โ โโโ StorePushifyRequest.php
โ โ โโโ Resources/
โ โ โโโ PushifyResource.php
โ โโโ Models/
โ โ โโโ Pushify.php
โ โโโ Providers/
โ โ โโโ FirebaseProvider.php
โ โ โโโ OneSignalProvider.php
โ โโโ Services/
โ โ โโโ FirebaseService.php
โ โ โโโ OneSignalService.php
โ โ โโโ PushifyService.php
โ โโโ PushifyServiceProvider.php
โโโ stubs/
โ โโโ Http/
โ โ โโโ Controllers/
โ โ โ โโโ PushifyController.stub
โ โ โโโ Requests/
โ โ โ โโโ StorePushifyRequest.stub
โ โ โโโ Resources/
โ โ โโโ PushifyResource.stub
โ โโโ routes/
โ โโโ pushify.stub
โโโ composer.json
Table of Contents
- Requirements
- Installation
- Publish
- Migration
- Configuration
- Routes
- Usage
- Notification Statuses
- Scheduled Command
- Customizing the HTTP Layer
- Adding a Custom Provider
- Store Endpoint Payload
- Response Structure
- Authors
- License
Requirements
- PHP >= 8.2
- Laravel >= 12.0
- OpenSSL PHP extension (required for Firebase JWT signing โ no external Google SDK needed)
Installation
composer require badawy24/pushify
Then clear the cache:
php artisan optimize:clear
Publish
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
# 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
php artisan migrate
Creates the pushify_notifications table:
id, title, body, image, data, scheduled_at,
status, sent_at, failed_at, error_message,
created_at, updated_at
Configuration
Published config file: config/pushify.php
Only one provider is active at a time, selected from .env.
๐ฅ 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
PUSHIFY_PROVIDER=onesignal ONESIGNAL_APP_ID=your-app-id ONESIGNAL_API_KEY=your-api-key ONESIGNAL_API_URL=https://api.onesignal.com/notifications
OneSignal sends to included_segments => ['All'].
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
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
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
Inject the service interface
use Badawy\Pushify\Contracts\PushifyServiceInterface;
The package binds this interface automatically โ no manual binding required.
Send immediately
use Badawy\Pushify\Contracts\PushifyServiceInterface; class OfferController extends Controller { public function notify(PushifyServiceInterface $push) { $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, ); return response()->json($notification); } }
Both Firebase and OneSignal send immediately when scheduledAt is null.
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(), );
| Provider | Behavior |
|---|---|
| ๐ฅ Firebase | Saved as pending โ sent by the command when scheduled_at <= now() |
| ๐ฃ OneSignal | Submitted immediately with native send_after โ OneSignal handles the delay |
Create only (without sending)
$notification = $push->create([ 'title' => 'Draft notification', 'body' => 'Notification body', 'data' => ['type' => 'general'], 'image' => null, 'scheduled_at' => null, ]);
Saved as pending. Nothing is sent until you manually call send() or run the command.
Send an existing notification
use Badawy\Pushify\Models\Pushify; $notification = Pushify::findOrFail($id); $notification = $push->send($notification);
Quick test via Tinker
php artisan tinker
app(\Badawy\Pushify\Contracts\PushifyServiceInterface::class) ->sendToAll( title: 'Hello', body: 'Test from Tinker', data: ['type' => 'test'], image: null, scheduledAt: null, );
Available interface methods
// Store a notification record without sending public function create(array $payload): Pushify; // Create and send (or schedule) in one call public function sendToAll( string $title, string $body, array $data = [], ?string $image = null, ?string $scheduledAt = null ): Pushify; // Send an existing stored notification public function send(Pushify $notification): Pushify; // Mark due OneSignal scheduled notifications as sent locally public function markScheduledAsSent(): int;
Notification Statuses
| Status | Meaning |
|---|---|
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
php artisan pushify:send-scheduled
| Provider | What it does |
|---|---|
| ๐ฅ Firebase | Sends all pending notifications where scheduled_at <= now() |
| ๐ฃ OneSignal | Marks all scheduled notifications where scheduled_at <= now() as sent locally โ OneSignal already delivered them |
Add to Laravel Scheduler
In routes/console.php:
Schedule::command('pushify:send-scheduled')->everyMinute();
Or via cron:
* * * * * php /path/to/project/artisan pushify:send-scheduled >> /dev/null 2>&1
Customizing the HTTP Layer
After publishing with --tag=pushify-http, you can freely edit:
| File | Purpose |
|---|---|
app/Http/Controllers/Pushify/PushifyController.php |
Request handling & response |
app/Http/Requests/Pushify/StorePushifyRequest.php |
Validation rules & authorization |
app/Http/Resources/Pushify/PushifyResource.php |
JSON output shape |
The published controller injects PushifyServiceInterface โ extend or replace any logic without touching the package internals.
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 { // Your HTTP call or SDK integration here 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
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
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
License
MIT