Skip to content

Instantly share code, notes, and snippets.

@dsumer
Created May 18, 2022 11:09
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dsumer/3594cda57e84a93a9019cddc71831882 to your computer and use it in GitHub Desktop.
Save dsumer/3594cda57e84a93a9019cddc71831882 to your computer and use it in GitHub Desktop.
A function which validates Paddle Webhook requests
import type { NextApiRequest } from 'next';
import crypto from 'crypto';
import * as Serialize from 'php-serialize';
const allowedIpAdresses = [
// Sandbox
'34.194.127.46',
'54.234.237.108',
'3.208.120.145',
// Production
'34.232.58.13',
'34.195.105.136',
'34.237.3.244',
];
const getIpAddress = (req: NextApiRequest): string => {
const forwarded = req.headers['x-forwarded-for'] || '';
if (typeof forwarded === 'string') {
return forwarded.split(',')[0] || req.socket.remoteAddress || '';
}
return forwarded[0] || req.socket.remoteAddress || '';
};
function ksort(obj) {
const keys = Object.keys(obj).sort();
const sortedObj = {};
for (const i in keys) {
sortedObj[keys[i]] = obj[keys[i]];
}
return sortedObj;
}
export function validateWebhook(req: NextApiRequest) {
if (!allowedIpAdresses.includes(getIpAddress(req))) {
console.error('No valid paddle ip address');
return false;
}
let jsonObj = req.body;
// Grab p_signature
const mySig = Buffer.from(jsonObj.p_signature, 'base64');
// Remove p_signature from object - not included in array of fields used in verification.
delete jsonObj.p_signature;
// Need to sort array by key in ascending order
jsonObj = ksort(jsonObj);
for (const property in jsonObj) {
if (jsonObj.hasOwnProperty(property) && typeof jsonObj[property] !== 'string') {
if (Array.isArray(jsonObj[property])) {
// is it an array
jsonObj[property] = jsonObj[property].toString();
} else {
//if its not an array and not a string, then it is a JSON obj
jsonObj[property] = JSON.stringify(jsonObj[property]);
}
}
}
// Serialise remaining fields of jsonObj
const serialized = Serialize.serialize(jsonObj);
// verify the serialized array against the signature using SHA1 with your public key.
const verifier = crypto.createVerify('sha1');
verifier.update(serialized);
verifier.end();
const publicKey = process.env.PADDLE_PUBLIC_KEY.replace(/\\n/g, '\n');
const isValid = verifier.verify(publicKey, mySig);
if (!isValid) {
console.error('Invalid paddle signature');
}
return isValid;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment