Skip to content

Integraciones externas

Casos para validar que Stripe (único SaaS), el storage local y el SMTP local (Postfix) funcionen end-to-end. Los tests de Stripe tocan la sandbox real; los de storage y SMTP tocan el propio servidor.

Duración estimada: 30 minutos.

Stripe — pagos

TC-INT-001 · Checkout Session completa exitosamente

Severidad: 🔴 Crítica

Precondiciones:

  • Stripe en test mode (STRIPE_SECRET empieza con sk_test_)
  • Candidato logueado sin membresía

Pasos:

  1. Dashboard candidato → "Contratar membresía"
  2. En Stripe Checkout: tarjeta 4242 4242 4242 4242, fecha futura, CVC 123
  3. Completar pago

Resultado esperado:

  • Payment.status = succeeded
  • Membership.status = active con expires_at = now + 180 days
  • Correo "Membresía activa" en MailHog (dev) o inbox real (staging/prod)

Validación en Stripe Dashboard:

  • Stripe → Payments → última tx con status succeeded
  • Amount = 499 MXN
  • Metadata incluye user_id, membership_plan_id

TC-INT-002 · Webhook con firma inválida

Severidad: 🔴 Crítica

Pasos:

bash
curl -X POST http://localhost:8000/api/webhooks/stripe \
  -H "Content-Type: application/json" \
  -H "Stripe-Signature: t=123,v1=invalid" \
  -d '{"type":"checkout.session.completed","data":{"object":{"id":"cs_fake"}}}'

Resultado esperado:

  • Response 400 "Invalid signature"
  • Log Laravel: [StripeWebhook] SignatureVerificationException
  • NO se crea Payment ni Membership

TC-INT-003 · Stripe CLI — disparar webhook de prueba

Severidad: 🟠 Alta

Precondiciones: Stripe CLI instalado, stripe listen corriendo

Pasos:

bash
# Terminal 1
stripe listen --forward-to localhost:8000/api/webhooks/stripe

# Terminal 2
stripe trigger checkout.session.completed

Resultado esperado:

  • Stripe CLI muestra: <-- [200] POST ...
  • Backend loguea: [StripeWebhook] checkout.session.completed processed

Variaciones:

  • Replay del mismo evento → 200 idempotente, no duplica membership

TC-INT-004 · Webhook retry automático (fallar y recuperar)

Severidad: 🟠 Alta

Precondiciones: Ambiente donde puedas apagar el backend temporalmente

Pasos:

  1. Candidato inicia checkout
  2. Apagar el backend antes de que Stripe envíe el webhook
  3. Completar pago en Stripe
  4. Stripe intentará el webhook → falla (backend caído)
  5. Encender el backend
  6. Stripe reintenta automáticamente (3 retries con backoff exponencial)

Resultado esperado:

  • Eventualmente webhook llega y procesa
  • Membresía activa
  • Dashboard Stripe → Webhooks → evento muestra retry history

Nota: Stripe reintenta hasta por 3 días. Si el webhook nunca llega a completar, el equipo recibe alerta.

Storage local — archivos

TC-INT-005 · Upload de avatar

Severidad: 🟠 Alta

Pasos:

  1. Candidato logueado → perfil → "Cambiar foto"
  2. Imagen JPG 2MB válida → upload

Resultado esperado:

  • URL del tipo {APP_URL}/storage/avatars/{user_id}/{hash}.webp
  • User.avatar_url apunta a esa URL
  • User.avatar_path guarda avatars/{user_id}/{hash}.webp
  • Archivo físico existe en humae_backend/storage/app/public/avatars/{user_id}/
  • GET {avatar_url} devuelve 200 con Content-Type: image/webp
  • Dimensiones: 400×400 (recortadas con cover)

Variaciones:

  • Subir archivo > 4MB → 422 "Archivo demasiado grande" (rechazado en el FormRequest)
  • Subir PDF como avatar → 422 "Formato inválido"
  • Cambiar avatar una segunda vez → el archivo anterior se borra del disco (verificar con ls); queda sólo el nuevo

TC-INT-006 · Upload de documento grande (PDF 9MB)

Severidad: 🟡 Media

