DEV Community

loading...

¿Cómo integrar Mercado Pago a tu web?

nicolascastrogarcia profile image Nicolás Castro Garcia ・11 min read

La semana pasada, se me presentó un "examen" de certificación en el que tenía que integrar a una web el checkout de Mercado Pago (de acá en más MP, porque me voy a cansar de escribirlo).

Por suerte, ya había integrado MP en CourseIt (fue más un rework igual), por lo que tenía cierta experiencia.

Igualmente, ni bien me encontré con este desafío, tuve varios problemas, entre ellos que la documentación de mercado pago no es la más amigable, y me las tuve que rebuscar mucho para poder probar las cosas. (Si tienen problemas utilizando sandbox de MP avisen!)

Es por eso, que escribo este artículo, con la intención de ayudar a personas que tengan que hacer algo similar

El propósito de este artículo es que practicamente cualquier persona pueda seguir y entender su contenido, por lo que las explicaciónes intentan ser lo mas abarcativas posibles


Conocimientos Necesarios

Para poder seguir con facilidad este tutorial, van a necesitar lo siguiente:

  1. Conocimientos de Javascript
  2. Tener instalado node
  3. Saber usar el comando cd de la terminal
  4. Ganas de Aprender!
  5. Las API keys/tokens de MP, que las pueden encontrar acá

Menu


Lo que vamos a hacer

  1. Vamos a crear una API en Node, más puntualmente express.
  2. En dicha API, vamos a crear dos rutas, una para generar el link de pago, y otra para recibir las notificaciones de pago que MP nos mande (webhooks).
  3. Esas rutas van a utilizar un controller y un service. En los que vamos a tener que conectarnos con la API de MP.

Empecemos con el Set Up

  1. Lo primero que vamos a hacer es instalar express-generator, que nos va a permitir, en el próximo paso, generar una aplicación de express:

    $ npm install express-generator -g

  2. Lo segundo que vamos a hacer es crear una aplicación de express

    $ express --view=none MercadoPagoCheckout

    Con este comando lo que estamos haciendo es crear una API, que se llama MercadoPagoCheckout

  3. Una vez que estemos en la carpeta MercadoPagoCheckout (que se acaba de crear) ejecutamos:

    $ npm install

    Esto va a instalar los node_modules.
    Dependiendo de su computadora, puede tardar poco o mucho, asi que tengan paciencia.

  4. También tenemos que instalar axios, que lo vamos a utilizar para hacer los request a la API de MP

    $ npm install axios --save

Y con eso ya hicimos el setup básico de la API!


Creación de Rutas

Una vez que tengamos todo instalado, tenemos que abrir nuestro proyecto en nuestro editor de código preferido.

Dentro de los archivos, nos vamos a encontrar con uno que se llama app.js, que contiene lo siguiente:

var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');

var app = express();

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', indexRouter);
app.use('/users', usersRouter);

module.exports = app;
Enter fullscreen mode Exit fullscreen mode

Lo que tenemos que hacer a continuación es crear las rutas.

Tengan en cuenta que vamos a crear 2 rutas: una para generar el link de MP y otra para recibir las notificaciones(webhook) de MP

Vamos a eliminar algunas rutas que NO vamos a usar como:

app.use('/', indexRouter);
app.use('/users', usersRouter);
Enter fullscreen mode Exit fullscreen mode

Y agregar las rutas que SI vamos a usar que son:

app.post("/payment/new", (req, res) => 
  PaymentInstance.getMercadoPagoLink(req, res) 
);
Enter fullscreen mode Exit fullscreen mode

Donde vamos a llamar a la función getMercadoPagoLink() que va a estar en el controller que vamos a crear más adelante.

Esta función se va a ejecutar cuando se realice un request POST a la url localhost:3000/payment/new

app.post("/webhook", (req, res) => PaymentInstance.webhook(req, res));
Enter fullscreen mode Exit fullscreen mode

Esta línea lo que hace es muy similar a la linea anterior.

Cuando se reciba un request POST en la url localhost:3000/webhook se va a ejecutar la función webhook() que está dentro del controller (que todavía no creamos)

Por último, importé el Controller y el Service que vamos a estar creando en los próximos pasos:

const PaymentController = require("./controllers/PaymentController");
 //importamos el controller

const PaymentService = require("./services/PaymentService"); 
//importamos el service

const PaymentInstance = new PaymentController(new PaymentService()); 
// Permitimos que el controller pueda usar el service
Enter fullscreen mode Exit fullscreen mode

Entonces, nos tendría que quedar así:

var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

var app = express();

const PaymentController = require("./controllers/PaymentController");
 //importamos el controller

const PaymentService = require("./services/PaymentService"); 
//importamos el service

const PaymentInstance = new PaymentController(new PaymentService()); 
// Permitimos que el controller pueda usar el service

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.post("/payment/new", (req, res) => 
  PaymentInstance.getMercadoPagoLink(req, res) 
);

app.post("/webhook", (req, res) => PaymentInstance.webhook(req, res)); 

module.exports = app;
Enter fullscreen mode Exit fullscreen mode

Creación del Controller

Un controller, es una función o conjunto de funciones que nos van a permitir ordenar la información que recibamos de bases de datos, o en nuestro caso, datos de una API externa.

Lo primero que vamos a hacer es crear, dentro de una carpeta que se llame controllers (creala si es necesario), un nuevo archivo que se llame PaymentController.js , y adentro, vamos a crear una clase que se llame PaymentController.

Dentro de esa clase vamos a crear dos funciones, una que se llame getMercadoPagoLink(), y otra que se llame webhook().

class PaymentController {
  constructor(paymentService) {
    this.paymentService = paymentService; 
  }

  async getMercadoPagoLink(req, res) {

  }

  webhook(req, res) { 

  }
}

module.exports = PaymentController;
Enter fullscreen mode Exit fullscreen mode

La función getMercadoPagoLink(), va a llamar al service (que estamos importando desde el constructor) y ejecutar la función createPaymentMercadoPago() que va a recibir información del producto o servicio que queramos vender, como por ejemplo:
nombre, precio, cantidad

async getMercadoPagoLink(req, res) {
    const { name, price, unit, img } = req.body; 
    try {
      const checkout = await this.paymentService.createPaymentMercadoPago(
        name, // nombre del producto o servicio
        price, //precio del producto o servicio
        unit,  //cantidad que estamos vendiendo
        img  // imagen de referencia del producto o servicio
      );

      return res.redirect(checkout.init_point); 
     //si es exitoso los llevamos a la url de Mercado Pago

      return res.json({url: checkout.init_point})
     // o si queres devolver la url al front 


    } catch (err) { 
// si falla devolvemos un status 500

      return res.status(500).json({
        error: true,
        msg: "Hubo un error con Mercado Pago"
      });
    }
  }
Enter fullscreen mode Exit fullscreen mode

La función tiene que ser declarada con un async, ya que vamos a estar haciendo un await a la función del service.

También vamos a leer, del body del request, la información que nos va a llegar del frontend(name, price, unit, img).

Una vez resuelta PaymentService.createPaymentMercadoPago(), vamos a tener la url de pago que nos da MP.

La función webhook(), en cambio, debería verse así:

webhook(req, res) { 
    if (req.method === "POST") { 
      let body = ""; 
      req.on("data", chunk => {  
        body += chunk.toString();
      });
      req.on("end", () => {  
        console.log(body, "webhook response"); 
        res.end("ok");
      });
    }
    return res.status(200); 
  }
Enter fullscreen mode Exit fullscreen mode

En la función webhook(), vamos a verificar que el método de request sea POST, ya que es requisito para recibir la información que nos va a mandar MP.

Luego, declaramos una variable que se llama body, que vamos a sobre escribir a medida que el request se vaya resolviendo, porque la información nos va a llegar en chunks, y necesitamos llevarla a algo que sea legible (string).

En síntesis, nuestro PaymentController, debería quedar:

class PaymentController {
  constructor(paymentService) {
    this.paymentService = paymentService; 
  }

