miinventario-v2/docs/01-epicas/MII-011-pagos-tarjeta.md
rckrdmrd 1a53b5c4d3 [MIINVENTARIO] feat: Initial commit - Sistema de inventario con análisis de video IA
- Backend NestJS con módulos de autenticación, inventario, créditos
- Frontend React con dashboard y componentes UI
- Base de datos PostgreSQL con migraciones
- Tests E2E configurados
- Configuración de Docker y deployment

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 02:25:48 -06:00

9.5 KiB

MII-011: Pagos con Tarjeta


id: MII-011 type: Epic status: Pendiente priority: P0 phase: 3 story_points: 8 created_date: 2026-01-10 updated_date: 2026-01-10 simco_version: "4.0.0"

Metadata

Campo Valor
ID MII-011
Nombre Pagos con Tarjeta
Fase 3 - Monetizacion
Prioridad P0
Story Points 8
Estado Pendiente

1. Descripcion

Implementar pagos con tarjeta de credito/debito usando Stripe, con PCI compliance delegado y experiencia de pago fluida.

Objetivo

Permitir a los usuarios comprar creditos instantaneamente con tarjeta de manera segura.


2. Requerimientos Relacionados

RF Descripcion Prioridad
FR-100 Pago inmediato con tarjeta, PCI delegado (Stripe) P0
FR-103 Confirmacion de pago via webhooks P0
FR-104 Reconciliacion (CREATED/PENDING/PAID/EXPIRED/CANCELED) P0

3. Criterios de Aceptacion

AC-001: Pago con Tarjeta

DADO que seleccione un paquete
CUANDO ingreso mis datos de tarjeta
ENTONCES el pago se procesa inmediatamente
Y recibo confirmacion de exito o error
Y mis creditos se acreditan al instante

AC-002: Formulario Seguro

DADO que estoy en el checkout
CUANDO ingreso datos de tarjeta
ENTONCES uso el componente Stripe Elements
Y los datos nunca tocan mi servidor
Y veo indicadores de seguridad

AC-003: Tarjeta Guardada

DADO que pague con una tarjeta
CUANDO hago otra compra
ENTONCES puedo usar la tarjeta guardada
Y solo ingreso CVV
O puedo agregar otra tarjeta

AC-004: Error de Pago

DADO que mi pago falla
CUANDO veo el error
ENTONCES el mensaje es claro (fondos, tarjeta invalida, etc)
Y puedo intentar de nuevo
Y no se cobran creditos

AC-005: Webhook de Confirmacion

DADO que Stripe confirma el pago
CUANDO recibo el webhook
ENTONCES actualizo el estado de la orden
Y acredito los creditos
Y envio confirmacion al usuario

4. Tareas Tecnicas

ID Tarea Estimacion Estado
T-001 Configurar Stripe SDK backend 1 SP Pendiente
T-002 Crear endpoints de pago 2 SP Pendiente
T-003 Implementar webhook handler 2 SP Pendiente
T-004 Integrar Stripe Elements en mobile 2 SP Pendiente
T-005 Implementar guardado de tarjetas 1 SP Pendiente

5. Modelo de Datos

Tabla: payment_orders

CREATE TABLE payment_orders (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id UUID REFERENCES users(id),
  package_id UUID REFERENCES packages(id),
  amount_mxn DECIMAL(10,2) NOT NULL,
  credits_amount DECIMAL(12,4) NOT NULL,
  payment_method VARCHAR(20), -- 'CARD', 'OXXO', 'SEVEN_ELEVEN'
  status VARCHAR(20) DEFAULT 'CREATED',
  stripe_payment_intent_id VARCHAR(100),
  stripe_customer_id VARCHAR(100),
  paid_at TIMESTAMP,
  expires_at TIMESTAMP,
  metadata JSONB,
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);

-- Status: CREATED, PENDING, PAID, FAILED, EXPIRED, CANCELED, REFUNDED

Tabla: payment_methods

CREATE TABLE payment_methods (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id UUID REFERENCES users(id),
  type VARCHAR(20), -- 'CARD'
  stripe_payment_method_id VARCHAR(100),
  last_four VARCHAR(4),
  brand VARCHAR(20),
  exp_month INT,
  exp_year INT,
  is_default BOOLEAN DEFAULT false,
  created_at TIMESTAMP DEFAULT NOW()
);

6. Flujo de Pago

