Ir al contenido

Esta guía cubre los escenarios de soporte más frecuentes con pasos concretos para diagnosticar y resolver cada uno.

Herramientas que necesitarás:

  • Acceso al panel admin en /admin
  • Acceso a Stripe Dashboard
  • Prisma Studio: wasp db studio (solo en desarrollo o con acceso a la DB)

1. Usuario pagó pero no tiene acceso al curso

Sección titulada «1. Usuario pagó pero no tiene acceso al curso»

Este es el problema más común. Puede tener varias causas.

Paso 1: Verificar en Stripe que el pago fue exitoso

  1. Ir a Stripe Dashboard → Payments
  2. Buscar el email del usuario
  3. Verificar que el estado del pago es “Succeeded” (no “Pending” ni “Failed”)
  4. Anotar el payment_intent_id o session_id

Paso 2: Verificar que el webhook fue procesado

En Stripe Dashboard → Developers → Webhooks:

  1. Encontrar el webhook de producción (/payments-webhook)
  2. Buscar el evento checkout.session.completed con la fecha del pago
  3. Si el evento tiene estado “Failed” o no existe → el webhook falló

Paso 3: Verificar la inscripción en la base de datos

Con Prisma Studio (wasp db studio) o via SQL:

-- Buscar el usuario
SELECT id, email FROM "User" WHERE email = 'usuario@ejemplo.com';
-- Ver sus inscripciones
SELECT e.*, c.title as course_title
FROM "Enrollment" e
JOIN "Course" c ON e."courseId" = c.id
WHERE e."userId" = <user_id>;

Si no hay inscripción → el webhook no creó el registro.

Si el webhook falló:

  1. En Stripe Dashboard → Webhooks → el evento fallido → “Resend”
  2. Esto reintenta el webhook y debería crear la inscripción

Si la inscripción no existe y no puedes reenviar el webhook:

Crear la inscripción manualmente con Prisma Studio:

  1. Abrir wasp db studio
  2. Tabla Enrollment → Add record:
    • userId: ID del usuario
    • courseId: ID del curso comprado
    • enrolledAt: fecha actual
    • completed: false
  3. Notificar al usuario que ya tiene acceso

Si el usuario compró una suscripción y no tiene acceso:

Verificar que el campo subscriptionStatus del usuario es "active":

SELECT id, email, "subscriptionStatus", "subscriptionPlan"
FROM "User"
WHERE email = 'usuario@ejemplo.com';

Si no es "active", puede ser un problema de sincronización con Stripe. Verificar el estado de la suscripción en Stripe Dashboard → Customers.


2. Certificado no se generó después de completar un curso

Sección titulada «2. Certificado no se generó después de completar un curso»

Paso 1: Verificar que el curso realmente está completado

SELECT e.completed, e."completedAt", e."courseId"
FROM "Enrollment" e
WHERE e."userId" = <user_id> AND e."courseId" = <course_id>;

Si completed = false → el usuario no completó el curso (todas las lecciones deben estar completadas).

Paso 2: Verificar el progreso de lecciones

SELECT lp."lessonId", lp.completed, lp."watchedSeconds"
FROM "LessonProgress" lp
JOIN "Lesson" l ON lp."lessonId" = l.id
WHERE lp."userId" = <user_id>
AND l."courseId" = <course_id>
ORDER BY l."order";

Comparar con el total de lecciones del curso — todas deben estar completed = true.

Paso 3: Buscar el certificado

SELECT * FROM "Certificate"
WHERE "userId" = <user_id> AND "courseId" = <course_id>;

Si el curso está completado pero no hay certificado:

El certificado se genera en la action completeCourse. Forzar la generación:

  1. En Prisma Studio → tabla Certificate → Add record:

    • userId: ID del usuario
    • courseId: ID del curso
    • issuedAt: fecha actual
    • certificateCode: generar UUID único (ej. cert_YYYYMMDD_userId_courseId)
  2. Notificar al usuario con el link a /certificados

Si hay lecciones sin completar:

Verificar si el usuario reporta que sí las completó. Puede ser un bug de sincronización. Revisar LessonProgress y si corresponde, marcar manualmente:

UPDATE "LessonProgress"
SET completed = true, "completedAt" = NOW()
WHERE "userId" = <user_id> AND "lessonId" = <lesson_id>;

Los videos funcionan diferente en desarrollo y producción.

Diferencia crítica: Desarrollo vs. Producción

Sección titulada «Diferencia crítica: Desarrollo vs. Producción»
EntornoFuente de videosCómo funcionan
DesarrolloMP4 públicos W3C (storageVideoKey)URL directa, sin firma
ProducciónAWS S3 + CloudFront o AzureURL firmada con expiración de 1 hora

⚠️ Importante: En producción, todos los videos deben estar en S3 o Azure Blob Storage. YouTube fue eliminado como fuente de video (abril 2026).

Paso 1: Verificar si la URL firmada expira

Las CloudFront Signed URLs expiran en 1 hora. Si el usuario deja el video abierto más de 1 hora sin recargar, el video deja de funcionar. Solución: recargar la página.

