# 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 OrbiQuant, 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] OrbiQuant IA                                            │
│                                                                  │
│            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: OrbiQuant IA                                 │ │
│  │                                                             │ │
│  │  Estado: ✅ Activo                                          │ │
│  │  Emitido: 05/12/2025 15:45:00 UTC                          │ │
│  └────────────────────────────────────────────────────────────┘ │
│                                                                  │
│  Este certificado puede ser verificado en cualquier momento en: │
│  orbiquant.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 OrbiQuant IA
  - 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: "OrbiQuant IA"
  - Fecha de emisión: "Diciembre 2025"
  - ID de certificado: "OQI-EDU-A3F8D291"
  - URL de verificación: "orbiquant.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 orbiquant.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=OrbiQuant%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
