trading-platform/docs/02-definicion-modulos/OQI-005-payments-stripe/requerimientos/RF-PAY-002-checkout.md
rckrdmrd c1b5081208 feat(ml): Complete FASE 11 - BTCUSD update and comprehensive documentation alignment
ML Engine Updates:
- Updated BTCUSD with Polygon API data (2024-2025): 215,699 new records
- Re-trained all ML models: Attention (R²: 0.223), Base, Metamodel (87.3% confidence)
- Backtest results: +176.71R profit with aggressive_filter strategy

Documentation Consolidation:
- Created docs/99-analisis/_MAP.md index with 13 new analysis documents
- Consolidated inventories: removed duplicates from orchestration/inventarios/
- Updated ML_INVENTORY.yml with BTCUSD metrics and training results
- Added execution reports: FASE11-BTCUSD, correction issues, alignment validation

Architecture & Integration:
- Updated all module documentation with NEXUS v3.4 frontmatter
- Fixed _MAP.md indexes across all folders
- Updated orchestration plans and traces

Files: 229 changed, 5064 insertions(+), 1872 deletions(-)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 09:31:29 -06:00

17 KiB

id title type status priority epic project version created_date updated_date
RF-PAY-002 Checkout con Stripe Elements Requirement Done Alta OQI-005 trading-platform 1.0.0 2025-12-05 2026-01-04

RF-PAY-002: Checkout con Stripe Elements

Version: 1.0.0 Fecha: 2025-12-05 Estado: Implementado Prioridad: P0 (Crítica) Story Points: 8 Épica: OQI-005


Descripción

El sistema debe proporcionar interfaces de checkout seguras y optimizadas usando Stripe Elements y Stripe Checkout para procesar pagos únicos y suscripciones, cumpliendo con estándares PCI-DSS sin manejar datos de tarjeta directamente.


Objetivo de Negocio

  • Maximizar conversión con UX optimizada de checkout
  • Eliminar responsabilidad PCI-DSS
  • Reducir fraude con validaciones nativas de Stripe
  • Soportar múltiples métodos de pago (tarjetas, wallets)
  • Optimizar para mobile y desktop

Modos de Checkout

Modo 1: Embedded Checkout (Stripe Elements)

Uso: Compras de cursos, pagos únicos dentro de la app

Ventajas:

  • Control total de UX
  • Integración nativa en SPA
  • Customización de estilos
  • Mejor para flujos complejos

Componentes:

- CardNumberElement
- CardExpiryElement
- CardCvcElement
- PaymentElement (todo-en-uno)

Modo 2: Hosted Checkout (Stripe Checkout)

Uso: Suscripciones, compras rápidas

Ventajas:

  • Implementación rápida
  • Optimización automática de conversión
  • Soporte automático de wallets (Apple Pay, Google Pay)
  • Reducción de fricción

Requisitos Funcionales

RF-PAY-002.1: Inicialización de Stripe Elements

