# 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` (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.