Paso 2: Verificar las variables de entorno

Si múltiples usuarios reportan el problema:

Ventana de terminal
flyctl ssh console -a talentbricksai -C "env | grep -E 'CLOUDFRONT|AWS_S3'"

Verificar que todas las variables están configuradas:

  • CLOUDFRONT_DOMAIN
  • CLOUDFRONT_KEY_PAIR_ID
  • CLOUDFRONT_PRIVATE_KEY
  • AWS_S3_IAM_ACCESS_KEY
  • AWS_S3_IAM_SECRET_KEY
  • AWS_S3_FILES_BUCKET
  • AWS_S3_REGION

Paso 3: Verificar que el archivo existe en S3

  1. Ir a AWS Console → S3
  2. Buscar el bucket talentbricksai-videos
  3. Verificar que el archivo del video existe

Paso 4: Verificar permisos de CloudFront

Verificar que la política del bucket S3 permite acceso a CloudFront OAC. Ver Despliegue → Fase 3.


En desarrollo, los emails se imprimen en la consola del servidor (no se envían). Para ver el link de reset:

Ventana de terminal
# En la terminal donde corre wasp start
# Buscar líneas como:
# Subject: Reset your password
# Body: Click this link: http://localhost:3000/password-reset?token=...

El usuario debe:

  1. Ir a la página de login → “¿Olvidaste tu contraseña?”
  2. Ingresar su email
  3. Revisar su bandeja de entrada (y carpeta de spam)
  4. Click en el link del email (válido por 1 hora)

Si el email no llega después de 5 minutos:

  1. Verificar en SendGrid Dashboard → Activity que el email fue enviado
  2. Verificar que el dominio está autenticado en SendGrid
  3. Verificar que SENDGRID_API_KEY está configurada correctamente

Solo los administradores pueden hacer a otros usuarios administradores. Hay dos formas:

En Prisma Studio o SQL:

UPDATE "User"
SET "isAdmin" = true
WHERE email = 'nuevo-admin@empresa.com';

La variable ADMIN_EMAILS en el servidor puede listar emails que automáticamente tienen acceso admin:

Ventana de terminal
# En desarrollo (.env.server):
ADMIN_EMAILS="tu@email.com,otro@email.com"
# En producción (Fly.io):
wasp deploy fly cmd --context server secrets set ADMIN_EMAILS="admin1@empresa.com,admin2@empresa.com"

Nota: Los cambios en ADMIN_EMAILS solo aplican al login siguiente del usuario. No cambia el campo isAdmin en la DB automáticamente — depende de la lógica de la app.


TalentBricksAI no tiene sistema de reembolsos automático. Los reembolsos se procesan manualmente en Stripe.

  1. Ir a Stripe Dashboard → Payments
  2. Buscar el pago del usuario por email o monto
  3. Hacer clic en el pago → “Refund”
  4. Seleccionar monto (total o parcial) y razón
  5. Confirmar el reembolso

Stripe notifica automáticamente al usuario por email.

Después del reembolso, si quieres también revocar el acceso al curso:

UPDATE "Enrollment"
SET completed = false
WHERE "userId" = <user_id> AND "courseId" = <course_id>;
-- O eliminar la inscripción completamente:
DELETE FROM "Enrollment"
WHERE "userId" = <user_id> AND "courseId" = <course_id>;

Política de reembolsos: Definir e implementar una política clara antes de lanzar. Stripe permite 30 días para la mayoría de los reembolsos.


7. Queries de diagnóstico útiles con Prisma Studio

Sección titulada «7. Queries de diagnóstico útiles con Prisma Studio»

Abrir con wasp db studio (corre en http://localhost:5555).

-- Ver todos los cursos disponibles
SELECT id, title, slug, "isPublished" FROM "Course" ORDER BY title;
-- Ver inscripciones de un usuario
SELECT e.id, c.title, e."enrolledAt", e.completed
FROM "Enrollment" e
JOIN "Course" c ON e."courseId" = c.id
WHERE e."userId" = <user_id>;
-- Ver usuarios con suscripción activa
SELECT id, email, "subscriptionStatus", "subscriptionPlan"
FROM "User"
WHERE "subscriptionStatus" = 'active';
-- Ver certificados emitidos
SELECT cert.id, u.email, c.title, cert."issuedAt"
FROM "Certificate" cert
JOIN "User" u ON cert."userId" = u.id
JOIN "Course" c ON cert."courseId" = c.id
ORDER BY cert."issuedAt" DESC
LIMIT 20;
-- Buscar un usuario por email
SELECT id, email, "isAdmin", "subscriptionStatus", "createdAt"
FROM "User"
WHERE email ILIKE '%@ejemplo.com%';

Si un problema no está cubierto aquí o requiere cambios de código:

  1. Documentar el problema con capturas de pantalla y pasos para reproducir
  2. Crear un issue en GitHub con el label bug o support
  3. Notificar al usuario que se está investigando y dar un tiempo estimado de respuesta