# Solución Mercado Pago Odoo 18 – Return URL Fix

Odoo 18 con Mercado Pago como pasarela de pagos

En una implementación reciente de Odoo 18 con Mercado Pago como pasarela de pagos, me encontré con un problema que no estaba documentado oficialmente y que tampoco tenía soluciones funcionales en foros o repositorios: al finalizar la compra, el modal de Mercado Pago no se abría. En su lugar, aparecía un error que indicaba que las “Return URL” no estaban configuradas.

Este error impedía que el usuario pudiera completar el pago, y lo más frustrante fue descubrir que no había forma de configurar esas URLs desde la interfaz de Odoo. Aunque el módulo `payment_mercado_pago` permite definir credenciales y activar el proveedor, no ofrece ningún campo para establecer las `back_urls` que Mercado Pago necesita para redirigir correctamente al usuario después del pago.

## ¿Por qué no se pueden configurar las back_urls desde la interfaz?

El módulo oficial de Mercado Pago para Odoo, conocido como `payment_mercado_pago`, es mantenido por la comunidad y no sigue un ciclo de actualizaciones regular como los módulos oficiales de Odoo. Esto significa que las actualizaciones no tienen una frecuencia establecida y dependen de contribuciones voluntarias.

En consecuencia, este módulo no incluye una opción en la configuración del proveedor para definir las URLs de retorno. Y como el payload que se envía a Mercado Pago se construye de forma automática en el backend, no hay forma de intervenir desde la interfaz para modificarlo.

## ¿Por qué edité el código directamente?

Lo ideal en estos casos sería crear un módulo personalizado que herede las clases necesarias y sobreescriba los métodos que queremos modificar. De esa forma, los cambios se mantienen separados del código original y no se pierden con futuras actualizaciones del módulo.

Sin embargo, como este módulo no se actualiza con frecuencia y el entorno en el que estaba trabajando contaba con un entorno de staging para pruebas, decidí modificar directamente el archivo fuente. Obviamente, esto no es lo más recomendable para producción sin un control de versiones o entorno de pruebas previo.

