Last active
March 22, 2023 20:36
-
-
Save edorivai/a3c3697633d9b70d667a8543f6c3f6ed to your computer and use it in GitHub Desktop.
Generates the HMAC signature as required by the Adyen API.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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= |
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"
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
Thanks for this!