  async getMercadoPagoLink(req, res) {
    const { name, price, unit, img } = req.query; 
    try {
      const checkout = await this.paymentService.createPaymentMercadoPago(
        name, 
        price,
        unit, 
        img 
      );

      return res.redirect(checkout.init_point);  

    } catch (err) { 

      return res.status(500).json({
        error: true,
        msg: "Hubo un error con Mercado Pago"
      });
    }
  }

 webhook(req, res) { 
    if (req.method === "POST") { 
      let body = ""; 
      req.on("data", chunk => {  
        body += chunk.toString();
      });
      req.on("end", () => {  
        console.log(body, "webhook response"); 
        res.end("ok");
      });
    }
    return res.status(200); 
  }
}

module.exports = PaymentController;

Enter fullscreen mode Exit fullscreen mode

Creación del Service

Un service, es una función o conjunto de funciones que nos permiten ejecutar un query en nuestra base de datos, o conectarnos a una API externa.

Lo primero que vamos a hacer es crear, dentro de la carpeta services (creala si no existe), un nuevo archivo que se llame PaymentService.js , y adentro, vamos a crear una clase que se llame PaymentService.

Vamos a necesitar un constructor también, que es donde vamos a guardar las keys/tokens de MP.

Y por último, tenemos que importar axios, que es la herramienta que vamos a usar para conectarnos a la API de MP.

const axios = require("axios"); 

class PaymentService {
  constructor() {
    this.tokensMercadoPago = {
      prod: {},
      test: {
        access_token:
          "APP_USR-6317427424180639-042414-47e969706991d3a442922b0702a0da44-469485398" 
     // el access_token de MP
      }
    }; 
// declaramos de la siguiente manera el token
// para que sea más fácil cambiarlo dependiendo del ambiente
    this.mercadoPagoUrl = "https://api.mercadopago.com/checkout"; 
 // declaramos la url en el constructor para poder accederla a lo largo de toda la class
  }
}
Enter fullscreen mode Exit fullscreen mode

Dentro de esa clase, vamos a crear una sola función llamada CreatePaymentMercadoPago().

En esta función async, vamos a recibir las variables que le enviamos desde la función getMercadoPagoLink() que está en PaymentController.js.

También, vamos a declarar una variable llamada url que es la dirección de MP a donde vamos a ir a pedir la información mediante el método POST. Utilizamos las variables declaradas en el constructor para formarla.

  async createPaymentMercadoPago(name, price, unit, img) {  

    const url = `${this.mercadoPagoUrl}/preferences?access_token=${this.tokensMercadoPago.test.access_token}`; 

  }

Enter fullscreen mode Exit fullscreen mode

Para continuar, vamos a crear dentro de la función createPaymentMercadoPago() un array de objetos[{}] llamado items.

Este Array, va a contener la información de los productos o servicios que estamos vendiendo.

También vamos a utilizar las variables name, unit, y price que nos estan llegando de la función getMercadoPagoLink() que está en PaymentController.js


   const items = [
      {
        id: "1234", 
// id interno (del negocio) del item
        title: name, 
// nombre que viene de la prop que recibe del controller
        description: "Descripción del producto o servicio", 
// descripción del producto
        picture_url: "https://localhost:3000/static/product.png", 
// url de la imágen del producto, tiene que ser una url válida
        category_id: "1234", 
 // categoría interna del producto (del negocio)
        quantity: parseInt(unit), 
// cantidad que estamos vendiendo, que tiene que ser un intiger
        currency_id: "ARS",
 // id de la moneda, que tiene que ser en ISO 4217
        unit_price: parseFloat(price) 
// el precio, que por su complejidad tiene que ser tipo FLOAT
      }, {
// si queremos agregar otro item, pasamos la misma información acá
}
    ];  

Enter fullscreen mode Exit fullscreen mode

Ahora, vamos a declarar un objeto llamado preferences, que contiene las preferencias de pago de nuestro array items.

Todo esto lo estamos haciendo de acuerdo a la documentación de la API de MP que pueden encontrar acá

  const preferences = { 
      items, 
// el array de objetos, items que declaramos más arriba
      external_reference: "referencia del negocio", 
// referencia para identificar la preferenciaç
      payer: { 
// información del comprador, si estan en producción tienen que traerlos del request
//(al igual que hicimos con el precio del item) 
        name: "Lalo",
        surname: "Landa",
        email: "test_user_63274575@testuser.com", 
// si estan en sandbox, aca tienen que poner el email de SU usuario de prueba si estan 
//en producción, deberian completar esta información 
//de la misma manera que lo hicimos con items, units, y price

        phone: {
          area_code: "11",
          number: "22223333"
        },
        address: {
          zip_code: "1111",
          street_name: "False",
          street_number: "123"
        }
      }, 
      payment_methods: {
 // declaramos el método de pago y sus restricciones
        excluded_payment_methods: [
// aca podemos excluir metodos de pagos, tengan en cuenta que es un array de objetos
// donde el id de cada objeto es la exclusión
          {
            id: "amex"
// acá estamos excluyendo el uso de la tarjeta American Express
          }
        ],
        excluded_payment_types: [{ id: "atm" }],
 // aca podemos excluir TIPOS de pagos, es un array de objetos
// Por ejemplo, aca estamos excluyendo pago por cajero
        installments: 6, 
// mayor cantidad de cuotas permitidas
        default_installments: 6 
// la cantidad de cuotas que van a aparecer por defecto
      }, 
      back_urls: {
// declaramos las urls de redireccionamiento
        success: "https://localhost:3000/success", 
// url a la que va a redireccionar si sale todo bien
        pending: "https://localhost:3000.com/pending",
// url a la que va a redireccionar si decide pagar en efectivo por ejemplo
        failure: "https://localhost:3000.com/error"
 // url a la que va a redireccionar si falla el pago
      }, 
      notification_url: "https://localhost:3000/webhook", 
// declaramos nuestra url donde recibiremos las notificaciones
// es la misma ruta que declaramos en app.js
      auto_return: "approved" 
// si la compra es exitosa automaticamente redirige a "success" de back_urls
    };

//NOTA: TODAS las URLS que usemos tienen que ser reales,
// si prueban con localhost, va a fallar

Enter fullscreen mode Exit fullscreen mode

Por último, nos queda realizar el POST con axios:


      const request = await axios.post(url, preferences, { 
// hacemos el POST a la url que declaramos arriba, con las preferencias
        headers: { 
// y el header, que contiene content-Type
          "Content-Type": "application/json"
        }
      });

Enter fullscreen mode Exit fullscreen mode

El PaymentService.js les debería haber quedado:

const axios = require("axios"); 

class PaymentService {
  constructor() {
    this.tokensMercadoPago = {
      prod: {},
      test: {
        access_token:
          "APP_USR-6317427424180639-042414-47e969706991d3a442922b0702a0da44-469485398" 
// el access_token de MP
      }
    }; 
// declaramos de la siguiente manera el token, para que sea más fácil cambiarlo dependiendo del ambiente
    this.mercadoPagoUrl = "https://api.mercadopago.com/checkout"; 
 // declaramos la url en el constructor para poder accederla a lo largo de toda la clase
  }

  async createPaymentMercadoPago(name, price, unit, img) {  
// recibimos las props que le mandamos desde el PaymentController
    const url = `${this.mercadoPagoUrl}/preferences?access_token=${this.tokensMercadoPago.test.access_token}`; 
// url a la que vamos a hacer los requests

    const items = [
      {
        id: "1234", 
// id interno (del negocio) del item
        title: name, 
// nombre que viene de la prop que recibe del controller
        description: "Dispositivo movil de Tienda e-commerce",
 // descripción del producto
        picture_url: "https://courseit.com.ar/static/logo.png", 
// url de la imágen del producto
        category_id: "1234",  
// categoría interna del producto (del negocio)
        quantity: parseInt(unit), 
// cantidad, que tiene que ser un intiger
        currency_id: "ARS", 
// id de la moneda, que tiene que ser en ISO 4217
        unit_price: parseFloat(price)
 // el precio, que por su complejidad tiene que ser tipo FLOAT
      }
    ];  

    const preferences = { 
// declaramos las preferencias de pago
      items, 
// el array de objetos, items que declaramos más arriba
      external_reference: "referencia del negocio", 
// referencia para identificar la preferencia, puede ser practicamente cualquier valor
      payer: { 
// información del comprador, si estan en producción tienen que //traerlos del request
//(al igual que hicimos con el precio del item) 
        name: "Lalo",
        surname: "Landa",
        email: "test_user_63274575@testuser.com",
 // si estan en sandbox, aca tienen que poner el email de SU usuario de prueba
        phone: {
          area_code: "11",
          number: "22223333"
        },
        address: {
          zip_code: "1111",
          street_name: "False",
          street_number: "123"
        }
      }, 
      payment_methods: { 
// declaramos el método de pago y sus restricciones
        excluded_payment_methods: [ 
// aca podemos excluir metodos de pagos, tengan en cuenta que es un array de objetos
          {
            id: "amex"
          }
        ],
        excluded_payment_types: [{ id: "atm" }], 
// aca podemos excluir TIPOS de pagos, es un array de objetos
        installments: 6, 
// limite superior de cantidad de cuotas permitidas
        default_installments: 6 
// la cantidad de cuotas que van a aparecer por defecto
      }, 
      back_urls: {
// declaramos las urls de redireccionamiento
        success: "https://localhost:3000/success", 
// url que va a redireccionar si sale todo bien
        pending: "https://localhost:3000.com/pending", 
// url a la que va a redireccionar si decide pagar en efectivo por ejemplo
        failure: "https://localhost:3000.com/error" 
// url a la que va a redireccionar si falla el pago
      }, 
      notification_url: "https://mercadopago-checkout.herokuapp.com/webhook", 
// declaramos nuestra url donde recibiremos las notificaciones
      auto_return: "approved" 
// si la compra es exitosa automaticamente redirige a "success" de back_urls
    };

    try {
      const request = await axios.post(url, preferences, {
 // hacemos el POST a la url que declaramos arriba, con las preferencias
        headers: { 
// y el header, que contiene content-Type
          "Content-Type": "application/json"
        }
      });

      return request.data; 
// devolvemos la data que devuelve el POST
    } catch (e) {
      console.log(e); 
// mostramos error en caso de que falle el POST
    }
  }
}

//NOTA: TODAS las URLS que usemos tienen que ser reales, 
//si prueban con localhost, va a fallar

module.exports = PaymentService;
Enter fullscreen mode Exit fullscreen mode

Y eso es todo del lado del back. Para terminar con el ciclo, desde el front, deberían realizar un POST request a /payment/new y eso les va a devolver un link, que es el del pago.

Recuerden que el POST ejecutado desde el front tiene que tener un body que contenga (por lo menos), name, unit,img, y price.


Espero que este tutorial les haya servido como guía para iniciarse en este tipo de integraciones o para resolver dudas!

Toda la documentación de Mercado Pago, y lo que use para crear este código, está acá

Para crear sus usuarios de prueba consulten acá

Me pueden mandar sus consultas o mejoras del código (porque hay mil cosas para mejorar) a mi twitter @ncastrogarcia

Si te sirvió me podes comprar un cafecito

Muchas gracias por tu tiempo!

Discussion (55)

pic
Editor guide
Collapse
rogemita profile image
Roger

Hola Nicolás, muy buena toda la información que compartiste y ademas de las respuestas que son muy completas!, te cuento que estuve renegando hasta recién con la primer experiencia de integración con MP y por suerte buscando sobre webhooks me encontré con este valioso post que hiciste, mil gracias por eso.
Si no es molestia quisiera dejarte aquí unas preguntas:

1 - Si podes darme alguna sugerencia para probar los webhooks en la etapa sandbox. Es decir, puedo crear el endpoint que me va a servir para atender los webhooks, pero como estoy en mi localhost, me gustaría saber si me das una sugerencia para probar, no se si usar alguna app que me permita hacer un tunel de mi local para la internet y usar ese endpoint cuando envio notification_url en mi "preferencia" de MP. Y sabrás alguna documentación concreta de los schemas de información que llegan al endpoint del webhook?

2 - Sabrás que diferencia habría entre webhooks y ipn y cual conviene usar?

