# OQI-005: Análisis de Gaps - Funcionalidades Faltantes

**Módulo:** OQI-005 (pagos-stripe)
**Fecha:** 2026-01-25
**Criticidad:** Las siguientes features no están implementadas pero serían valiosas para UX completa

---

## Gap 1: Refunds UI (Devoluciones)

**Prioridad:** Alta
**Complejidad:** Media
**Impacto:** UX, Legal, Soporte

### Descripción del Gap

Actualmente no existe:
- UI para iniciar devoluciones de pagos
- Panel de historial de reembolsos procesados
- Validación de período de devolución (ej: 30 días)
- Estados de devolución (pending, processing, completed, failed)
- Notificaciones al usuario cuando devolución se procesa

### Casos de Uso

```
1. Usuario solicita reembolso de suscripción
   ├── Verifica si está dentro de período permitido (14 días)
   ├── Selecciona razón de devolución
   ├── Backend procesa via Stripe API
   ├── Estado se actualiza a "pending"
   └── Usuario notificado cuando se completa (1-3 días)

2. Usuario ve historial de reembolsos
   ├── Tabla en Billing.tsx → Nueva tab "Reembolsos"
   ├── Muestra:
   │   ├── Fecha de solicitud
   │   ├── Monto reembolsado
   │   ├── Motivo
   │   ├── Estado (pending/completed/failed)
   │   └── Método devolución (mismo método pago original)
   └── Filtros: status, fecha range

3. Administrador revisa reembolso rechazado
   ├── Dashboard de admin ver motivo rechazo
   ├── Opción de re-procesar
   └── Notificar usuario
```

### Componentes Necesarios

```typescript
// 1. RefundRequestModal (modal para solicitar devolución)
interface RefundRequestModalProps {
  isOpen: boolean;
  onClose: () => void;
  subscriptionId: string;
  daysRemaining: number;              // Días dentro período permitido
  refundableAmount: number;
  onSuccess?: () => void;
}

// 2. RefundList (tabla historial reembolsos)
interface RefundListProps {
  subscriptionId?: string;
  compact?: boolean;
  itemsPerPage?: number;
}

// 3. RefundDetail (modal detalle reembolso)
interface RefundDetailProps {
  refundId: string;
  onClose: () => void;
}
```

### Endpoint Backend Requerido

```
POST /payments/refunds
├── Body: { subscriptionId, amount, reason, requestedAt }
├── Valida: Dentro de período permitido (30 días default)
├── Respuesta: { id, status, amount, reason, processedAt? }
└── Webhook: refund.completed → Actualizar wallet usuario

GET /payments/refunds
├── Query params: subscriptionId, status, dateRange
└── Respuesta: Paginated list

GET /payments/refunds/{id}
├── Detalle de reembolso
└── Historial de actualizaciones

DELETE /payments/refunds/{id}
├── Solo si status=pending
└── Cancelar devolución pendiente
```

### Lógica de Negocio

```
Período Devolución: 14 días desde primera facturación
├── Free trial: +7 días adicionales
├── Payment failed: No aplica devolución
└── Upgrade/downgrade: Proporcional a días usados

Estados:
├── pending: Enviada a Stripe, sin procesar
├── processing: Stripe la está procesando
├── completed: Completada, fondos devueltos
├── failed: Rechazada por banco/Stripe
└── cancelled: Usuario canceló solicitud

Reglas:
├── 1 reembolso/cliente/30 días (máximo)
├── Monto: hasta 100% de suscripción
└── Notificación: Email cuando se complete
```

### Estimación Esfuerzo

- **Backend:** 8-12 horas (Stripe API integration, webhook, DB)
- **Frontend UI:** 6-8 horas (modal, tabla, detalle)
- **Testing:** 4-6 horas
- **Documentación:** 2-3 horas
- **Total:** ~20-29 horas

---

## Gap 2: Histórico de Cambios de Plan

**Prioridad:** Media
**Complejidad:** Baja
**Impacto:** UX, Auditoría, Soporte

### Descripción del Gap

