# 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
