Test Redsys notifications without placing real orders
Paste the notification Redsys sent to your Ds_Merchant_MerchantURL: the tool decodes Ds_MerchantParameters, checks Ds_Signature with your merchant key and tells you what Ds_Response means. Or generate a correctly signed notification and replay it against your local handler as many times as you need.
The key is only used to compute the signature on our server and is never stored or logged. Use sandbox keys when you can.
Capture the real notification first
Set Ds_Merchant_MerchantURL to a webhook URL from the inspector (no signup), run a test payment in the Redsys sandbox, and the notification shows up live with its headers and raw body — then paste it here or forward it to localhost with the CLI:
npx webhook-toolkit listen --forward http://localhost:3000/redsys/notify
How the signature is computed
- • HMAC_SHA256_V1: the merchant key (base64, from the Redsys admin) is decoded to 24 bytes; the order number (Ds_Order) is encrypted with 3DES-CBC, zero IV, zero padding — that is the per-order key.
- • Ds_Signature = base64 of HMAC-SHA256(per-order key, Ds_MerchantParameters exactly as received). Notifications use URL-safe base64 (- and _): normalise before comparing.
- • HMAC_SHA512_V2: the first 16 characters of the secret are an AES-128 key; the order is encrypted with AES-CBC (zero IV, PKCS7), the base64 text of the result is the HMAC-SHA512 key.
- • Always sign the Ds_MerchantParameters string you received — never a re-encoded JSON — and read Ds_Order from the decoded parameters (Ds_Order in notifications, DS_MERCHANT_ORDER in requests).
import crypto from "node:crypto";
export function redsysSignature(merchantKeyB64, merchantParameters) {
const params = JSON.parse(Buffer.from(merchantParameters, "base64").toString());
const order = Buffer.from(params.Ds_Order ?? params.DS_MERCHANT_ORDER);
const padded = Buffer.alloc(Math.ceil(order.length / 8) * 8);
order.copy(padded);
const des = crypto.createCipheriv("des-ede3-cbc", Buffer.from(merchantKeyB64, "base64"), Buffer.alloc(8));
des.setAutoPadding(false);
const key = Buffer.concat([des.update(padded), des.final()]);
return crypto.createHmac("sha256", key).update(merchantParameters).digest("base64");
}
// compare with Ds_Signature after replacing "-" by "+" and "_" by "/"Frequently asked questions
Why does my Redsys signature not match?
Most often: the signature is computed on a re-encoded Ds_MerchantParameters instead of the exact string received; the URL-safe base64 of Ds_Signature (- and _) is compared with standard base64 (+ and /); the key used is the one from another terminal or from the test environment; or Ds_Order is read from the wrong field. The tool shows the expected signature so you can see which one it is.
Which Ds_Response codes mean the payment succeeded?
0000 to 0099 are authorised payments. Everything else is a refusal: 0190 generic denial, 0184 3-D Secure authentication failed, 0116 insufficient funds, 9915 cancelled by the customer, 0101 expired card.
Can I test notifications against localhost?
Yes. Redsys only calls public URLs, so either give it a Webhook Toolkit URL and run npx webhook-toolkit listen --forward http://localhost:3000/redsys/notify, or generate a signed notification here and send it with the curl command.
Does it support Bizum?
Yes: Bizum payments go through the same Redsys notification with the same signature; only the payment method fields differ.