3 - Hablando de la lógica de negocio, debería yo crear una orden pendiente cuando creo una "preference" y enviarle en el atributo "external_reference" el "id" de mi orden (db), para luego cuando llegue la notificación de webhook poder ir a buscarla a la MP API con ese mismo ID que guardé en "external_reference" y así ver el estado de pago? (eso suponiendo que me va a llegar el "external_reference" cuando me llegue una notificación de webhook si no mal entiendo, claro. De no ser así, que "referencia" me recomiendas guardar en la orden "pendiente" de mi sistema para luego utilizar para ir a buscar la información concreata del estado del pago?. De ser así y crear la orden al momento que genero el "preference" para el checkout, supongo que de alguna manera hay que luego limpiar las ordenes viejas que la gente no pagó y ya vencieron, o bien porque abrieron el checkout y se fueron o por cualquier otro motivo?

4 - Probaste el "modo binario" que ofrece MP alguna vez? De ser así, que experiencia tuviste con esto?

Millón de gracias, espero no molestar con las preguntas, gracias.

Collapse
nicolascastrogarcia profile image
Nicolás Castro Garcia Author

Hola Roger!

Te contesto por partes:

  1. Para probar los webhooks en "sandbox" tenes que seguir los siguientes pasos:

    • Tenes que crear ambos usuarios de prueba, el comprador y el vendedor.
    • Usar las keys de tu usuario vendedor para generar toda la configuración del webhook y de la preferencia.
    • Comprar con la cuenta de tu usuario comprador (de prueba) pero no desde el link que devuelve init_point de sandbox, si no desde el init_point de producción directamente.
    • Con tu cuenta de usuario vendedor (de prueba), logueate a producción de mercadopago y ahí deberías ver la compra instantanea (proba el pago automático con unas de las tarjetas que están acá, teniendo en cuenta que el código de todas esas tarjetas son 123) Y ya con eso se debería ver tu notificación webhook
  2. Lejos de ser un experto en este tema, los webhooks se sienten más rápidos con una respuesta más limpia, en síntesis, más modernos. Estuve buscando un poco, y lo único que encontré es algo relacionado a paypal, en stackoverflow

  3. El external_reference es tal cual como lo estas diciendo. Igual te recomiendo que veas la respuesta que le hice a Luis Pastén en este mismo post, donde discutimos un poco de como viene la información de los webhooks y dejo un par de ejemplos. Igualmente te dejo un repositorio en github funcionando de toda esta ingregación (se supone que sigue viva esas keys, de última levantame un issue que lo reviso)

  4. Nunca lo probé el modo binario!

Gracias a vos por preguntar!

Si te quedaste con alguna otra duda, sentite libre de preguntar!

Saludos!

Collapse
rogemita profile image
Roger

Mil gracias Nico, gracias por tus respuestas. Genial, lo de webhook en realidad te preguntaba como emularlo para que lleguen las notificaciones a mi maquina local, y se me ocurría usar alguna app que haga de tunel para afuera y pasarle esa url a las notificaciones que le mando en la preferencia de pago. No se que hiciste vos para probar los handlers de los webhooks o si tenes alguna recomendación al respecto, gracias Nico!

Thread Thread
nicolascastrogarcia profile image
Nicolás Castro Garcia Author

Roger!

Perdón que respondi a medias jaja

Para hostear usé heroku, y eso me permitió ver las notificaciones y otros detalles, en un proyecto simil vivo.

Collapse
yordymora98 profile image
Yordy Mora

Hola Nicolás, primero que nada muy buen tutorial, muy bien explicado!
Quisiera preguntarte, yo estoy desarrollando en Angular, cabe la posibilidad de poder modificar el código que da mercadopago developers, o es necesario que este sea en Node.
Gracias nuevamente por la guía!

Collapse
nicolascastrogarcia profile image
Nicolás Castro Garcia Author

Hola Yordy! gracias por leer!

La verdad es que nunca trabajé con Angular. No lo manejo. Desconozco si podes hacer back + front integrado, como con React + Nextjs o con Handlebars + node.

La realidad es que algo de backend vas a necesitar por las herramientas que brinda (manejo de requests y responses por ejemplo).

Saludos!

Collapse
yordymora98 profile image
Yordy Mora

Muchas Gracias Nicolás por tomarte el tiempo en poder responderme.
Y si, eso mismo estaba pensando, ya que sería re escribir código, pero tratando de implementarle la lógica y funcionalidades que tu planteas.
Agradezco la idea que planteas con React + Nextjs, espero que tengas buen día, y te agradezco nuevamente la guía.
¡Un saludo!

Collapse
sebalr profile image
Sebastian Larrieu

Hola Nicolás, te hago una consulta. Éstoy intentando hacer el checkout en modo modal en una web. Para eso hago una llamada ajax para obtener el id de preferencia pero por más que ponga el form con el script nunca aparece un boton ni nada que se le parezca (ni siquiera veo que se descargue el script .js que está dentro del form).
Tenés idea que pueda ser?
Gracias

Collapse
nicolascastrogarcia profile image
Nicolás Castro Garcia Author

Asumo que estás usando algo similar a esto en tu front

<form action="/procesar-pago" method="POST">
  <script
   src="https://www.mercadopago.com.ar/integrations/v1/web-payment-checkout.js"
   data-preference-id="$$id$$">
  </script>
</form>
Enter fullscreen mode Exit fullscreen mode

Lo importante es el valor de data-preference-id, que dependiendo el lenguaje que estes usando en el back, le tenes que pasar el id de una manera distinta, fijate acá en el paso 2.

También, fijate de no estar tratando de comprar con la misma cuenta que generó la preferencia de pago (asumo que la preferencia fue creada satisfactoriamente).

Por último si ves todo bien, y tenes armado bien tu circuito, podes mandar un ticket al soporte en este link. Responden relativamente rápido.

Espero que te sirva!

Saludos!

Collapse
sebalr profile image
Sebastian Larrieu

Gracias por la respuesta. Exactamente ese código estoy usando, pero el id lo estaba obteniendo via api en un proyecto de backend separado del de frontend (la generación anda joya). Asumo que esto no se puede, voy a probar con el link en lugar del modal o el web tokenizer

Collapse
sebalr profile image
Sebastian Larrieu

Para futuras referencias. El problema es Angular que no acepta tags script en los templates

Collapse
luispastendev profile image
Luis Pastén

Hola Nicolás buen tutorial, tengo una duda estoy trabajando justo ahora con la pasarela de MP, ya casi estoy por terminar, mi pregunta va con respecto a los webhooks ya que estoy probando con el modo sandbox, cuando se realizan pagos por oxxo o transferencia bancaria MP notifica cuando se liquida la orden? estaba leyendo la documentación y quiero entender que se recibe un json en el webhook con el action "payment.updated", estube checando el modo sandbox pero las ordenes se quedan como pendientes no tengo forma de que mp emita un payment.updated para simular el pago de una orden.

ya tienes tu aplicación en producción¡? como debo procesar las notificaciones que me llegan para tener sincronizada la base de datos de mi plataforma con el status de las ordenes de mp?

Muchas gracias te mando un saludo desde mex.

Collapse
nicolascastrogarcia profile image
Nicolás Castro Garcia Author • Edited

Hola Luis!

Te respondo por partes.
Primero que nada, es tal cual vos decis. Los pagos en efectivo (oxxo por ejemplo) se crean con un status pending. Es decir que al momento de generar el link, deberías plantear dentro de la preferencia del link, una back_urls.pending para que el usuario sepa que el pago todavía no fue realizado.

Segundo, vas a recibir 3 notificaciones de webhook. Una que se va a ver algo así:

{"action":"payment.created",
"api_version":"v1",
"data":{"id":"7125503101"},
"date_created":"2020-06-17T13:49:17Z",
"id":5990968261,
"live_mode":true,
"type":"payment",
"user_id":"469485398"}

luego te debería llegar esta notificación con topic: payment y resource:

    "id": 7125503101,
    "site_id": "MLA",
    "date_created": "2020-06-17T09:49:17.000-04:00",
    "date_approved": null,
    "money_release_date": null,
    "last_modified": "2020-06-17T09:49:17.000-04:00",
    "payer": {},
    "order_id": "1534217575",
    "external_reference": "referencia_externa",
    "merchant_order_id": "1534217575",
    "reason": "iPhone 8",
    "currency_id": "ARS",
    "transaction_amount": 600,
    "net_received_amount": 0,
    "total_paid_amount": 600,
    "shipping_cost": 0,
    "coupon_amount": 0,
    "coupon_fee": 0,
    "finance_fee": 0,
    "discount_fee": 0,
    "coupon_id": null,
    "status": "pending",
    "status_detail": "pending_waiting_payment",
    "issuer_id": null,
    "installment_amount": 0,
    "deferred_period": null,
    "payment_type": "ticket",
    "payment_method_id": "pagofacil", 
//aca te debería aparecer oxxo si lo seleccionaste
    "marketplace": "NONE",
    "operation_type": "regular_payment",
    "transaction_order_id": null,
    "statement_descriptor": null,
    "cardholder": null,
    "authorization_code": null,
    "marketplace_fee": 0,
    "deduction_schema": null,
    "refunds": [
    ],
    "amount_refunded": 0,
    "last_modified_by_admin": null,
    "api_version": "2",
    "concept_id": null,
    "concept_amount": 0,
    "collector": {}

y por último con topic: merchant_order:

{
  "id": 1534217575,
  "status": "closed",
  "external_reference": "referencia_externa",
  "preference_id": "469485398-1303f910-88c4-4fb1-ab09-6336e4b7e4c7",
  "payments": [
    {
      "id": 7125503101,
      "transaction_amount": 600,
      "total_paid_amount": 600,
      "shipping_cost": 0,
      "currency_id": "ARS",
      "status": "pending",
      "status_detail": "pending_waiting_payment",
      "operation_type": "regular_payment",
      "date_approved": "0001-01-01T00:00:00.000+00:00",
      "date_created": "2020-06-17T09:49:17.000-04:00",
      "last_modified": "2020-06-17T09:49:17.000-04:00",
      "amount_refunded": 0
    }
  ],
  "shipments": [
  ],
  "payouts": [
  ],
  "collector": {
    "id": 469485398,
    "email": "test_user_97555375@testuser.com",
    "nickname": "TETE2587273"
  },
  "marketplace": "NONE",
  "notification_url": "https://localhost:3000/webhook",
  "date_created": "2020-06-17T13:49:16.289+00:00",
  "last_updated": "2020-06-17T13:49:17.923+00:00",
  "sponsor_id": null,
  "shipping_cost": 0,
  "total_amount": 600,
  "site_id": "MLA",
  "paid_amount": 600,
  "refunded_amount": 0,
  "payer": {},
  "items": [
    {}
  ],
  "cancelled": false,
  "additional_info": "",
  "application_id": null,
  "order_status": "payment_in_process"
}

Y a partir de acá si sale todo bien, vas a volver a recibir 3 notificaciónes webhook:
Una con action: payment.updated:

{"action":"payment.updated", 
//nos dice que el pago con el id de data.id se actualizó
"api_version":"v1",
"data":{"id":"7125503101"},
"date_created":"2020-06-17T13:49:17Z",
"id":5990994133,
"live_mode":true,
"type":"payment",
"user_id":"469485398"
}

Una con topic: payment que va a recibir lo mismo que antes pero el status va a decir: approved

{
  "collection": {
    "id": 7125503101,
//este id fijate que es el mismo que el de la notificación anterior
    "site_id": "MLA",
    "date_created": "2020-06-17T09:49:17.000-04:00",
    "date_approved": null,
    "money_release_date": null,
    "last_modified": "2020-06-17T09:58:44.000-04:00",
    "payer": {},
    "order_id": "1534217575",
    "external_reference": "referencia_externa",
    "merchant_order_id": "1534217575",
    "reason": "iPhone 8",
    "currency_id": "ARS",
    "transaction_amount": 600,
    "net_received_amount": 0,
    "total_paid_amount": 600,
    "shipping_cost": 0,
    "coupon_amount": 0,
    "coupon_fee": 0,
    "finance_fee": 0,
    "discount_fee": 0,
    "coupon_id": null,
    "status": "approved",
//acá es donde vemos si el usuario pagó o no
    "status_detail": "by_payer",
    "issuer_id": null,
    "installment_amount": 0,
    "deferred_period": null,
    "payment_type": "ticket",
    "payment_method_id": "pagofacil",
// aca el método que usó para pagar en tu caso sería oxxo
    "marketplace": "NONE",
    "operation_type": "regular_payment",
    "transaction_order_id": null,
    "statement_descriptor": null,
    "cardholder": null,
    "authorization_code": null,
    "marketplace_fee": 0,
    "deduction_schema": null,
    "refunds": [
    ],
    "amount_refunded": 0,
    "last_modified_by_admin": null,
    "api_version": "2",
    "concept_id": null,
    "concept_amount": 0,
    "collector": {}
  }
}

Una con topic: merchant_order que va a recibir exactamento lo mismo que antes, pero payments[0].status se va a haber actualizado a approved y order_status a paid

{
  "id": 1534217575,
  "status": "closed",
  "external_reference": "referencia_externa",
  "preference_id": "469485398-1303f910-88c4-4fb1-ab09-6336e4b7e4c7",
  "payments": [
    {
      "id": 7125503101,
// este id es el del pago, de la notificación anterior
      "transaction_amount": 600,
      "total_paid_amount": 600,
      "shipping_cost": 0,
      "currency_id": "ARS",
      "status": "approved",
//acá confirma que pagó
      "status_detail": "by_payer",
      "operation_type": "regular_payment",
      "date_approved": "0001-01-01T00:00:00.000+00:00",
      "date_created": "2020-06-17T09:49:17.000-04:00",
      "last_modified": "2020-06-17T09:58:44.000-04:00",
      "amount_refunded": 0
    }
  ],
  "shipments": [
  ],
  "payouts": [
  ],
  "collector": {},
  "marketplace": "NONE",
  "notification_url": "https://mercadopago-checkout.herokuapp.com/webhook",
  "date_created": "2020-06-17T13:49:16.289+00:00",
  "last_updated": "2020-06-17T13:58:46.588+00:00",
  "sponsor_id": null,
  "shipping_cost": 0,
  "total_amount": 600,
  "site_id": "MLA",
  "paid_amount": 600,
  "refunded_amount": 0,
  "payer": {},
  "items": [],
  "cancelled": false,
  "additional_info": "",
  "application_id": null,
  "order_status": "paid"
//acá se actualizó porque pagó
}

En síntesis, la manera correcta de hacer el track a lo largo de toda la aplicación es con el merchant_order. El payment.id es una propiedad del objeto merchant_order.

Recordá que dentro de merchant_order, podes tener más de un método de pago, es decir, el usuario puede haber modificado el método de pago desde la aplicación y en ese caso, el objeto merchant_order cambiaría, teniendo dentro del array payments, algo similar a esto:

"payments": [
    {
      "id": 7125503101,
      "transaction_amount": 600,
      "total_paid_amount": 600,
      "shipping_cost": 0,
      "currency_id": "ARS",
      "status": "cancelled",
 //acá canceló el pago en efectivo
      "status_detail": "by_payer",
      "operation_type": "regular_payment",
      "date_approved": "0001-01-01T00:00:00.000+00:00",
      "date_created": "2020-06-17T09:49:17.000-04:00",
      "last_modified": "2020-06-17T09:58:44.000-04:00",
      "amount_refunded": 0
    },
    {
      "id": 7125727198,
      "transaction_amount": 600,
      "total_paid_amount": 600,
      "shipping_cost": 0,
      "currency_id": "ARS",
      "status": "approved",
// y acá pagó con otro método, que para saberlo, 
//tenemos que pegarle a     
// https://api.mercadopago.com/v1/payments/[ID]?access_token=[ACCESS_TOKEN]
// donde id es el id del payment (7125727198 en nuestro ejemplo)
// y ACCESS_TOKEN es el token de acceso de MP
      "status_detail": "accredited",
      "operation_type": "regular_payment",
      "date_approved": "2020-06-17T09:58:46.000-04:00",
      "date_created": "2020-06-17T09:58:45.000-04:00",
      "last_modified": "2020-06-17T09:58:46.000-04:00",
      "amount_refunded": 0
    }
  ],

Recordá que para hacer las pruebas en sandbox tenes que:
1- Utilizar el init_point para el link de pago y no el sandbox_init_point
2- Podes ingresar a la página de MP con tu usuario de test comprador, y ver el detalle de las transacciones directamente desde ahi
3- Podes cambiar el método de pago con tu usuario de test comprador, desde la página de MP

Te dejo por acá la documentación de los webhooks

Espero que te sea útil!

Collapse
luispastendev profile image
Luis Pastén • Edited

Gracias por contestar Nicolás, ahora me queda un poco mas claro, en este caso en producción llegan 3 notificaciones post al webhook?, actualmente estoy trabajando con php y estoy usando la sdk que proporcionan la cuestión es que en modo sandbox solo me esta llegando una notificación con la siguiente estructura:

{
    "action": "payment.created",
    "api_version": "v1",
    "data": {
        "id": "26774573"
    },
    "date_created": "2020-06-17T00:36:16Z",
    "id": 5988776238,
    "live_mode": false,
    "type": "payment",
    "user_id": "583365872"
}

En este caso lo que yo hago es utilizar el sdk que me imagino que internamente se conecta con la api de MP para extraer información extra de la orden como: (medio con el cual pago, monto etc.. etc..)

$body = @file_get_contents('php://input');
$data = json_decode($body);
$payment = \MercadoPago\Payment::find_by_id($data->data->id);
// TOMO LOS DATOS NECESARIOS PARA ACTUALIZAR MI ORDEN EN MI BASE DE DATOS... 

Te comparto mi estrategia haber que te parece

1-. cliente entra al checkout y realiza el submit
2-. genero la preferencia con los datos que me proporciono y los montos a cobrar
3-. genero una id única aleatoria para guardarla como folio de orden en mi base de datos la misma que pongo en la preferencia external_reference
4-. el cliente entra a mp y paga por el medio que quiera
5-. una ves concluido su pago puede haber 2 escenarios

5.1 PAGO CON TARJETA

  • capturo la el collection_id que me llega por get con la back_url de success y ejecuto de nuevo \MercadoPago\Payment::find_by_id() para buscar los datos de la orden
  • actualizo mi base de datos con algunos datos que me regresa y el status

5.2 PAGO CON TRANSFERENCIA O TICKET (OXXO 7VEN ETC)

  • genero una thankyou page con status pendiente he instrucciones postcompra
  • una ves pago el cliente me llega al webhook una notificación payment.updated en la cual también utilizo el id para buscar con \MercadoPago\Payment::find_by_id() y usar los datos para actualizar mi base de datos

A grandes rasgos esa es mi estrategia pero aún no subo mi plataforma a producción espero que todo se comporte de la misma manera aunque me deja un poco inquieto el saber que llegan 3 notificaciones cuando se recibe un pago por eso mismo te preguntaba si tu ya lo habías probado en live mode.

Nuevamente gracias por contestar y te envío un saludo!!

Thread Thread
nicolascastrogarcia profile image
Nicolás Castro Garcia Author • Edited

Debes estar recibiendo una sola notificación porque nunca se paga el link de sandbox, fijate de pagarlo con una cuenta de test.

Siempre podes filtrar las notificaciones, de las 3 que llegan son:
1- Una que es type: payment.created (se creo el link)
2- Una que es topic: payment (se creo el pago)
3- Una que es topic merchant_order (se creo la transacción)

Luego, una vez que la persona paga:

1- type: payment.updated (pagó)
2- topic: payment (se updatea la información del pago)
3- topic: merchant_order(se updateo la información de la transacción)

Después, en cuanto tu estratégia, la veo muy sólida. El único caso te quedaría para probar, es si la persona decide cambiar el método de pago desde la aplicación de Mercado Pago. Esto lo digo porque si sucede, es probable que se te cambie el payment_id que viene en la notificación del payment updated.

Thread Thread
luispastendev profile image
Luis Pastén • Edited

Muchas gracias Nico, dejo pendiente a ver como me va en las próximas semanas que subo el proyecto al servidor y te comento que tal me fue.

Gracias por el post, realmente de mucha ayuda para integrar ese medio de pago. Saludos!!

Collapse
fernando7390 profile image
Fernando7390

Hola Nicolas, recien me estoy incorporando a este mundo del diseño web. Te molesto por que vi que hiciste la certificación de MP. Yo trate de hacerla pero no pude, vi que estoy muy fuera de lo que piden, hago integraciones simples pero lo que piden no se como hacerlo, como ser:
Preferencia de pago *
Pega aquí el código de la preferencia de pago que creaste vía API, por ejemplo: 82632574-d1742963-3133-46af-aa25-5c3497f3d3b4

o

¿Recibiste la notificación de pago? indícanos el payment_id *
El payment_id es el ID del pago, por ejemplo: 645434222

y

JSON válido de la notificación Webhook *

Trabajo con PHP, quisiera saber si existe la posibilidad que me des un tutorial pago de como hacer lo que me piden. Como verás estoy bien mal, y encima lo que explican en MP no es muy amigable, no explican bien como hacerlo. Si me puedes dar una ayuda te lo agradecería, y como dije más arriba sería paga. Gracias y disculpa las molestias.

Collapse
nicolascastrogarcia profile image
Nicolás Castro Garcia Author

Hola Fer!

No te sientas desanimado, no es una integración sencilla, en lo absoluto. Y la realidad es que la documentación deja mucho que desear.

Paso a explicar qué es puntualmente lo que te piden (que tampoco es muy claro si no hiciste la integración previamente):

1- Código de la preferencia de pago

Una de las primeras cosas que hay que hacer para realizar la integración con MP es la creación de una preferencia de pago. Esto vendría a ser en otras palabras, "pegarle" al endpoint de la api de mercado pago: api.mercadopago.com/checkout/prefe...

Esta preferencia de pago es lo que contiene toda la información de la venta en sí, por ejemplo:
Nombre, apellido, email, y dirección del comprador, items comprados, información del vendedor, metodos de pagos disponibles, entre otros.

Puntualmente en mi tutorial es lo que hacemos en el "PaymentService.js" en la función "createPaymentMercadoPago()"

Una ves que se hace un POST request a esa url con todos los datos necesarios, si todo salió bien, Mercado Pago nos va a devolver un JSON (javascript object notation), que va a contener un campo que es ID.

Ese campo id es lo que tenemos que entregar cuando nos piden el "código de la preferencia de pago"

2- payment_id

Una vez que tenemos la preferencia de pago creada, también vamos a tener el link de pago. Entonces lo que tenemos que hacer es, con una cuenta de prueba compradora (te la dan en el examen), pagar ese link que generamos con la cuenta vendedora de prueba (también te la dan en el examen).

Una vez que se pague la transacción (venta), mercadopago nos va a mandar una notificación webhook (en realidad nos va a mandar varias). En una de esas notificaciones, te va a llegar algo similar a esto:

{
"action":"payment.created",
"api_version":"v1",
"data":{"id":"7125503101"},
"date_created":"2020-06-17T13:49:17Z",
"id":5990968261,
"live_mode":true,
"type":"payment",
"user_id":"469485398"
}

Esto que estoy mostrando es un JSON, puntualmente el JSON válido de la notificación WEBHOOK que te piden. Pero lo importante es el id 7125503101, que es el ID de la notificación de pago que te piden.

Ahora bien, desconozco tus conocimientos previos, y experiencia. Pero independientemente de eso, algo que quiero que sepas y que es de suma importancia es que: NO ES UNA INTEGRACIÓN FÁCIL. No te tenes que sentir mal por no lograrlo, cuesta MUCHO hacer que funcione como uno quiere, si siquiera funciona.

En este momento no estoy dando clases. Pero te ofrezco que cualquier duda que tengas, o ayuda que necesites con mercado pago u otro tema relacionado a la programación me la digas por twitter en @ncastrogarcia que voy a estar más que dispuesto a ayudarte.

Abrazo!

Collapse
tonqac profile image
Gastón Giménez

Hola Nico, estuve leyendo atentamente tu post y sos un crack! Sabes que estoy intentando completar la certificación y lo único que me falta es mandar el JSON que devuelve la notificación webhook, pero no estoy pudiendo capturar esos datos. Sólo recibo algo parecido a esto:

{"data_id":"10463481227","source_news":"webhooks","type":"payment"}

¿Me podrás orientar? Graciass!!

Thread Thread
nicolascastrogarcia profile image
Nicolás Castro Garcia Author

Lo cierto es que no recuerdo muy bien qué era lo que pedian. Si mal no recuerdo con lo que me pasaste en este comentario deberia ser suficiente. Aunque siempre podes pegarle al endpoint de payments con el id que tenes en data_id para ver si está todo ok.

Saludos!

Collapse
uri99 profile image
urimihura

Nicoo gran tutorial, pero me quedaron 2 dudas:
1) dice que habría que hacerle un request POST al localhost:3000/payment/new , mi consulta es como haces para levantar todo el 'servicio' al localhost:3000?

