Configurar notificaciones de pago
Las notificaciones Webhooks, tambiĆ©n conocidas como devoluciones de llamada web, son un mĆ©todo efectivo que permiten a los servidores de Mercado Pago enviar informaciĆ³n en tiempo real cuando ocurre un evento especĆfico relacionado con tu integraciĆ³n.
En lugar de que tu sistema realice consultas constantes para verificar actualizaciones, los Webhooks permiten la transmisiĆ³n de datos de manera pasiva y automĆ”tica entre Mercado Pago y tu integraciĆ³n a travĆ©s de una solicitud HTTP POST, optimizando la comunicaciĆ³n y reduciendo la carga en los servidores.
Consulta el flujo general de una notificaciĆ³n en el diagrama a continuaciĆ³n.
A continuaciĆ³n, presentamos un paso a paso para configurar las notificaciones de creaciĆ³n y actualizaciĆ³n de pagos. Una vez configuradas, las notificaciones Webhook se enviarĆ”n cada vez que se cree un pago o se modifique su estado (Pendiente, Rechazado o Aprobado). En el proceso de integraciĆ³n con Mercado Pago, puedes configurar las notificaciones de dos maneras:
Tipo de ConfiguraciĆ³n | DescripciĆ³n | Ventajas | CuĆ”ndo Usar |
ConfiguraciĆ³n a travĆ©s de Tus Integraciones | Este mĆ©todo permite configurar notificaciones directamente en tu Panel de Desarrollador. Puedes configurar notificaciones para cada una de tus aplicaciones, identificar cuentas distintas si es necesario, y validar el origen de la notificaciĆ³n mediante una firma secreta. | - IdentificaciĆ³n sencilla de cuentas distintas, asegurando una adecuada gestiĆ³n en entornos diversos. - Alta seguridad al validar el origen de las notificaciones mediante una firma secreta, que garantiza la integridad de la informaciĆ³n recibida. - MĆ”s versĆ”til y eficaz para mantener un control centralizado y gestionar la comunicaciĆ³n con las aplicaciones de manera eficiente. | Recomendado para la mayorĆa de las integraciones. |
ConfiguraciĆ³n durante la creaciĆ³n de pagos o preferencias | Las notificaciones se configuran para cada transacciĆ³n individualmente durante la creaciĆ³n del pago o preferencia. | - Ajustes especĆficos para cada transacciĆ³n. - Flexibilidad en casos de necesidad de parĆ”metros dinĆ”micos obligatorios. - Ideal para integraciones como plataformas de pago para mĆŗltiples vendedores. | Conveniente en los casos en que sea necesario enviar un query parameter dinĆ”mico de forma obligatoria, ademĆ”s de ser adecuado para integraciones que funcionan como una plataforma de pago para mĆŗltiples vendedores. |
ConfiguraciĆ³n a travĆ©s de Tus integraciones
Puedes configurar notificaciones para cada una de tus aplicaciones directamente desde Tus integraciones de manera eficiente y segura. En este apartado, explicaremos cĆ³mo:
- Indicar las URLs de notificaciĆ³n y configurar eventos
- Validar el origen de una notificaciĆ³n
- Simular el recibimiento de una notificaciĆ³n
1. Indicar URLs de notificaciĆ³n y configurar el evento
Para configurar notificaciones Webhooks, es necesario indicar las URLs a las que las mismas serĆ”n enviadas. Para hacerlo, sigue el paso a paso a continuaciĆ³n:
- Ingresa a Tus integraciones y selecciona la aplicaciĆ³n integrada con Checkout Pro para la que deseas activar las notificaciones.
- En el menĆŗ de la izquierda, selecciona Webhooks > Configurar notificaciones.
- Selecciona la pestaƱa Modo productivo y proporciona una
URL HTTPS
para recibir notificaciones con tu integraciĆ³n productiva.
- Selecciona lo evento Pagos para recibir notificaciones, que serƔn enviadas en formato
JSON
a travƩs de unHTTPS POST
a la URL especificada anteriormente.
- Por Ćŗltimo, haz clic en Guardar configuraciĆ³n. Esto generarĆ” una clave secreta exclusiva para la aplicaciĆ³n, que permitirĆ” validar la autenticidad de las notificaciones recibidas, garantizando que hayan sido enviadas por Mercado Pago. Ten en cuenta que esta clave generada no tiene plazo de caducidad y su renovaciĆ³n periĆ³dica no es obligatoria, aunque sĆ recomendada. Para hacerlo, basta con cliquear en el botĆ³n Restablecer.
2. Simular la recepciĆ³n de la notificaciĆ³n
Para garantizar que las notificaciones sean configuradas correctamente, es necesario simular su recepciĆ³n. Para hacerlo, sigue el paso a paso a continuaciĆ³n.
- DespuĆ©s de configurar las URLs y los Eventos, haz clic en Guardar configuraciĆ³n.
- Luego, haz clic en Simular para probar si la URL indicada estĆ” recibiendo las notificaciones correctamente.
- En la pantalla de simulaciĆ³n, selecciona la URL que se va a probar, que puede ser la URL de prueba o la de producciĆ³n.
- A continuaciĆ³n, elige el tipo de evento e ingresa la identificaciĆ³n que se enviarĆ” en el cuerpo de la notificaciĆ³n (Data ID).
- Por Ćŗltimo, haz clic en Enviar prueba para verificar la solicitud, la respuesta proporcionada por el servidor y la descripciĆ³n del evento. RecibirĆ”s una respuesta similar al ejemplo a continuaciĆ³n, que representa el
body
de la notificaciĆ³n recibida en tu servidor.
plain
{ "action": "payment.updated", "api_version": "v1", "data": { "id": "123456" }, "date_created": "2021-11-01T02:02:02Z", "id": "123456", "live_mode": false, "type": "payment", "user_id": 724484980 }
3. Validar origen de la notificaciĆ³n
La validaciĆ³n del origen de una notificaciĆ³n es fundamental para asegurar la seguridad y la autenticidad de la informaciĆ³n recibida. Este proceso ayuda a prevenir fraudes y garantiza que solo las notificaciones legĆtimas sean procesadas.
Mercado Pago enviarĆ” a su servidor una notificaciĆ³n similar al ejemplo a continuaciĆ³n para una alerta del tĆ³pico payment
. En este ejemplo, se incluye la notificaciĆ³n completa, que contiene los query params
, el body
y el header
de la notificaciĆ³n.
- Query params: Son parƔmetros de consulta que acompaƱan la URL. En el ejemplo, tenemos
data.id=123456
ytype=payment
. - Body: El cuerpo de la notificaciĆ³n contiene informaciĆ³n detallada sobre el evento, como
action
,api_version
,data
,date_created
,id
,live_mode
,type
yuser_id
. - Header: El encabezado contiene metadatos importantes, incluyendo la firma secreta de la notificaciĆ³n
x-signature
.
plain
POST /test?data.id=123456&type=payment HTTP/1.1 Host: prueba.requestcatcher.com Accept: */* Accept-Encoding: * Connection: keep-alive Content-Length: 177 Content-Type: application/json Newrelic: eyJ2IjpbMCwxXSwiZCI6eyJ0eSI6IkFwcCIsImFjIjoiOTg5NTg2IiwiYXAiOiI5NjA2MzYwOTQiLCJ0eCI6IjU3ZjI4YzNjOWE2ODNlZDYiLCJ0ciI6IjY0NjA0OTM3OWI1ZjA3MzMyZDdhZmQxMjEyM2I5YWE4IiwicHIiOjAuNzk3ODc0LCJzYSI6ZmFsc2UsInRpIjoxNzQyNTA1NjM4Njg0LCJ0ayI6IjE3MDk3MDcifX0= Traceparent: 00-646049379b5f07332d7afd12123b9aa8-e7f77a41f687aecd-00 Tracestate: 1709707@nr=0-0-989586-960636094-e7f77a41f687aecd-57f28c3c9a683ed6-0-0.797874-1742505638684 User-Agent: restclient-node/4.15.3 X-Request-Id: bb56a2f1-6aae-46ac-982e-9dcd3581d08e X-Rest-Pool-Name: /services/webhooks.js X-Retry: 0 X-Signature: ts=1742505638683,v1=ced36ab6d33566bb1e16c125819b8d840d6b8ef136b0b9127c76064466f5229b X-Socket-Timeout: 22000 {"action":"payment.updated","api_version":"v1","data":{"id":"123456"},"date_created":"2021-11-01T02:02:02Z","id":"123456","live_mode":false,"type":"payment","user_id":724484980}
A partir de la notificaciĆ³n Webhook recibida, podrĆ”s validar la autenticidad de su origen. Mercado Pago siempre incluirĆ” la clave secreta en las notificaciones Webhooks que serĆ”n recibidas, lo que permitirĆ” validar su autenticidad. Esta clave serĆ” enviada en el header x-signature
, que serĆ” similar al ejemplo debajo.
plain
`ts=1742505638683,v1=ced36ab6d33566bb1e16c125819b8d840d6b8ef136b0b9127c76064466f5229b`
Para confirmar la validaciĆ³n, es necesario extraer la clave contenida en el header y compararla con la clave otorgada para tu aplicaciĆ³n en Tus integraciones. Para eso, sigue el paso a paso a continuaciĆ³n. Al final, disponibilizamos nuestros SDKs con ejemplos de cĆ³digos completos para facilitar el proceso.
- Para extraer el timestamp (
ts
) y la clave (v1
) del headerx-signature
, divide el contenido del header por el carĆ”cter ā,", lo que resultarĆ” en una lista de elementos. El valor para el prefijots
es el timestamp (en milisegundos) de la notificaciĆ³n y v1 es la clave encriptada. Siguiendo el ejemplo presentado anteriormente,ts=1742505638683
yv1=ced36ab6d33566bb1e16c125819b8d840d6b8ef136b0b9127c76064466f5229b
. - Utilizando el template a continuaciĆ³n, sustituye los parĆ”metros con los datos recibidos en tu notificaciĆ³n.
plain
id:[data.id_url];request-id:[x-request-id_header];ts:[ts_header];
- Los parƔmetros con el sufijo
_url
provienen de query params. Ejemplo: [data.id_url] se sustituirĆ” por el valor correspondiente al ID del evento (data.id
). Este query param puede ser hallado en la notificaciĆ³n recibida. En el ejemplo de notificaciĆ³n mencionado anteriormente, eldata.id_url
es123456
. - [x-request-id_header] deberĆ” ser sustituido por el valor recibido en el header
x-request-id
. En el ejemplo de notificaciĆ³n mencionado anteriormente, elx-request-id
esbb56a2f1-6aae-46ac-982e-9dcd3581d08e
. - [ts_header] serĆ” el valor
ts
extraĆdo del headerx-signature
. En el ejemplo de notificaciĆ³n mencionado anteriormente, elts
es1742505638683
. - Al aplicar los datos al template, quedarĆa de la siguiente manera:
id:123456;request-id:bb56a2f1-6aae-46ac-982e-9dcd3581d08e;ts:1742505638683;
- En Tus integraciones, selecciona la aplicaciĆ³n integrada, haz clic en Webhooks > Configurar notificaciĆ³n y revela la clave secreta generada.
- Genera la contraclave para la validaciĆ³n. Para hacer esto, calcula un HMAC con la funciĆ³n de
hash SHA256
en base hexadecimal, utilizando la clave secreta como clave y el template con los valores como mensaje.
$cyphedSignature = hash_hmac('sha256', $data, $key);
const crypto = require('crypto');
const cyphedSignature = crypto
.createHmac('sha256', secret)
.update(signatureTemplateParsed)
.digest('hex');
String cyphedSignature = new HmacUtils("HmacSHA256", secret).hmacHex(signedTemplate);
import hashlib, hmac, binascii
cyphedSignature = binascii.hexlify(hmac_sha256(secret.encode(), signedTemplate.encode()))
- Finalmente, compara la clave generada con la clave extraĆda del header, asegurĆ”ndote de que tengan una correspondencia exacta. AdemĆ”s, puedes usar el timestamp extraĆdo del header para compararlo con un timestamp generado en el momento de la recepciĆ³n de la notificaciĆ³n, con el fin de establecer una tolerancia de demora en la recepciĆ³n del mensaje.
A continuaciĆ³n, puedes ver ejemplos de cĆ³digo completo:
<?php
// Obtain the x-signature value from the header
$xSignature = $_SERVER['HTTP_X_SIGNATURE'];
$xRequestId = $_SERVER['HTTP_X_REQUEST_ID'];
// Obtain Query params related to the request URL
$queryParams = $_GET;
// Extract the "data.id" from the query params
$dataID = isset($queryParams['data.id']) ? $queryParams['data.id'] : '';
// Separating the x-signature into parts
$parts = explode(',', $xSignature);
// Initializing variables to store ts and hash
$ts = null;
$hash = null;
// Iterate over the values to obtain ts and v1
foreach ($parts as $part) {
// Split each part into key and value
$keyValue = explode('=', $part, 2);
if (count($keyValue) == 2) {
$key = trim($keyValue[0]);
$value = trim($keyValue[1]);
if ($key === "ts") {
$ts = $value;
} elseif ($key === "v1") {
$hash = $value;
}
}
}
// Obtain the secret key for the user/application from Mercadopago developers site
$secret = "your_secret_key_here";
// Generate the manifest string
$manifest = "id:$dataID;request-id:$xRequestId;ts:$ts;";
// Create an HMAC signature defining the hash type and the key as a byte array
$sha = hash_hmac('sha256', $manifest, $secret);
if ($sha === $hash) {
// HMAC verification passed
echo "HMAC verification passed";
} else {
// HMAC verification failed
echo "HMAC verification failed";
}
?>
// Obtain the x-signature value from the header
const xSignature = headers['x-signature']; // Assuming headers is an object containing request headers
const xRequestId = headers['x-request-id']; // Assuming headers is an object containing request headers
// Obtain Query params related to the request URL
const urlParams = new URLSearchParams(window.location.search);
const dataID = urlParams.get('data.id');
// Separating the x-signature into parts
const parts = xSignature.split(',');
// Initializing variables to store ts and hash
let ts;
let hash;
// Iterate over the values to obtain ts and v1
parts.forEach(part => {
// Split each part into key and value
const [key, value] = part.split('=');
if (key && value) {
const trimmedKey = key.trim();
const trimmedValue = value.trim();
if (trimmedKey === 'ts') {
ts = trimmedValue;
} else if (trimmedKey === 'v1') {
hash = trimmedValue;
}
}
});
// Obtain the secret key for the user/application from Mercadopago developers site
const secret = 'your_secret_key_here';
// Generate the manifest string
const manifest = `id:${dataID};request-id:${xRequestId};ts:${ts};`;
// Create an HMAC signature
const hmac = crypto.createHmac('sha256', secret);
hmac.update(manifest);
// Obtain the hash result as a hexadecimal string
const sha = hmac.digest('hex');
if (sha === hash) {
// HMAC verification passed
console.log("HMAC verification passed");
} else {
// HMAC verification failed
console.log("HMAC verification failed");
}
import hashlib
import hmac
import urllib.parse
# Obtain the x-signature value from the header
xSignature = request.headers.get("x-signature")
xRequestId = request.headers.get("x-request-id")
# Obtain Query params related to the request URL
queryParams = urllib.parse.parse_qs(request.url.query)
# Extract the "data.id" from the query params
dataID = queryParams.get("data.id", [""])[0]
# Separating the x-signature into parts
parts = xSignature.split(",")
# Initializing variables to store ts and hash
ts = None
hash = None
# Iterate over the values to obtain ts and v1
for part in parts:
# Split each part into key and value
keyValue = part.split("=", 1)
if len(keyValue) == 2:
key = keyValue[0].strip()
value = keyValue[1].strip()
if key == "ts":
ts = value
elif key == "v1":
hash = value
# Obtain the secret key for the user/application from Mercadopago developers site
secret = "your_secret_key_here"
# Generate the manifest string
manifest = f"id:{dataID};request-id:{xRequestId};ts:{ts};"
# Create an HMAC signature defining the hash type and the key as a byte array
hmac_obj = hmac.new(secret.encode(), msg=manifest.encode(), digestmod=hashlib.sha256)
# Obtain the hash result as a hexadecimal string
sha = hmac_obj.hexdigest()
if sha == hash:
# HMAC verification passed
print("HMAC verification passed")
else:
# HMAC verification failed
print("HMAC verification failed")
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"net/http"
"strings"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// Obtain the x-signature value from the header
xSignature := r.Header.Get("x-signature")
xRequestId := r.Header.Get("x-request-id")
// Obtain Query params related to the request URL
queryParams := r.URL.Query()
// Extract the "data.id" from the query params
dataID := queryParams.Get("data.id")
// Separating the x-signature into parts
parts := strings.Split(xSignature, ",")
// Initializing variables to store ts and hash
var ts, hash string
// Iterate over the values to obtain ts and v1
for _, part := range parts {
// Split each part into key and value
keyValue := strings.SplitN(part, "=", 2)
if len(keyValue) == 2 {
key := strings.TrimSpace(keyValue[0])
value := strings.TrimSpace(keyValue[1])
if key == "ts" {
ts = value
} else if key == "v1" {
hash = value
}
}
}
// Get secret key/token for specific user/application from Mercadopago developers site
secret := "your_secret_key_here"
// Generate the manifest string
manifest := fmt.Sprintf("id:%v;request-id:%v;ts:%v;", dataID, xRequestId, ts)
// Create an HMAC signature defining the hash type and the key as a byte array
hmac := hmac.New(sha256.New, []byte(secret))
hmac.Write([]byte(manifest))
// Obtain the hash result as a hexadecimal string
sha := hex.EncodeToString(hmac.Sum(nil))
if sha == hash {
// HMAC verification passed
fmt.Println("HMAC verification passed")
} else {
// HMAC verification failed
fmt.Println("HMAC verification failed")
}
})
}
Una vez que las notificaciones sean configuradas, consulta las acciones necesarias despuĆ©s de recibir una notificaciĆ³n para informar que las mismas fueron debidamente recibidas:
Acciones necesarias despuĆ©s de recibir la notificaciĆ³n
Cuando recibes una notificaciĆ³n en tu plataforma, Mercado Pago espera una respuesta para validar que esa recepciĆ³n fue correcta. Para eso, debes devolver un HTTP STATUS 200 (OK)
o 201 (CREATED)
.
El tiempo de espera para esa confirmaciĆ³n serĆ” de 22 segundos. Si no se envĆa esta respuesta, el sistema entenderĆ” que la notificaciĆ³n no fue recibida y realizarĆ” un nuevo intento de envĆo cada 15 minutos, hasta que reciba la respuesta. DespuĆ©s del tercer intento, el plazo serĆ” prorrogado, pero los envĆos continuarĆ”n sucediendo.
Luego de responder la notificaciĆ³n, confirmando su recibimiento, puedes obtener toda la informaciĆ³n sobre el evento del tĆ³pico payments
notificado haciendo un GET al endpoint v1/payments/{id}.
Con esta informaciĆ³n podrĆ”s realizar las actualizaciones necesarias a tu plataforma, como por ejemplo, actualizar un pago aprobado.
AdemĆ”s, para consultar el estado del evento posterior a la notificaciĆ³n, puedes utilizar los diferentes mĆ©todos de nuestros SDKs para realizar la consulta con el ID que fue enviado en la notificaciĆ³n.
MercadoPago.SDK.setAccessToken("ENV_ACCESS_TOKEN");
switch (type) {
case "payment":
Payment payment = Payment.findById(data.id);
break;
case "plan":
Plan plan = Plan.findById(data.id);
break;
case "subscription":
Subscription subscription = Subscription.findById(data.id);
break;
case "invoice":
Invoice invoice = Invoice.findById(data.id);
break;
case "point_integration_wh":
// POST contiene la informaciĆ²n relacionada a la notificaciĆ²n.
break;
}
mercadopago.configurations.setAccessToken('ENV_ACCESS_TOKEN');
switch (type) {
case 'payment':
const payment = await mercadopago.payment.findById(data.id);
break;
case 'plan':
const plan = await mercadopago.plans.get(data.id);
break;
case 'subscription':
const subscription = await mercadopago.subscriptions.get(data.id);
break;
case 'invoice':
const invoice = await mercadopago.invoices.get(data.id);
break;
case 'point_integration_wh':
// Contiene la informaciĆ²n relacionada a la notificaciĆ²n.
break;
}
MercadoPago::SDK.configure(access_token: 'ENV_ACCESS_TOKEN')
case payload['type']
when 'payment'
payment = MercadoPago::Payment.search(id: payload['data']['id'])
when 'plan'
plan = MercadoPago::Plan.search(id: payload['data']['id'])
when 'subscription'
subscription = MercadoPago::Subscription.search(id: payload['data']['id'])
when 'invoice'
invoice = MercadoPago::Invoice.search(id: payload['data']['id'])
when 'point_integration_wh'
# Contiene la informaciĆ²n relacionada a la notificaciĆ²n.
end
MercadoPagoConfig.AccessToken = "ENV_ACCESS_TOKEN";
switch (type)
{
case "payment":
Payment payment = await Payment.FindByIdAsync(payload["data"]["id"].ToString());
break;
case "plan":
Plan plan = await Plan.FindByIdAsync(payload["data"]["id"].ToString());
break;
case "subscription":
Subscription subscription = await Subscription.FindByIdAsync(payload["data"]["id"].ToString());
break;
case "invoice":
Invoice invoice = await Invoice.FindByIdAsync(payload["data"]["id"].ToString());
break;
case "point_integration_wh":
// Contiene la informaciĆ²n relacionada a la notificaciĆ²n.
break;
}
sdk = mercadopago.SDK("ENV_ACCESS_TOKEN")
notification_type = data["type"]
if notification_type == "payment":
payment = sdk.payment().get(payload["data"]["id"])
elif notification_type == "plan":
plan = sdk.preapproval().get(payload["data"]["id"])
elif notification_type == "subscription":
subscription = sdk.preapproval().get(payload["data"]["id"])
elif notification_type == "invoice":
invoice = sdk.invoice().get(payload["data"]["id"])
elif notification_type == "point_integration_wh":
# Contiene la informaciĆ²n relacionada a la notificaciĆ²n.
else:
return
accessToken := "{{ACCESS_TOKEN}}"
cfg, err := config.New(accessToken)
if err != nil {
fmt.Println(err)
return
}
client := customer.NewClient(cfg)
switch req.Type {
case "payment":
client := payment.NewClient(cfg)
resource, err = client.Get(context.Background(), resource.ID)
if err != nil {
fmt.Println(err)
return
}
case "plan":
client := preapprovalplan.NewClient(cfg)
resource, err := client.Get(context.Background(), preApprovalPlanID)
if err != nil {
fmt.Println(err)
return
}
case "invoice":
client := invoice.NewClient(cfg)
resource, err := client.Get(context.Background(), req.Data.ID)
if err != nil {
fmt.Println(err)
return
}
case "point_integration_wh":
// Contiene la informaciĆ²n relacionada a la notificaciĆ²n.
}