DEBE:

  1. Cargar Stripe.js desde CDN (https://js.stripe.com/v3/)
  2. Inicializar con Publishable Key
  3. Crear instancia de Elements con opciones de estilo
  4. Montar elementos en contenedores DOM específicos
  5. Aplicar tema según modo claro/oscuro de la app

Ejemplo de configuración:

const stripe = await loadStripe(STRIPE_PUBLISHABLE_KEY);
const elements = stripe.elements({
  appearance: {
    theme: 'stripe',
    variables: {
      colorPrimary: '#0066ff',
      colorBackground: '#ffffff',
      colorText: '#1a1a1a',
      colorDanger: '#df1b41',
      fontFamily: 'Inter, system-ui, sans-serif',
      spacingUnit: '4px',
      borderRadius: '8px',
    }
  },
  clientSecret: paymentIntent.clientSecret
});

RF-PAY-002.2: Creación de Payment Intent

Backend DEBE:

  1. Recibir request con amount, currency, type, metadata
  2. Validar monto mínimo ($0.50 USD)
  3. Crear PaymentIntent en Stripe
  4. Guardar registro en billing.payments (status: pending)
  5. Retornar clientSecret al frontend

Request:

POST /api/v1/payments/create-payment-intent
{
  "amount": 4900,
  "currency": "usd",
  "type": "course_purchase",
  "courseId": "uuid-curso",
  "description": "Curso de Trading Avanzado"
}

Response:

{
  "clientSecret": "pi_3Abc123_secret_xyz",
  "paymentIntentId": "pi_3Abc123",
  "amount": 4900,
  "currency": "usd"
}

RF-PAY-002.3: Procesamiento de Pago

Frontend DEBE:

  1. Recopilar información de tarjeta (via Stripe Elements)
  2. Validar campos antes de submit
  3. Llamar stripe.confirmCardPayment(clientSecret, { payment_method })
  4. Manejar 3D Secure (SCA) automáticamente
  5. Mostrar estados de loading/success/error

Flujo de confirmación:

const { error, paymentIntent } = await stripe.confirmCardPayment(
  clientSecret,
  {
    payment_method: {
      card: cardElement,
      billing_details: {
        name: userName,
        email: userEmail,
      }
    }
  }
);

if (error) {
  // Mostrar error
} else if (paymentIntent.status === 'succeeded') {
  // Pago exitoso
}

RF-PAY-002.4: Hosted Checkout Session

Backend DEBE:

  1. Crear Checkout Session en Stripe
  2. Configurar success_url y cancel_url
  3. Incluir line items con productos
  4. Configurar modo (payment o subscription)
  5. Retornar URL de checkout

Request:

POST /api/v1/payments/create-checkout-session
{
  "priceId": "price_1Sb3k64dPtEGmLmpm5n5bbJH",
  "mode": "subscription",
  "successUrl": "https://app.trading.com/checkout/success?session_id={CHECKOUT_SESSION_ID}",
  "cancelUrl": "https://app.trading.com/pricing"
}

Response:

{
  "url": "https://checkout.stripe.com/c/pay/cs_test_abc123...",
  "sessionId": "cs_test_abc123"
}

RF-PAY-002.5: Validación de Formulario

DEBE validar:

  • Número de tarjeta completo y válido (Luhn algorithm)
  • Fecha de expiración futura
  • CVC de 3-4 dígitos
  • Nombre del titular no vacío
  • Email válido

Feedback en tiempo real:

  • Indicador visual de validez de campo
  • Detección automática de marca de tarjeta (Visa, Mastercard, etc.)
  • Mensajes de error específicos

RF-PAY-002.6: Manejo de 3D Secure (SCA)

DEBE:

  1. Detectar automáticamente si tarjeta requiere SCA
  2. Mostrar modal/iframe de autenticación del banco
  3. Esperar confirmación del usuario
  4. Continuar procesamiento si autenticación exitosa
  5. Rechazar pago si autenticación falla

Flujo Completo de Checkout

┌─────────────┐     ┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│   Usuario   │     │  Frontend   │     │   Backend   │     │   Stripe    │
└──────┬──────┘     └──────┬──────┘     └──────┬──────┘     └──────┬──────┘
       │                   │                   │                   │
       │ Inicia compra     │                   │                   │
       │──────────────────▶│                   │                   │
       │                   │                   │                   │
       │                   │ POST /create-     │                   │
       │                   │ payment-intent    │                   │
       │                   │──────────────────▶│                   │
       │                   │                   │                   │
       │                   │                   │ Create            │
       │                   │                   │ PaymentIntent     │
       │                   │                   │──────────────────▶│
       │                   │                   │◀──────────────────│
       │                   │                   │ clientSecret      │
       │                   │                   │                   │
       │                   │                   │ Save to DB        │
       │                   │                   │ (pending)         │
       │                   │                   │                   │
       │                   │◀──────────────────│                   │
       │                   │ { clientSecret }  │                   │
       │                   │                   │                   │
       │◀──────────────────│                   │                   │
       │ Muestra form      │                   │                   │
       │ de tarjeta        │                   │                   │
       │                   │                   │                   │
       │ Ingresa datos     │                   │                   │
       │ de tarjeta        │                   │                   │
       │──────────────────▶│                   │                   │
       │                   │                   │                   │
       │ Click "Pagar"     │                   │                   │
       │──────────────────▶│                   │                   │
       │                   │                   │                   │
       │                   │ confirmCardPayment│                   │
       │                   │ (clientSecret)    │                   │
       │                   │──────────────────────────────────────▶│
       │                   │                   │                   │
       │                   │                   │                   │ Requiere
       │◀──────────────────────────────────────────────────────────│ 3DS?
       │ Modal 3DS         │                   │                   │
       │ del banco         │                   │                   │
       │                   │                   │                   │
       │ Autentica         │                   │                   │
       │──────────────────────────────────────────────────────────▶│
       │                   │                   │                   │
       │                   │◀──────────────────────────────────────│
       │                   │ { paymentIntent:  │                   │
       │                   │   status:         │                   │
       │                   │   'succeeded' }   │                   │
       │                   │                   │                   │
       │                   │                   │◀──────────────────│
       │                   │                   │ Webhook:          │
       │                   │                   │ payment_intent.   │
       │                   │                   │ succeeded         │
       │                   │                   │                   │
       │                   │                   │ Update DB         │
       │                   │                   │ (succeeded)       │
       │                   │                   │ Grant access      │
       │                   │                   │                   │
       │◀──────────────────│                   │                   │
       │ "Pago exitoso!"   │                   │                   │
       │                   │                   │                   │

Reglas de Negocio

RN-001: Montos Mínimos

  • Pago único: $0.50 USD mínimo
  • Suscripción: según plan ($19, $49, $99)
  • Bloquear pagos inferiores con mensaje claro

RN-002: Monedas Soportadas

  • USD: Moneda principal
  • MXN, COP, ARS, CLP, PEN: LATAM (futuro)
  • Conversión automática con tasas de Stripe

RN-003: Reintentos de Pago

  • No reintentar automáticamente en frontend
  • Mostrar error específico al usuario
  • Permitir editar datos de tarjeta e intentar de nuevo
  • Backend registra intentos fallidos para análisis

RN-004: Timeouts

  • Payment Intent válido por 24 horas
  • Checkout Session válido por 24 horas
  • Expirar clientSecret después de uso exitoso

Métodos de Pago Soportados

Método Embedded Hosted Región
Tarjetas (Visa, MC, Amex) Global
Apple Pay ⚠️ via PaymentRequest USA, Mx
Google Pay ⚠️ via PaymentRequest Global
OXXO México
Efecty Colombia
PSE Colombia

Estilos y UX

Customización de Stripe Elements

const appearance = {
  theme: 'stripe', // 'stripe' | 'night' | 'flat'
  variables: {
    colorPrimary: '#0066ff',
    colorBackground: '#ffffff',
    colorText: '#1a1a1a',
    colorDanger: '#df1b41',
    fontFamily: 'Inter, system-ui, sans-serif',
    spacingUnit: '4px',
    borderRadius: '8px',
    fontSizeBase: '16px',
  },
  rules: {
    '.Input': {
      border: '1px solid #e0e0e0',
      boxShadow: 'none',
    },
    '.Input:focus': {
      border: '1px solid #0066ff',
      boxShadow: '0 0 0 3px rgba(0, 102, 255, 0.1)',
    },
    '.Input--invalid': {
      border: '1px solid #df1b41',
    }
  }
};

Estados del Formulario

Estado Indicador Visual
Idle Campos vacíos, botón habilitado
Typing Validación en tiempo real
Valid Checkmark verde, botón resaltado
Invalid Mensaje de error rojo
Processing Spinner, botón deshabilitado
Success Checkmark animado, redirect
Error Mensaje de error, retry habilitado

Manejo de Errores

Errores de Stripe Elements

Error Code Mensaje Usuario Acción
card_declined Tu tarjeta fue rechazada. Intenta con otra. Permitir cambiar tarjeta
insufficient_funds Fondos insuficientes. Sugerir otra tarjeta
expired_card Tarjeta expirada. Verifica la fecha. Validar fecha
incorrect_cvc Código de seguridad incorrecto. Reintentar CVC
processing_error Error de procesamiento. Intenta de nuevo. Retry
rate_limit Demasiados intentos. Espera un momento. Backoff 30s

Errores de Backend

Error Código HTTP Mensaje Usuario
Monto inválido 400 El monto debe ser mayor a $0.50 USD
Producto no encontrado 404 El curso no existe
Ya comprado 409 Ya tienes acceso a este curso
Stripe API error 502 Error de procesamiento. Contacta soporte.

Seguridad

PCI Compliance

  • NUNCA enviar datos de tarjeta a backend
  • Usar Stripe.js para tokenización
  • Validar solo en frontend con Stripe Elements
  • Backend solo maneja tokens pm_xxx

Validación de Origen

  • Validar userId del JWT contra metadata.userId del PaymentIntent
  • Verificar que el usuario no haya comprado ya el producto
  • Rate limiting: máximo 5 intentos por 15 minutos

Prevención de Fraude

  • Stripe Radar activado (detección automática)
  • Requerir CVC siempre
  • Habilitar 3D Secure para transacciones > $30 USD
  • Bloquear IPs con alto índice de rechazo

Configuración Requerida

# Frontend (.env.local)
VITE_STRIPE_PUBLISHABLE_KEY=pk_test_51Sb3k64dPtEGmLmp...

# Backend (.env)
STRIPE_SECRET_KEY=sk_test_51Sb3k64dPtEGmLmp...
STRIPE_WEBHOOK_SECRET=whsec_...
FRONTEND_URL=https://app.trading.com

Stripe Checkout URLs

// success_url
https://app.trading.com/checkout/success?session_id={CHECKOUT_SESSION_ID}

// cancel_url
https://app.trading.com/pricing

Webhooks Relacionados

Evento Acción
payment_intent.succeeded Actualizar pago a succeeded, otorgar acceso
payment_intent.payment_failed Actualizar pago a failed, enviar email
payment_intent.canceled Actualizar pago a canceled
checkout.session.completed Confirmar suscripción/compra
checkout.session.expired Notificar expiración

Performance

Optimizaciones

  • Lazy load Stripe.js solo en páginas de checkout
  • Prefetch clientSecret al mostrar producto
  • Cache Price IDs en memoria (Redis)
  • Timeout de 30s para confirmCardPayment

Métricas a Rastrear

  • Tiempo de carga de Stripe.js
  • Tasa de conversión por paso (form → submit → success)
  • Tasa de rechazo por tipo de error
  • Tiempo promedio de checkout

Criterios de Aceptación

  • Stripe Elements se carga sin errores
  • Formulario valida tarjeta en tiempo real
  • Marcas de tarjeta se detectan automáticamente
  • Payment Intent se crea correctamente desde backend
  • 3D Secure funciona para tarjetas que lo requieren
  • Errores de Stripe se muestran claramente al usuario
  • Hosted Checkout redirige a Stripe correctamente
  • Success/cancel URLs funcionan después de Checkout
  • Estilos de Elements coinciden con tema de app
  • Checkout es responsive en mobile y desktop

Especificación Técnica Relacionada

Historias de Usuario Relacionadas