I am not sure but feels like the realm is transforming the request body before passing it to the attached function. Therefore “stripe.webhooks.constructEvent” is not getting the data in the desired format.
We solved the issue by manually verifying the signature with a custom function.
/***
* This function manually validates the stripe signature for the webhook event
*
* Returns: boolean => true if valid request
*
* stripeSignature: string => Stripe-Signature received from request header i.e. request.headers["Stripe-Signature"][0]
* requestBody:string => request body from header i.e. request.body.text()
*/
exports = function (stripeSignature, requestBody) {
// e.g stripeSignature = "t=2345345,v1=sdfas234sfasd, v0=akhjgfska1234234123"
const { getUnixTime } = require("date-fns");
//0. tolerance of 5 mins
const TOLERANCE = 300;
//1. get webhook endpoint secret from the values
const STRIPE_WEBHOOK_SECRET = context.values.get("STRIPE_WEBHOOK_SECRET");
console.log("STRIPE_WEBHOOK_SECRET: ", STRIPE_WEBHOOK_SECRET);
// 2. split the signature using "," to get a list of signature elements
const signatureComponents = stripeSignature.split(",");
// 3. Create a dictionary with with prefix as key for the splitted item
const signComponentsDict = {};
signatureComponents.forEach((item) => {
const [prefix, value] = item.split("=");
if (["v1", "t"].includes(prefix)) {
signComponentsDict[prefix] = value;
}
});
// 4. crete a signed payload by concatenting "t" element and the request body
const signedPayload = `${signComponentsDict["t"]}.${requestBody}`;
//5. the "v1" is actual signature from stripe
const incomingSignature = signComponentsDict["v1"];
//6. compute your own signature using the signed payload created in step 4
const expectedSignature = utils.crypto.hmac(
input = signedPayload,
secret = STRIPE_WEBHOOK_SECRET,
hash_function = "sha256",
output_format = "hex"
);
//7. Get current time in unix format and also get the time from stripeSignature
const incomingTimestamp = signComponentsDict["t"];
const currTimestamp = getUnixTime(new Date());
const timestampDiff = currTimestamp - incomingTimestamp;
//8. Comparision the signature respecting tolerance
if (timestampDiff <= TOLERANCE) {
if (incomingSignature === expectedSignature) {
return true;
}
}
return false;
};