- FASE-0: Diagnostic audit of 500+ files, 33 findings cataloged (7P0/8P1/12P2/6P3) - FASE-1: Resolved 7 P0 critical conflicts (ports, paths, dedup OQI-010/ADR-002, orphan schemas) - FASE-2: Resolved 8 P1 issues (traces, README/CLAUDE.md, DEPENDENCY-GRAPH v2.0, DDL drift, stack versions, DoR/DoD) - FASE-3: Resolved 12 P2 issues (archived tasks indexed, RNFs created, OQI-010 US/RF/ET, AGENTS v2.0) - FASE-4: Purged 3 obsolete docs to _archive/, fixed MODELO-NEGOCIO.md broken ref - FASE-5: Cross-layer validation (DDL→OQI 66%, OQI→BE 72%, BE→FE 78%, Inventories 95%) - FASE-6: INFORME-FINAL, SA-INDEX (18 subagents), METADATA COMPLETED 27/33 findings resolved (82%), 6 P3 deferred to backlog. 18 new files created, 40+ modified, 4 archived. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
264 lines
7.0 KiB
Markdown
264 lines
7.0 KiB
Markdown
---
|
|
id: "README"
|
|
title: "Sistema de Pagos con Stripe"
|
|
type: "Documentation"
|
|
project: "trading-platform"
|
|
version: "1.0.0"
|
|
updated_date: "2026-02-06"
|
|
---
|
|
|
|
# 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
|
|
|
|
---
|
|
|
|
## Schemas DDL Asignados
|
|
|
|
Este modulo es owner del siguiente schema DDL:
|
|
|
|
| Schema | Tablas | Descripcion |
|
|
|--------|--------|-------------|
|
|
| **financial** | 11 | wallets, wallet_transactions, payment_methods, payments, invoices, subscriptions, subscription_plans, refunds, commissions, commission_payouts, price_alerts |
|
|
|
|
**Total tablas:** 11
|
|
**Nota DDL drift:** Documentacion previa listaba ~5 tablas. Las tablas no documentadas son: subscription_plans, refunds, commissions, commission_payouts, price_alerts, wallet_transactions. Actualizado por TASK-2026-02-06 F2.6.
|
|
|
|
---
|
|
|
|
*Documentacion generada: 2025-12-05*
|