2) y como se vería el post request? sería algo tipo así?:
axios.post("localhost:3000/payment/new" , {
name: "nombre",
price: 100,
unit: 1,
img: "imagen",
})
y despues el response sería la url del item publicado? cosa que podría hacer nose .then((response) => {console.log(response)}) para chusmearla nomás por ahora

y una tercera duda de yapa:
si quiero que el access token de la persona que va a recibir la plata sea una variable, mas que que esté hardcodeada, podría meterle una funcion que sea tipo setAccessToken(newAccessToken) que pueda ser llamada desde el front? (porque tengo entendido que en el tutorial está hardcodeado el access token en PaymentService.js)
Esto es porque en el programa que estoy haciendo, el dueño de la pagina no es el que recibe la plata, sino el usuario actual. Cosa que habria que pedirle al usuario acutal su access token (ahí setearía el access token), y que luego el link generado sea tal que para que él reciba la plata, no el dueño de la página.

desde ya muchisimas gracias!

Collapse
nicolascastrogarcia profile image
Nicolás Castro Garcia Author

Hola!

1- Eso es porque estoy corriendo un servidor de nodejs en mi computadora local.
2- Lo del post es correcto, la response sería la url de pago del item.
3- Te tenes que meter con marketplace de mercadopago, es parecido a esto con un poquito más de dificultad porque tenes que configurar más cosas. El marketplace permite que haya muchos vendedores y muchos compradores, y vos como dueño del marketplace te puedas llevar una comision (fee) por cada transacción.

