- HERENCIA-SIMCO.md actualizado con directivas v3.7 y v3.8 - Cambios en backend y frontend Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
8.3 KiB
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 plataformagetLLMConfig(tenantId)- Con fallback a plataformaresolveTenantFromPhoneNumberId(phoneNumberId)- Para webhooks
Controladores
-
IntegrationsController: API REST para tenants (protegido con JWT)
GET /integrations/status- Estado de todas las integracionesPUT /integrations/whatsapp- Configurar WhatsApp propioPUT /integrations/llm- Configurar LLM propio
-
InternalIntegrationsController: API interna para whatsapp-service
GET /internal/integrations/:tenantId/whatsappGET /internal/integrations/:tenantId/llmGET /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
- Todos los métodos aceptan
-
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
tenantIden todo el flujo de conversación
- Resuelve tenant desde
Flujo de un Mensaje Entrante
- Meta envía webhook con
metadata.phone_number_id - WebhookController extrae phoneNumberId del payload
- WebhookService.processIncomingMessage() recibe phoneNumberId
- CredentialsProviderService.resolveTenantFromPhoneNumberId()
- Consulta backend (
/internal/integrations/resolve-tenant/:id) - Retorna
tenantIdonull(plataforma)
- Consulta backend (
- Contexto de conversación incluye tenantId
- WhatsAppService.sendTextMessage(to, text, tenantId)
- Obtiene credenciales para ese tenant (o plataforma)
- Envía mensaje con las credenciales correctas
- 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
- 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
- Credenciales encriptadas en JSONB (recomendación: usar pg_crypto)
- API Interna protegida con X-Internal-Key header
- JWT obligatorio para endpoints de configuración
- No se exponen API keys en respuestas al frontend
- 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)