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 }'Autenticación server-to-server
EnterpriseAPI Keys
Para llamar a la API desde tu backend sin un usuario logueado (por ejemplo, para registrar pagos esperados), usá una API key en lugar de un JWT. Solo el rol OWNER puede crearlas o revocarlas, y siempre vía tu access token — las keys no pueden gestionarse con otra key.
/api/v1/businesses/{businessId}/api-keysCrea una API key. Devuelve key (formato pa_live_...) solo en este response.
curl -X POST https://pay-alert-api.onrender.com/api/v1/businesses/{businessId}/api-keys \
-H "Authorization: Bearer <tu-access-token>" \
-H "Content-Type: application/json" \
-d '{
"name": "Integración ERP",
"expiresAt": "2027-01-01T00:00:00.000Z"
}'/api/v1/businesses/{businessId}/api-keysLista las keys activas del comercio (sin el valor crudo).
curl https://pay-alert-api.onrender.com/api/v1/businesses/{businessId}/api-keys \
-H "Authorization: Bearer <tu-access-token>"/api/v1/businesses/{businessId}/api-keys/{keyId}Revoca una key de forma inmediata para nuevos requests. Por caché, una key ya revocada puede seguir aceptándose hasta 5 minutos.
curl -X DELETE https://pay-alert-api.onrender.com/api/v1/businesses/{businessId}/api-keys/{keyId} \
-H "Authorization: Bearer <tu-access-token>"Usar la key en tus requests
Mandala como Authorization: Bearer pa_live_... o como header x-api-key: pa_live_.... Los endpoints que aceptan API key también aceptan JWT — usá lo que tenga sentido según si hay o no un usuario logueado del lado que llama. Límite: 300 requests/minuto por key.
Transferencias y pagos fuera de Mercado Pago
EnterprisePagos esperados
Registrá de antemano un pago que esperás recibir (típicamente una transferencia) con una referencia propia y un monto. Cuando Pay Alert detecta un pago que matchea, lo vincula automáticamente — vos solo consultás el estado. Auth: JWT o API Key.
/api/v1/businesses/{businessId}/expected-paymentsCrea un pago esperado. externalReference es tu propio identificador (ej: número de factura) y es de un solo uso por comercio — no se puede reutilizar aunque el pago original haya expirado o se haya cancelado. expiresAt es opcional.
curl -X POST https://pay-alert-api.onrender.com/api/v1/businesses/{businessId}/expected-payments \
-H "Authorization: Bearer pa_live_..." \
-H "Content-Type: application/json" \
-d '{
"externalReference": "factura-4521",
"amount": 15000,
"expiresAt": "2026-07-25T00:00:00.000Z"
}'/api/v1/businesses/{businessId}/expected-payments/{externalReference}Consulta el estado. Si pasó expiresAt sin match, el estado se resuelve a EXPIRED automáticamente. Cuando el estado es MATCHED, incluye el pago vinculado en data.payment.
curl https://pay-alert-api.onrender.com/api/v1/businesses/{businessId}/expected-payments/factura-4521 \
-H "Authorization: Bearer pa_live_..."{
"data": {
"id": "cmpz1a2b30001y5abcd12345",
"externalReference": "factura-4521",
"amount": "15000.00",
"status": "MATCHED",
"expiresAt": "2026-07-25T00:00:00.000Z",
"createdAt": "2026-07-20T18:04:11.000Z",
"payment": {
"id": "cmpx9k2ab0001y5wnabc12345",
"mpPaymentId": "123456789",
"amount": "15000.00",
"currency": "ARS",
"status": "APPROVED",
"description": "Producto XYZ",
"payerEmail": null,
"payerName": "Juan García",
"paymentMethod": "account_money",
"paidAt": "2026-07-20T18:05:02.000Z",
"receivedAt": "2026-07-20T18:05:03.000Z"
}
}
}/api/v1/businesses/{businessId}/expected-payments/{externalReference}Cancela un pago esperado. Solo funciona mientras sigue PENDING — si ya fue matcheado, expiró, o ya está cancelado, devuelve error.
curl -X DELETE https://pay-alert-api.onrender.com/api/v1/businesses/{businessId}/expected-payments/factura-4521 \
-H "Authorization: Bearer pa_live_..."Estados posibles
PENDING → MATCHED, EXPIRED o CANCELLED. Son estados terminales — un pago esperado nunca vuelve a PENDING.
Cómo matchea
Se vincula automáticamente solo si hay exactamente un pago esperado PENDING vigente con el mismo monto (tolerancia ±0.01). Si hay ambigüedad, no matchea nada — consultá por polling o usá un webhook para enterarte.
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.
Para plataformas
¿Tu software gestiona el negocio de otros comercios?
Si desarrollás un ERP, POS o sistema de gestión y necesitás que tus clientes tengan Pay Alert activo sin pasar por nuestra UI, nuestra arquitectura ya está preparada para ese tipo de integración a nivel de plataforma. Escribinos y lo evaluamos juntos.
¿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.