Validates MercadoPago webhook signatures (HMAC-SHA256).
Call validate/5 on every incoming webhook. It returns {:ok, ts} on success
and {:error, %InvalidSignatureError{}} on failure — an invalid signature is
an expected outcome for any public endpoint, so it is reported as data, not by
raising. Use validate!/5 when you prefer a raising variant.
Legacy QR Code notifications are not signed by MercadoPago, so validation will always fail for them. QR payments delivered through the Orders API are signed and should be validated like any other event.
Example
case Mercadopago.Webhook.Validator.validate(
conn.req_headers |> Map.new() |> Map.get("x-signature"),
conn.req_headers |> Map.new() |> Map.get("x-request-id"),
conn.params["data"]["id"],
webhook_secret,
tolerance_seconds: 300
) do
{:ok, _ts} -> send_resp(conn, 200, "")
{:error, %InvalidSignatureError{} = e} -> send_resp(conn, 401, e.message)
endRaising variant:
:ok = Mercadopago.Webhook.Validator.validate!(x_sig, x_req, data_id, secret)
Summary
Functions
Validates the signature of a MercadoPago webhook notification.
Like validate/5, but returns :ok on success and raises
InvalidSignatureError on failure.
Functions
@spec validate( String.t() | nil, String.t() | nil, String.t() | nil, String.t(), keyword() ) :: {:ok, String.t()} | {:error, Mercadopago.Webhook.Validator.InvalidSignatureError.t()}
Validates the signature of a MercadoPago webhook notification.
Arguments
x_signature- raw value of thex-signaturerequest headerx_request_id- value of thex-request-idheader (may be nil)data_id- value of thedata.idquery parameter (may be nil). Pass it exactly as received; it is lowercased internally before the HMAC, as the MercadoPago manifest requires.secret- HMAC key configured in Tus Integraciones
Options
:tolerance_seconds- max allowed drift between header timestamp and now:supported_versions- list of accepted signature versions (default:["v1"]):now- zero-arity function returning current time in milliseconds (for testing)
Returns
{:ok, ts} on success (where ts is the verified timestamp string),
{:error, %InvalidSignatureError{}} on failure. Raises ArgumentError
when secret is missing — that is a caller misconfiguration, not webhook
input.
@spec validate!( String.t() | nil, String.t() | nil, String.t() | nil, String.t(), keyword() ) :: :ok
Like validate/5, but returns :ok on success and raises
InvalidSignatureError on failure.