I have developed a Shopify App , had deploy it on aws and now i want to publish it on shopify app store , but verify Webhooks with HMAC signatures is coming { i had added middleware in my backend api , i.e the code below , but still these verify with hmac signatures in coming ]
import {
Injectable,
NestMiddleware,
UnauthorizedException,
} from ‘@nestjs/common’;
import { Request, Response, NextFunction } from ‘express’;
import * as crypto from ‘crypto’;
@Injectable()
export class VerifyShopifyWebhookMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
const hmacHeader = req.headers[‘x-shopify-hmac-sha256’] as string; // use lowercase key
const secret = process.env.SHOPIFY_API_SECRET as string;
console.log(secret, secret);
// console.log(hmacHeader, hmacHeader);
const rawBody = req.body; // should be Buffer from express.raw()
const generatedHmac = crypto
.createHmac(‘sha256’, secret)
.update(rawBody, ‘utf8’)
.digest(‘base64’);
if (generatedHmac !== hmacHeader) {
throw new UnauthorizedException(‘Invalid Shopify webhook HMAC’);
}
// Optional: parse JSON so controller can use it easily
try {
req.body = JSON.parse(rawBody.toString(‘utf8’));
} catch {
throw new UnauthorizedException(‘Invalid JSON body’);
}
next();
}
}
Hello @sachin20
You’re on the right track with verifying Shopify Webhooks using HMAC in a NestJS middleware. However, based on your description and the code snippet, it seems the issue likely comes from how you’re handling the request body.
Key thing Shopify expects:
Shopify webhooks must be verified using the raw request body (as a Buffer) — but express.json() middleware (or similar) would have already parsed the body into an object, losing the raw Buffer you need for HMAC verification.
Solution:
You need to use express.raw({ type: ‘application/json’ }) middleware before your HMAC verification middleware — this allows you to get the raw Buffer of the webhook request body.
Step-by-step Fix:
- Use express.raw for webhook route only
In your main app file (e.g., main.ts or wherever you configure middleware), apply express.raw() to your webhook routes before applying any other middleware (like express.json()):
import * as express from 'express';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Use express.raw() specifically for webhooks
app.use('/webhooks', express.raw({ type: 'application/json' }));
await app.listen(3000);
}
bootstrap();
- Fix Middleware to use Buffer directly
Update your middleware to not parse the body again and work directly with the raw Buffer:
import {
Injectable,
NestMiddleware,
UnauthorizedException,
} from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import * as crypto from 'crypto';
@Injectable()
export class VerifyShopifyWebhookMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
const hmacHeader = req.headers['x-shopify-hmac-sha256'] as string;
const secret = process.env.SHOPIFY_API_SECRET as string;
const rawBody = req.body; // raw Buffer from express.raw()
const generatedHmac = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('base64');
if (generatedHmac !== hmacHeader) {
throw new UnauthorizedException('Invalid Shopify webhook HMAC');
}
// Parse body to JSON for controllers if needed
try {
req.body = JSON.parse(rawBody.toString('utf8'));
} catch {
throw new UnauthorizedException('Invalid JSON body');
}
next();
}
}
Summary
. Use express.raw() only for the webhook route (before express.json()).
. Don’t assume req.body is JSON — it’s a Buffer.
. Don’t re-use express.json() for webhook routes.
Thank
you