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

Rms Push Notifications Bundle Laravel Package

opwoco/rms-push-notifications-bundle

A Symfony bundle for integrating RMS push notifications into your application. It provides configuration and delivery support for connecting app events to RMS push services, simplifying notification setup.

View on GitHub
Deep Wiki
Context7

Getting Started

Read this first: it is a Symfony bundle, not a Laravel package. opwoco/rms-push-notifications-bundle depends on symfony/framework-bundle, symfony/dependency-injection and symfony/http-kernel. There is no ServiceProvider, no facade, no publishable config. Dropping it into composer require on a Laravel app will either fail dependency resolution or give you a bundle class Laravel never boots.

The practical path for a Laravel developer is to use the underlying library directly — richsage/rms-push-notifications — and wrap it in a thin Laravel service yourself. The bundle is only DI glue plus a YAML config tree; everything useful (APNSMessage, GCMessage, the senders) lives in the library.

composer require richsage/rms-push-notifications

Where to look first, in this order:

  1. DependencyInjection/RMSPushNotificationsExtension.php and Resources/config/services.xml in the bundle repo — this is your only accurate source of truth for the config keys (apns.certificate, apns.passphrase, apns.environment, gcm.api.key) and the service id (rms_push_notifications).
  2. The library's Service/ directory — getAPNS() and getGCM() are the two entry points you will actually call.
  3. The Message/ directory — APNSMessage, GCMessage, and the MessageInterface they implement.

First use case — send an APNS alert to one token. Translating the bundle's Symfony YAML into config/services.php:

// config/services.php
'apns' => [
    'certificate' => env('APNS_CERT'),      // absolute path to .pem
    'passphrase'  => env('APNS_PASSPHRASE'),
    'environment' => env('APNS_ENV', 'sandbox'),
],
'gcm' => [
    'key' => env('GCM_API_KEY'),
],
// app/Services/PushNotifier.php
namespace App\Services;

use RMS\PushNotificationsBundle\Message\APNSMessage;
use RMS\PushNotificationsBundle\Service\NotificationService;

class PushNotifier
{
    public function __construct(private NotificationService $rms) {}

    public function alert(string $token, string $body, array $extra = []): bool
    {
        $message = new APNSMessage();
        $message->setDeviceIdentifier($token);
        $message->setMessage($body);
        $message->setCustomData($extra);   // must be an array; becomes "aps" siblings

        return $this->rms->getAPNS()->send($message);
    }
}

Bind it in a provider:

$this->app->singleton(NotificationService::class, function () {
    return new NotificationService([
        'apns' => config('services.apns'),
        'gcm'  => config('services.gcm'),
    ]);
});

Do a sandbox APNS send to one hard-coded device token before building anything else. If that does not work, stop — the rest of this package's value is zero.

Implementation Patterns

Wrap, don't leak. Keep RMS\... classes out of your controllers, jobs and notifications. One PushNotifier (or separate ApnsSender/GcmSender) with sendAlert(), sendSilent(), and sendBatch(). When (not if) you migrate to kreait/laravel-firebase or laravel-notification-channels/apn, you change one class.

Channel-per-platform, not one channel. APNS and FCM payloads diverge sharply — APNS nests everything under aps, GCM/FCM takes a flat data map. Model them as two Laravel notification channels:

// app/Notifications/Channels/ApnsChannel.php
public function send($notifiable, Notification $notification): void
{
    foreach ($notifiable->routeNotificationForApns() as $token) {
        $this->apns->sendAlert($token, ...$notification->toApns($notifiable));
    }
}

Then via() returns [ApnsChannel::class, GcmChannel::class] and the notification stays platform-agnostic.

Always queue. RMS has no bulk send — send() is one blocking socket round-trip per device. Looping 5k tokens inline in a request will time out. Pattern: one SendPushNotification job dispatched per chunk of ~200 tokens, tries = 3, backoff = [10, 60, 300], and a ShouldBeUnique key per campaign so a retry doesn't double-notify.

