New projects created: - michangarrito (marketplace mobile) - template-saas (SaaS template) - clinica-dental (dental ERP) - clinica-veterinaria (veterinary ERP) Architecture updates: - Move catalog from core/ to shared/ - Add MCP servers structure and templates - Add git management scripts - Update SUBREPOSITORIOS.md with 15 new repos - Update .gitignore for new projects Repository infrastructure: - 4 main repositories - 11 subrepositorios - Gitea remotes configured 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
10 KiB
10 KiB
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
backend:
framework: NestJS / Express
payment_processor: Stripe
database: PostgreSQL
packages:
- "stripe"
Dependencias NPM
{
"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
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
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
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
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
const customer = await stripeService.getOrCreateCustomer(
userId,
userEmail
);
// { stripeCustomerId: 'cus_xxx', ... }
2. Crear checkout session
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
const subscription = await subscriptionService.getSubscriptionByUserId(userId);
if (subscription?.status === 'active') {
// Usuario tiene suscripción activa
const hasFeature = subscription.plan.apiAccess;
}
4. Billing Portal (autoservicio)
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
// Al final del período
await subscriptionService.cancelSubscription(userId, false, 'User requested');
// Inmediatamente
await subscriptionService.cancelSubscription(userId, true);
6. Cambiar de plan
await subscriptionService.changePlan(userId, newPlanId, 'yearly');
Webhook Handler
// 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
# 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
await walletService.deposit({
userId: 'user-uuid',
amount: 100.00,
currency: 'usd',
description: 'Initial deposit',
});
Usar balance para pago
const canPay = await walletService.canAfford(userId, 50.00);
if (canPay) {
await walletService.debit(userId, 50.00, 'Course purchase');
}
Reembolso al wallet
await walletService.credit(userId, 25.00, 'Partial refund');
Códigos Promocionales
// 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
- Productos en Stripe: Crear productos y precios en dashboard
- Webhook URL: Configurar endpoint público
- Planes: Ajustar features según tu modelo de negocio
- Moneda: Configurar currency (usd, eur, mxn, etc.)
- Trial: Configurar período de prueba si aplica
- Tax: Configurar impuestos si aplica (Stripe Tax)
Referencias
Mantenido por: Sistema NEXUS Proyecto origen: Trading Platform