Saludos!

Collapse
uri99 profile image
urimihura

Genial, y otra consulta a ver si me podrías dar una mano:
Levanté el servidor pero cuando le hago una request me dice que falta el CORS header "Access-Control-Allow-Origin". Leí bastante del tema y entendí lo que significa, y entendí la solucion tambien, hay que agregarle unas cosas al header de la respuesta que le devuelve en la solicitud preliminar que le hace el navegador a la api. Mi pregunta es, sabes donde está esta solicitud preliminar? la busqué por todos lados pero no la pude encontrar.
Gracias!

Thread Thread
nicolascastrogarcia profile image
Nicolás Castro Garcia Author

Vas a tener que isntalar un paquete de npm que se llama cors:
y extendiendo desde tu servidor:
app.use(
require("cors")({
origin: function (origin, callback) {
callback(null, origin);
},
credentials: true
})
);

Thread Thread
uri99 profile image
urimihura

Nico ahí lo instalé y metí eso en alguna parte del servidor, pero me sigue tirando el error de antes. Que significa "extendiendo desde tu servidor"? Donde tendría que poner eso que me estas pasando??
y como siempre muchas gracias por tu ayuda

Thread Thread
uri99 profile image
urimihura

lo metí en app.js y funcionó, pero ahora me tira: Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at mercadopago.com.ar/checkout/v1/red.... (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).