Si te interesa ver un ejemplo de cómo crear un módulo personalizado para extender funcionalidades sin tocar el core, escribí un artículo sobre cómo personalizar el frontend de Odoo (CSS, JS y XML) usando herencia de módulos: 🔗 [Cómo personalizar el frontend de Odoo](https://tuwebmaster.com.ar/como-personalizar-el-frontend-de-odoo/)

## Modificación del archivo payment_transaction.py

La solución consistió en editar el método `_mercado_pago_prepare_preference_request_payload`, que es el encargado de construir el payload que se envía a Mercado Pago para crear la preferencia de pago.

Este método se encuentra en:

addons/payment_mercado_pago/models/payment_transaction.py

Dentro de ese método, agregué manualmente el bloque `back_urls` dentro del diccionario que se retorna. Este diccionario representa el cuerpo del request que se envía a Mercado Pago, y es ahí donde deben estar definidas las URLs de retorno.

## Fragmento modificado:

```
return {
    'auto_return': 'approved',
    'back_urls': {
        'success': "https://tusitio.com/payment/mp/return",
        'pending': "https://tusitio.com/payment/mp/return",
        'failure': "https://tusitio.com/payment/mp/return",
    },
    'external_reference': self.reference,
    ...
}
```

Este bloque se encuentra dentro del método que también define otros parámetros como el `external_reference`, los `items`, el `notification_url` y los datos del `payer`. Es importante que las `back_urls` estén en este nivel, ya que Mercado Pago espera recibirlas como parte del payload de la preferencia.

## Implementación del controlador de retorno

Una vez que Mercado Pago tiene las URLs de retorno, necesita que esas rutas existan en Odoo. Si no están definidas, el usuario será redirigido a una página 404.

La actualización de enero de 2026 incorpora el controlador completo que uso para conservar la sesión del carrito y llevar al cliente a la confirmación del pedido.

Agregué save_session=True a la ruta. El controlador toma status o, si no está presente, collection_status, y registra la referencia y el estado mediante el logger estándar de Odoo.

Los estados approved, success, pending e in_process llevan a /shop/confirmation. Los demás estados vuelven a /shop.

Mostrar la página de confirmación para un pago pendiente o en proceso no significa que el dinero haya sido acreditado. La redirección del navegador no valida el pago: la transacción debe actualizarse con la notificación verificada de Mercado Pago.

**Archivo:** `return_controller.py` **Ubicación:** `payment_mercado_pago/controllers/`

```
# path: payment_mercado_pago/controllers/return_controller.py

import logging

from odoo import http

from odoo.http import request

_logger = logging.getLogger(__name__)

class MercadoPagoReturnController(http.Controller):

    # Agregamos save_session=True para asegurar que no se pierda el carrito/usuario

    @http.route('/payment/mp/return', type='http', auth='public', csrf=False, save_session=True)

    def mercadopago_return(self, **kwargs):

        """Captura el retorno del cliente desde Mercado Pago y redirige."""

        

        # 1. Obtenemos estado y referencia

        # MP a veces usa 'collection_status' en lugar de 'status'

        status = kwargs.get('status') or kwargs.get('collection_status')

        reference = kwargs.get('external_reference')

        # 2. Log estándar de Odoo (más rápido que escribir en ir.logging manualmente)

        _logger.info("MercadoPago Return: Ref=%s - Status=%s - Params=%s", reference, status, kwargs)

        # 3. Estados que muestran la confirmación del pedido

        

        confirmation_states = ['approved', 'success', 'pending', 'in_process']

        if status in confirmation_states:

            # Si es aprobado O pendiente (rapipago/tarjeta en revision), va a la Thank You Page

            return request.redirect('/shop/confirmation')

            

        # 4. Solo si falla realmente, volvemos al shop

        _logger.warning("MercadoPago Return no aprobado. Redirigiendo a shop. Status: %s", status)

        return request.redirect('/shop')
```

Y lo importé en el `__init__.py` correspondiente:

```
from . import main
from . import return_controller
```

Después de reiniciar el servicio de Odoo (`sudo systemctl restart odoo`), el flujo quedó funcional.

## Resultado

- El modal de Mercado Pago se abre correctamente.

- El usuario puede completar el pago sin errores.

- Al finalizar, los pagos aprobados, pendientes o en proceso se muestran en `/shop/confirmation`; los demás estados vuelven a `/shop`.

- Se evita el error 404 que ocurre cuando no hay controlador para la URL de retorno.

Si te sirve, genial. Si lo adaptás, mejor. Y si tenés dudas, sugerencias o querés compartir tu experiencia, los comentarios están abiertos. Me interesa que esto sea útil para otros técnicos que trabajen con Odoo y pasarelas de pago.

Tag de autor integrado al borde

Nota del autor

Contenido

    Soy **Sasha Herscovich**, y me dedico al diseño, desarrollo y soporte técnico de tiendas online en WordPress y Odoo. Trabajo todos los días resolviendo problemas reales, y cada tanto me gusta compartir lo que me toca enfrentar, por si a alguien más le sirve.
  

    En este blog vas a encontrar ideas, soluciones y fragmentos de código que uso en proyectos reales. Algunas cosas están pensadas para quienes quieren autogestionar su sitio, otras para desarrolladores o diseñadores que buscan mejorar lo que hacen, ahorrar tiempo o simplemente entender cómo resolver algo puntual.
  

    No vendo fórmulas mágicas ni soluciones universales. Solo comparto lo que me funciona, lo que pruebo y lo que aprendo en el camino. Si te sirve, bienvenido. Y si querés comentar, mejorar o adaptar algo, me encantaría que lo hagas.

Página canónica: https://tuwebmaster.com.ar/solucion-mercado-pago-odoo-18-return-url-fix/
