---
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*
