miinventario-v2/docs/02-integraciones/INT-004-firebase-fcm.md
rckrdmrd 1a53b5c4d3 [MIINVENTARIO] feat: Initial commit - Sistema de inventario con análisis de video IA
- Backend NestJS con módulos de autenticación, inventario, créditos
- Frontend React con dashboard y componentes UI
- Base de datos PostgreSQL con migraciones
- Tests E2E configurados
- Configuración de Docker y deployment

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 02:25:48 -06:00

5.8 KiB

INT-004: Integracion Firebase FCM


id: INT-004 type: Integration status: Pendiente version: "1.0.0" created_date: 2026-01-10 updated_date: 2026-01-10 simco_version: "4.0.0"

Metadata

Campo Valor
ID INT-004
Servicio Firebase Cloud Messaging
Proposito Notificaciones push
Criticidad P1
Estado Pendiente

1. Descripcion

Integracion con Firebase Cloud Messaging (FCM) para enviar notificaciones push a los usuarios sobre resultados de inventario, pagos y otros eventos.


2. Informacion del Servicio

Campo Valor
Proveedor Google Firebase
Documentacion https://firebase.google.com/docs/cloud-messaging
Consola https://console.firebase.google.com
SDK firebase-admin (node), @react-native-firebase/messaging

3. Configuracion

Variables de Entorno

FIREBASE_PROJECT_ID=miinventario-xxx
FIREBASE_CLIENT_EMAIL=firebase-adminsdk@...
FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n..."

Instalacion Backend

npm install firebase-admin

Instalacion Mobile

npm install @react-native-firebase/app @react-native-firebase/messaging

4. Tipos de Notificaciones

Tipo Evento Prioridad
INVENTORY_READY Inventario procesado Alta
PAYMENT_CONFIRMED Pago confirmado Alta
PAYMENT_PENDING Voucher por vencer Media
REFERRAL_ACTIVATED Referido activado Media
LOW_CREDITS Creditos bajos Baja
PROMO_ACTIVE Promocion activa Baja

5. Implementacion Backend

Inicializacion

import * as admin from 'firebase-admin';

@Module({})
export class FirebaseModule {
  static forRoot(): DynamicModule {
    admin.initializeApp({
      credential: admin.credential.cert({
        projectId: process.env.FIREBASE_PROJECT_ID,
        clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
        privateKey: process.env.FIREBASE_PRIVATE_KEY.replace(/\\n/g, '\n'),
      }),
    });

    return {
      module: FirebaseModule,
      global: true,
    };
  }
}

Servicio de Notificaciones

@Injectable()
export class NotificationsService {
  async sendToUser(
    userId: string,
    notification: { title: string; body: string; data?: Record<string, string> }
  ) {
    const user = await this.usersService.findOne(userId);
    if (!user.fcmToken) return;

    await admin.messaging().send({
      token: user.fcmToken,
      notification: {
        title: notification.title,
        body: notification.body,
      },
      data: notification.data,
      android: {
        priority: 'high',
        notification: {
          channelId: 'default',
          sound: 'default',
        },
      },
      apns: {
        payload: {
          aps: {
            sound: 'default',
            badge: 1,
          },
        },
      },
    });
  }

  async sendToTopic(
    topic: string,
    notification: { title: string; body: string }
  ) {
    await admin.messaging().sendToTopic(topic, {
      notification,
    });
  }
}

6. Implementacion Mobile

Configuracion

// App.tsx
import messaging from '@react-native-firebase/messaging';

const requestPermission = async () => {
  const authStatus = await messaging().requestPermission();
  const enabled =
    authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
    authStatus === messaging.AuthorizationStatus.PROVISIONAL;

  if (enabled) {
    const token = await messaging().getToken();
    await api.updateFcmToken(token);
  }
};

Handlers

// Foreground
messaging().onMessage(async remoteMessage => {
  // Mostrar notificacion in-app
  showLocalNotification(remoteMessage);
});

// Background/Quit
messaging().setBackgroundMessageHandler(async remoteMessage => {
  // Procesar datos
  console.log('Background message:', remoteMessage);
});

// Tap en notificacion
messaging().onNotificationOpenedApp(remoteMessage => {
  // Navegar a pantalla
  navigateToScreen(remoteMessage.data.screen);
});

7. Modelo de Datos

Tabla: notification_tokens

CREATE TABLE notification_tokens (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id UUID REFERENCES users(id),
  token VARCHAR(255) NOT NULL,
  platform VARCHAR(20), -- 'ios', 'android'
  device_info JSONB,
  is_active BOOLEAN DEFAULT true,
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);

Tabla: notification_logs

CREATE TABLE notification_logs (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id UUID REFERENCES users(id),
  type VARCHAR(50),
  title VARCHAR(255),
  body TEXT,
  data JSONB,
  sent_at TIMESTAMP DEFAULT NOW(),
  delivered_at TIMESTAMP,
  opened_at TIMESTAMP
);

8. Templates de Notificacion

const NOTIFICATION_TEMPLATES = {
  INVENTORY_READY: {
    title: 'Inventario listo',
    body: 'Tu inventario de {{storeName}} esta listo. {{totalItems}} productos detectados.',
  },
  PAYMENT_CONFIRMED: {
    title: 'Pago confirmado',
    body: 'Tu pago de ${{amount}} MXN fue confirmado. +{{credits}} creditos.',
  },
  REFERRAL_ACTIVATED: {
    title: 'Referido activado',
    body: '{{referredName}} activo su cuenta. +{{reward}} creditos.',
  },
};

9. Testing

Enviar Notificacion de Prueba

# Usando Firebase CLI
firebase messaging:send \
  --token "device_token" \
  --notification-title "Test" \
  --notification-body "This is a test"

Simulador iOS

FCM no funciona en simulador iOS. Usar dispositivo fisico o Expo Go.


10. Referencias


Ultima Actualizacion: 2026-01-10