Skip to content

Smart Storages - Mercado Pago Integration Guide

1. Arquitectura y Flujo (Architecture & Flow)

El sistema utiliza una arquitectura Multi-Tenant donde cada organización (Inquilino) configura sus propias credenciales de Mercado Pago.

Diagrama de Secuencia Simplificado

mermaid
sequenceDiagram
    participant User as Cliente
    participant FE as Frontend (Smart Storages)
    participant BE as Backend (Django)
    participant MP as Mercado Pago API

    Note over User, MP: Configuración (Setup)
    User->>FE: Ingresa Public Key & Access Token
    FE->>BE: Guarda Credenciales (Encriptadas)
    
    Note over User, MP: Pago con QR (QR Payment)
    User->>FE: Click "Pagar con QR"
    FE->>BE: Mutation GenerateQRPayment(saleId)
    BE->>MP: POST /instore/orders (Crea Orden en Caja)
    MP-->>BE: 201 Created
    BE-->>FE: Success
    FE->>User: Muestra "Escanea el QR ahora"
    User->>MP: Escanea QR con App MP
    MP->>BE: Webhook (topic=merchant_order)
    BE->>BE: Procesa Pago y Actualiza Venta

2. Configuración (Configuration)

2.1 Modelo de Datos (MercadoPagoIntegration)

Ubicación: backend/payments/models.py

CampoDescripción
organizationFK a la Organización (Tenant) dueña de la cuenta.
access_token_encryptedToken de MP (Production o Sandbox) encriptado con Fernet.
webhook_secret_encryptedSecret para validar la firma HMAC de notificaciones (IPN).
environmentsandbox (Pruebas) o production (Dinero real).

2.2 Gestión de Credenciales

  • Interfaz: Settings > Mercado Pago
  • Seguridad: Los tokens nunca se devuelven en texto plano al frontend. Se usa ************* para indicar que existe valor.

3. Webhooks & Seguridad

3.1 Endpoint

POST /api/payments/webhook/mp/?schema={schema_name}

El parámetro ?schema= es CRÍTICO para enrutar la notificación al tenant correcto en la base de datos (PostgreSQL Schemas).

3.2 Validación de Firma (HMAC)

Ubicación: backend/payments/services.py > validate_webhook_signature

Cada notificación debe incluir el header x-signature. El sistema calcula el HMAC-SHA256 usando el webhook_secret del tenant y lo compara.

  • Producción: Si la firma falla, se rechaza con 403 Forbidden.
  • Sandbox/Local: Si la firma falla, se loguea una advertencia (WARNING) pero se procesa para facilitar el desarrollo (p.ej. usando Ngrok).

4. Reglas de Negocio (Business Rules)

4.1 Generación de QR

  • Validación de Saldo: No se puede generar un QR por un monto mayor al saldo pendiente de la venta.
  • Venta Pagada: Si la venta ya está pagada (is_paid=True), el sistema bloquea la generación de nuevos QRs.
  • Modelo Híbrido: Usamos QRs Estáticos (imagen fija impresa) pero con Órdenes Dinámicas (el monto se inyecta al momento de la venta). Por eso las Cajas se crean con fixed_amount=False.

4.2 Webhook Processing

El webhook maneja actualizaciones de estado de pagos:

  • approved: Crea un registro SalePayment y, si cubre el total, marca la venta como pagada.
  • refunded: NUEVO. Si se detecta un reembolso, busca el pago original y actualiza el estado (ej. desmarca is_paid en SalePayment) para reflejar que la deuda se mantiene.

5. Salida a Producción (Go Live)

Paso 1: Obtener Credenciales

El cliente final debe:

  1. Ir a Mercado Pago Developers.
  2. Crear una Aplicación.
  3. Copiar Production Access Token y Public Key.

Paso 2: Configurar en Smart Storages

  1. Ir a Configuración > Integraciones.
  2. Cambiar Entorno a Production.
  3. Pegar las credenciales.
  4. Configurar el Webhook Url en Mercado Pago: https://{dominio}/api/payments/webhook/mp/?schema={tenant}.
  5. Copiar el Webhook Secret de MP y pegarlo en Smart Storages.

Paso 3: Validar

Realizar una compra real de monto bajo ($10) y verificar que impacte en el sistema. Luego se puede reembolsar desde el panel de MP.


6. Testing (QA)

