Verify Shopify Webhooks for private apps

Shopify Partner
1 0 1

I am setting up Shopify webhooks for my app and I want to verify the webhooks. I am creating the webhooks using this API. I am verifying the webhooks in Node Js and this is the code that I am using to do it.



const verifyWebhook = async (req, res, next) => {
  const hmac = req.get("X-Shopify-Hmac-Sha256");
  const body = await getRawBody(req);

  // Create hash based on body and key
  const generatedHash = crypto
    .createHmac("sha256", secretKey)

  // Compare our hash to Shopify's hash
  if (generatedHash !== hmac) {
    return res.sendStatus(401);

  req.body = JSON.parse(body);



However, I am not so sure about the secret that I should use. I tried using the API secret available on the credentials tab for an app but it doesn't seem to work.

However, when I tried creating the webhooks from the store settings and used the secret available there then it works fine but I am not sure about the secret I should be using in case of webhooks created using the API. I have read the documentation and it mentions some shared secret but I am not able to figure out what it is.

Any help is highly appreciated and thanks in advance.

Replies 2 (2)

Shopify Partner
8 0 1

Hi, any luck with this?

Shopify Partner
1 0 0

I'm also having a similar issue. Here is my code.



// pages/api/shopifyWebhook.js
import crypto from 'crypto';
import clientPromise from '../../utils/mongodb'; // Import MongoDB utility

export const config = {
    api: {
        bodyParser: false, // Disables Next.js body parsing to access the raw body

export default async function handler(req, res) {
    const secret = 'SECRET_HERE'; //I've tried all the secrets I have access to in shopify apps, and not sure which one to use. 

    if (req.method === 'POST') {
        // Extract the Shopify HMAC signature from the request headers
        const hmacHeader = req.headers['x-shopify-hmac-sha256'];
        // Get the raw body of the request
        const body = await getRawBody(req);

        // Compute an HMAC with SHA256 hash of the body using the secret
        const hash = crypto
            .createHmac('sha256', secret)
            .update(body, 'utf8')

        if (crypto.timingSafeEqual(Buffer.from(hash), Buffer.from(hmacHeader))) {
            // Parse the verified webhook payload
            const webhook = JSON.parse(body);
            console.log('Webhook verified and processed:', webhook);
                    res.status(200).json({ message: 'Webhook processed and data saved' });
                } catch (error) {
                    console.error('Database operation failed', error);
                    res.status(500).json({ success: false, message: 'Internal server error' });
            } else {
                res.status(200).json({ message: 'Webhook verified but no action taken' });
        } else {
            return res.status(403).json({ error: 'Verification failed' });
    } else {
        // Handle non-POST requests or other logic
        res.setHeader('Allow', ['POST']);
        res.status(405).end(`Method ${req.method} Not Allowed`);

async function getRawBody(req) {
    return new Promise((resolve, reject) => {
        let body = '';
        req.on('data', (chunk) => { body += chunk.toString(); });
        req.on('end', () => { resolve(body); });
        req.on('error', (err) => { reject(err); });