Files
mercadodevida/spec/tech.md
2026-08-22 07:09:03 +02:00

63 lines
3.7 KiB
Markdown

# F-152 — Tech Spec
## Principios
- Best-effort: un email fallido o SMTP no configurado **nunca** debe fallar el
registro ni el webhook de pagos. Se loguea y se continúa.
- Reusar infraestructura existente: SMTP desde `store_settings`
(`smtp_host/port/secure/user/pass/from`), mismo patrón que
`SettingsPasswordResetMailer` y `sendOrderStatusEmail`.
- Boundaries: identity no importa orders/payments a nivel de dominio; el wiring
del order-confirmation email se hace en el *route handler* de payments (posee
`deps.pool`), reusando `sendOrderStatusEmail` exportado públicamente por
`orders/index.ts`.
## Cambios
### 1. Order confirmation on PaymentSucceeded (payments)
- `orders/index.ts`: exportar `sendOrderStatusEmail` (y `ORDER_STATE_LABELS`).
- `payments/api/payments.routes.ts`: en el handler de `/payments/webhook`, tras
`const outcome = await service.handleWebhook(event)`, si
`event.type === 'PaymentSucceeded' && event.orderId && outcome.kind === 'processed'`,
fetch customer email (`identity_users.email` via `orders_orders.user_id`) y
`sendOrderStatusEmail(deps.pool, { to, orderId: event.orderId, state: 'PAID' })`
dentro de try/catch; log de advertencia si falla SMTP/no-config.
### 2. Welcome email on registration (identity)
- `identity/domain/ports.ts`: nuevo puerto `WelcomeMailer` con
`sendWelcome(input: { email: string; name?: string }): Promise<void>` (name es
opcional: `User` no almacena nombre en el dominio actual).
- `identity/infrastructure/settings-welcome-mailer.ts`: `SettingsWelcomeMailer(pool)`
modelado en `SettingsPasswordResetMailer` — lee SMTP de `store_settings`, usa
`nodemailer`, y una función pura `buildWelcomeEmail` (verificable sin SMTP).
El subject (`¡Bienvenido a Mercado de Vida!`) coincide con la plantilla
`account_created` de notificaciones. Lanza si SMTP no está configurado.
- `identity/api/identity.routes.ts`: `IdentityRoutesDeps.welcomeMailer?: WelcomeMailer`;
en el handler de `POST /auth/register`, tras `registerUser.execute` exitoso, se
despacha el welcome email *fire-and-forget* (`void mailer.sendWelcome(...).catch(
request.log.warn(...))`). Best-effort: un fallo SMTP se loguea (warning) y se
traga; el registro nunca se rompe por email. `RegisterUser` se mantiene sin
depender de email (puro orquestación de dominio).
- `identity/index.ts`: re-exporta `SettingsWelcomeMailer` (y
`SettingsPasswordResetMailer`) para que `build-app.ts` los importe desde el
index en lugar de deep-importar infra (cumple R2 de boundaries).
- `notifications/domain/notification.ts` + `notifications/api/notifications.routes.ts`:
añadir `account_created` al union `EmailTemplate`, a los mapas SUBJECTS/BODIES
de `LoggingEmailProvider` y al enum del schema de `POST /notifications/dispatch`.
## SMTP / store_settings
Claves existentes: `smtp_host, smtp_port, smtp_secure, smtp_user, smtp_pass, smtp_from`.
El welcome mailer reusa exactamente estas claves.
## Testing
- `settings-welcome-mailer.test.ts`: `buildWelcomeEmail` (greeting, nombre, XSS),
`SettingsWelcomeMailer.sendWelcome` (nodemailer mockeado: sendmail called once
con recipient == email y subject == plantilla `account_created`), y lanza cuando
SMTP no está configurado (`SMTP is not configured`).
- `orders/tests/orders-index.test.ts`: el barrel de `orders/index.ts` re-exporta
`sendOrderStatusEmail`, `buildOrderStatusEmail`, `ORDER_STATE_LABELS`.
- `order-status-mailer.test.ts` ya existe cubriendo `buildOrderStatusEmail` (PAID
pertenece a `ORDER_STATE_LABELS`).
- `tsc --noEmit` limpio; `prettier --check` y `eslint` limpios en los archivos tocados;
suite completa de vitest verde (197 tests); `lint:boundaries` sin nuevas violaciones.
- verify.sh green.