Herramientas

  • Sandbox Mode: Habilitar en configuración. Usa Test Access Token.
  • Tarjetas de Prueba: Usar las provistas en la documentación oficial.
  • Refund API: Usar la mutación refundPayment(paymentId) para simular devoluciones sin ir al panel de MP.

Mutation: Refund Payment (Test Helper)

graphql
mutation {
  refundPayment(paymentId: "1234567890") {
    success
    status
  }
}

Esta documentación describe la implementación completa de la integración de Mercado Pago con el sistema Smart Storages, incluyendo arquitectura multi-tenant, webhooks y seguridad.

Índice

  1. Arquitectura General
  2. Configuración (Nuevo Modelo)
  3. Flujo de Pago
  4. Webhooks
  5. Actualización en Tiempo Real
  6. Seguridad
  7. Multi-Tenancy
  8. Archivos Clave
  9. Variables de Entorno
  10. Troubleshooting

Arquitectura General

┌─────────────────┐      ┌────────────────┐      ┌─────────────────┐
│   Frontend      │      │  Mercado Pago  │      │   Backend       │
│   (Next.js)     │      │    (API)       │      │   (Django)      │
└────────┬────────┘      └───────┬────────┘      └────────┬────────┘
         │                       │                        │
         │ 1. Crear preferencia  │                        │
         │──────────────────────────────────────────────▶│
         │                       │                        │
         │ 2. URL de checkout    │                        │
         │◀──────────────────────────────────────────────│
         │                       │                        │
         │ 3. Redirect a MP      │                        │
         │──────────────────────▶│                        │
         │                       │                        │
         │                       │ 4. Webhook POST        │
         │                       │───────────────────────▶│
         │                       │                        │
         │                       │                        │ 5. Identificar Tenant
         │                       │                        │    via ?schema=xxx
         │                       │                        │
         │                       │                        │ 6. Verificar pago
         │                       │                        │    via API MP
         │                       │                        │
         │                       │                        │ 7. Registrar
         │                       │                        │    SalePayment

Configuración (Nuevo Modelo)

Modelo MercadoPagoIntegration

Se ha migrado de un modelo monolítico MercadoPagoConfig a un sistema modular MercadoPagoIntegration que permite mútiples configuraciones por tenant:

python
# payments/models.py
class MercadoPagoIntegration(TimeStampedModel):
    INTEGRATION_TYPES = [
        ('checkout_pro', 'Checkout Pro'),
        ('qr_code', 'Código QR'),
    ]
    
    organization = models.ForeignKey(...)
    integration_type = models.CharField(...)
    public_key = models.CharField(...)
    access_token_encrypted = models.TextField()  # Encriptado
    webhook_secret_encrypted = models.TextField() # Encriptado
    environment = models.CharField(choices=[('sandbox', 'Sandbox'), ('production', 'Production')])
    is_active = models.BooleanField(default=True)
    
    # ... metadata adicional

Gestión de Credenciales

Las credenciales se gestionan desde el panel de administración (/settings/integrations/mercadopago) que ahora unifica:

  1. Credenciales: Checkout Pro y Webhook Secrets.
  2. QR & Sucursales: Gestión de Cajas (POS) y códigos QR físicos.

Las credenciales sensibles se encriptan usando Fernet antes de guardarse en la base de datos.


Flujo de Pago

3. API & Schema

Mutations

  1. generatePaymentLink (Admin/User Authenticated)

    • Input: saleId (ID!)
    • Output: initPoint, sandboxInitPoint, preferenceId
    • Use Case: Used by internal users or admins to generate links manually.
  2. generatePublicPaymentLink (Public/Anonymous)

    • Input: saleToken (String!)
    • Output: initPoint, sandboxInitPoint
    • Use Case: Used by the public shop frontend after order creation.
    • Security: Validates saleToken matches a valid, unpaid sale.
  3. refundPayment

    • Input: paymentId (String!)
    • Output: status, success

Webhooks

  • Endpoint: /api/payments/webhook/
  • Method: POST
  • Query Param: ?schema={schema_name} (Required for multi-tenancy)

2. Tenant Context en Preferencia

Para asegurar que los webhooks identifiquen correctamente al tenant, se anexa el esquema en la URL de notificación:

python
# payments/api/schema.py
notification_url = f"{settings.WEBHOOK_BASE_URL}/api/v1/payments/webhook/?schema={connection.schema_name}"

