miinventario-v2/docs/01-epicas/MII-012-pagos-oxxo.md
rckrdmrd c24f889f70
Some checks failed
Build / Build Backend (push) Has been cancelled
Build / Build Mobile (TypeScript Check) (push) Has been cancelled
Lint / Lint Backend (push) Has been cancelled
Lint / Lint Mobile (push) Has been cancelled
Test / Backend E2E Tests (push) Has been cancelled
Test / Mobile Unit Tests (push) Has been cancelled
Build / Build Docker Image (push) Has been cancelled
[MIINVENTARIO] feat: Add exports, reports, integrations modules and CI/CD pipeline
- Add exports module with PDF/CSV/Excel generation
- Add reports module for inventory analytics
- Add POS integrations module
- Add database migrations for exports, movements and integrations
- Add GitHub Actions CI/CD workflow with Docker support
- Add mobile export and reports screens with tests
- Update epic documentation with traceability
- Add deployment and security guides

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

13 KiB

MII-012: Pagos OXXO


id: MII-012 type: Epic status: Completado priority: P0 phase: 3 story_points: 13 created_date: 2026-01-10 updated_date: 2026-01-13 simco_version: "4.0.0"

Metadata

Campo Valor
ID MII-012
Nombre Pagos OXXO
Fase 3 - Monetizacion
Prioridad P0
Story Points 13
Estado Completado

1. Descripcion

Implementar pagos en efectivo via OXXO usando Stripe OXXO vouchers, permitiendo a usuarios sin tarjeta comprar creditos.

Objetivo

Habilitar pagos en efectivo para el mercado mexicano donde muchos usuarios prefieren pagar en tiendas de conveniencia.


2. Requerimientos Relacionados

RF Descripcion Prioridad
FR-101 Generacion de voucher OXXO, confirmacion asincrona P0
FR-103 Confirmacion de pago via webhooks P0
FR-104 Reconciliacion de estados P0
FR-105 Expiracion de referencia (24-72h configurable) P1

3. Criterios de Aceptacion

AC-001: Generar Voucher

DADO que seleccione pago en OXXO
CUANDO confirmo el pedido
ENTONCES recibo un voucher con:
  - Numero de referencia
  - Monto a pagar
  - Codigo de barras
  - Fecha de expiracion

AC-002: Ver Voucher

DADO que genere un voucher
CUANDO voy a "Mis pagos pendientes"
ENTONCES puedo ver el voucher
Y puedo compartirlo o guardarlo
Y veo cuanto tiempo me queda

AC-003: Pago en Tienda

DADO que tengo un voucher valido
CUANDO pago en OXXO
ENTONCES recibo un ticket de confirmacion
Y en 10-30 minutos mis creditos se acreditan
Y recibo notificacion push

AC-004: Voucher Expirado

DADO que mi voucher expiro
CUANDO intento pagar
ENTONCES OXXO rechaza el pago
Y en la app veo que el voucher expiro
Y puedo generar uno nuevo

AC-005: Confirmacion Asincrona

DADO que pague en OXXO
CUANDO Stripe confirma el pago
ENTONCES el webhook actualiza mi orden
Y mis creditos se acreditan
Y recibo confirmacion

4. Tareas Tecnicas

ID Tarea Estimacion Estado
T-001 Configurar Stripe OXXO en backend 2 SP Completado
T-002 Crear generador de vouchers 2 SP Completado
T-003 Implementar pantalla de voucher 2 SP Completado
T-004 Crear vista de pagos pendientes 2 SP Completado
T-005 Implementar webhook OXXO 2 SP Completado
T-006 Crear job de expiracion 1 SP Completado
T-007 Implementar compartir voucher 2 SP Completado

5. Flujo de Pago OXXO

┌─────────────────────────────────────────────────────────────────┐
│                    FLUJO DE PAGO OXXO                           │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  APP                          OXXO                    STRIPE    │
│   │                            │                        │       │
│   │ 1. Seleccionar OXXO        │                        │       │
│   │─────────────────────────────────────────────────────▶       │
│   │                            │     PaymentIntent      │       │
│   │◀─────────────────────────────────────────────────────       │
│   │    Voucher + Referencia    │                        │       │
│   │                            │                        │       │
│   │ 2. Mostrar voucher         │                        │       │
│   │    al usuario              │                        │       │
│   │                            │                        │       │
│   │              3. Usuario va │                        │       │
│   │                 a OXXO     │                        │       │
│   │                    ───────▶│                        │       │
│   │                            │ 4. Paga y recibe      │       │
│   │                            │    ticket             │       │
│   │                            │────────────────────────▶       │
│   │                            │                        │       │
│   │                            │      5. Confirma pago  │       │
│   │◀─────────────────────────────────────────────────────       │
│   │         Webhook            │                        │       │
│   │                            │                        │       │
│   │ 6. Acreditar creditos      │                        │       │
│   │    Notificar usuario       │                        │       │
│   │                            │                        │       │
└─────────────────────────────────────────────────────────────────┘

