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.
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.
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: 🔗
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.
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.
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.
Para evitar eso, creé un controlador personalizado en el módulo:
Archivo: return_controller.py Ubicación: payment_mercado_pago/controllers/
from odoo import http
from odoo.http import request
class MercadoPagoReturnController(http.Controller):
@http.route('/payment/mp/return', type='http', auth='public', csrf=False)
def mercadopago_return(self, **kwargs):
status = kwargs.get('status')
if status == 'approved':
return request.redirect('/shop/confirmation')
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.
/shop/confirmation o /shop según el estado del pago.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.
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.
Un comentario
Sasha
Dejo una actualización para la configuración de la url de retorno a la thankyou page.
Actualmente para que un usuario sea redirigido a la página de confirmación de su pedido, es necesario actualizar el Archivo: return_controller.py Ubicación: payment_mercado_pago/controllers/
Reemplazar directamente con:
# 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. LISTA DE ESTADOS EXITOSOS
success_states = [‘approved’, ‘success’, ‘pending’, ‘in_process’]
if status in success_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’)