Actualmente no existe:
- Visualización del historial de cambios de plan
- Fechas de cambio y razones
- Comparativa plan anterior vs nuevo
- Créditos/cargos aplicados en cada cambio
- Timeline visual de evolución de suscripción

### Casos de Uso

```
1. Usuario revisa su historial de planes
   ├── Nueva tab en Billing: "Historial de Planes"
   ├── Timeline/tabla mostrando:
   │   ├── Fecha cambio
   │   ├── Plan anterior → Plan nuevo
   │   ├── Precio anterior → Precio nuevo
   │   ├── Crédito prorrateo (si aplica)
   │   ├── Cargo adicional (si aplica)
   │   └── Razón del cambio (upgrade/downgrade/etc)
   └── Botones: Ver detalles, Descargar recibo

2. Usuario entiende su evolución de suscripción
   ├── Cuándo cambió de Free → Pro
   ├── Cuándo hizo upgrade Pro → Enterprise
   └── Visualizar inversión total en plataforma

3. Soporte consulta historial cliente
   ├── Dashboard admin con historia completa
   ├── Auditoría de cambios (quién, cuándo)
   └── Facilita resolución de disputas
```

### Componentes Necesarios

```typescript
// 1. PlanHistoryTimeline (visualización timeline)
interface PlanHistoryTimelineProps {
  subscriptionId: string;
  compact?: boolean;
}

// 2. PlanChangeDetail (modal detalle de cambio)
interface PlanChangeDetailProps {
  changeId: string;
  onClose: () => void;
}
```

### Estructura de Datos

```typescript
interface SubscriptionPlanChange {
  id: string;
  subscriptionId: string;
  planIdFrom: string;
  planNameFrom: string;
  planIdTo: string;
  planNameTo: string;
  changeType: 'upgrade' | 'downgrade' | 'lateral_change';
  billingCycleFrom: 'monthly' | 'yearly';
  billingCycleTo: 'monthly' | 'yearly';
  amountFrom: number;
  amountTo: number;
  proratedCredit: number;
  chargeAmount: number;
  netAmount: number;
  effectiveDate: string;
  reason?: string;
  initiatedBy: 'user' | 'admin' | 'system';
  invoiceId?: string;
  createdAt: string;
}
```

### Endpoint Backend Requerido

```
GET /payments/subscription/changes
├── Query params: subscriptionId, limit, offset
├── Respuesta: Paginated list of changes
└── Incluye: amountBefore, amountAfter, credits, invoiceId

GET /payments/subscription/changes/{id}
├── Detalle cambio
├── Incluye: invoiceDetails si aplica
└── Historial de estados (si cambio fue procesado en fases)
```

### Lógica de Negocio

```
Cambio de Plan:
├── Fecha: Inmediata (upgrade) o fin de período (downgrade)
├── Crédito: Prorrateo por días no usados del plan anterior
├── Cargo: Diferencia (si upgrade) o crédito (si downgrade)
├── Factura: Nueva línea en siguiente ciclo o invoice immediata
└── Notificación: Confirmación al usuario

Datos Calculados:
├── changeType: Compara precio plan anterior vs nuevo
├── Prorration: (daysRemaining / daysInPeriod) * planPrice
└── Timeline: Mostrar cuando cambios se aplicaron
```

### Estimación Esfuerzo

- **Backend:** 4-6 horas (queries, DB si no existe tabla)
- **Frontend UI:** 4-6 horas (timeline, tabla, modal)
- **Testing:** 2-3 horas
- **Documentación:** 1-2 horas
- **Total:** ~11-17 horas

---

## Gap 3: Preview de Factura

**Prioridad:** Media
**Complejidad:** Baja
**Impacto:** UX, Confianza del Usuario

### Descripción del Gap

Actualmente no existe:
- Preview de factura ANTES de completar pago
- Visualización de items que se facturarán
- Cálculo claro de subtotal, impuestos, descuentos
- Confirmación de direcciones de facturación
- Mostrar si hay cambios desde checkout anterior

### Casos de Uso

```
1. Usuario en checkout ve qué va a pagar
   ├── Antes de hacer click "Pagar"
   ├── Muestra:
   │   ├── Plan seleccionado
   │   ├── Precio
   │   ├── Período (1 mes, 1 año)
   │   ├── Subtotal
   │   ├── Impuestos calculados (si aplica)
   │   ├── Descuentos (cupones)
   │   ├── Total a pagar
   │   ├── Ciclo de facturación
   │   ├── Dirección de facturación
   │   └── Método de pago (últimos 4 dígitos)
   └── Botón: "Confirmar y Pagar"

2. Usuario cambia información de facturación
   ├── Actualiza dirección
   ├── Sistema recalcula impuestos
   ├── Preview se actualiza automáticamente
   └── Muestra cambio en total (si aplica)

3. Usuario aplica cupón
   ├── Ingresa código
   ├── Preview actualiza con nuevo total
   ├── Muestra descuento claramente
   └── Botón confirmar disponible
```

### Componentes Necesarios

```typescript
// 1. InvoicePreview (modal/drawer con preview factura)
interface InvoicePreviewProps {
  planId: string;
  billingCycle: 'monthly' | 'yearly';
  couponCode?: string;
  billingInfo?: BillingInfo;
  paymentMethodId?: string;
  onConfirm?: () => void;
  onCancel?: () => void;
}

// Integrado en CheckoutFlow (modal/step adicional)
```

### Estructura de Datos

```typescript
interface InvoicePreview {
  planId: string;
  planName: string;
  description: string;
  subtotal: number;
  tax: number;
  taxRate?: number;
  discount: number;
  discountReason?: string;
  total: number;
  currency: string;
  billingCycle: 'monthly' | 'yearly';
  itemizedBreakdown: {
    description: string;
    quantity: number;
    unitPrice: number;
    amount: number;
  }[];
  billingInfo: {
    name: string;
    email: string;
    address: string;
  };
  paymentMethod: {
    type: string;
    last4: string;
    brand: string;
  };
  nextBillingDate: string;
  estimatedTotal12Months?: number;  // Si yearly, show equiv annual
}
```

### Endpoint Backend Requerido

```
POST /payments/invoice/preview
├── Body: {
│   planId,
│   billingCycle,
│   couponCode?,
│   billingInfo?,
│   paymentMethodId?
├── Valida: País (para impuestos), cupón (si existe)
├── Calcula: Impuestos según dirección billing
└── Respuesta: InvoicePreview completo

GET /payments/taxes/estimate
├── Query: country, state, zipcode
├── Respuesta: { taxRate, taxName }
└── Usado para cálculos en tiempo real
```

### Lógica de Negocio

```
Cálculo de Impuestos:
├── Por país/estado
├── VAT en EU (21% típicamente)
├── Sales tax en USA (5-10% según estado)
├── GST en CA (5%)
├── Integración con TaxJar/Avalara (opcional)
└── Almacenar en factura final

Créditos (si aplica):
├── Trial period: Mostrar cuando se cobra
├── Prorated: Crédito plan anterior (si existe)
├── Coupon: Descuento aplicado
└── One-time: Bonificación admin

Preview Real-time:
├── Usuario actualiza dirección → Recalcula impuestos
├── Usuario escribe cupón → Valida y recalcula
├── Usuario cambia billing cycle → Recalcula todo
└── Debounce requests para no sobrecargar backend
```

### Estimación Esfuerzo

- **Backend:** 6-8 horas (cálculo impuestos, endpoints)
- **Frontend UI:** 4-6 horas (modal, forms, updates)
- **Tax Integration:** 2-4 horas (si usa servicio externo)
- **Testing:** 3-4 horas
- **Documentación:** 1-2 horas
- **Total:** ~16-24 horas

---

## Gap 4: Payment Intent Flow (Completo)

**Prioridad:** Alta (Seguridad)
**Complejidad:** Alta
**Impacto:** Seguridad, PCI-DSS Compliance

### Descripción del Gap

