--- id: "README" title: "Sistema de Pagos con Stripe" type: "Documentation" project: "trading-platform" version: "1.0.0" updated_date: "2026-01-04" --- # OQI-005: Sistema de Pagos con Stripe **Estado:** ✅ Implementado **Fecha:** 2025-12-05 **Modulo:** `apps/backend/src/modules/payments` --- ## Descripcion Sistema completo de pagos integrado con Stripe para: - Pagos unicos (compra de cursos) - Suscripciones mensuales (planes Basic, Pro, Premium) - Webhooks para eventos de pago - Historial de transacciones --- ## Arquitectura ``` ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ Frontend │────▶│ NestJS API │────▶│ Stripe │ │ (React) │ │ /payments/* │ │ API │ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ ▼ ┌─────────────────┐ │ PostgreSQL │ │ billing schema │ └─────────────────┘ ``` --- ## Endpoints | Metodo | Ruta | Descripcion | Auth | |--------|------|-------------|------| | POST | `/payments/create-payment-intent` | Crear pago directo | JWT | | POST | `/payments/create-checkout-session` | Checkout hosted | JWT | | POST | `/payments/subscriptions` | Crear suscripcion | JWT | | GET | `/payments/subscriptions/current` | Suscripcion actual | JWT | | DELETE | `/payments/subscriptions/:id` | Cancelar suscripcion | JWT | | GET | `/payments/history` | Historial de pagos | JWT | | POST | `/payments/webhook` | Webhook Stripe | - | --- ## Entidades ### Payment ```typescript @Entity({ name: 'payments', schema: 'billing' }) class Payment { id: string; // UUID userId: string; // FK a users type: PaymentType; // course_purchase | subscription | one_time status: PaymentStatus; // pending | processing | succeeded | failed | cancelled | refunded amount: number; // Monto en USD currency: string; // USD stripePaymentIntentId: string; stripeCustomerId: string; courseId?: string; // Si es compra de curso subscriptionId?: string; // Si es pago de suscripcion description?: string; metadata?: object; failureReason?: string; refundedAt?: Date; createdAt: Date; updatedAt: Date; } ``` ### Subscription ```typescript @Entity({ name: 'subscriptions', schema: 'billing' }) class Subscription { id: string; // UUID userId: string; // FK a users plan: SubscriptionPlan; // basic | pro | premium status: SubscriptionStatus; // active | past_due | cancelled | incomplete | trialing | unpaid stripeSubscriptionId: string; stripeCustomerId: string; stripePriceId: string; price: number; // Precio mensual currency: string; // USD billingInterval: string; // month | year currentPeriodStart: Date; currentPeriodEnd: Date; trialEnd?: Date; cancelledAt?: Date; cancelAtPeriodEnd: boolean; metadata?: object; createdAt: Date; updatedAt: Date; } ``` --- ## Planes de Suscripcion | Plan | Precio | Price ID | Caracteristicas | |------|--------|----------|-----------------| | Basic | $19/mes | `price_1Sb3k64dPtEGmLmpeAdxvmIu` | Predicciones basicas, cursos intro | | Pro | $49/mes | `price_1Sb3k64dPtEGmLmpm5n5bbJH` | Predicciones avanzadas, todos los cursos | | Premium | $99/mes | `price_1Sb3k74dPtEGmLmpHfLpUkvQ` | Todo + soporte prioritario | --- ## Flujo de Pago ### Pago Unico (Payment Intent) ``` 1. Frontend solicita crear payment intent 2. Backend crea PaymentIntent en Stripe 3. Backend guarda registro en BD (status: pending) 4. Frontend recibe clientSecret 5. Frontend muestra formulario de tarjeta (Stripe Elements) 6. Usuario completa pago 7. Stripe envia webhook (payment_intent.succeeded) 8. Backend actualiza status a succeeded 9. Backend otorga acceso al recurso ``` ### Suscripcion ``` 1. Frontend solicita crear suscripcion 2. Backend obtiene/crea Customer en Stripe 3. Backend adjunta metodo de pago 4. Backend crea Subscription en Stripe 5. Backend guarda registro en BD 6. Stripe cobra automaticamente cada mes 7. Webhooks actualizan estado ``` --- ## Configuracion ### Variables de Entorno ```env STRIPE_SECRET_KEY=sk_test_xxx STRIPE_WEBHOOK_SECRET=whsec_xxx FRONTEND_URL=http://localhost:5173 ``` ### Webhook Events | Evento | Accion | |--------|--------| | `payment_intent.succeeded` | Actualizar pago a succeeded, otorgar acceso | | `payment_intent.payment_failed` | Actualizar pago a failed | | `customer.subscription.updated` | Sincronizar estado de suscripcion | | `customer.subscription.deleted` | Marcar suscripcion como cancelled | --- ## Ejemplo de Uso ### Crear Payment Intent ```bash curl -X POST http://localhost:3000/payments/create-payment-intent \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "amount": 2900, "currency": "usd", "type": "course_purchase", "courseId": "uuid-del-curso", "description": "Curso de Trading Basico" }' ``` Respuesta: ```json { "clientSecret": "pi_xxx_secret_xxx", "paymentIntentId": "pi_xxx", "amount": 2900, "currency": "usd" } ``` ### Crear Suscripcion ```bash curl -X POST http://localhost:3000/payments/subscriptions \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "plan": "pro", "paymentMethodId": "pm_xxx" }' ``` --- ## Modo Mock Cuando `STRIPE_SECRET_KEY` no esta configurado, el sistema funciona en modo mock: - Retorna payment intents simulados - Las suscripciones se crean localmente - Util para desarrollo sin cuenta Stripe --- ## Archivos ``` apps/backend/src/modules/payments/ ├── dto/ │ ├── payment.dto.ts │ └── subscription.dto.ts ├── entities/ │ ├── payment.entity.ts │ └── subscription.entity.ts ├── payments.controller.ts ├── payments.service.ts └── payments.module.ts ``` --- ## Seguridad - Todos los endpoints requieren JWT (excepto webhook) - Webhook valida firma de Stripe - Claves secretas en variables de entorno - Nunca exponer `sk_test_*` en frontend --- *Documentacion generada: 2025-12-05*