6. Generacion de Voucher

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

  // Verificar ordenes pendientes
  const pending = await this.ordersService.findPending(userId, 'OXXO');
  if (pending.length >= 3) {
    throw new BadRequestException('Maximo 3 vouchers pendientes');
  }

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

  // Crear PaymentIntent con OXXO
  const paymentIntent = await this.stripe.paymentIntents.create({
    amount: Math.round(pkg.priceMxn * 100),
    currency: 'mxn',
    payment_method_types: ['oxxo'],
    payment_method_data: {
      type: 'oxxo',
      billing_details: {
        name: user.name || 'Usuario MiInventario',
        email: user.email
      }
    },
    metadata: {
      orderId: order.id,
      userId,
      packageId
    }
  });

  // Confirmar para generar voucher
  const confirmed = await this.stripe.paymentIntents.confirm(
    paymentIntent.id
  );

  const oxxoDetails = confirmed.next_action?.oxxo_display_details;

  await this.ordersService.update(order.id, {
    stripePaymentIntentId: paymentIntent.id,
    status: 'PENDING',
    metadata: {
      oxxoNumber: oxxoDetails.number,
      oxxoExpiresAt: oxxoDetails.expires_after,
      hostedVoucherUrl: oxxoDetails.hosted_voucher_url
    }
  });

  return {
    orderId: order.id,
    voucher: {
      number: oxxoDetails.number,
      amount: pkg.priceMxn,
      expiresAt: order.expiresAt,
      hostedUrl: oxxoDetails.hosted_voucher_url
    }
  };
}

7. Endpoints API

Metodo Endpoint Descripcion Auth
POST /payments/oxxo/create Crear voucher OXXO JWT
GET /payments/pending Listar pagos pendientes JWT
GET /payments/oxxo/:orderId/voucher Obtener voucher JWT
POST /payments/oxxo/:orderId/cancel Cancelar voucher JWT
POST /payments/webhook/stripe Webhook Stripe Stripe Sig

8. Pantallas Mobile

Pantalla Componentes
OxxoVoucherScreen Referencia, monto, barcode, timer
PendingPaymentsScreen Lista vouchers pendientes
VoucherShareScreen Opciones compartir, guardar
PaymentConfirmScreen Exito, creditos acreditados

9. UI de Voucher

┌─────────────────────────────────────────┐
│          PAGO EN OXXO                   │
├─────────────────────────────────────────┤
│                                         │
│  Presenta este codigo en cualquier      │
│  tienda OXXO para completar tu pago     │
│                                         │
│  ┌─────────────────────────────────┐   │
│  │                                 │   │
│  │  ||||| |||| ||||| |||| |||||   │   │
│  │     [CODIGO DE BARRAS]          │   │
│  │                                 │   │
│  │     1234 5678 9012 3456         │   │
│  │                                 │   │
│  └─────────────────────────────────┘   │
│                                         │
│  REFERENCIA                             │
│  ─────────────                          │
│  1234 5678 9012 3456                    │
│                                         │
│  MONTO A PAGAR                          │
│  ─────────────                          │
│  $100.00 MXN                            │
│                                         │
│  EXPIRA EN                              │
│  ─────────────                          │
│  ⏱️ 71:45:32                            │
│  (12 Ene 2026, 10:30 AM)                │
│                                         │
│  ┌─────────────────────────────────┐   │
│  │      📤 COMPARTIR VOUCHER       │   │
│  └─────────────────────────────────┘   │
│                                         │
│  ┌─────────────────────────────────┐   │
│  │      💾 GUARDAR IMAGEN          │   │
│  └─────────────────────────────────┘   │
│                                         │
│  ⓘ El pago se refleja en 10-30 min     │
│    despues de pagar en tienda           │
│                                         │
└─────────────────────────────────────────┘

10. Webhook OXXO

async handleOxxoPayment(paymentIntent: Stripe.PaymentIntent) {
  const orderId = paymentIntent.metadata.orderId;

  switch (paymentIntent.status) {
    case 'succeeded':
      // Pago exitoso
      await this.completeOxxoPayment(orderId);
      break;

    case 'canceled':
      // Voucher expirado o cancelado
      await this.cancelOxxoPayment(orderId);
      break;
  }
}

async completeOxxoPayment(orderId: string) {
  const order = await this.ordersService.findOne(orderId);

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

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

  await this.notificationsService.send(order.userId, {
    title: 'Pago recibido',
    body: `Tu pago en OXXO fue confirmado. +${order.creditsAmount} creditos`,
    data: { screen: 'wallet' }
  });
}

11. Job de Expiracion

@Cron('0 * * * *') // Cada hora
async expireOxxoVouchers() {
  const expired = await this.ordersService.findExpired('OXXO');

  for (const order of expired) {
    await this.ordersService.update(order.id, {
      status: 'EXPIRED'
    });

    await this.notificationsService.send(order.userId, {
      title: 'Voucher expirado',
      body: 'Tu voucher OXXO ha expirado. Genera uno nuevo si deseas continuar.'
    });
  }
}

12. Dependencias

Entrada (Requiere)

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

Salida (Bloquea)

  • Ninguna directa

13. Configuracion

Parametro Valor Default Descripcion
OXXO_EXPIRATION_HOURS 72 Horas hasta expiracion
OXXO_MAX_PENDING 3 Max vouchers por usuario
OXXO_MIN_AMOUNT 10 Monto minimo MXN
OXXO_MAX_AMOUNT 10000 Monto maximo MXN

14. Riesgos

Riesgo Probabilidad Impacto Mitigacion
Usuario olvida pagar Alta Bajo Recordatorios, push
Webhook perdido Baja Alto Reconciliacion periodica
Voucher ilegible Baja Medio PDF de alta calidad

15. Referencias


Ultima Actualizacion: 2026-01-10