Skip to content

Firebase Cloud Messaging (FCM) Integration - Complete Setup Guide

Status: โœ… Phases 1-3 Complete
Date: April 10, 2026
Implementation Time: ~2 hours


๐Ÿ“‹ What Was Implemented

Phase 1: Database & Models โœ…

1.1 DeviceToken Model

  • File: models/notify.py
  • Stores FCM tokens per user with metadata
  • Fields: user_id, fcm_token (unique), platform, device_name, is_active, last_used_at
  • Automatically tracks created_on and updated_on

1.2 Alembic Migration

1.3 Push Templates (Already Existed)

  • File: seed/template_seed.json
  • Push templates for: send2FA, INTERNAL_TRANSFER, DEPOSIT, INITIAL_DEPOSIT
  • Support for: en, pt, fr languages
  • Uses same placeholder system as SMS/Email

Phase 2: Firebase Backend โœ…

2.1 Firebase Service Wrapper

  • File: utils/notify/firebase.py
  • Singleton pattern for Firebase Admin SDK initialization
  • Methods:
  • send_notification() - Single device push
  • send_multicast() - Push to multiple devices
  • send_to_topic() - Push to topic subscribers
  • subscribe_to_topic() / unsubscribe_from_topic() - Topic management
  • Error handling for invalid/unregistered tokens
  • Logging for all operations

2.2 Push Notification Celery Task

  • File: utils/notify/tasks.py
  • Updated send_push_notification() async function with full FCM integration
  • Supports title, body, data payload, and image_url
  • Returns CustomResponse with FCM message ID
  • Integrated into send_via_service() router
  • Extracts push-specific data from message.udf: title, push_data, image_url

2.3 Configuration

  • File: core/config.py
  • Added FIREBASE_CONFIG dictionary with:
  • credentials_path: Path to Firebase service account JSON
  • project_id: Firebase project ID

Phase 3: Integration & APIs โœ…

3.1 Extended queue_template_notification()

  • File: controller/notify.py
  • Added user_id parameter for device token lookup
  • Added push channel-specific processing:
  • Stores title (from template.subject)
  • Stores push_data with template context
  • Stores image_url if provided in placeholders
  • Maintains backward compatibility with SMS/Email

3.2 Device Token Management Endpoints

  • File: routers/device_tokens.py
  • Base URL: /api/v1/notifications/devices
  • Endpoints:
  • POST /register - Register new device token
  • GET / - List user's device tokens
  • GET /{id} - Get specific device token
  • PUT /{id}/deactivate - Deactivate token
  • PUT /{id}/activate - Reactivate token
  • DELETE /{id} - Delete single token
  • DELETE / - Delete all tokens
  • All endpoints require authentication
  • Filter by active_only query parameter

3.3 Router Integration

  • File: main.py
  • Component imported: from routers.device_tokens import router as device_token_router
  • Added to routers list for inclusion in FastAPI app

๐Ÿš€ Getting Started

Prerequisites

  1. Firebase Project created at https://console.firebase.google.com
  2. Firebase Admin SDK enabled
  3. Service account JSON downloaded with private key

Step 1: Setup Environment Variables

Add to your .env file:

# Firebase Configuration
FIREBASE_PROJECT_ID=your-project-id
FIREBASE_CREDENTIALS_PATH=/app/firebase-credentials.json

For Docker, mount the credentials file:

COPY firebase-credentials.json /app/firebase-credentials.json

Step 2: Install Dependencies

pip install firebase-admin

Or add to requirements.txt:

firebase-admin>=6.0.0

Step 3: Run Database Migration

cd backend
alembic upgrade head

This creates the notify.device_tokens table.

Step 4: (Optional) Seed Push Templates

Push templates already exist in seed/template_seed.json. They're automatically seeded via the existing template seeding process.


๐Ÿ“ฑ API Usage Examples

Register Device Token (Mobile/Web)

curl -X POST http://localhost:8000/api/v1/notifications/devices/register \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "fcm_token": "eHxO_SA:APA91bH...",
    "platform": "mobile",
    "device_name": "iPhone 14 Pro"
  }'

Response:

{
  "id": 1,
  "fcm_token": "eHxO_SA:APA91bH...",
  "platform": "mobile",
  "device_name": "iPhone 14 Pro",
  "is_active": true,
  "created_on": "2026-04-10T10:00:00.000Z"
}

List User's Device Tokens

curl http://localhost:8000/api/v1/notifications/devices/ \
  -H "Authorization: Bearer YOUR_TOKEN"

Send Push via Notify Pipeline

from controller.notify import queue_template_notification
from sqlalchemy.orm import Session

queue_template_notification(
    db=db_session,
    channel="push",
    template_name="INTERNAL_TRANSFER",
    receiver="USER_FCM_TOKEN_HERE",  # FCM token from device_tokens table
    sender="CodeForge Banking",
    placeholders={
        "debit_name": "John Doe",
        "credit_name": "Jane Smith",
        "amount": "500.00",
        "currency": "USD",
        "debit_account": "123456789",
        "credit_account": "987654321",
        "transaction_reference_id": "TXN123456"
    },
    service="push",
    priority=True,  # Send immediately (optional)
    language="en"
)

