Weave Code
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

Web Push Laravel Package

minishlink/web-push

PHP library to send Web Push notifications to browser push endpoints (RFC 8030). Handles VAPID and payload encryption, supports batching and reporting, and works with modern PHP (8.2+) via Composer for integrating push into your backend.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require minishlink/web-push
    

    Ensure your PHP environment meets requirements: PHP 8.2+, bcmath/gmp, mbstring, curl, and openssl (with elliptic curve support).

  2. Generate VAPID Keys (one-time setup):

    openssl ecparam -genkey -name prime256v1 -out vapid_private.pem
    openssl ec -in vapid_private.pem -pubout -outform DER | tail -c 65 | base64 | tr -d '=' | tr '/+' '_-' > vapid_public.txt
    openssl ec -in vapid_private.pem -outform DER | tail -c +8 | head -c 32 | base64 | tr -d '=' | tr '/+' '_-' > vapid_private.txt
    

    Or use PHP:

    use Minishlink\WebPush\VAPID;
    $keys = VAPID::createVapidKeys(); // Store $keys['publicKey'] and $keys['privateKey']
    
  3. Initialize WebPush:

    use Minishlink\WebPush\WebPush;
    $webPush = new WebPush([
        'VAPID' => [
            'subject' => 'mailto:your-email@example.com',
            'publicKey' => file_get_contents('vapid_public.txt'),
            'privateKey' => file_get_contents('vapid_private.txt'),
        ]
    ]);
    
  4. First Push:

    • Store the client-side PushSubscription (from subscription.toJSON()) in your database.
    • Retrieve and decode it:
      $subscription = \Minishlink\WebPush\Subscription::create(json_decode($storedSubscription, true));
      
    • Send a notification:
      $webPush->sendOneNotification($subscription, json_encode(['message' => 'Hello!']));
      

Implementation Patterns

Core Workflow

  1. Subscription Management:

    • Store raw PushSubscription data (from subscription.toJSON()) in your database (e.g., subscriptions table with endpoint, keys, and contentEncoding).
    • Retrieve and create a Subscription object:
      $subscription = Subscription::create($dbSubscriptionData);
      
  2. Batch Processing:

    • Queue notifications for efficiency:
      $webPush->queueNotification($subscription, $payload);
      
    • Flush in batches (default: 1000):
      foreach ($webPush->flush() as $report) {
          if ($report->isSuccess()) {
              log("Sent to {$report->getEndpoint()}");
          } else {
              log("Failed: {$report->getReason()}");
          }
      }
      
  3. Event-Driven Triggers:

    • Integrate with Laravel events (e.g., sent, failed):
      event(new PushSent($subscription, $payload));
      
    • Listen to events in a service:
      public function handle(PushSent $event) {
          $webPush->queueNotification($event->subscription, $event->payload);
      }
      
  4. Service Worker Integration:

    • Frontend (JavaScript):
      navigator.serviceWorker.register('/sw.js').then(reg => {
          reg.pushManager.subscribe({
              userVisibleOnly: true,
              applicationServerKey: urlBase64ToUint8Array(vapidPublicKey)
          }).then(sub => {
              fetch('/push/subscribe', {
                  method: 'POST',
                  body: JSON.stringify(sub),
                  headers: { 'Content-Type': 'application/json' }
              });
          });
      });
      
  5. API Endpoints:

    • Subscribe:
      Route::post('/push/subscribe', function (Request $request) {
          $subscription = Subscription::create($request->json()->all());
          // Store in DB and return success
      });
      
    • Unsubscribe:
      Route::post('/push/unsubscribe', function (Request $request) {
          $endpoint = $request->input('endpoint');
          // Delete from DB
      });
      

