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

3.7 KiB

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.