---
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-06-01T15:33:16+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<NotificationRequestItem> 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<NotificationRequestItemContainer> 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)
