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

Notificationhubs Rest Php Laravel Package

openpp/notificationhubs-rest-php

A PHP client for the Azure Notification Hubs REST API, helping Laravel and PHP applications send push notifications and manage registrations or installations through a simple, framework-friendly interface.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a thin REST wrapper around Azure Notification Hubs: it generates the Shared Access Signature (SAS) token, builds the ServiceBusNotification-* headers, and POSTs to https://{namespace}.servicebus.windows.net/{hub}/messages. That is 90% of its value. It is not an SDK for managing registrations/installations, and it has a very small user base, so treat it as a stable but minimal dependency you wrap yourself.

What you need from Azure before writing code

  • Notification Hub namespace (e.g. myapp-ns)
  • Hub name (e.g. myapp-prod)
  • A shared access policy and key. Use a dedicated send-only policy (Listen/Send as needed) rather than DefaultFullSharedAccessSignature in application config. The connection string looks like: Endpoint=sb://myapp-ns.servicebus.windows.net/;SharedAccessKeyName=send-only;SharedAccessKey=...
  • Platform credentials already configured on the hub (APNS certificate or token, FCM server key / v1 service account, WNS package SID + secret).

Install and orient yourself

composer require openpp/notificationhubs-rest-php

Read these, in order:

  1. README.md — constructor signature and the one or two send examples.
  2. src/ — the client class and, most importantly, the SAS token generator. This is the code you will end up debugging; know exactly how it encodes the resource URI.
  3. composer.json — PHP version and Guzzle constraint. If you are on PHP 8.2+ / Guzzle 7, verify compatibility before committing.

The package is small enough that verifying the exact method names once is faster than guessing. The snippets below are illustrative of the shape; map them to the installed version.

Laravel wiring

// config/services.php
'azure_push' => [
    'connection_string' => env('AZURE_NH_CONNECTION_STRING'),
    'hub' => env('AZURE_NH_HUB'),
],
// App\Providers\AppServiceProvider::register()
$this->app->singleton(\Openpp\NotificationHubs\NotificationHub::class, function () {
    return new \Openpp\NotificationHubs\NotificationHub(
        config('services.azure_push.connection_string'),
        config('services.azure_push.hub')
    );
});

Do not resolve the client inside a config file, and never cache the raw connection string in a public config dump. Keep the key in the environment / a secret manager.

First use case: send to a tag, not to everyone

$hub->send(
    format: 'apns', // apns | fcm/gcm | wns | adm | template
    body: ['aps' => ['alert' => 'Hello from Laravel', 'sound' => 'default']],
    tagExpression: 'user:42' // target a single user's registrations
);

Send to a dedicated dev hub first, watch the Azure portal's “Test send” and metrics blade, then confirm on a real device. A 201 Created means accepted, not delivered; the portal metrics and per-message feedback are where delivery is confirmed.

Implementation Patterns

Put an adapter in front of it. Expose a PushNotifier interface with sendToUser(), sendToTopic(), sendRaw(). This is a 5-star, 0-dependent package; the adapter is your insurance policy if you later swap to the official Azure SDK, a fork, or raw Guzzle.

Targeting strategy — pick one deliberately per feature

  • Tag expression — the normal path. user:42, tenant:7 && role:admin, ios || android. Register tags at login/device-registration time and keep them in sync.
  • Direct device handle — bypasses registrations and sends to a token/device handle using ServiceBusNotification-DeviceHandle. Useful for one-off diagnostic sends; not a substitute for registration hygiene.
  • Template — send one logical payload and let each registration's template render platform-specific shapes. Good for “new message” style notifications; rigid if you need rich APNS/FCM-specific fields.
  • Broadcast — no tags. Treat as an admin-only operation and guard it in your adapter.

Registration / installation lifecycle

The REST surface has /registrations and /installations; this package may only wrap /messages. If it does not expose registration management, do one of:

  • manage installations yourself against the REST API (small amount of Guzzle code), or
  • send directly to device handles while you bootstrap, or
  • pair it with the official Azure SDK just for registration CRUD.

Day-to-day pattern: on login/device start, upsert an installation keyed by the device token with tags (user:{id}, tenant:{id}, platform:ios); on preference change, update tags; on logout, delete the installation. Reconcile periodically — stale tokens are the main source of confusing delivery numbers.

Queue the sends. Outbound HTTP to Azure should never run inside a web request. Use a job:

final class SendPush implements ShouldQueue
{
    use Queueable, SerializesModels;

    public int $tries = 3;
    public array $backoff = [5, 30, 120];

    public function __construct(
        public string $format,
        public array $body,
        public string $tagExpression,
    ) {}

    public function handle(PushNotifier $push): void
    {
        $push->send($this->format, $this->body, $this->tagExpression);
    }

    public function uniqueId(): string
    {
        return hash('sha256', $this->tagExpression.json_encode($this->body));
    }
}

Serialize payload data, not the client. Use ShouldBeUnique if duplicate enqueues would produce duplicate pushes.

Build a Laravel notification channel. This is the highest-leverage integration if you already use Notification classes:

class AzurePushChannel
{
    public function __construct(private PushNotifier $push) {}

    public function send($notifiable, Notification $notification): void
    {
        if (! method_exists($notification, 'toAzurePush')) {
            return;
        }

        $message = $notification->toAzurePush($notifiable);

        $this->push->send(
            $message['format'],
            $message['body'],
            $message['tags'] ?? 'user:'.$notifiable->getKey()
        );
    }
}