Advanced Patterns

  1. Dynamic Payloads:

    • Use Laravel Blade or templates for dynamic content:
      $payload = json_encode([
          'title' => 'New Update',
          'body' => 'Check your dashboard!',
          'icon' => asset('images/icon.png'),
          'data' => ['url' => route('dashboard')]
      ]);
      
  2. Retry Logic:

    • Implement exponential backoff for failed subscriptions:
      if (!$report->isSuccess()) {
          $retryAfter = $report->getRetryAfter();
          if ($retryAfter) {
              sleep($retryAfter);
              $webPush->sendOneNotification($subscription, $payload);
          }
      }
      
  3. Analytics:

    • Track push success/failure rates:
      $stats = [
          'total' => 0,
          'success' => 0,
          'failed' => 0,
      ];
      foreach ($webPush->flush() as $report) {
          $stats['total']++;
          if ($report->isSuccess()) $stats['success']++;
          else $stats['failed']++;
      }
      // Store $stats in DB
      
  4. Queue Integration:

    • Use Laravel Queues to defer push sends:
      PushNotification::dispatch($subscription, $payload)
          ->onQueue('push-notifications');
      
    • Queue job:
      class PushNotification implements ShouldQueue {
          public function handle() {
              $webPush = app(WebPush::class);
              $webPush->sendOneNotification($this->subscription, $this->payload);
          }
      }
      
  5. Testing:

    • Mock WebPush in tests:
      $mockWebPush = Mockery::mock(WebPush::class);
      $mockWebPush->shouldReceive('sendOneNotification')
          ->once()
          ->andReturn(new MessageSentReport(true, null, null, null));
      $this->app->instance(WebPush::class, $mockWebPush);
      

Gotchas and Tips

Pitfalls

  1. VAPID Key Management:

    • Gotcha: Losing your VAPID private key means you cannot send pushes to existing subscriptions. Store it securely (e.g., environment variables or encrypted storage).
    • Tip: Use Laravel's config or .env for keys:
      VAPID_PUBLIC_KEY=your_public_key_here
      VAPID_PRIVATE_KEY=your_private_key_here
      
      Load in config/webpush.php:
      'vapid' => [
          'subject' => env('VAPID_SUBJECT', 'mailto:your-email@example.com'),
          'publicKey' => env('VAPID_PUBLIC_KEY'),
          'privateKey' => env('VAPID_PRIVATE_KEY'),
      ],
      
  2. Payload Size:

    • Gotcha: Payloads > 3052 bytes may fail silently on Firefox. Test with:
      $payload = json_encode(['data' => str_repeat('a', 3000)]); // Safe
      
    • Tip: Compress payloads if needed:
      $compressed = gzcompress($payload, 9);
      $webPush->sendOneNotification($subscription, $compressed, [
          'contentEncoding' => 'aesgcm',
          'contentType' => 'application/octet-stream',
      ]);
      
  3. Subscription Expiry:

    • Gotcha: Subscriptions expire (e.g., after 30 days of inactivity). Check isSubscriptionExpired():
      if ($report->isSubscriptionExpired()) {
          // Delete from DB and re-subscribe user
      }
      
    • Tip: Implement a cron job to validate subscriptions:
      $webPush = new WebPush();
      $subscriptions = Subscription::query()->limit(100)->get();
      foreach ($subscriptions as $sub) {
          $report = $webPush->sendOneNotification($sub, null);
          if (!$report->isSuccess()) {
              $sub->delete();
          }
      }
      
  4. HTTPS Requirement:

    • Gotcha: Push services (e.g., FCM, Mozilla) require HTTPS. Use Laravel Valet/Forge or a trusted CA for local testing.
    • Tip: For local testing, use ngrok:
      ngrok http 80
      
      Update your subject in VAPID to match the ngrok URL (e.g., https://your-subdomain.ngrok.io).
  5. Browser-Specific Quirks:

    • Chrome/FCM: May require payload: null for some endpoints.
    • Firefox: Requires urgency field (default to 'normal'):
      $webPush->set
      
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.
terminal42/code-quality-tools
codifyo/ts-generator-bundle
andydefer/laravel-cluster
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky