Created
May 18, 2022 11:09
-
-
Save dsumer/3594cda57e84a93a9019cddc71831882 to your computer and use it in GitHub Desktop.
A function which validates Paddle Webhook requests
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 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