preference_data = {
    "items": [...],
    "external_reference": enc_ref,  # Tenant + Sale ID encriptado (Backup de seguridad)
    "notification_url": notification_url,
    # ...
}

Webhooks

URL del Webhook

POST /api/v1/payments/webhook/?schema={tenant_slug}

Procesamiento del Webhook (PaymentWebhookView)

  1. Identificación del Tenant:

    • Prioridad 1: Parámetro ?schema=xxx en la URL.
    • Prioridad 2 (Fallback): demo context.
  2. Obtención de Credenciales:

    • Se busca una MercadoPagoIntegration activa del tipo checkout_pro para ese tenant.
    • Se desencripta el Access Token.
  3. Validación con API MP:

    • Se consulta sdk.payment().get(id) para verificar autenticidad.
    • Se desencripta el external_reference como capa de seguridad adicional.
  4. Registro:

    • Se crea/actualiza el registro SalePayment.
    • Se actualiza el saldo de la Sale.

Actualización en Tiempo Real

Para mejorar la experiencia de usuario, el frontend implementa Polling Inteligente en lugar de WebSockets (por simplicidad de infraestructura).

1. Detalle de Venta (/sales/[id])

  • Lógica: El componente SaleDetailPage verifica el estado del pago cada 3 segundos.
  • Condiciones de Activación:
    • Venta NO cancelada.
    • Venta con saldo pendiente.
  • Limpieza: El polling se detiene automáticamente (stopPolling) si:
    • El pago se completa.
    • Se sale de la página (unmount).
    • La venta se cancela.

2. Cajón de Pago QR (QRPaymentDrawer)

  • Lógica: Verifica el estado de la orden QR (GetPendingQROrder) cada 3 segundos.
  • Optimización: SOLO inicia el polling después de haber generado un QR exitosamente. Se detiene inmediatamente al cerrar el cajón.

Seguridad

Capas de Seguridad Implementadas

  1. Validación de Origen (Tenant Context): El webhook url explícitamente indica a qué tenant pertenece el pago vía ?schema=.
  2. Verificación API: NUNCA confiamos en los datos del body del webhook ciegamente. Siempre consultamos a GET /v1/payments/{id} usando nuestras credenciales.
  3. External Reference Encriptado: El external_reference contiene json({"s": schema, "i": sale_id}) encriptado con Fernet. Esto impide que un atacante inyecte pagos a otros tenants.
  4. Encriptación de Base de Datos: Access Tokens y Webhook Secrets están encriptados en reposo.

HMAC Validation

Estado: Deshabilitado por incompatibilidad con notification_url dinámico.

Al definir una notification_url personalizada por pago (necesario para multi-tenancy dinámico), Mercado Pago firma los webhooks con una clave distinta o deshabilita ciertas validaciones de firma estándar. La seguridad recae en la Verificación API obligatoria.


Multi-Tenancy

Identificación del Tenant (Doble Factor)

  1. Vía URL: ?schema={tenant} permite al middleware de Django enrutar la request a la base de datos correcta.
  2. Vía Payload: Al consultar la API de MP, el external_reference desencriptado confirma que el pago realmente pertenece a ese tenant y esa venta.

Archivos Clave

ArchivoDescripción
payments/models.pyModelo MercadoPagoIntegration
payments/views.pyPaymentWebhookView (Lógica de recepción y ruteo)
payments/services.pyMercadoPagoService (SDK, encriptación, validación)
payments/api/schema.pyMutations GraphQL (GeneratePaymentLink, GenerateQRPayment)
frontend/app/sales/[id]/page.tsxUI de Venta + Polling
frontend/components/sales/QRPaymentDrawer.tsxComponente UI para cobro QR

Troubleshooting

Webhook falla con "Tenant not found"

  • Causa: La notification_url no tenía el parámetro ?schema=.
  • Solución: Verificar que GeneratePaymentLink en schema.py esté anexando correctamente el parámetro.

Polling "eterno" en el frontend

  • Causa: Verificación incondicional en useQuery.
  • Solución: Asegurar que pollInterval sea 0 por defecto y usar startPolling/stopPolling basado en condiciones lógicas.

Error "Invalid Credentials" al procesar webhook

  • Causa: El tenant tiene una integración configurada pero el Access Token es inválido o de otro ambiente (Sandbox vs Prod).
  • Solución: Ir a /settings/integrations/mercadopago, re-validar las credenciales usando el botón "Probar Conexión".

