michangarrito/backups/docs-backup-2026-01-10/docs/90-transversal/ARQUITECTURA-MULTI-TENANT-INTEGRACIONES.md
rckrdmrd 97f407c661 [MIGRATION-V2] feat: Migrar michangarrito a estructura v2
- Prefijo v2: MCH
- TRACEABILITY-MASTER.yml creado
- Listo para integracion como submodulo

Workspace: v2.0.0 | SIMCO: v4.0.0
2026-01-10 11:28:54 -06:00

8.3 KiB

Arquitectura Multi-Tenant de Integraciones

Resumen

MiChangarrito implementa una arquitectura multi-tenant donde cada cliente (tenant) puede configurar sus propias credenciales para integraciones externas (WhatsApp Business, proveedores LLM, pasarelas de pago), con fallback automático a las credenciales de plataforma si el tenant no tiene las suyas.

Diagrama de Flujo

┌─────────────────┐     ┌──────────────────────┐     ┌─────────────────┐
│   WhatsApp      │     │   whatsapp-service   │     │     Backend     │
│   Webhook       │────▶│                      │────▶│   (Internal)    │
└─────────────────┘     │  - Resolver tenant   │     │                 │
                        │  - Get credentials   │     │  GET /internal/ │
                        │  - Send via correct  │     │  integrations/  │
                        │    WhatsApp account  │     └─────────────────┘
                        └──────────────────────┘              │
                                    │                         │
                                    ▼                         ▼
                        ┌──────────────────────┐     ┌─────────────────┐
                        │   Meta WhatsApp API  │     │   PostgreSQL    │
                        │   (Tenant o Platform)│     │   tenant_       │
                        └──────────────────────┘     │   integration_  │
                                                     │   credentials   │
                                                     └─────────────────┘

Componentes Principales

1. Backend - Módulo de Integraciones

Ubicación: apps/backend/src/modules/integrations/

Entidades

  • TenantIntegrationCredential: Almacena credenciales por tenant e integración
  • TenantWhatsAppNumber: Mapea phoneNumberId → tenantId para webhooks

Servicios

  • TenantIntegrationsService:
    • getWhatsAppCredentials(tenantId) - Con fallback a plataforma
    • getLLMConfig(tenantId) - Con fallback a plataforma
    • resolveTenantFromPhoneNumberId(phoneNumberId) - Para webhooks

Controladores

  • IntegrationsController: API REST para tenants (protegido con JWT)

    • GET /integrations/status - Estado de todas las integraciones
    • PUT /integrations/whatsapp - Configurar WhatsApp propio
    • PUT /integrations/llm - Configurar LLM propio
  • InternalIntegrationsController: API interna para whatsapp-service

    • GET /internal/integrations/:tenantId/whatsapp
    • GET /internal/integrations/:tenantId/llm
    • GET /internal/integrations/resolve-tenant/:phoneNumberId

2. WhatsApp Service

Ubicación: apps/whatsapp-service/src/

Servicios Refactorizados

  • CredentialsProviderService (common/):

    • Cache de credenciales con TTL de 5 minutos
    • Consulta al backend y cachea resultados
    • Fallback a variables de entorno
  • WhatsAppService (whatsapp/):

    • Todos los métodos aceptan tenantId?: string
    • Cache de clientes axios por tenant
    • Usa credenciales correctas automáticamente
  • LlmService (llm/):

    • Obtiene config LLM por tenant
    • Soporta múltiples proveedores (OpenAI, OpenRouter, etc.)
    • System prompts personalizados por tenant
  • WebhookService (webhook/):

    • Resuelve tenant desde metadata.phone_number_id
    • Pasa tenantId en todo el flujo de conversación

Flujo de un Mensaje Entrante

  1. Meta envía webhook con metadata.phone_number_id
  2. WebhookController extrae phoneNumberId del payload
  3. WebhookService.processIncomingMessage() recibe phoneNumberId
  4. CredentialsProviderService.resolveTenantFromPhoneNumberId()
    • Consulta backend (/internal/integrations/resolve-tenant/:id)
    • Retorna tenantId o null (plataforma)
  5. Contexto de conversación incluye tenantId
  6. WhatsAppService.sendTextMessage(to, text, tenantId)
    • Obtiene credenciales para ese tenant (o plataforma)
    • Envía mensaje con las credenciales correctas
  7. LlmService.processMessage(text, context)
    • Obtiene config LLM para el tenant
    • Usa API key y modelo del tenant (o plataforma)

Configuración de Variables de Entorno

Backend (.env)