osea parece ser que está habiendo una falla de comunicación entre la api y mercadopago, ya no mas entre mi pagina y la api.

y ese link que devuelve (que está adentro del error) parece ser la respuesta que quiero, pero siempre que lo trato de abrir me dice "error: no puedes pagarte a vos mismo". Traté desde incognito, otro navegador, el celular y la computadora de 3 amigos, y siempre tira lo mismo. Lo raro es que a uno de esos 3 amigos si le abre bien el coso para pagar, con el nombre y precio apropiado

Sabes que podría ser? Desde ya sabelo que cuando termine te voy a estar comprando unos cafecitos por tu trabajo jajaja

Collapse
andresgiacosa profile image
Andres

Hola Nicolás, excelente tu publicación!!

Mientras leía me surgió una duda, todos los pagos que se darían en tu página de ejemplo son a la misma cuenta de MP? Es decir, todo lo que se pague se acreditaría en la cuenta de MP registrada con ese access_token?

Te pregunto esto porque me interesa saber si hay una forma de redirigir el pago directamente a una cuenta vendedor específica para que en mi aplicación yo no sea intermediario de la transacción. Por ejemplo, si tengo una pagina donde personas ofrecen servicios (vendedores) y clientes contratan (compradores) un servicio elegido, que el checkout se haga a la cuenta de MP de ese vendedor seleccionado. En este caso tendría que tener el access_token de cada vendedor?

Desde ya muchas gracias y nuevamente excelente todo!

Collapse
nicolascastrogarcia profile image
Nicolás Castro Garcia Author

Hola, tenes que implementar un marketplace mercadopago.com.ar/developers/es/g...

Podes cobrar hasta un fee por cada transacción. Saludos!

Collapse
dannter profile image
Daniel Es

Excelente material Nicolás! muchas gracias!. Tengo una pregunta, espero me puedas ayudar ya que no encuentro referencia en ninguna parte. Sabes cómo activar el estilo para mobiles que aparece en la documentación mercadopago.com.mx/developers/en/g...

Quiero integrar el web checkout dentro de un chatbot en messenger pero no encuentro como hacer para que se adapte el estilo a mobile, como se muestra en la documentación del link, en la sección de Header.
Saludos!

Collapse
nicolascastrogarcia profile image
Nicolás Castro Garcia Author

Hola Daniel,

si estas usando webtokenize, si es así, no deberías tener problemas en personalizar ciertas cosas

Si no estás usando eso, pasame más información de lo que estas tratando de hacer que vemos como solucionarlo.

Saludos!

Collapse
dannter profile image
Daniel Es

Hola Nicolás, si efectivamente es ese el que estoy usando y lo implemento dentro de un webview de messenger developers.facebook.com/docs/messe.... Estoy pensando que tal vez se implementa internamente dentro de un iFrame y por eso no detecta que es mobile, o aún así debería verse la versión mobile? tu que opinas, saludos!

Collapse
sara1297 profile image
sara1297

Hola Nicolás, estoy integrando mercado pago con next, sin embargo no me aparece el botón para realizar el pago, aunque si lo incluyo en el head aparece al final de mi página, pero no donde yo lo quisiera colocar, me podrías colaborar por favor? Quedo atenta muchísimas gracias

Collapse
susegroj profile image
El Rojo ● • Edited

¿Hay alguien que haya implementado el checkout pro de node js en alguna app de react?
Intento hacerlo con gatsby (parecido a nextjs) pero no se cómo controlar la acción del botón que genera el script que se agrega

Collapse
nicolascastrogarcia profile image
Nicolás Castro Garcia Author

Hola! Si te referis a implementarlo en el front (con este tipo de checkout), la unica manera es con Nexjs, que nos va a permitir tener el back y front juntos. Si no, tenes que hacer un backend y desde tu front en React, hacer el request

Collapse
drahdavid profile image
drahdavid • Edited

Hola como estás Nicolás , antes que nada mil gracias por tu tiempo y por la novedad en el tema que publicás, no hay casi material al respecto. Quería hacerte algunas consultas básicas, ya que no hace mucho que estoy en esto, y hasta lo más básico me resulta un poco confuso. Quería saber como levanto el servidor para realizar las peticiones desde postman. Vos utilizás el live server de Visual Studio, o algo como Xammp ?

Y en caso de levantar el server hacia que URL realizo el Post : Actualmente estoy intentando con localhost:3000/payment/new

Intengo iniciar el servidor :

[nodemon] 2.0.4
[nodemon] to restart at any time, enter rs
[nodemon] watching path(s): .
[nodemon] watching extensions: js,mjs,json
[nodemon] starting node ./bin/www

Agradecería si me orientas . Mil gracias de nuevo.

Collapse
nicolascastrogarcia profile image
Nicolás Castro Garcia Author

Hola! para ejecutar el servidor lo hago con npm, veo que usas nodemon, por lo que estas encaminado. Y por defecto node ejecuta el proceso en el puerto 3000. No sé si respondi tu duda. Saludos!

Collapse
fadelgado profile image
Facu Delgado • Edited

Hola Nicolas, se que dijiste q no programas en Angular, igualmente te hago una consulta porque no encuentro como resolverlo y capaz q vos tengas más idea. Resulta q usando el sdk De MP cuando quiero integrar el botón q genera el script de MP, no me aparece nada. Un usuario comento que es porque Angular bloquea los script de los templates o algo parecido a eso. Ahora, usando algo similar a lo q posteaste, un poco más sencillo poruqe hago la integración con checkout pro, logro generar el link de MP para redireccionar pero me aparece un problema de CORS. Hice de todo lo relacionado con CORS, instale la librería, puse los headers manualmente, le agregué req.method para las options, etc. Pero sigo con la misma falla, q es la que dejo en imagen. Dejo una leve descripción de la falla: Access to XMLHttpRequest at "link de MP generado correctamente con los datos para cobrar" redirected from http:localhost:3000/checkout from Origin mi página BLOCKED BY CORS POLICY. Redirect is not allowed for a preflight request.
Si sabes como resolver eso me sería de gran ayuda. Muchas gracias.

Collapse
jorgeguevarab profile image
Jorge Guevara Bohórquez

Amigo, esto es oro puro. Gracias por compartir tu conocimiento

Collapse
nicolascastrogarcia profile image
Nicolás Castro Garcia Author

Gracias a vos por tomarte el tiempo de leerlo!

Collapse
storchia profile image
storchia • Edited

Hola Nicolas!
Brillante laburo y muchisimas gracias por compartirlo, en breve te invito unos cafecitos como corresponde

Realice al pie de la letra tu implementacion y funciona barbaro.

Ahora en una situacion de Carro de Compras en donde hay que pasar varios "items". Como lo resolverias? entiendo que me esta faltando una parte del codigo porque logro pasar en el body:

{
name: [ 'Otro mas', 'First Book' ],
unit: [ '2', '1' ],
img: [ 'images\1602475489688-Page1.png', 'images\1602463339226-3.jpg' ],
price: [ '1456', '125' ],
}

pero luego no puedo enviarlo a MP, envia solo el primer articulo o sea el [0] del obejto.

Alguna idea?

