Skip to content

Instantly share code, notes, and snippets.

@edorivai
Last active March 22, 2023 20:36
Show Gist options
  • Save edorivai/a3c3697633d9b70d667a8543f6c3f6ed to your computer and use it in GitHub Desktop.
Save edorivai/a3c3697633d9b70d667a8543f6c3f6ed to your computer and use it in GitHub Desktop.
Generates the HMAC signature as required by the Adyen API.
import crypto from 'crypto';
// Docs: https://docs.adyen.com/developers/payments/accepting-payments/hmac-signature-calculation
export default function generateSignature(body, hmacKey) {
const keys = Object
.keys(body)
// Step 1: Sort by keys
.sort();
const values = keys
.map(key => body[key])
// Step 2a: Replace falsy values with empty strings
.map(value => ((value === null || value === undefined) ? '' : value.toString()))
// Step 2b: Escape slashes and colons
.map(value => value.replace(/\\/g, '\\\\').replace(/:/g, '\\:'));
// Step 3: Concatenate the key names, first, followed by the values. Use a colon (“:”) to
// delimit the key names and values
const hashingInput = [...keys, ...values].join(':');
// Step 4: Convert the HMAC key to the binary representation.
const hmac = crypto.createHmac('sha256', new Buffer(hmacKey, 'hex'));
hmac.update(hashingInput);
// Step 5: Calculate the HMAC with the signing string
// Step 6: Encode the result using the Base64 encoding scheme to obtain the signature.
return hmac.digest('base64');
}
// Usage:
const testKey = '44782DEF547AAA06C910C43932B1EB0C71FC68D9D0C057550C48EC2ACF6BA056';
const body = {
shopperLocale : 'en_GB',
merchantReference: 'paymentTest:143522\\64\\39255',
merchantAccount : 'TestMerchant',
sessionValidity : '2018-07-25T10:31:06Z',
shipBeforeDate : '2018-07-30',
paymentAmount : 1995,
currencyCode : 'EUR',
skinCode : 'X7hsNDWp',
};
generateSignature(body, testKey); // 8SFtIc6zQlswxAZqDKXL+BpRmlDvIWyjOwU8wdl0zK4=
@Corjen
Copy link

Corjen commented Aug 22, 2019

Thanks for this!

@advename
Copy link

Awesome!

As an alternative, adyen provide their own Hmac generator:

import { hmacValidator } from "@adyen/api-library";

const body = {
    amount: { currency: "DKK", value: 0 },
    eventCode: "AUTHORISATION",
    merchantAccountCode: "Banana",
    merchantReference: "123",
    pspReference: "999",
    success: "true",
};

const validator = new hmacValidator();

// generate Body with Hmac
const signature = validator.calculateHmac({ ...body }, "ABC");

const bodyWithHmac = {
    ...body,
    additionalData: { hmacSignature: signature },
};

// Validate body with Hmac
const valid = validator.validateHMAC(bodyWithHmac, "ABC");
console.log("Valid? ", valid); // "Valid? true"

@smoore4moma
Copy link

My version of the same. Thanks for the Buffer hint on the hmacKey (slightly changed btw).

  // Verify incoming webhook.
  function verifyWebhook(payload) {

    const hmac = payload.additionalData.hmacSignature;
    const hmacKey = process.env.HMAC_KEY;

    const pspReference = payload.pspReference || '' ;
    const originalReference = payload.originalReference || '' ;
    const merchantAccountCode = payload.merchantAccountCode || '' ;
    const merchantReference = payload.merchantReference || '' ;
    const value = payload.amount.value.toString() || '' ;
    const currency = payload.amount.currency || '' ;
    const eventCode = payload.eventCode || '' ;
    const success = payload.success || '' ;

    let payloadToSign = pspReference + ':' + originalReference + ':' + merchantAccountCode + ':' + merchantReference + ':' + value + ':' + currency + ':' + eventCode + ':' + success;

    const genHash = crypto.createHmac("sha256", Buffer.from(hmacKey, 'hex'))
      .update(payloadToSign.toString(), "utf-8")
      .digest("base64");
    return genHash === hmac;
  }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment