--- id: "US-EDU-008" title: "Obtener Certificado" type: "User Story" status: "Done" priority: "Media" epic: "OQI-002" story_points: 3 created_date: "2025-12-05" updated_date: "2026-01-04" --- # US-EDU-008: Obtener Certificado ## Metadata | Campo | Valor | |-------|-------| | **ID** | US-EDU-008 | | **Épica** | OQI-002 - Módulo Educativo | | **Módulo** | education | | **Prioridad** | P2 | | **Story Points** | 3 | | **Sprint** | Sprint 5 | | **Estado** | Pendiente | | **Asignado a** | Por asignar | --- ## Historia de Usuario **Como** usuario que completó un curso, **quiero** obtener un certificado digital verificable, **para** validar mi logro, agregarlo a mi perfil profesional y compartirlo en redes sociales. ## Descripción Detallada El usuario debe recibir automáticamente un certificado digital en formato PDF al completar el 100% de un curso. El certificado debe tener diseño profesional con logo de Trading Platform, nombre del usuario, título del curso, fecha de finalización, ID único de verificación, y QR code. El usuario debe poder descargar el PDF, compartir en LinkedIn, y el certificado debe ser verificable públicamente. ## Mockups/Wireframes ``` [MODAL DE CURSO COMPLETADO] ┌─────────────────────────────────────────────────────────────────┐ │ ✨ 🎓 ✨ │ │ │ │ ¡FELICIDADES! CURSO COMPLETADO │ │ │ │ Fibonacci Retracement Básico │ │ │ │ +200 XP ganados │ │ │ │ ┌────────────────────────────────────────────────────────────┐ │ │ │ │ │ │ │ [PREVIEW DEL CERTIFICADO] │ │ │ │ │ │ │ │ ┌──────────────────────────────────────┐ │ │ │ │ │ [LOGO ORBIQUANT] │ │ │ │ │ │ CERTIFICADO DE FINALIZACIÓN │ │ │ │ │ │ │ │ │ │ │ │ Se certifica que │ │ │ │ │ │ JUAN PÉREZ │ │ │ │ │ │ Ha completado exitosamente │ │ │ │ │ │ "Fibonacci Retracement Básico" │ │ │ │ │ │ │ │ │ │ │ │ 05/12/2025 │ │ │ │ │ │ OQI-EDU-A3F8D291 │ │ │ │ │ │ │ │ │ │ │ │ [Firma Instructor] [Firma OQI] │ │ │ │ │ │ [QR CODE] │ │ │ │ │ └──────────────────────────────────────┘ │ │ │ │ │ │ │ └────────────────────────────────────────────────────────────┘ │ │ │ │ [📥 Descargar PDF] [💼 Compartir en LinkedIn] [✕ Cerrar] │ │ │ └─────────────────────────────────────────────────────────────────┘ [PÁGINA DE CERTIFICADOS] ┌─────────────────────────────────────────────────────────────────┐ │ MIS CERTIFICADOS [🔍] │ │ │ │ Has obtenido 12 certificados │ │ │ │ Filtros: [Todos ▼] [Más recientes ▼] │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ [THUMBNAIL] │ │ [THUMBNAIL] │ │ [THUMBNAIL] │ │ │ │ │ │ │ │ │ │ │ │ Fibonacci │ │ Day Trading │ │ Candlestick │ │ │ │ Básico │ │ Pro │ │ Patterns │ │ │ │ │ │ │ │ │ │ │ │ 05/12/2025 │ │ 28/11/2025 │ │ 15/11/2025 │ │ │ │ OQI-...-291 │ │ OQI-...-8F2 │ │ OQI-...-4A1 │ │ │ │ │ │ │ │ │ │ │ │ [Ver] [📥] │ │ [Ver] [📥] │ │ [Ver] [📥] │ │ │ │ [💼 LinkedIn]│ │ [💼 LinkedIn]│ │ [💼 LinkedIn]│ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ │ [1] 2 3 4 │ │ │ └─────────────────────────────────────────────────────────────────┘ [PÁGINA DE VERIFICACIÓN PÚBLICA] ┌─────────────────────────────────────────────────────────────────┐ │ [LOGO] Trading Platform │ │ │ │ VERIFICACIÓN DE CERTIFICADO │ │ │ │ Certificado: OQI-EDU-A3F8D291 │ │ │ │ ┌────────────────────────────────────────────────────────────┐ │ │ │ ✓ CERTIFICADO VÁLIDO │ │ │ │ │ │ │ │ Otorgado a: Juan Pérez │ │ │ │ Curso: Fibonacci Retracement Básico │ │ │ │ Categoría: Análisis Técnico │ │ │ │ Fecha de finalización: 05/12/2025 │ │ │ │ Duración del curso: 2.5 horas │ │ │ │ Módulos: 5 | Lecciones: 23 │ │ │ │ │ │ │ │ Instructor: Carlos Mendoza │ │ │ │ Institución: Trading Platform │ │ │ │ │ │ │ │ Estado: ✅ Activo │ │ │ │ Emitido: 05/12/2025 15:45:00 UTC │ │ │ └────────────────────────────────────────────────────────────┘ │ │ │ │ Este certificado puede ser verificado en cualquier momento en: │ │ trading.com/verify/OQI-EDU-A3F8D291 │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` --- ## Criterios de Aceptación **Escenario 1: Completar curso genera certificado** ```gherkin DADO que el usuario completó todas las lecciones de un curso Y aprobó todos los quizzes obligatorios CUANDO se marca la última lección como completada ENTONCES se genera automáticamente un certificado Y se registra en backend con ID único (OQI-EDU-XXXXXXXX) Y se genera PDF con diseño profesional Y se almacena PDF en S3 o CDN Y se muestra modal de felicitación Y se envía email con certificado adjunto ``` **Escenario 2: Ver certificado en modal** ```gherkin DADO que se generó el certificado CUANDO se muestra el modal de curso completado ENTONCES se muestra preview del certificado Y se muestra botón "Descargar PDF" Y se muestra botón "Compartir en LinkedIn" Y se muestra "Ver todos mis certificados" ``` **Escenario 3: Descargar certificado en PDF** ```gherkin DADO que el usuario tiene un certificado CUANDO hace click en "Descargar PDF" ENTONCES se descarga archivo PDF Y el PDF contiene: - Logo de Trading Platform - Título "Certificado de Finalización" - Nombre completo del usuario - Título del curso - Fecha de finalización - ID único del certificado - Firmas digitales (instructor + plataforma) - QR code para verificación - Footer con URL de verificación ``` **Escenario 4: Compartir en LinkedIn** ```gherkin DADO que el usuario quiere compartir su certificado CUANDO hace click en "Compartir en LinkedIn" ENTONCES se abre nueva pestaña de LinkedIn Y el formulario de certificación está pre-llenado con: - Nombre: "Fibonacci Retracement Básico" - Organización: "Trading Platform" - Fecha de emisión: "Diciembre 2025" - ID de certificado: "OQI-EDU-A3F8D291" - URL de verificación: "trading.com/verify/..." ``` **Escenario 5: Ver galería de certificados** ```gherkin DADO que el usuario tiene 12 certificados CUANDO accede a /education/certificates ENTONCES se muestra galería de todos los certificados Y cada certificado muestra: thumbnail, título del curso, fecha Y se muestra contador "Has obtenido 12 certificados" Y se pueden filtrar por: categoría, fecha Y se pueden ordenar por: más reciente, alfabético ``` **Escenario 6: Verificar certificado públicamente** ```gherkin DADO que alguien tiene el ID de un certificado CUANDO accede a trading.com/verify/OQI-EDU-A3F8D291 ENTONCES se muestra página de verificación pública Y NO requiere login Y se muestra: - Estado: ✅ Válido - Nombre del usuario - Título del curso - Fecha de finalización - Detalles del curso Y se confirma autenticidad del certificado ``` **Escenario 7: Verificar certificado inválido** ```gherkin DADO que alguien accede con ID inválido CUANDO accede a /verify/INVALID-ID-123 ENTONCES se muestra "Certificado no encontrado" Y se muestra sugerencia "Verifica el ID ingresado" Y se muestra link "¿Cómo verificar un certificado?" ``` **Escenario 8: Email de certificado** ```gherkin DADO que se generó un certificado CUANDO se envía el email ENTONCES el email contiene: - Asunto: "¡Felicidades! Certificado de [Curso]" - Mensaje de felicitación personalizado - Estadísticas: duración, lecciones completadas - PDF adjunto del certificado - Botones: Ver certificado, Compartir en LinkedIn - Sugerencias de próximos cursos relacionados ``` **Escenario 9: Curso sin certificado disponible** ```gherkin DADO que un curso está marcado como "no certifiable" CUANDO el usuario completa el curso ENTONCES NO se genera certificado Y se muestra "Curso completado" sin opción de certificado Y se explica "Este curso no otorga certificado" ``` **Escenario 10: Certificado con requisitos adicionales** ```gherkin DADO que un curso requiere quiz final aprobado Y el usuario completó todas las lecciones PERO no aprobó el quiz final CUANDO intenta obtener certificado ENTONCES se muestra "Debes aprobar el quiz final" Y se muestra score actual del quiz Y se muestra "Intentos restantes: X" Y el certificado NO se genera hasta aprobar ``` ## Criterios Adicionales - [ ] Watermark en PDF para evitar falsificación - [ ] Blockchain verification (opcional, fase 2) - [ ] Traducción del certificado a inglés - [ ] Certificado físico por correo (premium) - [ ] Badge de LinkedIn auto-agregado via API - [ ] Opción de hacer certificado público/privado - [ ] Perfil público con todos los certificados del usuario --- ## Tareas Técnicas **Database:** - [ ] DB-EDU-022: Tabla education.certificates - [ ] DB-EDU-023: Campos: id, certificate_number, user_id, course_id, issued_at, pdf_url, status - [ ] DB-EDU-024: Tabla certificate_verifications (log de verificaciones) - [ ] DB-EDU-025: Índice único en certificate_number **Backend:** - [ ] BE-EDU-057: Endpoint POST /education/certificates/generate - [ ] BE-EDU-058: Endpoint GET /education/certificates (del usuario) - [ ] BE-EDU-059: Endpoint GET /education/certificates/:id - [ ] BE-EDU-060: Endpoint GET /api/public/certificates/verify/:number - [ ] BE-EDU-061: Implementar CertificateService.generate() - [ ] BE-EDU-062: Generar PDF con Puppeteer o PDFKit - [ ] BE-EDU-063: Generar QR code con qrcode library - [ ] BE-EDU-064: Upload de PDF a S3 con signed URL - [ ] BE-EDU-065: Event handler en course completion - [ ] BE-EDU-066: Email service para enviar certificado - [ ] BE-EDU-067: Rate limiting en verificación (100/hora por IP) **Frontend:** - [ ] FE-EDU-070: Modal CourseCompletedModal.tsx con preview - [ ] FE-EDU-071: Crear CertificatesPage.tsx - [ ] FE-EDU-072: Crear componente CertificateCard.tsx - [ ] FE-EDU-073: Crear VerifyCertificatePage.tsx (pública) - [ ] FE-EDU-074: Botón "Compartir en LinkedIn" con pre-fill - [ ] FE-EDU-075: Preview de PDF en modal - [ ] FE-EDU-076: Galería con filtros y búsqueda - [ ] FE-EDU-077: Implementar certificatesStore **Tests:** - [ ] TEST-EDU-030: Test generación de certificado - [ ] TEST-EDU-031: Test validación de certificado válido - [ ] TEST-EDU-032: Test verificación de certificado inválido - [ ] TEST-EDU-033: Test E2E completar curso y obtener certificado --- ## Dependencias **Depende de:** - [ ] US-EDU-005: Completar lección - Estado: Pendiente - [ ] RF-EDU-005: Sistema de certificados - [ ] PDF generation library (Puppeteer/PDFKit) - [ ] S3 bucket para almacenar PDFs - [ ] Email service **Bloquea:** - Ninguna (es funcionalidad final) --- ## Notas Técnicas **Generación del certificado:** ```javascript // Triggered on course completion async function onCourseCompleted(userId, courseId) { // 1. Validar requisitos const isEligible = await validateCertificateEligibility(userId, courseId); if (!isEligible) return; // 2. Generar ID único const certificateNumber = generateCertificateId(); // OQI-EDU-A3F8D291 // 3. Generar PDF const pdfBuffer = await generateCertificatePDF({ userName, courseName, completedDate, certificateNumber }); // 4. Upload a S3 const pdfUrl = await uploadToS3(pdfBuffer, certificateNumber); // 5. Guardar en DB await saveCertificate({ userId, courseId, certificateNumber, pdfUrl }); // 6. Enviar email await sendCertificateEmail(userId, pdfUrl); // 7. Otorgar XP bonus await awardXP(userId, 100, 'certificate_earned'); } ``` **Endpoint GET /api/public/certificates/verify/:number:** ```typescript // Response para certificado válido { valid: true, certificate: { certificateNumber: "OQI-EDU-A3F8D291", recipientName: "Juan Pérez", courseTitle: "Fibonacci Retracement Básico", courseCategory: "Análisis Técnico", completedAt: "2025-12-05T15:45:00Z", issuedAt: "2025-12-05T15:45:00Z", courseDuration: 150, // minutos moduleCount: 5, lessonCount: 23, instructor: "Carlos Mendoza", status: "active" } } // Response para certificado inválido { valid: false, error: "Certificate not found" } ``` **Template del PDF:** - Usar HTML + CSS para diseño - Puppeteer para generar PDF desde HTML - Incluir logo en base64 para evitar carga externa - QR code generado con library qrcode.js - Firmas como imágenes PNG embebidas **LinkedIn pre-fill URL:** ```javascript const linkedInUrl = `https://www.linkedin.com/profile/add?startTask=CERTIFICATION_NAME&name=${encodeURIComponent(courseTitle)}&organizationName=Trading Platform%20IA&issueYear=${year}&issueMonth=${month}&certUrl=${encodeURIComponent(verifyUrl)}&certId=${certificateNumber}`; ``` **Seguridad:** - Rate limiting en endpoint de verificación - Signed URLs de S3 con expiración de 1 hora para descargas - No exponer lista de todos los certificados (solo del usuario logueado) - Validar que usuario solo puede descargar sus propios certificados **Entidades/Tablas:** - `education.certificates` - `education.certificate_verifications` (log) --- ## Definition of Ready (DoR) - [x] Historia claramente escrita - [x] Criterios de aceptación definidos - [x] Story points estimados - [x] Dependencias identificadas - [x] Sin bloqueadores - [x] Diseño/mockup disponible - [x] API spec disponible ## Definition of Done (DoD) - [ ] Código implementado según criterios - [ ] Tests unitarios escritos y pasando - [ ] Tests de integración pasando - [ ] Code review aprobado - [ ] Documentación actualizada - [ ] QA aprobado - [ ] Desplegado en ambiente de pruebas --- ## Historial de Cambios | Fecha | Cambio | Autor | |-------|--------|-------| | 2025-12-05 | Creación | Requirements-Analyst | --- **Creada por:** Requirements-Analyst **Fecha:** 2025-12-05 **Última actualización:** 2025-12-05