# Credenciales de plataforma (fallback)
WHATSAPP_ACCESS_TOKEN=EAAxxxxxxx
WHATSAPP_PHONE_NUMBER_ID=123456789
WHATSAPP_BUSINESS_ACCOUNT_ID=987654321
WHATSAPP_VERIFY_TOKEN=mi_token_secreto

# LLM de plataforma
OPENAI_API_KEY=sk-xxxxxxx
LLM_PROVIDER=openai
LLM_MODEL=gpt-4o-mini

# API interna
INTERNAL_API_KEY=clave_secreta_para_servicios_internos

WhatsApp Service (.env)

# URL del backend
BACKEND_URL=http://localhost:3141/api/v1

# API key para llamadas internas
INTERNAL_API_KEY=clave_secreta_para_servicios_internos

# Credenciales de plataforma (fallback)
WHATSAPP_ACCESS_TOKEN=EAAxxxxxxx
WHATSAPP_PHONE_NUMBER_ID=123456789
OPENAI_API_KEY=sk-xxxxxxx

API REST - Configuración de Integraciones

Obtener Estado de Integraciones

GET /api/v1/integrations/status
Authorization: Bearer <jwt_token>

Respuesta:

{
  "whatsapp": {
    "configured": false,
    "usesPlatformNumber": true,
    "isVerified": false
  },
  "llm": {
    "configured": false,
    "usesPlatformDefault": true,
    "provider": "openai",
    "model": "gpt-4o-mini"
  },
  "payments": {
    "stripe": { "configured": false },
    "mercadopago": { "configured": false },
    "clip": { "configured": false }
  }
}

Configurar WhatsApp Propio

PUT /api/v1/integrations/whatsapp
Authorization: Bearer <jwt_token>
Content-Type: application/json

{
  "credentials": {
    "accessToken": "EAAxxxxx",
    "phoneNumberId": "111222333",
    "businessAccountId": "444555666"
  },
  "phoneNumber": "+525512345678",
  "displayName": "Mi Tiendita"
}

Configurar LLM Propio

PUT /api/v1/integrations/llm
Authorization: Bearer <jwt_token>
Content-Type: application/json

{
  "provider": "openrouter",
  "credentials": {
    "apiKey": "sk-or-xxxxx"
  },
  "config": {
    "model": "anthropic/claude-3-haiku",
    "maxTokens": 1500,
    "temperature": 0.8,
    "systemPrompt": "Eres el asistente de Mi Tiendita..."
  }
}

Proveedores Soportados

WhatsApp

  • Meta Business (único proveedor)

LLM

  • OpenAI - gpt-4o, gpt-4o-mini, etc.
  • OpenRouter - Acceso a múltiples modelos
  • Anthropic - Claude 3
  • Azure OpenAI - Despliegues enterprise
  • Ollama - Modelos locales

Pagos (Futuro)

  • Stripe
  • MercadoPago
  • Clip

Esquema de Base de Datos

-- Tabla de credenciales de integración por tenant
CREATE TABLE tenant_integration_credentials (
    id UUID PRIMARY KEY,
    tenant_id UUID NOT NULL REFERENCES tenants(id),
    integration_type VARCHAR(50) NOT NULL,  -- whatsapp, llm, stripe, etc.
    provider VARCHAR(50) NOT NULL,          -- meta, openai, openrouter, etc.
    credentials JSONB NOT NULL DEFAULT '{}', -- Datos sensibles encriptados
    config JSONB DEFAULT '{}',              -- Configuración no sensible
    is_active BOOLEAN DEFAULT true,
    is_verified BOOLEAN DEFAULT false,
    UNIQUE(tenant_id, integration_type, provider)
);

-- Mapeo de números WhatsApp a tenants
CREATE TABLE tenant_whatsapp_numbers (
    id UUID PRIMARY KEY,
    tenant_id UUID NOT NULL,
    phone_number_id VARCHAR(50) UNIQUE NOT NULL,
    phone_number VARCHAR(20),
    display_name VARCHAR(100),
    is_active BOOLEAN DEFAULT true
);

Seguridad

  1. Credenciales encriptadas en JSONB (recomendación: usar pg_crypto)
  2. API Interna protegida con X-Internal-Key header
  3. JWT obligatorio para endpoints de configuración
  4. No se exponen API keys en respuestas al frontend
  5. Cache de credenciales para reducir queries a BD

Consideraciones de Escalabilidad

  • Cache de credenciales con TTL de 5 minutos
  • Cache de clientes axios por tenant
  • Invalidación de cache al actualizar credenciales
  • Conexión separada de BD para servicio de WhatsApp (futuro)