Apariencia
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 destinatarioCanal database (in-app)
- Tabla
notificationsestándar Laravel con UUID primary key. - El campo
dataes JSON con el payload de la notificación. - El campo
read_atqueda 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ón | Ruta |
|---|---|
| Listar | GET /api/v1/me/notifications?unread_only=0 |
| Contador sin leer | GET /api/v1/me/notifications/unread-count |
| Marcar una como leída | POST /api/v1/me/notifications/{id}/mark-read |
| Marcar todas como leídas | POST /api/v1/me/notifications/mark-all-read |
| Eliminar una | DELETE /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_atrelativo. - Click en item → marca como leída + navega al deep link.
Canal mail
- Driver: SMTP (
MAIL_MAILER=smtp) apuntando a127.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). Queueabletrait en cada Notification → los correos se envían en background si hay queue driver (database/redis).- En dev,
QUEUE_CONNECTION=syncenví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 boolEl 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
pendingasucceeded.
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 →