๐Ÿ”„ How Push Notifications Flow

1. User registers FCM token via API
   โ†“
2. Application queues notification via queue_template_notification()
   - Template formatted with placeholders
   - Message stored in notify.message table with status="pending"
   โ†“
3. Celery worker picks up message
   - Calls send_message task
   - Routes to send_via_service based on service type
   โ†“
4. Firebase Service sends notification
   - Uses FCM token as recipient
   - Sends title, body, data via FCM Admin SDK
   โ†“
5. Firebase Cloud Messaging
   - Delivers to registered device
   - Handles retries and failures
   โ†“
6. Mobile/Web app receives notification
   - Firebase SDK on client displays notification
   - App can handle additional logic

โš™๏ธ Configuration Reference

environment Variables

Variable Description Example
FIREBASE_PROJECT_ID Firebase Project ID my-banking-app
FIREBASE_CREDENTIALS_PATH Path to service account JSON /app/firebase-credentials.json

Message Templates

Templates use consistent placeholder syntax:

{
  "channel_id": "push",
  "language_id": "en",
  "name": "TRANSACTION_ALERT",
  "subject": "Transaction Alert",
  "message": "Transaction of {currency} {amount} to {recipient_name}"
}

Available placeholders: See TransactionTemplate documentation

Device Token Fields

Field Type Purpose
fcm_token String(unique) Firebase Cloud Messaging token
platform String "mobile" or "web"
device_name String Device identifier (e.g., "iPhone 14")
is_active Boolean Whether token can receive notifications
last_used_at DateTime Track token usage

๐Ÿ›ก๏ธ Error Handling

Token Errors

  • InvalidArgumentError: Token format invalid โ†’ log and return 400
  • UnregisteredError: Token expired/revoked โ†’ mark as inactive (410)
  • FirebaseError: Firebase service issue โ†’ return 500

Recovery Strategies

  1. Automatic token deactivation on UnregisteredError
  2. Circuit breaker pattern for provider failures
  3. Exponential backoff for retries
  4. Dead letter queue for failed messages

๐Ÿ“Š Monitoring & Logging

All push operations logged to: - File: Configured via Python logging in logger setup - Celery logs: Track async task execution - FCM logs: Firebase admin SDK operations

Key metrics to monitor: - messages_sent_total - Successfully sent - messages_failed_total - Failed messages - messages_requeued_* - Retried messages - FCM API response times


๐Ÿ” Security Considerations

  1. Firebase Credentials
  2. Keep firebase-credentials.json private
  3. Use environment variables, never hardcode
  4. Mount as Docker secret in production

  5. FCM Tokens

  6. Encryption: Stored as EncryptedType in database (like receiver/sender)
  7. Access: Only user can access their own tokens
  8. Authentication: All endpoints require valid JWT

  9. Payload Validation

  10. Title: Max 240 characters (FCM limit)
  11. Body: Max 240 characters (FCM limit)
  12. Data: String key-value pairs only
  13. Image URL: Must be HTTPS

๐Ÿงช Testing

Unit Tests (To Create)

def test_register_device_token():
    # Test token registration
    pass

def test_send_push_notification():
    # Test Firebase integration
    pass

def test_invalid_fcm_token():
    # Test error handling
    pass

Manual Testing

# 1. Register device
curl -X POST http://localhost:8000/api/v1/notifications/devices/register \
  -H "Authorization: Bearer TOKEN" \
  -d '{"fcm_token":"TEST_TOKEN","platform":"mobile"}'

# 2. Create test message
python manage.py send_push --user_id=1

# 3. Check Celery logs
docker logs backend-celery-worker

๐Ÿ“š Next Steps (Phase 4)

Mobile App Integration (Flutter)

  • [ ] Firebase initialization in flutter app
  • [ ] Generate FCM registration token
  • [ ] Auto-register token with backend API
  • [ ] Setup Firebase Cloud Messaging handlers
  • [ ] Display notifications in system tray

Web App Integration (React)

  • [ ] Service Worker setup
  • [ ] Firebase Web SDK initialization
  • [ ] Generate FCM token
  • [ ] Register token via API
  • [ ] Handle notification clicks

See PUSH_NOTIFICATIONS_MOBILE_SETUP.md


๐Ÿ†˜ Troubleshooting

Firebase credentials not found

Error: Failed to initialize Firebase: [Errno 2] No such file or directory
Fix: Ensure FIREBASE_CREDENTIALS_PATH points to valid JSON file

Invalid FCM Token Error

Error: Invalid FCM token or message
Fix: Token expired or invalid format. User should re-register device.

Service not in notify.channel

CustomException: Notification channel 'push' not found
Fix: Ensure "push" channel exists in notify.channel table. Check seeded data.

Tokens not being sent

Message stuck with status="pending"
Fix: 1. Check Celery worker is running 2. Verify Redis connection 3. Check FCM credentials are valid


๐Ÿ“ž Support

For issues or questions: 1. Check Firebase Admin SDK docs: https://firebase.google.com/docs/admin/setup 2. Review Celery documentation: https://docs.celeryproject.io/ 3. Check notify module: controller/notify.py