workspace/core/catalog/payments
rckrdmrd ea1879f4ad feat: Initial workspace structure with multi-level Git configuration
- Configure workspace Git repository with comprehensive .gitignore
- Add Odoo as submodule for ERP reference code
- Include documentation: SETUP.md, GIT-STRUCTURE.md
- Add gitignore templates for projects (backend, frontend, database)
- Structure supports independent repos per project/subproject level

Workspace includes:
- core/ - Reusable patterns, modules, orchestration system
- projects/ - Active projects (erp-suite, gamilit, trading-platform, etc.)
- knowledge-base/ - Reference code and patterns (includes Odoo submodule)
- devtools/ - Development tools and templates
- customers/ - Client implementations template

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 10:44:23 -06:00
..
IMPLEMENTATION.md feat: Initial workspace structure with multi-level Git configuration 2025-12-08 10:44:23 -06:00
README.md feat: Initial workspace structure with multi-level Git configuration 2025-12-08 10:44:23 -06:00

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

  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


Mantenido por: Sistema NEXUS Proyecto origen: Trading Platform