Muchisimas gracias nuevamente!

Collapse
nicolascastrogarcia profile image
Nicolás Castro Garcia Author

Hola!

Los items los tenes que mandar en la preferencia. La clave items dentro del objeto de preferencia tiene que ser un array [item1, item2, item3], donde cáda item tiene que ser un objeto que contenga los requisitos que explican en la documentación de mercadopago acá.

En síntesis, usando tu ejemplo, debería quedarte algo así:

   const items = [   
         {
            "id": "",
            "picture_url": "",
            "title": "Libro 1",
            "description": "Libro 1",
            "category_id": "",
            "currency_id": "ARS",
            "quantity": 1,
            "unit_price": 10
        },       {
            "id": "",
            "picture_url": "",
            "title": "Libro 2",
            "description": "libro 2",
            "category_id": "",
            "currency_id": "ARS",
            "quantity": 1,
            "unit_price": 10
        }
    ];  

Enter fullscreen mode Exit fullscreen mode

Entonces después cuando creas la preferencia, le pasas ese array de objetos a la clave items de la preferencia:

const preferences = { 
      items: items, // ACÁ TIENE QUE IR EL ARRAY DE ITEMS QUE CREAMOS ARRIBA
      external_reference: "referencia del negocio", 
      payer: { 
        name: "Lalo",
        surname: "Landa",
        email: "test_user_63274575@testuser.com", 
        phone: {
          area_code: "11",
          number: "22223333"
        },
        address: {
          zip_code: "1111",
          street_name: "False",
          street_number: "123"
        }
      }, 
      payment_methods: {
        excluded_payment_methods: [
          {
            id: "amex"
          }
        ],
        excluded_payment_types: [{ id: "atm" }],
        installments: 6,
        default_installments: 6 
      }, 
      back_urls: {
        success: "https://localhost:3000/success", 
        pending: "https://localhost:3000.com/pending",
        failure: "https://localhost:3000.com/error"
      }, 
      notification_url: "https://localhost:3000/webhook", 
      auto_return: "approved" 
    };
Enter fullscreen mode Exit fullscreen mode

Si no anduvo podes preguntarme devuelta, o hablarme a mi twitter

Espero que se arregle!

Saludos!

Collapse
lucasadlerstein profile image
Lucas

Hola Nico,
Excelente lo detallado de lo que escribiste!
Te hago una consulta, esto te abre el checkout pro de mercadopago?
Me refiero a si te abre todo con su interfaz para que la gente elija con que pagar y pueda pagar con dinero en cuenta.
Gracias!

Collapse
nicolascastrogarcia profile image
Nicolás Castro Garcia Author

Mil disculpas por tardar tanto en contestar! pero sí, te redirecciona a mercado pago y el comprador puede elegir entre las opciones que vos le indicaste en las preferencias de pago.

Collapse
matiascorteze profile image
matiascorteze

Hola Nicolás, cómo te va? Soy un muy novato aprendiz en todo esto. Arranqué un proyecto de tienda online con carrito usando HTML/CSS/vanilla Javascript, quise integrar un simple checkout de Mercadopago que me lea el total del carrito y derive a Mercadopago con ese valor, pero no pude.

Investigando, me dijeron que eso se hace usando Node.JS así que estuve viendo un par de tutos para integrar, lo cual parecía que iba a funcionar, pero no jajaja.

Ahora me encuentro con este post increíble de tu parte que enseña todo paso a paso, pero se ve que en algo estoy fallando porque sigo sin poder integrar.

Quizá te puedo escribir por privado para pagarte una tutoría y que me enseñes integración básica para una web de ventas simple? O si hay alguna versión más APB y sencilla que pueda seguir para que al tocar el botón de COMPRAR me derive a una pantalla de MercadoPago con el valor del carrito?

Cualquier info se agradece muchísimo,
un abrazo!

Collapse
nicolascastrogarcia profile image
Nicolás Castro Garcia Author

Hola Matías!, si queres mandame un mensaje privado por acá, o contactame por twitter en @ncastrogarcia y vemos como te puedo ayudar.

Igualmente, si queres algo sencillo, podes crear links de pagos únicos desde la misma app de mercado pago.

Saludos!

mercadopago.com.ar/tools/create

Collapse
kiquetal profile image
Enrque\m/Talavera

Se podría saber cuál fue el exámen de certificación?Saludos!

Collapse
nicolascastrogarcia profile image
Nicolás Castro Garcia Author

Buenas! Es el Developer Partner Program Mercado Pago.

Me contactaron por mail, para una "jornada de certificación". La jornada consistió básicamente de una charla en la que explicaban el aspecto comercial y técnico de Mercado Pago. Y después la resolución de un "examen" que es lo que ves en este post.

El mail que me llego fue este alt

Fijate de revisar por acá partners.mercadopago.com/ si te interesa

Espero que te sirva!

Saludos

Collapse
jhonzsoto profile image
jhonzsoto

hola Nicolas te puedo hacer un pregunta medio o totalmente boluda, lo que hiciste lo puedo hacer con React.js ? recien comienzo y estoy tratando de integrar mercado pago a una ecommerce que estoy haciendo pero me resulta muy complicado ver la manera de intergrar MP. saludos !

Collapse
nicolascastrogarcia profile image
Nicolás Castro Garcia Author

Hola! no porque react no te permite hacer parte de back, tendrias que usar Nextjs como adicional a React para poder hacerlo. Saludos

Collapse
orestes7054 profile image
orestes7054

Excelente tutorial. Es verdad que la documentación es super escasa, y con lo poco que hay es imposible customizar cada venta. Estoy armando un carrito de compras sencillo, la verdad que intenté obviar node js., pero solo por cuestión de servidores (todavía no elegí el hosting). Te hago dos preguntas:
1) Existe alguna forma de poder probar el código en un localhost (sé que decía que no una de las notas, pero no encuentro otra forma de probar el pago).
2) Cuando suba los archivos al servidor, la ejecución de nodejs en que parte se haría. Gracias de nuevo! Zarpado tu laburo!

Collapse
tomasmarmay1 profile image
Tomi

Nico una pregunta, tenes idea como integrarlo con python mas especifico con Flask ?

Collapse
nicolascastrogarcia profile image
Nicolás Castro Garcia Author

Hola Tomi, no manejo python!

Collapse
claudioderio profile image
ClaudioDerio

Hola Nicolas, excelente la data que compartiste! Felicitaciones!

Soy nuevo en el mundo de nodejs, no en el de la programacion, y necesito hacer un conector entre una tabla propia mysql y mercado libre, para que actualice precios y stock de items.

Lo primero que se necesita es autenticarse en ml.
Estuve leyendo la documentacion del site ML y tratando de utilizar el sdk para nodejs github.com/mercadolibre/nodejs-sdk

Para autenticarte primero debes obtener un code, y luego cambiarlo por un access_token. La parte del access_token me funciona, pero la de obtener el code, no!

el codigo para obtener el code tiene algo como ....
....
apiInstance.auth(responseType, clientId, redirectUri, (error, data, response) => {
if (error) {
console.error(error);
} else {
console.log('API called successfully.');
}
});

En donde devuelve el code? Como lo obtengo?
data = viene vacio
response = es el http response

Gracias por tu tiempo y por la ayuda que puedas darme!

Saludos
Claudio

Collapse
developer_noob profile image
Ivan C • Edited

Buenas amigo, muy buen post me esta ayudando mucho, junto con los comentarios a los que has respondido, pero tengo una duda, en desarrollo ya cree mi orden o mi ticket para pagar en oxxo, mi duda es hay forma de cambiar o actualizar esa compra de pendiente a pagada de forma manual para poder checar como actuan mi webhook de payment.update ?

Collapse
nicolascastrogarcia profile image
Nicolás Castro Garcia Author

Hola ivan, tenes que usar los usuarios de prueba, tanto el vendedor como el comprador tienen que ser de prueba. Podes leer un poco más sobre eso acá mercadopago.com.ar/developers/es/g...