Sistema NEXUS v3.4 migrado con: Estructura principal: - core/orchestration: Sistema SIMCO + CAPVED (27 directivas, 28 perfiles) - core/catalog: Catalogo de funcionalidades reutilizables - shared/knowledge-base: Base de conocimiento compartida - devtools/scripts: Herramientas de desarrollo - control-plane/registries: Control de servicios y CI/CD - orchestration/: Configuracion de orquestacion de agentes Proyectos incluidos (11): - gamilit (submodule -> GitHub) - trading-platform (OrbiquanTIA) - erp-suite con 5 verticales: - erp-core, construccion, vidrio-templado - mecanicas-diesel, retail, clinicas - betting-analytics - inmobiliaria-analytics - platform_marketing_content - pos-micro, erp-basico Configuracion: - .gitignore completo para Node.js/Python/Docker - gamilit como submodule (git@github.com:rckrdmrd/gamilit-workspace.git) - Sistema de puertos estandarizado (3005-3199) Generated with NEXUS v3.4 Migration System EPIC-010: Configuracion Git y Repositorios
469 lines
10 KiB
Markdown
469 lines
10 KiB
Markdown
# Integración de Pagos
|
|
|
|
**Versión:** 1.0.0
|
|
**Origen:** projects/trading-platform
|
|
**Estado:** Producción
|
|
**Última actualización:** 2025-12-08
|
|
|
|
---
|
|
|
|
## Descripción
|
|
|
|
Sistema completo de pagos con integración Stripe:
|
|
- Gestión de clientes en Stripe
|
|
- Checkout sessions para pagos seguros
|
|
- Suscripciones con ciclos de facturación
|
|
- Métodos de pago (tarjetas)
|
|
- Billing portal de autoservicio
|
|
- Webhooks para eventos de Stripe
|
|
- Wallet interno para balance de usuario
|
|
- Códigos promocionales
|
|
|
|
---
|
|
|
|
## Características
|
|
|
|
| Característica | Descripción |
|
|
|----------------|-------------|
|
|
| Stripe Checkout | Hosted checkout pages |
|
|
| Suscripciones | Monthly/yearly con trial |
|
|
| Payment Intents | One-time payments |
|
|
| Billing Portal | Self-service portal |
|
|
| Webhooks | Eventos en tiempo real |
|
|
| Wallet | Balance interno |
|
|
| Promo codes | Descuentos y cupones |
|
|
| Invoices | Facturas automáticas |
|
|
| Refunds | Reembolsos parciales/totales |
|
|
|
|
---
|
|
|
|
## Stack Tecnológico
|
|
|
|
```yaml
|
|
backend:
|
|
framework: NestJS / Express
|
|
payment_processor: Stripe
|
|
database: PostgreSQL
|
|
|
|
packages:
|
|
- "stripe"
|
|
```
|
|
|
|
---
|
|
|
|
## Dependencias NPM
|
|
|
|
```json
|
|
{
|
|
"stripe": "^14.x"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Tablas Requeridas
|
|
|
|
| Tabla | Propósito |
|
|
|-------|-----------|
|
|
| financial.stripe_customers | Relación user-customer Stripe |
|
|
| financial.subscription_plans | Planes disponibles |
|
|
| financial.subscriptions | Suscripciones activas |
|
|
| financial.payments | Historial de pagos |
|
|
| financial.invoices | Facturas |
|
|
| financial.wallets | Balance del usuario |
|
|
| financial.wallet_transactions | Movimientos del wallet |
|
|
| financial.promo_codes | Códigos promocionales |
|
|
|
|
---
|
|
|
|
## Estructura del Módulo
|
|
|
|
```
|
|
payments/
|
|
├── services/
|
|
│ ├── stripe.service.ts # Integración con Stripe API
|
|
│ ├── subscription.service.ts # Gestión de suscripciones
|
|
│ └── wallet.service.ts # Wallet interno
|
|
├── controllers/
|
|
│ └── payments.controller.ts # Endpoints REST
|
|
├── webhooks/
|
|
│ └── stripe.webhook.ts # Handler de webhooks
|
|
├── types/
|
|
│ └── payments.types.ts # Tipos e interfaces
|
|
└── dto/
|
|
├── create-checkout.dto.ts
|
|
└── subscription.dto.ts
|
|
```
|
|
|
|
---
|
|
|
|
## Modelos de Datos
|
|
|
|
### StripeCustomer
|
|
|
|
```typescript
|
|
interface StripeCustomer {
|
|
id: string;
|
|
userId: string;
|
|
stripeCustomerId: string; // cus_xxx
|
|
email?: string;
|
|
defaultPaymentMethodId?: string; // pm_xxx
|
|
metadata?: Record<string, any>;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
}
|
|
```
|
|
|
|
### SubscriptionPlan
|
|
|
|
```typescript
|
|
interface SubscriptionPlan {
|
|
id: string;
|
|
name: string; // "Pro Plan"
|
|
slug: string; // "pro"
|
|
description?: string;
|
|
priceMonthly: number; // 29.99
|
|
priceYearly?: number; // 299.99
|
|
currency: string; // "usd"
|
|
stripePriceIdMonthly?: string; // price_xxx
|
|
stripePriceIdYearly?: string; // price_xxx
|
|
stripeProductId?: string; // prod_xxx
|
|
features: PlanFeature[];
|
|
isActive: boolean;
|
|
sortOrder: number;
|
|
}
|
|
```
|
|
|
|
### Subscription
|
|
|
|
```typescript
|
|
interface Subscription {
|
|
id: string;
|
|
userId: string;
|
|
planId: string;
|
|
stripeSubscriptionId?: string; // sub_xxx
|
|
stripeCustomerId?: string; // cus_xxx
|
|
status: 'trialing' | 'active' | 'past_due' | 'cancelled' | 'unpaid';
|
|
billingCycle: 'monthly' | 'yearly';
|
|
currentPeriodStart?: Date;
|
|
currentPeriodEnd?: Date;
|
|
trialStart?: Date;
|
|
trialEnd?: Date;
|
|
cancelAtPeriodEnd: boolean;
|
|
cancelledAt?: Date;
|
|
cancellationReason?: string;
|
|
currentPrice?: number;
|
|
currency: string;
|
|
}
|
|
```
|
|
|
|
### Wallet
|
|
|
|
```typescript
|
|
interface Wallet {
|
|
id: string;
|
|
userId: string;
|
|
currency: string;
|
|
balance: number;
|
|
availableBalance: number;
|
|
pendingBalance: number;
|
|
isActive: boolean;
|
|
dailyWithdrawalLimit: number;
|
|
monthlyWithdrawalLimit: number;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Flujo de Pago con Stripe Checkout
|
|
|
|
```
|
|
1. Usuario selecciona plan
|
|
│
|
|
▼
|
|
2. Backend crea Checkout Session
|
|
- Obtiene/crea customer en Stripe
|
|
- Configura line_items con price_id
|
|
- Define success_url y cancel_url
|
|
│
|
|
▼
|
|
3. Redirect a Stripe Checkout
|
|
│
|
|
▼
|
|
4. Usuario completa pago
|
|
│
|
|
▼
|
|
5. Stripe envía webhook
|
|
- checkout.session.completed
|
|
- invoice.paid
|
|
│
|
|
▼
|
|
6. Backend procesa webhook
|
|
- Crea/actualiza suscripción
|
|
- Registra pago
|
|
- Activa features del plan
|
|
```
|
|
|
|
---
|
|
|
|
## Uso Rápido
|
|
|
|
### 1. Crear cliente en Stripe
|
|
|
|
```typescript
|
|
const customer = await stripeService.getOrCreateCustomer(
|
|
userId,
|
|
userEmail
|
|
);
|
|
// { stripeCustomerId: 'cus_xxx', ... }
|
|
```
|
|
|
|
### 2. Crear checkout session
|
|
|
|
```typescript
|
|
const session = await stripeService.createCheckoutSession({
|
|
userId: 'user-uuid',
|
|
planId: 'plan-uuid',
|
|
billingCycle: 'monthly',
|
|
successUrl: 'https://app.com/success?session_id={CHECKOUT_SESSION_ID}',
|
|
cancelUrl: 'https://app.com/pricing',
|
|
promoCode: 'SUMMER20', // opcional
|
|
});
|
|
|
|
// Redirect usuario a session.url
|
|
```
|
|
|
|
### 3. Verificar suscripción
|
|
|
|
```typescript
|
|
const subscription = await subscriptionService.getSubscriptionByUserId(userId);
|
|
|
|
if (subscription?.status === 'active') {
|
|
// Usuario tiene suscripción activa
|
|
const hasFeature = subscription.plan.apiAccess;
|
|
}
|
|
```
|
|
|
|
### 4. Billing Portal (autoservicio)
|
|
|
|
```typescript
|
|
const portal = await stripeService.createBillingPortalSession(
|
|
userId,
|
|
'https://app.com/dashboard'
|
|
);
|
|
|
|
// Redirect a portal.url
|
|
// Usuario puede: actualizar tarjeta, ver facturas, cancelar
|
|
```
|
|
|
|
### 5. Cancelar suscripción
|
|
|
|
```typescript
|
|
// Al final del período
|
|
await subscriptionService.cancelSubscription(userId, false, 'User requested');
|
|
|
|
// Inmediatamente
|
|
await subscriptionService.cancelSubscription(userId, true);
|
|
```
|
|
|
|
### 6. Cambiar de plan
|
|
|
|
```typescript
|
|
await subscriptionService.changePlan(userId, newPlanId, 'yearly');
|
|
```
|
|
|
|
---
|
|
|
|
## Webhook Handler
|
|
|
|
```typescript
|
|
// POST /webhooks/stripe
|
|
async handleStripeWebhook(req: Request) {
|
|
const signature = req.headers['stripe-signature'];
|
|
const event = stripe.webhooks.constructEvent(
|
|
req.body,
|
|
signature,
|
|
process.env.STRIPE_WEBHOOK_SECRET
|
|
);
|
|
|
|
switch (event.type) {
|
|
case 'checkout.session.completed':
|
|
await this.handleCheckoutCompleted(event.data.object);
|
|
break;
|
|
|
|
case 'invoice.paid':
|
|
await this.handleInvoicePaid(event.data.object);
|
|
break;
|
|
|
|
case 'invoice.payment_failed':
|
|
await this.handlePaymentFailed(event.data.object);
|
|
break;
|
|
|
|
case 'customer.subscription.updated':
|
|
await this.handleSubscriptionUpdated(event.data.object);
|
|
break;
|
|
|
|
case 'customer.subscription.deleted':
|
|
await this.handleSubscriptionDeleted(event.data.object);
|
|
break;
|
|
}
|
|
|
|
return { received: true };
|
|
}
|
|
|
|
private async handleCheckoutCompleted(session: Stripe.Checkout.Session) {
|
|
const { userId, planId, billingCycle } = session.metadata!;
|
|
|
|
// Crear suscripción local
|
|
await db.query(`
|
|
INSERT INTO financial.subscriptions (
|
|
user_id, plan_id, stripe_subscription_id, stripe_customer_id,
|
|
status, billing_cycle, current_period_start, current_period_end
|
|
) VALUES ($1, $2, $3, $4, 'active', $5, $6, $7)
|
|
`, [userId, planId, session.subscription, session.customer, billingCycle, ...]);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Variables de Entorno
|
|
|
|
```env
|
|
# Stripe
|
|
STRIPE_SECRET_KEY=sk_live_xxx # o sk_test_xxx
|
|
STRIPE_PUBLISHABLE_KEY=pk_live_xxx # para frontend
|
|
STRIPE_WEBHOOK_SECRET=whsec_xxx
|
|
|
|
# URLs
|
|
FRONTEND_URL=https://app.example.com
|
|
STRIPE_SUCCESS_URL=${FRONTEND_URL}/success
|
|
STRIPE_CANCEL_URL=${FRONTEND_URL}/pricing
|
|
```
|
|
|
|
---
|
|
|
|
## Endpoints Principales
|
|
|
|
| Método | Ruta | Descripción |
|
|
|--------|------|-------------|
|
|
| GET | /plans | Listar planes disponibles |
|
|
| GET | /plans/:id | Obtener plan por ID |
|
|
| GET | /subscription | Obtener suscripción del usuario |
|
|
| POST | /checkout/subscription | Crear checkout para suscripción |
|
|
| POST | /checkout/course | Crear checkout para curso |
|
|
| GET | /billing-portal | Obtener URL del billing portal |
|
|
| POST | /subscription/cancel | Cancelar suscripción |
|
|
| POST | /subscription/resume | Reactivar suscripción |
|
|
| POST | /subscription/change-plan | Cambiar de plan |
|
|
| GET | /invoices | Listar facturas del usuario |
|
|
| GET | /payment-methods | Listar métodos de pago |
|
|
| POST | /payment-methods | Agregar método de pago |
|
|
| DELETE | /payment-methods/:id | Eliminar método de pago |
|
|
| POST | /webhooks/stripe | Webhook handler |
|
|
|
|
---
|
|
|
|
## Configuración en Stripe Dashboard
|
|
|
|
### 1. Productos y Precios
|
|
|
|
```
|
|
Producto: Pro Plan (prod_xxx)
|
|
├── Precio mensual: $29.99/month (price_monthly_xxx)
|
|
└── Precio anual: $299.99/year (price_yearly_xxx)
|
|
```
|
|
|
|
### 2. Webhook Endpoints
|
|
|
|
```
|
|
Endpoint: https://api.example.com/webhooks/stripe
|
|
Events:
|
|
- checkout.session.completed
|
|
- invoice.paid
|
|
- invoice.payment_failed
|
|
- customer.subscription.updated
|
|
- customer.subscription.deleted
|
|
- customer.subscription.created
|
|
```
|
|
|
|
### 3. Customer Portal
|
|
|
|
Configurar en Settings > Billing > Customer portal:
|
|
- Allow customers to update payment methods
|
|
- Allow customers to view invoice history
|
|
- Allow customers to cancel subscriptions
|
|
|
|
---
|
|
|
|
## Wallet (Balance Interno)
|
|
|
|
### Depositar fondos
|
|
|
|
```typescript
|
|
await walletService.deposit({
|
|
userId: 'user-uuid',
|
|
amount: 100.00,
|
|
currency: 'usd',
|
|
description: 'Initial deposit',
|
|
});
|
|
```
|
|
|
|
### Usar balance para pago
|
|
|
|
```typescript
|
|
const canPay = await walletService.canAfford(userId, 50.00);
|
|
if (canPay) {
|
|
await walletService.debit(userId, 50.00, 'Course purchase');
|
|
}
|
|
```
|
|
|
|
### Reembolso al wallet
|
|
|
|
```typescript
|
|
await walletService.credit(userId, 25.00, 'Partial refund');
|
|
```
|
|
|
|
---
|
|
|
|
## Códigos Promocionales
|
|
|
|
```typescript
|
|
// Validar código
|
|
const result = await promoService.validateCode('SUMMER20', {
|
|
userId,
|
|
planId,
|
|
amount: 29.99,
|
|
});
|
|
|
|
if (result.valid) {
|
|
// Aplicar descuento
|
|
const finalPrice = 29.99 - result.discountAmount;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Adaptaciones Necesarias
|
|
|
|
1. **Productos en Stripe**: Crear productos y precios en dashboard
|
|
2. **Webhook URL**: Configurar endpoint público
|
|
3. **Planes**: Ajustar features según tu modelo de negocio
|
|
4. **Moneda**: Configurar currency (usd, eur, mxn, etc.)
|
|
5. **Trial**: Configurar período de prueba si aplica
|
|
6. **Tax**: Configurar impuestos si aplica (Stripe Tax)
|
|
|
|
---
|
|
|
|
## Referencias
|
|
|
|
- [Stripe API Reference](https://stripe.com/docs/api)
|
|
- [Stripe Checkout](https://stripe.com/docs/payments/checkout)
|
|
- [Stripe Subscriptions](https://stripe.com/docs/billing/subscriptions)
|
|
- [Stripe Webhooks](https://stripe.com/docs/webhooks)
|
|
- [Stripe Customer Portal](https://stripe.com/docs/billing/subscriptions/customer-portal)
|
|
|
|
---
|
|
|
|
**Mantenido por:** Sistema NEXUS
|
|
**Proyecto origen:** Trading Platform
|