┌─────────────────────────────────────────────────────────────────┐
│                    FLUJO DE PAGO CON TARJETA                    │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  ┌──────────┐   ┌──────────┐   ┌──────────┐   ┌──────────┐     │
│  │ Seleccionar │→│  Crear   │→│  Pagar   │→│ Confirmar│     │
│  │  Paquete │   │  Orden   │   │ (Stripe) │   │(Webhook) │     │
│  └──────────┘   └──────────┘   └──────────┘   └──────────┘     │
│       │              │              │              │            │
│       │              │              │              │            │
│       ▼              ▼              ▼              ▼            │
│   Mostrar       CREATED         PENDING          PAID          │
│   precios       en DB        PaymentIntent   Acreditar         │
│                                                creditos         │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

7. Endpoints API

Metodo Endpoint Descripcion Auth
POST /payments/card/intent Crear PaymentIntent JWT
POST /payments/card/confirm Confirmar pago JWT
GET /payments/methods Listar tarjetas guardadas JWT
POST /payments/methods Agregar tarjeta JWT
DELETE /payments/methods/:id Eliminar tarjeta JWT
POST /payments/webhook/stripe Webhook Stripe Stripe Sig

8. Integracion Stripe

Backend - Crear PaymentIntent

async createCardPayment(userId: string, packageId: string) {
  const pkg = await this.packagesService.findOne(packageId);
  const user = await this.usersService.findOne(userId);

  // Crear o obtener Stripe Customer
  let customerId = user.stripeCustomerId;
  if (!customerId) {
    const customer = await this.stripe.customers.create({
      email: user.email,
      phone: user.phone,
      metadata: { userId }
    });
    customerId = customer.id;
    await this.usersService.update(userId, { stripeCustomerId: customerId });
  }

  // Crear orden
  const order = await this.ordersService.create({
    userId,
    packageId,
    amountMxn: pkg.priceMxn,
    creditsAmount: pkg.currentCredits,
    paymentMethod: 'CARD',
    status: 'CREATED'
  });

  // Crear PaymentIntent
  const paymentIntent = await this.stripe.paymentIntents.create({
    amount: Math.round(pkg.priceMxn * 100), // centavos
    currency: 'mxn',
    customer: customerId,
    metadata: {
      orderId: order.id,
      userId,
      packageId
    }
  });

  await this.ordersService.update(order.id, {
    stripePaymentIntentId: paymentIntent.id,
    stripeCustomerId: customerId,
    status: 'PENDING'
  });

  return {
    orderId: order.id,
    clientSecret: paymentIntent.client_secret
  };
}

Webhook Handler

@Post('webhook/stripe')
async handleStripeWebhook(@Req() req: RawBodyRequest) {
  const sig = req.headers['stripe-signature'];
  const event = this.stripe.webhooks.constructEvent(
    req.rawBody,
    sig,
    process.env.STRIPE_WEBHOOK_SECRET
  );

  switch (event.type) {
    case 'payment_intent.succeeded':
      await this.handlePaymentSuccess(event.data.object);
      break;
    case 'payment_intent.payment_failed':
      await this.handlePaymentFailed(event.data.object);
      break;
  }

  return { received: true };
}

async handlePaymentSuccess(paymentIntent: Stripe.PaymentIntent) {
  const orderId = paymentIntent.metadata.orderId;
  const order = await this.ordersService.findOne(orderId);

  // Actualizar orden
  await this.ordersService.update(orderId, {
    status: 'PAID',
    paidAt: new Date()
  });

  // Acreditar creditos
  await this.creditsService.addCredits(
    order.userId,
    order.creditsAmount,
    'PURCHASE',
    orderId
  );

  // Notificar usuario
  await this.notificationsService.send(order.userId, {
    title: 'Pago exitoso',
    body: `Se acreditaron ${order.creditsAmount} creditos a tu cuenta`
  });
}

9. Pantallas Mobile

Pantalla Componentes
CheckoutScreen Resumen, Stripe Elements, confirmar
SavedCardsScreen Lista tarjetas, agregar nueva
PaymentSuccessScreen Confirmacion, creditos, continuar
PaymentErrorScreen Error, reintentar, soporte

10. Dependencias

Entrada (Requiere)

  • MII-009: Wallet y Creditos
  • MII-010: Paquetes de Recarga

Salida (Bloquea)

  • Ninguna directa

11. Seguridad

Aspecto Implementacion
PCI DSS Delegado a Stripe
Datos tarjeta Nunca tocan nuestro servidor
Webhooks Verificacion de firma Stripe
Idempotencia Payment Intent ID unico

12. Riesgos

Riesgo Probabilidad Impacto Mitigacion
Fraude Media Alto Stripe Radar, 3D Secure
Webhook perdido Baja Alto Retry, reconciliacion
Doble cobro Baja Alto Idempotencia, locks

13. Referencias


Ultima Actualizacion: 2026-01-10