Pay Alert para Desarrolladores
Recibí notificaciones de pago en tiempo real en tu propio sistema. Pay Alert envía un POST firmado a tu servidor cada vez que ocurre un evento en tu comercio.
Cómo funciona
Flujo de un webhook
01
Registrás tu endpoint
Creás un webhook en Pay Alert con la URL de tu servidor y los eventos que querés recibir.
02
Ocurre un pago
Cuando un cliente paga, Pay Alert procesa el evento y envía un POST firmado a tu URL en segundos.
03
Verificás y procesás
Tu servidor verifica la firma HMAC-SHA256, parsea el JSON y ejecuta tu lógica de negocio.
Seguridad
Verificación de firma
Cada request incluye el header X-Pay-Alert-Signature con un HMAC-SHA256 del body firmado con tu secret. Siempre verificar antes de procesar.
Node.js
const crypto = require('crypto')
function verifySignature(secret, body, signature) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(body)
.digest('hex')
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
)
}
// Express / Fastify — leer el body como string crudo
app.post('/webhook/pay-alert', (req, res) => {
const sig = req.headers['x-pay-alert-signature']
const rawBody = req.rawBody // bodyParser con { verify } o similar
if (!verifySignature(process.env.WEBHOOK_SECRET, rawBody, sig)) {
return res.status(401).send('Firma inválida')
}
const event = JSON.parse(rawBody)
console.log('Evento recibido:', event.event, event.data)
res.sendStatus(200)
})Python
import hmac
import hashlib
def verify_signature(secret: str, body: bytes, signature: str) -> bool:
expected = 'sha256=' + hmac.new(
secret.encode(),
body,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)
# Flask
from flask import Flask, request
app = Flask(__name__)
@app.route('/webhook/pay-alert', methods=['POST'])
def webhook():
sig = request.headers.get('X-Pay-Alert-Signature', '')
raw_body = request.get_data()
if not verify_signature(os.environ['WEBHOOK_SECRET'], raw_body, sig):
return 'Firma inválida', 401
event = request.get_json()
print('Evento:', event['event'], event['data'])
return '', 200PHP
<?php
function verifySignature(string $secret, string $body, string $signature): bool {
$expected = 'sha256=' . hash_hmac('sha256', $body, $secret);
return hash_equals($expected, $signature);
}
$secret = getenv('WEBHOOK_SECRET');
$rawBody = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_PAY_ALERT_SIGNATURE'] ?? '';
if (!verifySignature($secret, $rawBody, $signature)) {
http_response_code(401);
exit('Firma inválida');
}
$event = json_decode($rawBody, true);
error_log('Evento: ' . $event['event']);
http_response_code(200);Referencia
Estructura del payload
Pay Alert envía un JSON con esta estructura en todos los eventos.
{
"event": "payment.approved",
"businessId": "cmpa61k6h0002y5sf33s3ixaj",
"timestamp": "2026-06-18T14:32:00.000Z",
"data": {
"id": "cmpx9k2ab0001y5wnabc12345",
"mpPaymentId": "123456789",
"amount": "15000.00",
"currency": "ARS",
"status": "APPROVED",
"description": "Producto XYZ",
"payerName": "Juan García",
"payerEmail": null,
"paymentMethod": "account_money",
"paidAt": "2026-06-18T14:31:58.000Z",
"receivedAt": "2026-06-18T14:32:00.000Z"
}
}eventstringTipo de evento. Ver tabla de eventos.
businessIdstringID del comercio en Pay Alert.
timestampstring (ISO 8601)Fecha y hora del envío en UTC.
data.idstringID interno del pago en Pay Alert.
data.mpPaymentIdstringID del pago en Mercado Pago.
data.amountstring (decimal)Monto del pago.
data.currencystringCódigo ISO 4217. Ej: "ARS".
data.statusstringEstado: APPROVED | REJECTED | REFUNDED | CANCELLED.
data.payerNamestring | nullNombre del pagador (si está disponible).
data.payerEmailstring | nullEmail del pagador (si está disponible).
data.paymentMethodstring | nullMétodo: account_money, bank_transfer, credit_card, etc.
data.paidAtstring | nullFecha de aprobación del pago.
data.receivedAtstringFecha en que Pay Alert recibió el evento de MP.
Eventos
Eventos disponibles
Suscribite solo a los eventos que tu sistema necesita. Un array vacío recibe todos.
| Evento | Cuándo se dispara |
|---|---|
payment.approved | Un pago es aprobado por Mercado Pago. Incluye pagos nuevos y actualizaciones de estado. |
payment.received | Pay Alert recibe un pago aprobado por primera vez (sin procesar antes). |
payment.refunded | Un pago es reembolsado total o parcialmente. |
payment.cancelled | Un pago pendiente es cancelado. |
API REST
Gestionar webhooks
Los webhooks se administran vía API con tu access token JWT. Rol mínimo requerido: ADMIN. El plan Enterprise es necesario para crear webhooks.
/api/v1/businesses/{businessId}/webhooksCrea un webhook. Devuelve el secret solo en la creación — guardalo de forma segura.
curl -X POST https://pay-alert-api.onrender.com/api/v1/businesses/{businessId}/webhooks \
-H "Authorization: Bearer <tu-access-token>" \
-H "Content-Type: application/json" \
-d '{
"url": "https://tu-servidor.com/webhook/pay-alert",
"events": ["payment.approved", "payment.refunded"],
"isActive": true
}'/api/v1/businesses/{businessId}/webhooksLista todos los webhooks del comercio (sin el secret).
curl https://pay-alert-api.onrender.com/api/v1/businesses/{businessId}/webhooks \
-H "Authorization: Bearer <tu-access-token>"/api/v1/businesses/{businessId}/webhooks/{webhookId}/testEnvía un evento de prueba payment.approved a la URL configurada. Útil para verificar que tu endpoint responde correctamente.
curl -X POST https://pay-alert-api.onrender.com/api/v1/businesses/{businessId}/webhooks/{webhookId}/test \
-H "Authorization: Bearer <tu-access-token>" \
-H "Content-Type: application/json" \
-d '{}'/api/v1/businesses/{businessId}/webhooks/{webhookId}Actualiza url, events, isActive. Con regenerateSecret: true rota el secret y devuelve el nuevo valor.
curl -X PUT https://pay-alert-api.onrender.com/api/v1/businesses/{businessId}/webhooks/{webhookId} \
-H "Authorization: Bearer <tu-access-token>" \
-H "Content-Type: application/json" \
-d '{ "regenerateSecret": true }'Buenas prácticas
Implementación recomendada
Responder 200 rápido
Devolvé 200 antes de procesar la lógica pesada. Si tardás más de 10 segundos, Pay Alert considera que falló la entrega.
Idempotencia
El mismo evento puede llegar más de una vez. Usá data.id o data.mpPaymentId como clave de idempotencia en tu base de datos.
Siempre verificar firma
Nunca procesar un webhook sin verificar X-Pay-Alert-Signature. Cualquiera puede hacer un POST a tu URL.
Rotación de secret
Si sospechás que tu secret fue comprometido, regeneralo desde la API con regenerateSecret: true. Actualizá tu variable de entorno de inmediato.
¿Necesitás ayuda con la integración?
Para clientes Enterprise, el soporte técnico está incluido. También podemos desarrollar la integración a medida para tu sistema.