Actualmente:
- ✅ Usa Stripe Hosted Checkout (seguro, delegado)
- ❌ NO implementa Payment Intent flow en cliente
- ❌ Métodos de pago sin tokenización Stripe.js
- ❌ NO soporta 3D Secure / SCA en cliente (delegado a backend)
- ❌ NO maneja tarjetas declinadas gracefully

### Casos de Uso

```
1. Usuario completa checkout con tarjeta nueva (Hosted Checkout)
   ├── Redirección a Stripe
   ├── Stripe maneja validación, 3DS, etc
   ├── Redirige back a CheckoutSuccess
   └── ✅ Funciona (implementado)

2. Usuario agrega método de pago desde Billing
   ├── Abre formulario PaymentMethodForm
   ├── ❌ Envía número de tarjeta SIN tokenizar
   ├── Backend debería rechazar (PCI risk)
   └── Debería usar Stripe.js Elements

3. Usuario paga con saved payment method
   ├── No aplica (solo checkout con hosted)
   └── Si se implementa: requiere Payment Intent

4. 3D Secure required
   ├── Backend detecta SCA required
   ├── Devuelve clientSecret
   ├── ❌ Frontend no maneja confirmación
   ├── Debería usar Stripe.js confirmCardPayment
   └── Mostrar modal de autenticación
```

### Componentes Necesarios

```typescript
// 1. StripeProvider (wrappear app)
import { Elements } from '@stripe/react-stripe-js';
import { loadStripe } from '@stripe/js';

const stripePromise = loadStripe(STRIPE_PUBLIC_KEY);

// 2. PaymentMethodForm refactorizado
interface PaymentMethodFormProps {
  onSuccess: (paymentMethodId: string) => void;
  onCancel?: () => void;
  onError?: (error: string) => void;
  setAsDefault?: boolean;
}

// 3. PaymentIntentModal (para confirmar 3DS)
interface PaymentIntentModalProps {
  clientSecret: string;
  onSuccess: () => void;
  onError: (error: string) => void;
}
```

### Cambios Necesarios

```typescript
// BEFORE (Inseguro)
const handleSubmit = async (e) => {
  const result = await addPaymentMethod({
    type: 'card',
    card: {
      number: cardNumber,       // ❌ PCI risk
      exp_month: expiry.split('/')[0],
      exp_year: 2000 + parseInt(expiry.split('/')[1]),
      cvc: cvc,                 // ❌ PCI risk
    },
  });
};

// AFTER (Seguro con Stripe.js)
const { stripe, elements } = useStripe();

const handleSubmit = async (e) => {
  const cardElement = elements.getElement(CardElement);

  // Crear payment method en Stripe (tokenizado)
  const { error, paymentMethod } = await stripe.createPaymentMethod({
    type: 'card',
    card: cardElement,
  });

  if (!error) {
    // Enviar token, no tarjeta
    const result = await addPaymentMethod(paymentMethod.id);
  }
};
```

### Endpoint Backend Requerido

```
POST /payments/methods
├── Body: { paymentMethodId, setAsDefault }
├── paymentMethodId: Token Stripe (ej: pm_1234567890)
├── Almacena en Stripe + DB
└── ✅ Compliant PCI-DSS

POST /payments/subscriptions/{id}/confirm-payment
├── Body: { clientSecret, paymentMethodId }
├── Si requiere SCA: Procesa autenticación
├── Respuesta: { success, paymentStatus }
└── Usado para 3D Secure flow
```

### Estimación Esfuerzo

- **Backend:** 4-6 horas (validar tokens, SCA handling)
- **Frontend Stripe.js:** 8-10 horas (Elements, intent confirmation)
- **Testing (incluye SCA test):** 6-8 horas
- **Documentación:** 2-3 horas
- **Total:** ~20-27 horas

---

## Gap 5: Apple Pay / Google Pay

**Prioridad:** Baja
**Complejidad:** Media
**Impacto:** UX, Conversión (móvil)

### Descripción del Gap

Actualmente no existe soporte para:
- Apple Pay (en iOS/macOS)
- Google Pay (en Android/Chrome)
- Botones "Pay with X" en checkout

