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
- 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>
13 KiB
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
- REQUERIMIENTOS-FUNCIONALES.md - Seccion 5.11
- INT-002 - Integracion OXXO
- ADR-0004 - Pagos en efectivo
Ultima Actualizacion: 2026-01-10