npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

fcm-notification-send

v0.0.2

Published

A starter for Medusa plugins.

Readme

FCM Notification Send Plugin for Medusa v2

A Medusa v2 plugin that sends push notifications to all customer devices using Firebase Cloud Messaging (FCM). This plugin integrates with medusa-notification-token-management to fetch customer FCM tokens and send notifications to Android, iOS, and Web devices.

Features

  • Multi-Device Support: Automatically sends notifications to all registered devices for a customer.
  • Token Management Integration: Integrates with medusa-notification-token-management plugin for token storage.
  • Multi-Platform Support: Send notifications to Android, iOS (via APNs), and Web.
  • Customer Authentication: Uses JWT authentication to identify customers.
  • Batch Sending: Sends to all customer tokens in parallel with detailed results.
  • Firebase Admin SDK: Leverages the official Firebase Admin SDK for reliable message delivery.
  • Custom Data: Support for sending custom data payloads along with notifications.
  • High Priority Support: Notifications are configured for high priority delivery by default.
  • Automatic Token Cleanup: Invalid tokens are automatically removed from the database when detected.

Prerequisites

  • Medusa v2 (>= 2.4.0)
  • A Firebase Project
  • Firebase Service Account credentials (JSON file)
  • medusa-notification-token-management plugin installed and configured

Installation

  1. Install both required plugins:
npm install fcm-notification-send medusa-notification-token-management
  1. Configure both plugins in your medusa-config.ts:
module.exports = defineConfig({
  // ...
  modules: {
    // Token management module
    notification_tokens: {
      resolve: "medusa-notification-token-management",
    },
    
    // Notification module with FCM provider
    notification: {
      resolve: "@medusajs/medusa/notification",
      options: {
        providers: [
          {
            resolve: "fcm-notification-send",
            id: "fcm-push-notification",
            options: {
              projectId: process.env.FCM_PROJECT_ID,
              clientEmail: process.env.FCM_CLIENT_EMAIL,
              privateKey: process.env.FCM_PRIVATE_KEY,
            },
          },
        ],
      },
    },
  },
})

Environment Variables

Ensure you have the following environment variables set in your .env file:

FCM_PROJECT_ID=your-project-id
FCM_CLIENT_EMAIL=your-service-account-email
FCM_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"

[!TIP] Make sure the FCM_PRIVATE_KEY is wrapped in quotes and includes the literal \n characters if you are pasting it as a single line.

Usage

Send Notification to All Customer Devices

Use the storefront API endpoint to send notifications to all registered devices for the authenticated customer:

Endpoint: POST /store/push-notifications/send

Headers:

Authorization: Bearer <customer-jwt-token>
Content-Type: application/json

Request Body:

{
  "title": "Order Shipped!",
  "body": "Your order #1234 has been shipped and is on its way.",
  "image_url": "https://example.com/shipping.png",
  "data": {
    "order_id": "1234",
    "action": "view_order",
    "custom_field": "any_value"
  }
}

Response:

{
  "success": true,
  "message": "Notifications sent to 3 out of 3 devices",
  "sent_count": 3,
  "failed_count": 0,
  "total_tokens": 3
}

Error Response (if some tokens fail):

{
  "success": true,
  "message": "Notifications sent to 2 out of 3 devices (1 invalid token(s) removed)",
  "sent_count": 2,
  "failed_count": 1,
  "total_tokens": 3,
  "revoked_count": 1,
  "errors": [
    {
      "token": "invalid-token-xyz",
      "error": "NotRegistered"
    }
  ],
  "revoked_tokens": [
    {
      "token_id": "token_id_here",
      "reason": "NotRegistered"
    }
  ]
}

[!NOTE] Automatic Token Cleanup: Invalid tokens (NotRegistered, InvalidArgument, Unregistered) are automatically revoked from the database when detected. This keeps your token list clean and prevents future failed attempts.

Frontend Integration Example

Complete Web Push Setup (Works in All Conditions):

// 1. Initialize Firebase in your frontend
import { initializeApp } from 'firebase/app';
import { getMessaging, getToken, onMessage, isSupported } from 'firebase/messaging';

const firebaseConfig = {
  apiKey: "YOUR_API_KEY",
  projectId: "your-project-id",
  messagingSenderId: "YOUR_SENDER_ID",
  appId: "YOUR_APP_ID"
};

const app = initializeApp(firebaseConfig);

// 2. Request permission and get token
async function setupPushNotifications() {
  const supported = await isSupported();
  if (!supported) {
    console.log('Firebase Messaging is not supported');
    return;
  }

  const messaging = getMessaging(app);
  
  // Request notification permission
  const permission = await Notification.requestPermission();
  
  if (permission === 'granted') {
    // Get FCM token
    const token = await getToken(messaging, {
      vapidKey: 'YOUR_VAPID_KEY' // Get from Firebase Console
    });
    
    // Register token with backend
    await fetch('/store/notification-tokens', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${customerJwtToken}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        token: token,
        device_info: {
          platform: 'web',
          browser: navigator.userAgent
        }
      })
    });
  }
}

// 3. Handle foreground messages (when tab is active)
onMessage(getMessaging(app), (payload) => {
  console.log('Foreground message received:', payload);
  
  // Show notification manually when tab is active
  if (Notification.permission === 'granted') {
    const notificationTitle = payload.notification?.title || payload.data?.title || 'New Notification';
    const notificationOptions = {
      body: payload.notification?.body || payload.data?.body || '',
      icon: payload.notification?.icon || payload.data?.imageUrl || '/icon-192x192.png',
      badge: '/badge-72x72.png',
      tag: 'notification',
      requireInteraction: false,
      data: payload.data
    };
    
    new Notification(notificationTitle, notificationOptions);
  }
});

// 4. Service Worker (firebase-messaging-sw.js) - handles background messages
// Place this file in your public folder
importScripts('https://www.gstatic.com/firebasejs/9.0.0/firebase-app-compat.js');
importScripts('https://www.gstatic.com/firebasejs/9.0.0/firebase-messaging-compat.js');

firebase.initializeApp({
  apiKey: "YOUR_API_KEY",
  projectId: "your-project-id",
  messagingSenderId: "YOUR_SENDER_ID",
  appId: "YOUR_APP_ID"
});

const messaging = firebase.messaging();

messaging.onBackgroundMessage((payload) => {
  console.log('Background message received:', payload);
  const notificationTitle = payload.notification.title;
  const notificationOptions = {
    body: payload.notification.body,
    icon: payload.notification.icon || '/icon-192x192.png',
    badge: '/badge-72x72.png'
  };
  
  self.registration.showNotification(notificationTitle, notificationOptions);
});

Send Notification:

const sendNotification = async () => {
  const response = await fetch('/store/push-notifications/send', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${customerJwtToken}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      title: 'New Message',
      body: 'You have received a new message',
      data: {
        message_id: '123',
        sender: 'Support Team'
      }
    })
  });

  const result = await response.json();
  console.log(`Sent to ${result.sent_count} devices`);
};

How It Works

  1. Customer Authentication: The endpoint extracts the customer ID from the JWT token in the Authorization header.
  2. Fetch Tokens: Queries the medusa-notification-token-management plugin to get all active FCM tokens for the customer.
  3. Send to All Devices: Sends the notification to all registered devices in parallel.
  4. Return Results: Returns a summary of successful and failed sends.

Supported Platforms

Android

  • High priority delivery
  • Default notification sound

iOS (APNs)

  • Default notification sound
  • Badge count set to 1 by default

WebPush

  • High urgency headers

API Reference

POST /store/push-notifications/send

Send push notification to all customer devices.

Authentication: Required (Customer JWT)

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | title | string | Yes | Notification title | | body | string | Yes | Notification body text | | image_url | string | No | URL to notification image | | data | object | No | Custom data payload (key-value pairs) |

Response: | Field | Type | Description | |-------|------|-------------| | success | boolean | Whether operation completed | | message | string | Summary message | | sent_count | number | Number of successful sends | | failed_count | number | Number of failed sends | | total_tokens | number | Total tokens attempted | | revoked_count | number | Number of invalid tokens automatically removed | | errors | array | List of errors (if any) | | revoked_tokens | array | List of revoked tokens with reasons (if any) |

License

MIT