Now via() returns [AzurePushChannel::class] and push sits alongside mail/database notifications.

Payload shapes to standardise in your adapter

// APNS
['aps' => ['alert' => ['title' => 'Order', 'body' => 'Shipped'], 'sound' => 'default', 'badge' => 3]]

// FCM (legacy shape; confirm your hub is configured for the API version you use)
['notification' => ['title' => 'Order', 'body' => 'Shipped'], 'data' => ['order_id' => '123']]

// WNS (XML, not JSON)
'<toast><visual><binding template="ToastText01"><text id="1">Shipped</text></binding></visual></toast>'

Normalise this in one place so feature code never hand-builds platform JSON.

Observability. Capture the response status, Location header (contains the notification ID used for per-message feedback), and Azure's tracking/correlation headers. Log the tag expression and format, but redact the Authorization header and the shared key. Emit counters for accepted, unauthorised, throttled, payload_too_large.

Testing. The useful tests are: (1) a unit test that asserts your adapter builds the correct headers/tag expression, using a fake Guzzle handler; (2) a SAS-generation test with a known fixture; (3) a gated integration test against a dev hub in CI. There is no local emulator, so keep integration tests behind an env flag and a dedicated hub.

Multi-tenant / multi-hub. If you have one hub per tenant or per environment, use a factory that caches clients by hub name rather than a single global singleton. The SAS token is per-hub-resource, so clients are not interchangeable.

Gotchas and Tips

SAS token generation is where 401s come from. The signature is HMAC-SHA256 over urlencode(resourceUri) + "\n" + expiry, base64-encoded, then URL-encoded, and the sr parameter must be the lowercase, percent-encoded resource URI. Double-encoding, uppercasing the host, or including a trailing slash the wrong way all produce 401 Unauthorized with a body that tells you nothing. If you fork or reimplement, test against Azure immediately.

Token expiry and clock skew. Cache the SAS token for a bounded period (15–60 minutes is typical; Azure allows up to 30 days but short is safer) and refresh on 401. If your host clock is off, tokens can be “expired” on arrival. Log the expiry you generated with.

Empty tags means broadcast. Omitting the tag expression sends to every registration in the hub. This is the most common production incident with Notification Hubs. Make the tag expression a required, non-empty parameter in your adapter, and add an explicit broadcast() method for the rare intentional case.

Payload size limits are per-platform and enforced. APNS ~4KB, FCM ~4KB (legacy), WNS ~5KB. Exceeding them returns 413. For rich notifications, send a data/silent notification and have the app fetch the asset.

Content-Type must match the format. JSON for APNS/FCM/ADM/Baidu, XML for WNS/MPNS. A mismatch returns 400 with a vague message. Keep the mapping in your adapter, not in call sites.

Tag hygiene. Tags are case-sensitive and have length/count limits per registration. Avoid spaces and the expression operators (&&, ||, !, parentheses) inside tag values. Sanitise user input before it becomes a tag — user:42 and user:42 are different tags, and a tag containing || will silently widen your audience.

201 Created is acceptance, not delivery. Notification Hubs is best-effort. If you need delivery telemetry, query per-message feedback using the notification ID from the Location header, or rely on the Azure portal metrics. Do not build product logic that assumes a 201 means the device displayed something.

Retries cause duplicate pushes. There is no idempotency key in the send path. With queue retries and HTTP retries you get at-least-once behaviour. Either accept occasional duplicates, or dedupe by business key in the app.

APNS environment mismatch is the classic “silent failure”. A sandbox token sent through a production APNS configuration (or vice versa) returns success at the hub and never arrives. Keep separate hubs or separate APNS configurations per environment and check which one your build is using.

FCM legacy vs HTTP v1. Google has been deprecating legacy FCM credentials. Small packages often still default to the legacy format header (gcm). Verify that your hub is configured with v1 credentials and that the package/API version you use supports it; if not, you may need to bypass the package for FCM or fork it. This is the most likely reason you outgrow the library.

Error codes worth mapping explicitly: 400 (payload/format/header), 401 (SAS or policy), 404 (hub name/path), 413 (payload too large), 429/5xx (throttle/transient — retry with jitter). Do not retry 400/401/413; you will just burn queue attempts.

Set HTTP timeouts. Guzzle's default has no timeout. Configure connect/request timeouts (e.g. 2s/5s) via the injected client if the package allows it; otherwise wrap the call in your own client. A hung push job blocks a queue worker.

Extension points to look for: whether the client accepts a custom Guzzle client (for middleware, logging, retries), and whether the SAS generator is a separate, reusable class. If the SAS helper is clean, you can keep it and replace the rest with your own Guzzle calls when you need endpoints the package does not expose (installations, per-message feedback, scheduled sends).

Pin and isolate. composer require openpp/notificationhubs-rest-php:^x.y and keep it behind your PushNotifier interface. With 5 stars and no dependents, upstream fixes for new Azure behaviour (FCM v1, APNS push-type headers, API versions) may lag. Your adapter plus contract tests is what makes that a one-file change instead of a refactor.

Never log the shared key or the full Authorization header. SAS tokens are bearer credentials for the hub until they expire. Redact them in Guzzle middleware and Laravel exception handlers.

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.
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
adachsoft/php-code-style