--- title: "Verify HMAC signatures" description: "Verify the integrity of webhook events using HMAC signatures." url: "https://docs.adyen.com/development-resources/webhooks/secure-webhooks/verify-hmac-signatures" source_url: "https://docs.adyen.com/development-resources/webhooks/secure-webhooks/verify-hmac-signatures.md" canonical: "https://docs.adyen.com/development-resources/webhooks/secure-webhooks/verify-hmac-signatures" last_modified: "2026-05-23T12:56:20+02:00" language: "en" --- # Verify HMAC signatures Verify the integrity of webhook events using HMAC signatures. [View source](/development-resources/webhooks/secure-webhooks/verify-hmac-signatures.md) To protect your server from unauthorised webhook events, we strongly recommend that you use [Hash-based message authentication code](https://en.wikipedia.org/wiki/HMAC) (HMAC) signatures for our webhooks that support it. For the ones that support [enabling HMAC signatures](#enable-hmac-signatures), each webhook event will include a signature calculated using a secret HMAC key and a payload from the webhook. By verifying this signature, you confirm that the webhook was sent by Adyen, and was not modified during transmission. To verify HMAC signatures, you can either: * [Use one of our libraries](#verify-using-our-libraries). * [Build your own custom solution](#verify-using-your-own-solution). ## Enable HMAC signatures Each secret HMAC key is linked to one webhook endpoint. If you have multiple endpoints for receiving webhooks, generate an HMAC key for each of them. You also need to generate a new HMAC key when you [switch from test to live](/development-resources/webhooks/configure-and-manage#test-and-go-live). To start receiving HMAC signed webhooks, if the option is available: 1. In your [Customer Area](https://ca-test.adyen.com/), go to **Developers** > **Webhooks**. 2. From the list of webhooks, select the one to configure. 3. Select **Edit webhook** or the edit icon **. 4. Under **Security**, generate a new HMAC key or enter an existing HMAC key.\ If you generate a new HMAC key, copy it and store it securely in your system. 5. Select **Save configuration**. The HMAC key will now be used to sign webhook events that we send to your server. For our [Standard webhooks](/development-resources/webhooks/webhook-types), the signature is included in the `additionalData` field. For [other types of webhooks](/development-resources/webhooks/webhook-types#other-webhooks), although rare, the signature can be included in the request header. When the HMAC signature is included in the header, the steps to follow to verify the signature is different. See [Verify HMAC keys returned in the header](#verify-hmac-in-header). ```json { "live":"false", "notificationItems":[ { "NotificationRequestItem":{ "additionalData":{ "hmacSignature":"+JWKfq4ynALK+FFzGgHnp1jSMQJMBJeb87dlph24sXw=" }, ... } } ] } ``` ### Changing your HMAC key If you need to change the secret HMAC key used to sign webhook events, it is enough to [generate a new HMAC key](/development-resources/webhooks/secure-webhooks/verify-hmac-signatures#enable-hmac-signatures) in your [Customer Area](https://ca-test.adyen.com/). If you generate a new HMAC key, it can take some time to propagate this in our infrastructure, so make sure that you can still accept webhook events signed with your previous HMAC key for some time. ## Verify using our libraries You can verify HMAC signatures using Adyen's packaged HMAC validator libraries: * [Java library](https://github.com/Adyen/adyen-java-api-library/blob/master/src/main/java/com/adyen/util/HMACValidator.java) * [PHP library](https://github.com/Adyen/adyen-php-api-library/blob/9c351e93d0ac7000524f70f31c164f682dbf7492/src/Adyen/Util/HmacSignature.php) * [C# library](https://github.com/Adyen/adyen-dotnet-api-library/blob/fc3301c13db828ac9d3ff4054eac2f4dcaecf208/Adyen/Util/HMACValidator.cs) * [JavaScript library](https://github.com/Adyen/adyen-node-api-library/blob/7e76d1b4c87d334f98a1ddc70b4e30a225244d2e/src/utils/hmacValidator.ts) * [Ruby Library](https://github.com/Adyen/adyen-ruby-api-library/blob/develop/lib/adyen/utils/hmac_validator.rb) * [Python Library](https://github.com/Adyen/adyen-python-api-library/blob/develop/Adyen/util.py) #### Java ```java // YOUR_HMAC_KEY from the Customer Area String hmacKey = "YOUR_HMAC_KEY"; // Notification Request JSON String notificationRequestJson = "NOTIFICATION_REQUEST_JSON"; HMACValidator hmacValidator = new HMACValidator(); WebhookHandler webhookHandler = new WebhookHandler(); NotificationRequest notificationRequest = webhookHandler.handleNotificationJson(notificationRequestJson); // Handle multiple notificationRequests List notificationRequestItems = notificationRequest.getNotificationItems(); for ( NotificationRequestItem notificationRequestItem : notificationRequestItems ) { // Handle the notification if ( hmacValidator.validateHMAC(notificationRequestItem, hmacKey) ) { // Process the notification based on the eventCode String eventCode = notificationRequestItem.getEventCode(); } else { // Non valid NotificationRequest System.out.print("Non valid NotificationRequest"); } } ``` #### PHP ```php // YOUR_HMAC_KEY from the Customer Area $hmacKey = "YOUR_HMAC_KEY"; // Notification Request JSON $jsonRequest = "NOTIFICATION_REQUEST_JSON"; $notificationRequest = json_decode($jsonRequest, true); $hmac = new \Adyen\Util\HmacSignature(); // Handling multiple notificationRequests foreach ( $notificationRequest["notificationItems"] as $notificationRequestItem ) { $params = $notificationRequestItem["NotificationRequestItem"]; // Handle the notification if ( $hmac->isValidNotificationHMAC($hmacKey, $params) ) { // Process the notification based on the eventCode $eventcode = $params['eventCode']; print_r($eventcode); } else { // Non valid NotificationRequest } } ``` #### C\# ```cs // YOUR_HMAC_KEY from the Customer Area string hmacKey = "YOUR_HMAC_KEY"; // Notification Request JSON string notificationRequestJson = "NOTIFICATION_REQUEST_JSON"; var hmacValidator = new HmacValidator(); var notificationHandler = new NotificationHandler(); var handleNotificationRequest = notificationHandler.HandleNotificationRequest(notificationRequestJson); // Handle multiple notificationRequests List notificationRequestItemContainers = handleNotificationRequest.NotificationItemContainers; foreach ( var notificationRequestItemContainer in notificationRequestItemContainers ) { var notificationItem = notificationRequestItemContainer.NotificationItem; // Handle the notification if ( hmacValidator.IsValidHmac(notificationItem, hmacKey) ) { // Process the notification based on the eventCode string eventCode = notificationItem.EventCode; } else { // Non valid NotificationRequest } } ``` #### NodeJS (JavaScript) ```js const { hmacValidator } = require('@adyen/api-library'); // YOUR_HMAC_KEY from the Customer Area const hmacKey = "YOUR_HMAC_KEY"; const validator = new hmacValidator() // Notification Request JSON const notificationRequest = NOTIFICATION_REQUEST_JSON; const notificationRequestItems = notificationRequest.notificationItems // Handling multiple notificationRequests notificationRequestItems.forEach(function(notificationRequestItem) { // Get the notification const notification = notificationRequestItem.NotificationRequestItem // Handle the notification if( validator.validateHMAC(notification, hmacKey) ) { // Process the notification based on the eventCode const eventCode = notification.eventCode; } else { // Non valid NotificationRequest console.log("Non valid NotificationRequest"); } }); ``` ## Verify using your own solution To build your own solution for verifying HMAC signatures, follow these steps: ### Step 1: Construct the payload The values used below are from an [example webhook](#example). To test your solution, replace the values with actual values that you receive in a webhook event. Concatenate the following values from the webhook event, in the given order: | Key | Value | | --------------------- | ----------------------------- | | `pspReference` | **7914073381342284** | | `originalReference` | | | `merchantAccountCode` | **TestMerchant** | | `merchantReference` | **TestPayment-1407325143704** | | `value` | **1130** | | `currency` | **EUR** | | `eventCode` | **AUTHORISATION** | | `success` | **true** | Assign an empty string to any fields that are empty, and use a colon (":") to delimit the values. For the above values, with an empty `originalReference`, you get: ```bash 7914073381342284::TestMerchant:TestPayment-1407325143704:1130:EUR:AUTHORISATION:true ``` ### Step 2: Calculate the HMAC signature For **hints** about how to calculate the signature, have a look at the [library code samples](#verify-using-our-libraries) above. 1. Convert the [payload that you constructed](#construct-the-payload) to a binary representation, given the UTF-8 charset. 2. Convert the hexadecimal HMAC key that you [generated in your Customer Area](/development-resources/webhooks/secure-webhooks/verify-hmac-signatures/#enable-hmac-signatures) from a hexadecimal string to a binary representation, given the UTF-8 charset. 3. Calculate an HMAC using: * The [SHA256](https://en.wikipedia.org/wiki/SHA-2) function. * The binary representation of the payload. * The binary representation of the HMAC key. 4. To get the final signature, [Base64](https://en.wikipedia.org/wiki/Base64)-encode the result. ### Step 3: Compare signatures If the signature that you calculated in [Step 2](#calculate-the-hmac-signature) matches the `hmacSignature` that you received, you'll know that the webhook event was sent by Adyen and was not modified during transmission. ### Example Sample HMAC key: ```bash 44782DEF547AAA06C910C43932B1EB0C71FC68D9D0C057550C48EC2ACF6BA056 ``` Sample webhook event signed using the above HMAC key: ```json { "live":"false", "notificationItems":[ { "NotificationRequestItem":{ "additionalData":{ "hmacSignature":"coqCmt/IZ4E3CzPvMY8zTjQVL5hYJUiBRg8UU+iCWo0=" }, "amount":{ "value":1130, "currency":"EUR" }, "pspReference":"7914073381342284", "eventCode":"AUTHORISATION", "eventDate":"2019-05-06T17:15:34.121+02:00", "merchantAccountCode":"TestMerchant", "operations":[ "CANCEL", "CAPTURE", "REFUND" ], "merchantReference":"TestPayment-1407325143704", "paymentMethod":"visa", "success":"true" } } ] } ``` ## Verify HMAC keys returned in the header For non-standard webhooks, Adyen can sign the webhook with an HMAC signature in the request header, instead of including it in the `additionalData` field. If you enabled [other webhooks](/development-resources/webhooks/webhook-types#other-webhooks) that follow this pattern, for example, the **Recurring tokens life cycle events** webhook, there are different steps to verify the HMAC signature. 1. For every webhook that you receive, get the values from the following headers: * `hmacsignature`: Contains the signature. * `protocol`: The protocol used to create the signature, **HmacSHA256**. 2. Calculate the signature using: * [SHA256](https://en.wikipedia.org/wiki/SHA-2). * The binary representation of the payload. * The binary representation of the HMAC key. * Make sure that the request body is as it is—do not deserialize it. 3. To get the final signature, [Base64](https://en.wikipedia.org/wiki/Base64)-encode the result. 4. Compare the `hmacsignature` received from the header and the calculated signature. If the signatures match, then the webhook was sent by Adyen and was not modified during transmission. Below is an examples HMAC signature validation. #### Java ```java import java.io.UnsupportedEncodingException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import javax.crypto.Mac; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.codec.DecoderException; import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Hex; import org.junit.Assert; import org.junit.Test; public class NotificationHmacExampleTest { @Test public void testNotificationHmac(){ Base64 base64 = new Base64(); // Example HEX Key (submitted at the moment of subscription) String hmacSignatureKey = "6D5BADA576A73109D879220DCB793FFD67DEF7AA18C74CCC0AB66FD87AC8AEEA"; // Signature: retrieved from HTTP header under the name HmacSignature. String hmacSignature = "nvsZjQiHBuscSdtcA2cl1E+PSLJfgjPeRdd0pSaRiA0="; // Protocol: retrieved from HTTP header under the name Protocol. String protocol = "HmacSHA256"; // Payload: body of the notification String payload = "{\"createdAt\":\"2024-01-14T18:10:49+01:00\",\"environment\":\"test\",\"type\":\"recurring.token.disabled\",\"data\":{\"merchantAccount\":\"YOUR_MERCHANT_ACCOUNT\",\"shopperReference\":\"YOUR_SHOPPER_REFERENCE\",\"storedPaymentMethodId\":\"M5N7TQ4TG5PFWR50\",\"type\":\"visa\"},\"eventId\":\"QBQQ9DLNRHHKGK38\"}"; try { // decode HEX Key into bytes byte[] keyBytes = Hex.decodeHex(hmacSignatureKey.toCharArray()); // get payload in bytes byte[] payloadBytes = payload.getBytes("UTF-8"); // instantiate the MAC object using HMAC / SHA256 Mac hmacSha256 = Mac.getInstance(protocol); // create a key object using the secret key and MAC object SecretKey secretKey = new SecretKeySpec(keyBytes, hmacSha256.getAlgorithm()); // initialise the MAC object hmacSha256.init(secretKey); // finalize the MAC operation byte[] signedPayload = hmacSha256.doFinal(payloadBytes); // encode the signed payload in Base64 byte[] encodedSignedPayload = base64.encode(signedPayload); System.out.println("original HMAC signature: " + hmacSignature); System.out.println("computed HMAC signature: " + new String(encodedSignedPayload, "ASCII")); // assert the calculated Base64 encoded HMAC is equal to the received Base64 encoded HMAC Assert.assertTrue(Arrays.equals(encodedSignedPayload, hmacSignature.getBytes("UTF-8"))); } catch (NoSuchAlgorithmException e) { // HmacSHA256 should be supported } catch (UnsupportedEncodingException e) { // UTF-8 should be supported } catch (DecoderException e) { // Check key for odd number or characters outside of HEX (base16) } catch (InvalidKeyException e) { // The key is invalid } } } ``` #### Python ```py import hmac import hashlib import base64 def checkHmac(payload, hmac_key, hmac_sig): # payload is the request body as it is # hmac_key is the secret # hmac_sig is the signature from the header hmac_key = binascii.a2b_hex(hmac_key) # Calculate signature calculatedHmac = hmac.new(hmac_key, payload.encode('utf-8'), hashlib.sha256).digest() calculatedHmac_b64 = base64.b64encode(calculatedHmac) receivedHmac_b64 = hmac_sig.encode('utf-8') validSignature = hmac.compare_digest(receivedHmac_b64, calculatedHmac_b64) if not validSignature: print('HMAC is invalid: {} {}'.format(receivedHmac_b64, calculatedHmac_b64)) return False return True ``` ## See also * [Webhooks](/development-resources/webhooks)