Saltar al contenido
Volver al blog

Solución Mercado Pago Odoo 18 – Return URL Fix

·

Odoo 18 con Mercado Pago como pasarela de pagos
Solución Mercado Pago Odoo 18 Return URL Fix

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: 🔗

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

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.

Dejá tu comentario

Tu comentario se revisará antes de publicarse. Tu email no se publica. Los campos con * son obligatorios.