# 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 ```gherkin 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 ```gherkin 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 ```gherkin 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 ```gherkin 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 ```gherkin 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 ```typescript 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 ```typescript 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 ```typescript @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](../00-vision-general/REQUERIMIENTOS-FUNCIONALES.md) - Seccion 5.11 - [INT-002](../02-integraciones/INT-002-oxxo.md) - Integracion OXXO - [ADR-0004](../97-adr/ADR-0004-pagos-efectivo-mexico.md) - Pagos en efectivo --- **Ultima Actualizacion:** 2026-01-10