badawy24/pushify

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

Maintainers

Package info

github.com/backend-2022/pushify

pkg:composer/badawy24/pushify

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

1.0.1 2026-04-27 11:20 UTC

This package is not auto-updated.

Last update: 2026-04-28 10:13:19 UTC


README

Latest Version on Packagist PHP Version Laravel Version License: MIT

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

  • 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

Badawy ยท Hassan

License

MIT