Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't get webhook.verify to work #13

Open
krjo opened this issue Feb 24, 2021 · 5 comments
Open

Can't get webhook.verify to work #13

krjo opened this issue Feb 24, 2021 · 5 comments

Comments

@krjo
Copy link

krjo commented Feb 24, 2021

Having trouble with webhook.verify using express. I think your webhook method arguments might be backwards in the readme, but I tried the following with no luck:

recharge.webhook.validate(req.body, process.env.API_CLIENT_SECRET).then((webhook) => console.log(webhook)).catch((err) => console.error(err));

recharge.webhook.validate(req, process.env.API_CLIENT_SECRET).then((webhook) => console.log(webhook)).catch((err) => console.error(err));

recharge.webhook.validate(process.env.API_CLIENT_SECRET, req).then((webhook) => console.log(webhook)).catch((err) => console.error(err));

recharge.webhook.validate(process.env.API_CLIENT_SECRET, req.body).then((webhook) => console.log(webhook)).catch((err) => console.error(err));

@yehorbobrovvkachava
Copy link

I have similar issue. @krjo are you fixed this issue?

@yehorbobrovvkachava
Copy link

I asked Recharge Support how to validate by nodejs and got next example

const express = require('express') const app = express() const crypto = require('crypto') const bodyParser = require('body-parser') const clientSecret = 'CLIENT SECRET HERE'; function isWebhookValid(reqBody, webhookHmac) { let calculatedHmac = crypto.createHash('sha256').update(clientSecret).update(reqBody).digest('hex'); if (calculatedHmac == webhookHmac) { console.log('Webhook validated successfully!'); return true; } else { console.log('Oops! There may be some third party interference going on.'); return false; } } app.use(bodyParser.text({type: '/'})); [app.post](http://app.post/)("/webhook", (request, response) => { let success = isWebhookValid(request.body, request.header('X-Recharge-Hmac-Sha256')) return response.status(success ? 200 : 400).json({result: success}); }) port = 5000 app.listen(port, () => console.log(Webhook validation app listening at http://localhost/:${port}))

It means that current library has incorrect code for hash generation.

@rgaino
Copy link

rgaino commented Jun 27, 2023

Here's my middleware function to validate Recharge webhooks for those coming in the future. You'll need:

  • The RECHARGE_API_CLIENT_SECRET env var as described here,

  • The raw body, using body-parser and this snippet with express:

app.use(
	bodyParser.json({
		verify: (req: Request, res: Response, buf: Buffer) => {
			req.rawBody = buf;
		},
	}),
);
  • And finally I use the http-errors package to throw http errors.
export const validateRechargeWebhook = (req: Request, res: Response, next: NextFunction) => {
	const { rawBody } = req;

	if (!rawBody) {
		throw createError.Forbidden();
	}

	const generatedHash = crypto
		.createHash('sha256')
		.update(process.env.RECHARGE_API_CLIENT_SECRET)
		.update(Buffer.from(rawBody))
		.digest('hex');

	if (generatedHash !== req.headers['x-recharge-hmac-sha256']) {
		throw createError.Forbidden();
	}

	next();
};

@travish
Copy link

travish commented Sep 13, 2023

Another middleware example, for NextJS, using the Web Crypto API:

async function generateHash(body: string): Promise<string> {
  const msgUint8 = new TextEncoder().encode(
    process.env.X_RECHARGE_ACCESS_SECRET + body,
  ); // encode as (utf-8) Uint8Array
  const hashBuffer = await crypto.subtle.digest("SHA-256", msgUint8); // hash the message
  const hashArray = Array.from(new Uint8Array(hashBuffer)); // convert buffer to byte array
  // convert bytes to hex string
  return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
}
export default async function validateWebhook(
  hmac: string | null,
  body: any,
): Promise<boolean> {
  return (await generateHash(JSON.stringify(body))) === hmac;
}

@david-supernormal
Copy link

Using express, I managed to validate the webhook using as middleware:

app.use(bodyParser.raw({ type: 'application/json' }))

And the validation function:

import * as crypto from 'crypto'

function validateRechargeWebhook(req, res, next) {
    const secret = process.env.RECHARGE_API_CLIENT_SECRET ?? ''
    const payload = Buffer.from(req.body).toString()
    const webhookHmac = req.header('X-Recharge-Hmac-Sha256')

    const hash = crypto
        .createHash('sha256')
        .update(secret, 'utf-8')
        .update(payload, 'utf-8')
        .digest('hex')

    if (hash === webhookHmac) {
        console.log('Webhook validated successfully!')
        next()
    } else {
        console.log(
            'Oops! There may be some third party interference going on.'
        )
        res.status(401).send()
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants