fcm-notification-send
v0.0.2
Published
A starter for Medusa plugins.
Maintainers
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-managementplugin 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-managementplugin installed and configured
Installation
- Install both required plugins:
npm install fcm-notification-send medusa-notification-token-management- 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_KEYis wrapped in quotes and includes the literal\ncharacters 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/jsonRequest 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
- Customer Authentication: The endpoint extracts the customer ID from the JWT token in the Authorization header.
- Fetch Tokens: Queries the
medusa-notification-token-managementplugin to get all active FCM tokens for the customer. - Send to All Devices: Sends the notification to all registered devices in parallel.
- 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
