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:
- Conocimientos de Javascript
- Tener instalado node
- Saber usar el comando
cd
de la terminal - Ganas de Aprender!
- Las API keys/tokens de MP, que las pueden encontrar acá
Lo que vamos a hacer
- Vamos a crear una API en Node, más puntualmente express.
- 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).
- 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
-
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
-
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
-
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. -
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;
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);
Y agregar las rutas que SI vamos a usar que son:
app.post("/payment/new", (req, res) =>
PaymentInstance.getMercadoPagoLink(req, res)
);
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));
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
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;
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;
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"
});
}
}
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);
}
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;
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
}
}
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}`;
}
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á
}
];
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
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"
}
});
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;
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
No respondo más los comentarios! si tienen dudas pueden consultarme por twitter!
Tambien les dejo este video, que explico lo mismo que este blog pero en formato video
Si te sirvió me podes comprar un cafecito
Muchas gracias por tu tiempo!
Top comments (64)
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.
Hola Roger!
Te contesto por partes:
Para probar los webhooks en "sandbox" tenes que seguir los siguientes pasos:
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
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)
Nunca lo probé el modo binario!
Gracias a vos por preguntar!
Si te quedaste con alguna otra duda, sentite libre de preguntar!
Saludos!
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!
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.
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!
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!
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!
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
Asumo que estás usando algo similar a esto en tu front
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!
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
Para futuras referencias. El problema es Angular que no acepta tags script en los templates
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.
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í:
luego te debería llegar esta notificación con
topic: payment
y resource:y por último con
topic: merchant_order
:Y a partir de acá si sale todo bien, vas a volver a recibir 3 notificaciónes webhook:
Una con action:
payment.updated
:Una con
topic: payment
que va a recibir lo mismo que antes pero el status va a decir: approvedUna con
topic: merchant_order
que va a recibir exactamento lo mismo que antes, peropayments[0].status
se va a haber actualizado aapproved
yorder_status
apaid
En síntesis, la manera correcta de hacer el track a lo largo de toda la aplicación es con el
merchant_order
. Elpayment.id
es una propiedad del objetomerchant_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 objetomerchant_order
cambiaría, teniendo dentro del arraypayments
, algo similar a esto:Recordá que para hacer las pruebas en sandbox tenes que:
1- Utilizar el
init_point
para el link de pago y no elsandbox_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!
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:
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..)
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
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 orden5.2
PAGO CON TRANSFERENCIA O TICKET (OXXO 7VEN ETC)
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 datosA 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!!
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.
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!!
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.
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:
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!
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!!
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!
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.
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!
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!
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!
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
})
);
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
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
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!
Hola, tenes que implementar un marketplace mercadopago.com.ar/developers/es/g...
Podes cobrar hasta un fee por cada transacción. Saludos!
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
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!
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!
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!
Some comments may only be visible to logged-in visitors. Sign in to view all comments.