Changelog

FechaCambio
2026-02-06Refactorización a MercadoPagoIntegration (Modular)
2026-02-06Fix de Webhook Tenant Context (?schema=)
2026-02-06Implementación de Polling Inteligente (Frontend)
2026-02-06Consolidación de UI de configuración
2026-02-10Corrección de redirección en Checkout (Auto Return & HTTPS)
2026-02-10Centralización de Traducciones (i18n) para Métodos de Pago
2026-02-10Visualización de detalles de pago en UI (Método específico)

7. Mejoras Recientes (2026-02-10)

7.1 Manejo de URL y Redirección

  • Ambiente Local vs Producción: Se implementó una lógica robusta en GeneratePublicPaymentLink para resolver FRONTEND_URL.
    • Local: Se fuerza IP 127.0.0.1 en lugar de localhost para evitar rechazos de MP. auto_return se deshabilita para facilitar debugging.
    • Producción: Se asegura HTTPS en back_urls y se habilita auto_return para una experiencia fluida.

7.2 Internacionalización (i18n)

  • Centralización: Todas las traducciones de métodos de pago (account_money, credit_card, etc.) se movieron a frontend/core/i18n/context.tsx.
  • Visualización: El componente de ventas ahora parsea las notas del pago para extraer y traducir el método específico (ej: "Dinero en cuenta") en lugar de mostrar texto técnico o genérico.

7.3 Registro de Pagos

  • Tipificación: El backend ahora guarda el payment_type_id específico (ej: debit_card) en lugar de un string genérico "Mercado Pago". Esto permite reportes más detallados y una mejor UI.

7.4 Visualización de Detalles (PaymentDetailsDrawer)

  • Componente: Se creó PaymentDetailsDrawer.tsx para mostrar metadatos extendidos recuperados directamente de la tabla de pagos de Mercado Pago (mpPayments).
  • Mapeo de Datos: Debido a que los pagos genéricos del sistema (SalePayment) se crean vía webhook, la relación con los datos específicos de MP se realiza dinámicamente en el frontend usando la función findMpPayment. Esta función busca un "Ref ID" en las notas del pago y lo vincula con el array mpPayments de la venta.
  • Campos Visualizados: Estado de acreditación, monto neto recibido, detalle de comisiones (fees), fecha de liberación de fondos, y datos de la tarjeta (últimos 4 dígitos).

8. Robustez del Schema GraphQL

Para que la interfaz de ventas funcione correctamente, la consulta GET_SALE_DETAIL DEBE incluir los siguientes campos de control de flujo:

  • currentStatus.allowedActions: Lista de acciones permitidas para el usuario según el estado actual. Sin este campo, los botones como "Registrar Pago" o "Facturar" no se renderizan.
  • workflow: Objeto que contiene la lista de estados y su orden. Es vital para el componente WorkflowTracker (Steps).
  • qrCode: Necesario para generar el código de entrega dinámica.
  • client.ivaCondition: Requerido por el motor de facturación (InvoiceDrawer).

9. Archivos Clave

ArchivoDescripción
payments/models.pyModelo MercadoPagoIntegration
payments/views.pyPaymentWebhookView (Lógica de recepción y ruteo)
payments/services.pyMercadoPagoService (SDK, encriptación, validación)
payments/api/schema.pyMutations GraphQL (GeneratePaymentLink, GenerateQRPayment)
frontend/app/sales/[id]/page.tsxUI de Venta + Polling + Integración de Drawer
frontend/components/sales/PaymentDetailsDrawer.tsxUI para ver detalles extendidos de MP
frontend/components/sales/QRPaymentDrawer.tsxComponente UI para cobro QR

Changelog

FechaCambio
2026-02-06Refactorización a MercadoPagoIntegration (Modular)
2026-02-06Fix de Webhook Tenant Context (?schema=)
2026-02-06Implementación de Polling Inteligente (Frontend)
2026-02-06Consolidación de UI de configuración
2026-02-10Corrección de redirección en Checkout (Auto Return & HTTPS)
2026-02-10Centralización de Traducciones (i18n) para Métodos de Pago
2026-02-10Visualización de detalles de pago en UI (Método específico)
2026-02-11Schema Fix: Restauración de campos críticos (allowedActions, workflow).
2026-02-11Documentación: Detalle técnico de PaymentDetailsDrawer y requisitos de esquema.