### Estimación Esfuerzo

- **Backend:** 2-4 horas (endpoint validation)
- **Frontend:** 6-8 horas (Stripe Payment Request Button)
- **Testing:** 4-6 horas (necesita dispositivos)
- **Total:** ~12-18 horas

---

## Gap 6: Retry Logic para Pagos Fallidos

**Prioridad:** Media
**Complejidad:** Media
**Impacto:** Retención, Ingresos

### Descripción del Gap

Actualmente:
- ❌ No hay reintentos automáticos de pagos fallidos
- ❌ No hay notificación al usuario cuando falla
- ❌ No hay UI para re-procesar pago manual
- ❌ No hay webhook handling para payment_intent.payment_failed

### Casos de Uso

```
1. Suscripción tiene status "past_due"
   ├── Payment falló (tarjeta rechazada, fondos insuficientes)
   ├── Sistema envía email al usuario
   ├── Usuario puede ver deuda pendiente en Billing
   ├── Usuario puede:
   │   ├── Actualizar método de pago
   │   └── Click "Reintentar pago"
   └── Stripe reintenta automáticamente (3 veces en 3 días)

2. Dashboard muestra estado "Pago Pendiente"
   ├── Badge rojo en SubscriptionCard
   ├── Info: "Tenemos un problema con tu pago"
   ├── Botones:
   │   ├── Actualizar método de pago
   │   └── Reintentar ahora
   └── Link a portal Stripe
```

### Endpoints Requeridos

```
POST /payments/subscription/{id}/retry-payment
├── Reintenta el pago pendiente
├── Requiere valid payment method
└── Respuesta: { success, newStatus, nextRetryDate? }

GET /payments/failed-payments
├── Listar pagos fallidos del usuario
└── Respuesta: Array de intentos fallidos con razones
```

### Estimación Esfuerzo

- **Backend:** 4-6 horas (retry logic, webhooks)
- **Frontend:** 3-4 horas (UI, forms)
- **Testing:** 2-3 horas
- **Total:** ~9-13 horas

---

## Priorización Recomendada

### Fase 1 (MVP - 2-3 semanas)
1. **Preview de Factura** (16-24 hrs) - Mejor UX antes de pagar
2. **Retry Logic** (9-13 hrs) - Aumenta retención

### Fase 2 (3-4 semanas)
3. **Refunds UI** (20-29 hrs) - Cumplimiento legal, UX completa
4. **Histórico Cambios Plan** (11-17 hrs) - Soporte + auditoría

### Fase 3 (2-3 semanas)
5. **Payment Intent Flow** (20-27 hrs) - Seguridad PCI-DSS
6. **Apple Pay / Google Pay** (12-18 hrs) - Conversión móvil

---

## Impacto de No Implementar

| Gap | Riesgo | Impacto |
|-----|--------|--------|
| Refunds | Legal/Cumplimiento | Posibles multas/disputas |
| Historial Cambios | Soporte | Tickets más complejos |
| Preview Factura | UX | Menor confianza en checkout |
| Payment Intent | Seguridad | PCI-DSS non-compliant |
| Apple/Google Pay | Conversión | Pérdida clientes móviles |
| Retry Logic | Ingresos | Pérdida 5-10% suscriptores |

---

## Checklist de Implementación

Cuando se implementen estos gaps:

- [ ] Crear rama feature separada por gap
- [ ] Actualizar tipos TypeScript en `payment.types.ts`
- [ ] Documentar endpoints en `OQI-005-CONTRATOS-API.md`
- [ ] Agregar tests unitarios + integración
- [ ] Actualizar `paymentStore.ts` con nuevos métodos
- [ ] Crear nuevos componentes en `components/payments/`
- [ ] Integrar en `Billing.tsx` con nuevas tabs
- [ ] Validar con Stripe test keys
- [ ] Documentar en nuevos archivos OQI-005-*.md
- [ ] Rebasar a main una vez QA aprobado
- [ ] Desplegar a producción con feature flags