Token table, not a column. A push_tokens table with (user_id, platform, token, last_seen_at, active) and a unique index on token. Users have multiple devices, tokens rotate, and you need to prune. Store the platform as an enum so the fan-out logic is a simple where('platform', 'ios').

Feedback loop. APNS exposes a feedback endpoint that tells you which tokens are dead. In RMS this surfaces as getAPNSFeedback(), returning objects with getDeviceIdentifier() and getTime(). Schedule a daily command:

foreach ($rms->getAPNSFeedback() as $response) {
    PushToken::where('token', $response->getDeviceIdentifier())->delete();
}

Without this, your token table rots and deliverability metrics look broken within a month.

Payload builders with hard size caps. APNS caps the payload (256 bytes on the legacy protocol, 4 KB on HTTP/2) and FCM caps at 4 KB. Trim aggressively and treat mb_strlen(json_encode($payload)) as an assertion, not a comment.

Config in .env, certs outside the repo. APNS_CERT=/var/secrets/apns.pem, APNS_PASSPHRASE=.... Never commit .pem files; a combined cert+key file is a production credential.

Swappable for tests. Bind PushNotifier to a FakePushNotifier that records messages in an array, then assert assertPushedTo('ios', fn ($m) => $m['body'] === 'Order shipped'). Since RMS returns bare booleans, this is also the only place you will get useful assertions.

Gotchas and Tips

Kill criteria — check these before writing any code. (a) The package has 0 stars, 0 dependents and a 0.03 score; there is no community to help you. (b) It is Symfony 2/3-era: expect composer install on PHP 8.2 + Laravel 11 to fail on symfony/http-kernel: ~2.x and php: >=5.3 constraints. (c) If the version you pull uses APNS's binary protocol on port 2195, it is dead — Apple retired that endpoint in March 2021. Verify the transport before committing. Any of these failing means the answer is "use edamov/pushok or kreait/laravel-firebase instead."

GCM is a ghost town. Google shut down the GCM HTTP legacy API and FCM legacy endpoints. getGCM() may still construct a request, but it will 404 or 401 against Google forever. Anything you write against the GCM path is throwaway — go straight to FCM HTTP v1 with OAuth2 service-account credentials (kreait/laravel-firebase handles this well). Only the APNS side of this package has a plausible reason to exist.

send() returns a boolean and swallows detail. A failure gives you false with no status code, no token attribution, no reason. Wrap every call in a try/catch and check the return; log the token, the platform and the timestamp so you can correlate with Apple/Google. Do not expect exceptions.

Certificate mismatches fail silently. Sandbox cert + production device token (or the reverse) typically yields a connection-level error, not a meaningful message. Sanity-check the cert out of band:

openssl s_client -connect gateway.sandbox.push.apple.com:2195 \
  -cert apns.pem -key apns.pem 2>&1 | head

If that handshake fails, the package will too — don't debug PHP for it.

Clean your device tokens. iOS tokens copied from a device log often arrive wrapped in < > and spaces; strip them and lowercase the hex. FCM tokens are a different shape entirely (~150+ chars) — never let one column accept both without a platform discriminator.

Silent/background pushes have extra requirements. APNS background delivery needs content-available: 1 (RMS: setContentAvailable(true)) and, on modern Apple, the apns-push-type: background header. If you are on an old RMS fork, that header may not exist — another reason the APNS transport may be a dead end.

Extension points. If you must fork, the clean seams are: implement MessageInterface for custom message types, subclass APNSMessage to inject headers the library doesn't expose, and swap the sender via your own container binding (getAPNS() just resolves a registered service). Vendor the fork under a VCS repository in composer.json and pin a tag — with 0 stars and 0 dependents, upstream is not going to fix anything for you.

Debugging checklist, in order: does composer show richsage/rms-push-notifications reveal the transport version → does the openssl s_client handshake pass → does a single sandbox send with a freshly generated dev token return true → does a token you know is unregistered also return false (confirming errors aren't swallowed) → only then integrate.

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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin