Skip to content

Sistema de notificaciones

HUMAE usa el sistema nativo de Laravel Notifications (Illuminate\Notifications\Notification) con dos canales activos: base de datos (in-app) y mail (correos transaccionales).

Arquitectura

Evento de dominio (ej. candidato pagó)


App\Services\MembershipService::activateFromCheckoutSession()


$user->notify(new MembershipActivatedNotification($membership))

         ├────▶ database channel  ──▶ tabla notifications (JSON)
         │                              │
         │                              ▼
         │                      campana en el header frontend

         └────▶ mail channel     ──▶ Postfix local (127.0.0.1:25)


                              SMTP saliente → inbox del destinatario

Canal database (in-app)

  • Tabla notifications estándar Laravel con UUID primary key.
  • El campo data es JSON con el payload de la notificación.
  • El campo read_at queda null hasta que el usuario marca como leído.

Estructura de la tabla

sql
notifications
├── id (UUID)
├── type          (e.g. "App\\Notifications\\MembershipActivatedNotification")
├── notifiable_type + notifiable_id  (polymorphic → App\Models\User)
├── data          (JSON)
├── read_at       (nullable timestamp)
├── created_at
├── updated_at

Índice: notifiable_type + notifiable_id (Laravel default).

Endpoints para el frontend

AcciónRuta
ListarGET /api/v1/me/notifications?unread_only=0
Contador sin leerGET /api/v1/me/notifications/unread-count
Marcar una como leídaPOST /api/v1/me/notifications/{id}/mark-read
Marcar todas como leídasPOST /api/v1/me/notifications/mark-all-read
Eliminar unaDELETE /api/v1/me/notifications/{id}

Componente frontend NotificationBell

  • Se muestra en el header cuando el usuario está autenticado.
  • Polling cada 60s al endpoint unread-count.
  • Al abrir el panel, llama GET /me/notifications?unread_only=1&per_page=10.
  • Cada item muestra icon + title + body + created_at relativo.
  • Click en item → marca como leída + navega al deep link.

Canal mail

  • Driver: SMTP (MAIL_MAILER=smtp) apuntando a 127.0.0.1:25 (Postfix instalado en el mismo servidor del backend). Ver SMTP local.
  • En dev: SMTP a MailHog (MAIL_HOST=mailhog MAIL_PORT=1025).
  • Queueable trait en cada Notification → los correos se envían en background si hay queue driver (database/redis).
  • En dev, QUEUE_CONNECTION=sync envía inmediatamente.

Envío

Cada Notification implementa toMail(User $user): MailMessage:

php
public function toMail(User $user): MailMessage
{
    return (new MailMessage)
        ->subject('Tu membresía HUMAE está activa')
        ->markdown('emails.membership_activated', [
            'user' => $user,
            'membership' => $this->membership,
        ]);
}

Plantillas

En resources/views/emails/*.blade.php. Usan el layout Markdown de Laravel por defecto, personalizado con:

  • Logo HUMAE en el header.
  • Paleta de marca.
  • Botón primario con background: #314259.
  • Footer con dirección fiscal y link de unsubscribe (fase 2).

Canales futuros (Fase 2)

  • SMS vía Twilio.
  • WhatsApp vía Twilio / Meta Business API.
  • Push web vía Firebase Cloud Messaging.
  • Slack webhook para alertas internas del equipo HUMAE.

Implementación: agregar el canal al via() de cada Notification:

php
public function via(User $user): array
{
    return ['database', 'mail', 'sms'];
}

Preferencias del usuario (Fase 2)

Tabla notification_preferences:

notification_preferences
├── user_id
├── category       (membership, interviews, pipeline, marketing)
├── channel_email  bool
├── channel_sms    bool
├── channel_whatsapp bool
├── channel_inapp  bool

El user podrá optar de recibir solo ciertas categorías por ciertos canales.

Rate limiting

  • Un user no recibe más de 5 emails por hora por defecto (throttle a nivel aplicación).
  • Excepciones: notificaciones críticas (password reset, verificación) no se throttle.
  • Las in-app no tienen rate limit.

Idempotencia

Si el mismo evento se dispara dos veces (ej. webhook de Stripe reintentado), la notificación solo se envía una vez:

  • El servicio verifica el estado antes de disparar.
  • La notificación de activación de membresía se envía solo cuando el Payment cambia de pending a succeeded.

Internacionalización

Todas las notificaciones están en español (es_MX). Para fase 2:

  • Detectar user.locale (campo a agregar).
  • Renderizar plantilla bilingüe.

Testing

Las tests feature usan Notification::fake():

php
Notification::fake();
// ... ejecuta el flujo ...
Notification::assertSentTo($user, MembershipActivatedNotification::class);

Siguiente

Detalle de las plantillas de correos: Correos transaccionales →

Manual de usuario HUMAE · Uso interno