Pasos:

  1. Perfil → "Documentos" → subir PDF de ~9MB

Resultado esperado:

  • Upload exitoso (≤ 10MB permitido)
  • CandidateDocument.file_provider = 'local'
  • CandidateDocument.file_public_id = 'documents/{profile_id}/{hash}.pdf'
  • CandidateDocument.file_url = '{APP_URL}/api/v1/me/profile/documents/{id}/download'
  • Archivo físico existe en storage/app/private/documents/{profile_id}/
  • Documento visible en la lista; botón de descarga funciona

Variaciones:

  • PDF de 11MB → 422
  • Nginx devuelve 413 en vez de 422 → ampliar client_max_body_size a 12M

TC-INT-007 · Descarga de documento privado requiere auth

Severidad: 🔴 Crítica

Pasos:

  1. Crear documento con un candidato
  2. Sin sesión, curl -I {APP_URL}/api/v1/me/profile/documents/{id}/download
  3. Con sesión del mismo candidato, repetir
  4. Con sesión de otro candidato, repetir

Resultado esperado:

  • Sin sesión → 401 Unauthorized
  • Mismo candidato → 200 con stream del archivo
  • Otro candidato → 404 (no revela existencia del recurso ajeno)

Severidad: 🟡 Media

Pasos:

bash
cd humae_backend
php artisan tinker --execute="Storage::disk('public')->put('probe.txt','ok');"
curl -I {APP_URL}/storage/probe.txt

Resultado esperado:

  • HTTP/2 200 con Content-Type: text/plain
  • Si devuelve 404: falta ejecutar php artisan storage:link o Nginx no está sirviendo /storage/*

SMTP local — correos

TC-INT-009 · Email transaccional llega al inbox con headers auth OK

Severidad: 🔴 Crítica

Precondiciones:

  • Ambiente con Postfix real (no MailHog)
  • DNS SPF/DKIM/DMARC/PTR ya configurados y propagados

Pasos:

bash
php artisan tinker --execute="\Mail::raw('Test QA '.now(), fn(\$m) => \$m->to('tu-email-real@gmail.com')->subject('HUMAE QA test'));"

Resultado esperado:

  • Email llega al inbox (no spam) en ≤ 2 minutos
  • Sender: HUMAE <no-reply@humae.com.mx>
  • Reply-To: soporte@humae.com.mx
  • Header Authentication-Results del destinatario muestra dkim=pass, spf=pass, dmarc=pass
  • /var/log/mail.log muestra status=sent

Variaciones:

  • Email cae en spam → revisar SPF/DKIM/DMARC/PTR (ver SMTP local)
  • connect to 127.0.0.1:25: connection refused → Postfix apagado (sudo systemctl start postfix)
  • status=deferred (Network is unreachable) → provider bloquea 25 saliente

Severidad: 🔴 Crítica

Pasos: Ejecutar TC-CAND-001 con un email real

Resultado esperado:

  • Email con subject "Verifica tu correo…"
  • HTML se renderiza bien en Gmail, Outlook, Apple Mail
  • Logo HUMAE visible
  • Botón CTA con color #314259 y texto legible
  • Link funciona al click

Verificación rápida de integraciones

Una sola pasada combinada si tienes poco tiempo:

bash
# 1. Health endpoint - todo verde
curl https://api.humae.com.mx/api/v1/health

# 2. Stripe listener activo
stripe listen --forward-to localhost:8000/api/webhooks/stripe
# En otra terminal:
stripe trigger checkout.session.completed

# 3. Storage local - escribir, leer HTTP, borrar
php artisan tinker --execute="Storage::disk('public')->put('probe.txt','ok');"
curl -I {APP_URL}/storage/probe.txt
php artisan tinker --execute="Storage::disk('public')->delete('probe.txt');"

# 4. SMTP local - enviar correo de prueba
php artisan tinker --execute="\Mail::raw('test', fn(\$m) => \$m->to('tu@gmail.com')->subject('HUMAE test'));"
sudo tail -n 20 /var/log/mail.log

Los 4 deben completar sin errores.

Siguiente

Casos edge y what-ifs →

Manual de usuario HUMAE · Uso interno