--- id: "RF-EDU-005" title: "Sistema de Certificados" type: "Requirement" status: "Done" priority: "Media" module: "education" epic: "OQI-002" version: "1.0" created_date: "2025-12-05" updated_date: "2026-01-04" --- # RF-EDU-005: Sistema de Certificados **Versión:** 1.0.0 **Fecha:** 2025-12-05 **Épica:** OQI-002 - Módulo Educativo **Prioridad:** P2 **Story Points:** 5 --- ## Descripción El sistema debe proporcionar certificados digitales verificables que se otorgan automáticamente al completar cursos, validando el conocimiento adquirido y permitiendo a los usuarios compartir sus logros profesionales en redes sociales y plataformas de empleo. --- ## Requisitos Funcionales ### RF-EDU-005.1: Generación de Certificados El sistema debe: - Generar certificado automáticamente al completar 100% de un curso - Validar que todos los quizzes obligatorios estén aprobados - Validar que todas las lecciones estén marcadas como completadas - Asignar ID único de certificado (formato: OQI-EDU-XXXXXXXX) - Generar PDF con diseño profesional - Almacenar PDF en S3 o similar - Registrar en blockchain para verificación (opcional, fase 2) ### RF-EDU-005.2: Contenido del Certificado Cada certificado debe incluir: - Logo de OrbiQuant IA - Título: "Certificado de Finalización" - Nombre completo del usuario - Título del curso completado - Fecha de finalización - ID único del certificado - Firma digital del instructor (imagen) - Firma digital de la plataforma - QR code para verificación online - Footer: "Verifica este certificado en orbiquant.com/verify/{certificateId}" Template: ``` ┌─────────────────────────────────────────────────────────┐ │ │ │ [LOGO ORBIQUANT] │ │ │ │ CERTIFICADO DE FINALIZACIÓN │ │ │ │ Se certifica que │ │ │ │ [NOMBRE USUARIO] │ │ │ │ Ha completado exitosamente el curso │ │ │ │ "[TÍTULO DEL CURSO]" │ │ │ │ Fecha: [DD/MM/YYYY] │ │ Certificado: OQI-EDU-XXXXXXXX │ │ │ │ ___________________ ___________________ │ │ [Firma Instructor] [Firma Plataforma] │ │ │ │ [QR CODE] │ │ Verifica en orbiquant.com/verify/XXXX │ │ │ └─────────────────────────────────────────────────────────┘ ``` ### RF-EDU-005.3: Verificación de Certificados El sistema debe: - Proveer página pública /verify/:certificateId - Mostrar información del certificado sin login - Validar que el ID existe en base de datos - Mostrar: nombre, curso, fecha, estado (válido/revocado) - Proteger contra scraping (rate limiting, captcha) - API pública GET /api/certificates/verify/:id - Responder en JSON para integraciones ### RF-EDU-005.4: Galería de Certificados del Usuario El sistema debe: - Página /education/certificates con todos los certificados del usuario - Mostrar: thumbnail, título del curso, fecha - Filtrar por: fecha, curso, categoría - Buscar por nombre de curso - Ordenar por: más reciente, alfabético, categoría - Vista de cuadrícula o lista - Contador: "Has obtenido X certificados" ### RF-EDU-005.5: Descarga y Compartir El sistema debe permitir: - Descargar PDF del certificado - Botón "Compartir en LinkedIn" (pre-rellenado) - Botón "Compartir en Twitter/X" - Botón "Copiar link de verificación" - Generar imagen social (Open Graph) para compartir - Agregar a perfil público del usuario (opcional) Integración LinkedIn: ```javascript // Pre-llenar certificación en LinkedIn const linkedInUrl = `https://www.linkedin.com/profile/add?startTask=CERTIFICATION_NAME&name=${encodeURIComponent(courseTitle)}&organizationName=OrbiQuant IA&issueYear=${year}&issueMonth=${month}&certUrl=${verifyUrl}&certId=${certificateId}`; ``` ### RF-EDU-005.6: Perfil Público de Certificados El sistema debe: - Permitir al usuario crear perfil público opcional - URL: orbiquant.com/u/:username/certificates - Mostrar solo certificados que el usuario hizo públicos - Galería visual de certificados - Bio del usuario - Enlaces a redes sociales - No requiere login para ver ### RF-EDU-005.7: Revocación de Certificados El sistema debe permitir (solo admins): - Revocar certificado por fraude - Agregar motivo de revocación - Notificar al usuario por email - Marcar certificado como "REVOKED" en verificación - Mantener historial de revocaciones ### RF-EDU-005.8: Plantillas de Certificados El sistema debe soportar: - Múltiples plantillas (por categoría o nivel) - Plantilla estándar para todos los cursos - Plantilla especial para cursos premium - Plantilla con colores de marca - Editor de plantillas para admins (fase 2) --- ## Datos de Salida ```typescript interface Certificate { id: string; certificateNumber: string; // OQI-EDU-XXXXXXXX userId: string; userName: string; courseId: string; courseTitle: string; courseCategory: string; completedAt: string; issuedAt: string; pdfUrl: string; verifyUrl: string; qrCodeUrl: string; status: 'active' | 'revoked'; revocationReason?: string; instructorSignature: string; metadata: { duration: number; // horas del curso moduleCount: number; lessonCount: number; finalScore?: number; // Si hay quiz final }; } interface VerificationResult { valid: boolean; certificate?: { certificateNumber: string; recipientName: string; courseTitle: string; completedAt: string; status: 'active' | 'revoked'; }; error?: string; } ``` --- ## Reglas de Negocio 1. **Requisitos para certificado:** - 100% de lecciones completadas - Todos los quizzes aprobados (si aplica) - Curso debe estar marcado como "completable" (algunos cursos no otorgan certificado) 2. **ID único:** Formato OQI-EDU-{8 dígitos hex aleatorios} 3. **Nombre en certificado:** Se usa nombre completo del perfil del usuario 4. **Fecha:** Fecha de finalización del curso (última lección completada) 5. **PDF inmutable:** Una vez generado, el PDF no se regenera aunque el usuario cambie su nombre 6. **Caducidad:** Los certificados no caducan 7. **Límite de verificaciones:** 100 verificaciones por IP por hora 8. **Perfil público:** Opt-in, deshabilitado por default --- ## Criterios de Aceptación ```gherkin Escenario: Usuario completa curso y obtiene certificado 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 muestra modal de felicitación Y se envía email con el certificado adjunto Y se muestra botón "Ver certificado" Escenario: Usuario descarga certificado DADO que el usuario tiene un certificado CUANDO accede a /education/certificates Y hace click en "Descargar PDF" ENTONCES se descarga archivo PDF con el certificado Y el PDF contiene: nombre, curso, fecha, ID, firmas, QR Escenario: Usuario comparte en LinkedIn DADO que el usuario está viendo su certificado CUANDO hace click en "Compartir en LinkedIn" ENTONCES se abre LinkedIn en nueva pestaña Y el formulario está pre-llenado con: - Nombre del curso - Organización: OrbiQuant IA - Fecha de emisión - URL de verificación - ID del certificado Escenario: Tercero verifica certificado DADO que alguien tiene el ID de un certificado CUANDO accede a orbiquant.com/verify/OQI-EDU-12345678 ENTONCES se muestra página de verificación Y se muestra: nombre del usuario, curso, fecha Y se muestra badge "✓ Certificado Válido" Y NO requiere login para ver Escenario: Verificar certificado inválido DADO que alguien accede a /verify/INVALID-ID CUANDO el ID no existe en la base de datos ENTONCES se muestra "Certificado no encontrado" Y se sugiere verificar el ID ingresado Escenario: Ver certificado revocado DADO que un certificado fue revocado por admin CUANDO alguien intenta verificarlo ENTONCES se muestra "Certificado Revocado" Y se muestra motivo de revocación Y se marca en rojo como inválido ``` --- ## Dependencias - PDF generation library (PDFKit, Puppeteer, o similar) - S3 para almacenar PDFs - QR code generator - Email service para enviar certificados - LinkedIn API para integración --- ## Notas Técnicas - Usar Puppeteer para generar PDFs desde HTML template - Almacenar PDFs con nombre: certificates/{userId}/{certificateId}.pdf - Generar QR codes con librería qrcode.js - Implementar caché de verificaciones (Redis) para reducir load - Considerar watermark en PDFs para prevenir falsificación - Usar signed URLs de S3 para descargas seguras - Implementar rate limiting agresivo en endpoint de verificación - Para blockchain: Guardar hash del certificado en Ethereum/Polygon --- ## Referencias - Schema: `/backend/src/database/schemas/education.sql` - API: `/backend/src/modules/courses/certificates.routes.ts` - Frontend: `/frontend/src/pages/Certificates.tsx` - Templates: `/backend/src/templates/certificate-template.html` --- ## Tareas Técnicas **Database:** - [ ] Tabla education.certificates - [ ] Campos: id, certificate_number, user_id, course_id, issued_at, pdf_url, status - [ ] Tabla certificate_verifications (log de verificaciones) - [ ] Índice único en certificate_number **Backend:** - [ ] Endpoint POST /education/certificates/generate (triggered on course completion) - [ ] Endpoint GET /education/certificates (listar del usuario) - [ ] Endpoint GET /education/certificates/:id - [ ] Endpoint GET /api/public/certificates/verify/:number (público) - [ ] Endpoint POST /admin/certificates/:id/revoke (admin only) - [ ] Implementar CertificateService.generatePDF() - [ ] Implementar generación de QR code - [ ] Event handler en course completion - [ ] Rate limiting en verificación **Frontend:** - [ ] Crear CertificatesPage.tsx - [ ] Crear componente CertificateCard.tsx - [ ] Crear CertificateDetailPage.tsx - [ ] Crear VerifyCertificatePage.tsx (pública) - [ ] Crear modal de celebración al obtener certificado - [ ] Botones de compartir social media - [ ] Preview de PDF en modal - [ ] Implementar certificatesStore **Tests:** - [ ] Test generación de PDF - [ ] Test verificación de certificado válido/inválido - [ ] Test E2E completar curso y obtener certificado --- **Creado por:** Requirements-Analyst **Fecha:** 2025-12-05 **Última actualización:** 2025-12-05