Enterprise · Plan requerido

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.

Importante: leer el body como string crudo antes de parsearlo como JSON. Modificar el body antes de verificar la firma causará fallos de validación.

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 '', 200

PHP

<?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"
  }
}
eventstring

Tipo de evento. Ver tabla de eventos.

businessIdstring

ID del comercio en Pay Alert.

timestampstring (ISO 8601)

Fecha y hora del envío en UTC.

data.idstring

ID interno del pago en Pay Alert.

data.mpPaymentIdstring

ID del pago en Mercado Pago.

data.amountstring (decimal)

Monto del pago.

data.currencystring

Código ISO 4217. Ej: "ARS".

data.statusstring

Estado: APPROVED | REJECTED | REFUNDED | CANCELLED.

data.payerNamestring | null

Nombre del pagador (si está disponible).

data.payerEmailstring | null

Email del pagador (si está disponible).

data.paymentMethodstring | null

Método: account_money, bank_transfer, credit_card, etc.

data.paidAtstring | null

Fecha de aprobación del pago.

data.receivedAtstring

Fecha 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.

EventoCuándo se dispara
payment.approvedUn pago es aprobado por Mercado Pago. Incluye pagos nuevos y actualizaciones de estado.
payment.receivedPay Alert recibe un pago aprobado por primera vez (sin procesar antes).
payment.refundedUn pago es reembolsado total o parcialmente.
payment.cancelledUn 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.

POST/api/v1/businesses/{businessId}/webhooks

Crea 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
  }'
GET/api/v1/businesses/{businessId}/webhooks

Lista 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>"
POST/api/v1/businesses/{businessId}/webhooks/{webhookId}/test

Enví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 '{}'
PUT/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.