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>
17 KiB
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:
- Cargar Stripe.js desde CDN (
https://js.stripe.com/v3/) - Inicializar con Publishable Key
- Crear instancia de Elements con opciones de estilo
- Montar elementos en contenedores DOM específicos
- 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:
- Recibir request con
amount,currency,type,metadata - Validar monto mínimo ($0.50 USD)
- Crear PaymentIntent en Stripe
- Guardar registro en
billing.payments(status:pending) - Retornar
clientSecretal 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:
- Recopilar información de tarjeta (via Stripe Elements)
- Validar campos antes de submit
- Llamar
stripe.confirmCardPayment(clientSecret, { payment_method }) - Manejar 3D Secure (SCA) automáticamente
- 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:
- Crear Checkout Session en Stripe
- Configurar
success_urlycancel_url - Incluir line items con productos
- Configurar modo (
paymentosubscription) - 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:
- Detectar automáticamente si tarjeta requiere SCA
- Mostrar modal/iframe de autenticación del banco
- Esperar confirmación del usuario
- Continuar procesamiento si autenticación exitosa
- 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
userIddel JWT contrametadata.userIddel 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