Nuevas Épicas (MCH-029 a MCH-033): - Infraestructura SaaS multi-tenant - Auth Social (OAuth2) - Auditoría Empresarial - Feature Flags - Onboarding Wizard Nuevas Integraciones (INT-010 a INT-014): - Email Providers (SendGrid, Mailgun, SES) - Storage Cloud (S3, GCS, Azure) - OAuth Social - Redis Cache - Webhooks Outbound Nuevos ADRs (0004 a 0011): - Notifications Realtime - Feature Flags Strategy - Storage Abstraction - Webhook Retry Strategy - Audit Log Retention - Rate Limiting - OAuth Social Implementation - Email Multi-provider Actualizados: - MASTER_INVENTORY.yml - CONTEXT-MAP.yml - HERENCIA-SIMCO.md - Mapas de documentación Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
309 lines
7.8 KiB
Markdown
309 lines
7.8 KiB
Markdown
---
|
|
id: INT-010
|
|
type: Integration
|
|
title: "Email Multi-Provider"
|
|
provider: "SendGrid/SES/SMTP"
|
|
status: Planificado
|
|
integration_type: "notifications"
|
|
created_at: 2026-01-10
|
|
updated_at: 2026-01-10
|
|
simco_version: "4.0.1"
|
|
tags:
|
|
- email
|
|
- notifications
|
|
- transactional
|
|
- multi-provider
|
|
---
|
|
|
|
# INT-010: Email Multi-Provider
|
|
|
|
## Metadata
|
|
|
|
| Campo | Valor |
|
|
|-------|-------|
|
|
| **Codigo** | INT-010 |
|
|
| **Proveedor** | SendGrid, AWS SES, SMTP |
|
|
| **Tipo** | Notificaciones |
|
|
| **Estado** | Planificado |
|
|
| **Multi-tenant** | Si |
|
|
| **Epic Relacionada** | MCH-029 |
|
|
| **Owner** | Backend Team |
|
|
|
|
---
|
|
|
|
## 1. Descripcion
|
|
|
|
Sistema de envio de emails transaccionales con soporte para multiples proveedores y fallback automatico. Incluye templates reutilizables, tracking de entrega y rate limiting por tenant.
|
|
|
|
**Casos de uso principales:**
|
|
- Emails de bienvenida y verificacion
|
|
- Notificaciones de pedidos
|
|
- Recordatorios de pago
|
|
- Alertas de inventario bajo
|
|
- Reportes programados
|
|
|
|
---
|
|
|
|
## 2. Credenciales Requeridas
|
|
|
|
### Variables de Entorno
|
|
|
|
| Variable | Descripcion | Tipo | Obligatorio |
|
|
|----------|-------------|------|-------------|
|
|
| `EMAIL_PROVIDER` | Proveedor principal (sendgrid/ses/smtp) | string | SI |
|
|
| `SENDGRID_API_KEY` | API Key de SendGrid | string | SI (si usa SendGrid) |
|
|
| `AWS_SES_REGION` | Region de AWS SES | string | SI (si usa SES) |
|
|
| `AWS_SES_ACCESS_KEY` | Access Key para SES | string | SI (si usa SES) |
|
|
| `AWS_SES_SECRET_KEY` | Secret Key para SES | string | SI (si usa SES) |
|
|
| `SMTP_HOST` | Host del servidor SMTP | string | SI (si usa SMTP) |
|
|
| `SMTP_PORT` | Puerto SMTP | number | SI (si usa SMTP) |
|
|
| `SMTP_USER` | Usuario SMTP | string | SI (si usa SMTP) |
|
|
| `SMTP_PASS` | Password SMTP | string | SI (si usa SMTP) |
|
|
| `EMAIL_FROM_ADDRESS` | Email de remitente | string | SI |
|
|
| `EMAIL_FROM_NAME` | Nombre de remitente | string | SI |
|
|
|
|
### Ejemplo de .env
|
|
|
|
```env
|
|
# Email Configuration
|
|
EMAIL_PROVIDER=sendgrid
|
|
EMAIL_FROM_ADDRESS=noreply@michangarrito.com
|
|
EMAIL_FROM_NAME=MiChangarrito
|
|
|
|
# SendGrid
|
|
SENDGRID_API_KEY=SG.xxxxxxxxxxxxxxxx
|
|
|
|
# AWS SES (fallback)
|
|
AWS_SES_REGION=us-east-1
|
|
AWS_SES_ACCESS_KEY=AKIAXXXXXXXXXXXXXXXX
|
|
AWS_SES_SECRET_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
|
|
|
# SMTP (ultimo recurso)
|
|
SMTP_HOST=smtp.mailtrap.io
|
|
SMTP_PORT=587
|
|
SMTP_USER=xxxxxxxx
|
|
SMTP_PASS=xxxxxxxx
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Endpoints/SDK Utilizados
|
|
|
|
### SendGrid
|
|
|
|
| Operacion | Metodo | Endpoint | Descripcion |
|
|
|-----------|--------|----------|-------------|
|
|
| Enviar email | POST | `/v3/mail/send` | Envio simple o con template |
|
|
| Templates | GET | `/v3/templates` | Listar templates |
|
|
|
|
### AWS SES
|
|
|
|
| Operacion | SDK Method | Descripcion |
|
|
|-----------|------------|-------------|
|
|
| Enviar email | `sendEmail()` | Via AWS SDK v3 |
|
|
| Enviar raw | `sendRawEmail()` | Con attachments |
|
|
|
|
### SMTP
|
|
|
|
```typescript
|
|
import * as nodemailer from 'nodemailer';
|
|
|
|
const transporter = nodemailer.createTransport({
|
|
host: process.env.SMTP_HOST,
|
|
port: parseInt(process.env.SMTP_PORT),
|
|
auth: {
|
|
user: process.env.SMTP_USER,
|
|
pass: process.env.SMTP_PASS,
|
|
},
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Rate Limits
|
|
|
|
| Proveedor | Limite | Periodo | Plan SendGrid |
|
|
|-----------|--------|---------|---------------|
|
|
| SendGrid | 100 | por segundo | Pro |
|
|
| SendGrid | 40,000 | por mes | Free |
|
|
| AWS SES | 14 | por segundo | Default |
|
|
| AWS SES | 50,000 | por dia | Verified |
|
|
|
|
### Rate Limiting por Tenant
|
|
|
|
| Plan MCH | Emails/hora | Emails/dia |
|
|
|----------|-------------|------------|
|
|
| Basic | 100 | 500 |
|
|
| Pro | 1,000 | 10,000 |
|
|
| Enterprise | Ilimitado | Ilimitado |
|
|
|
|
---
|
|
|
|
## 5. Manejo de Errores
|
|
|
|
| Codigo | Descripcion | Accion | Retry |
|
|
|--------|-------------|--------|-------|
|
|
| 400 | Email invalido | Validar formato | NO |
|
|
| 401 | API Key invalida | Verificar credencial | NO |
|
|
| 429 | Rate limit | Esperar + retry | SI |
|
|
| 500 | Error proveedor | Fallback a siguiente | SI |
|
|
| 503 | Servicio no disponible | Fallback | SI |
|
|
|
|
### Estrategia de Fallback
|
|
|
|
```typescript
|
|
const providers = ['sendgrid', 'ses', 'smtp'];
|
|
|
|
async function sendWithFallback(email: EmailDto) {
|
|
for (const provider of providers) {
|
|
try {
|
|
return await this.send(provider, email);
|
|
} catch (error) {
|
|
this.logger.warn(`Provider ${provider} failed, trying next`);
|
|
continue;
|
|
}
|
|
}
|
|
throw new Error('All email providers failed');
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 6. Templates
|
|
|
|
### Templates Disponibles
|
|
|
|
| Template | Descripcion | Variables |
|
|
|----------|-------------|-----------|
|
|
| welcome | Bienvenida | `{name}` |
|
|
| verify_email | Verificacion | `{name}, {link}` |
|
|
| order_confirmation | Pedido confirmado | `{order_id}, {items}` |
|
|
| payment_reminder | Recordatorio pago | `{amount}, {due_date}` |
|
|
| low_inventory | Alerta inventario | `{product}, {quantity}` |
|
|
|
|
### Formato de Template (Handlebars)
|
|
|
|
```html
|
|
<h1>Hola {{name}}</h1>
|
|
<p>Tu pedido #{{order_id}} ha sido confirmado.</p>
|
|
{{#each items}}
|
|
<li>{{this.name}} x {{this.quantity}}</li>
|
|
{{/each}}
|
|
```
|
|
|
|
---
|
|
|
|
## 7. Multi-tenant
|
|
|
|
### Modelo de Credenciales
|
|
|
|
- [x] **Global:** Credenciales compartidas por defecto
|
|
- [x] **Por Tenant:** Tenant puede configurar sus propias credenciales
|
|
- [x] **Branding:** From address personalizable por tenant (Enterprise)
|
|
|
|
### Almacenamiento
|
|
|
|
```sql
|
|
CREATE TABLE messaging.tenant_email_config (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID REFERENCES auth.tenants(id) NOT NULL,
|
|
provider VARCHAR(20) NOT NULL, -- sendgrid, ses, smtp
|
|
from_address VARCHAR(255) NOT NULL,
|
|
from_name VARCHAR(100),
|
|
credentials JSONB NOT NULL, -- Encriptado
|
|
is_active BOOLEAN DEFAULT true,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
UNIQUE(tenant_id)
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## 8. Tablas de BD
|
|
|
|
### email_templates
|
|
|
|
```sql
|
|
CREATE TABLE messaging.email_templates (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID REFERENCES auth.tenants(id),
|
|
key VARCHAR(100) NOT NULL,
|
|
name VARCHAR(255) NOT NULL,
|
|
subject VARCHAR(255) NOT NULL,
|
|
html_content TEXT NOT NULL,
|
|
text_content TEXT,
|
|
variables JSONB,
|
|
is_active BOOLEAN DEFAULT true,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
|
UNIQUE(tenant_id, key)
|
|
);
|
|
```
|
|
|
|
### email_logs
|
|
|
|
```sql
|
|
CREATE TABLE messaging.email_logs (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL,
|
|
template_id UUID REFERENCES messaging.email_templates(id),
|
|
to_address VARCHAR(255) NOT NULL,
|
|
subject VARCHAR(255) NOT NULL,
|
|
provider VARCHAR(20) NOT NULL,
|
|
provider_message_id VARCHAR(255),
|
|
status VARCHAR(20) NOT NULL, -- sent, delivered, opened, clicked, bounced, failed
|
|
error_message TEXT,
|
|
sent_at TIMESTAMP WITH TIME ZONE,
|
|
delivered_at TIMESTAMP WITH TIME ZONE,
|
|
opened_at TIMESTAMP WITH TIME ZONE,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## 9. Testing
|
|
|
|
### Modo Sandbox
|
|
|
|
| Ambiente | Proveedor | Comportamiento |
|
|
|----------|-----------|----------------|
|
|
| Development | Mailtrap | Emails atrapados, no enviados |
|
|
| Staging | SendGrid Sandbox | Emails simulados |
|
|
| Production | SendGrid/SES | Envio real |
|
|
|
|
### Test de Conexion
|
|
|
|
```bash
|
|
# Test SendGrid
|
|
curl -X POST "https://api.sendgrid.com/v3/mail/send" \
|
|
-H "Authorization: Bearer $SENDGRID_API_KEY" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"personalizations":[{"to":[{"email":"test@example.com"}]}],"from":{"email":"noreply@michangarrito.com"},"subject":"Test","content":[{"type":"text/plain","value":"Test email"}]}'
|
|
```
|
|
|
|
---
|
|
|
|
## 10. Monitoreo
|
|
|
|
### Metricas
|
|
|
|
| Metrica | Descripcion | Alerta |
|
|
|---------|-------------|--------|
|
|
| email_sent_total | Emails enviados | - |
|
|
| email_failed_total | Emails fallidos | > 5% |
|
|
| email_delivery_rate | Tasa de entrega | < 95% |
|
|
| email_bounce_rate | Tasa de rebote | > 5% |
|
|
|
|
---
|
|
|
|
## 11. Referencias
|
|
|
|
- [SendGrid API Docs](https://docs.sendgrid.com/api-reference)
|
|
- [AWS SES Developer Guide](https://docs.aws.amazon.com/ses/)
|
|
- [Nodemailer Docs](https://nodemailer.com/)
|
|
|
|
---
|
|
|
|
**Ultima actualizacion:** 2026-01-10
|
|
**Autor:** Backend Team
|