Verify webhook on remix

blueskydev
Shopify Partner
1 0 0

Hi,
I am a newbie on shopify app. I am confusing with step Implement HMAC signature. I setup mandatory webhook like this in my code, can some one guide me how to verify webhook please
Thank you so much!

 

 
const shopify = shopifyApp({
  apiKey: process.env.SHOPIFY_API_KEY,
  apiSecretKey: process.env.SHOPIFY_API_SECRET || "",
  apiVersion: LATEST_API_VERSION,
  scopes: process.env.SCOPES?.split(","),
  appUrl: process.env.SHOPIFY_APP_URL || "",
  authPathPrefix: "/auth",
  sessionStorage: new PrismaSessionStorage(prisma),
  distribution: AppDistribution.AppStore,
  restResources,
  webhooks: {
    APP_UNINSTALLED: {
      deliveryMethod: DeliveryMethod.Http,
      callbackUrl: "/webhooks",
    },
    CUSTOMERS_DATA_REQUEST: {
      deliveryMethod: DeliveryMethod.Http,
      callbackUrl: "/webhooks",
      callback: async (topic, shop, body, webhookId) => {
        const payload = JSON.parse(body);
      }
    },
    CUSTOMERS_REDACT: {
      deliveryMethod: DeliveryMethod.Http,
      callbackUrl: "/webhooks",
      callback: async (topic, shop, body, webhookId) => {
        const payload = JSON.parse(body);
      }
    },
    SHOP_REDACT: {
      deliveryMethod: DeliveryMethod.Http,
      callbackUrl: "/webhooks",
      callback: async (topic, shop, body, webhookId) => {
        const payload = JSON.parse(body);
      }
    },
  },
  hooks: {
    afterAuth: async ({ session }) => {
      shopify.registerWebhooks({ session });
    },
  },
  future: {
    v3_webhookAdminContext: true,
    v3_authenticatePublic: true,
    unstable_newEmbeddedAuthStrategy: true,
  },
  ...(process.env.SHOP_CUSTOM_DOMAIN
    ? { customShopDomains: [process.env.SHOP_CUSTOM_DOMAIN] }
    : {}),
});
import type { ActionFunctionArgs } from "@remix-run/node";
import { authenticate } from "../shopify.server";
import db from "../db.server";

export const action = async ({ request }: ActionFunctionArgs) => {
  const { topic, shop, session, admin, payload } = await authenticate.webhook(
    request
  );

  if (!admin) {
    // The admin context isn't returned if the webhook fired after a shop was uninstalled.
    throw new Response();
  }

  switch (topic) {
    case "APP_UNINSTALLED":
      if (session) {
        await db.session.deleteMany({ where: { shop } });
      }
      break;
    case "CUSTOMERS_DATA_REQUEST":
    case "CUSTOMERS_REDACT":
    case "SHOP_REDACT":
    default:
      throw new Response("Unhandled webhook topic", { status: 404 });
  }

  throw new Response();
};

 

blueskydev_0-1710724740974.png

 

Replies 8 (8)

abhishek1108
Shopify Partner
4 0 0

Hey, is this solved ? Currently stuck on the same issue.

 

HarshaKDeltaX
Shopify Partner
2 0 0

Hi @abhishek1108 , Were you able to solve this? We are stuck at the same step. It'll be great if you can help. 

abhishek1108
Shopify Partner
4 0 0
No I am still looking for a solution to this issue.
appaza
Shopify Partner
6 0 1

Were you able to solve this? We are stuck at the same step. It'll be great if you can help for me.

HarshaKDeltaX
Shopify Partner
2 0 0

Hi @abhishek1108 @appaza, If you are using Remix template, try using the latest version (I'm using @shopify/shopify-app-remix: "^2.7.0") version.

They have solved this issue for mandatory webhooks.

Refer these -> 

https://github.com/Shopify/shopify-api-js/issues/256

https://github.com/Shopify/shopify-app-js/issues/505

 

The only change we need to do, is in the webhooks.jsx, Need to add the break in Switch-Case. Adding the code below.. No need to change anything in shopify.server.js.

import { authenticate } from "../shopify.server";
import db from "../db.server";

export const action = async ({ request }) => {
  const { topic, shop, session, admin } = await authenticate.webhook(request);

  if (!admin) {
    // The admin context isn't returned if the webhook fired after a shop was uninstalled.
    throw new Response();
  }

  switch (topic) {
    case "APP_UNINSTALLED":
      if (session) {
        await db.deltaXDetails.deleteMany({
          where: { shop: session.shop }
        });
        await db.session.deleteMany({ where: { shop } });
      }
      break;
    case "CUSTOMERS_DATA_REQUEST":
      break;
    case "CUSTOMERS_REDACT":
      break;
    case "SHOP_REDACT":
      break;
    default:
      throw new Response("Unhandled webhook topic", { status: 404 });
  }

  throw new Response();
};

 

And while submission in partners dashboard, we need to put the mandatory webhook URLs as <Your-App-URL>/webhooks.

My Automatic test cases have passed with the above change.

 

 

yash_soni
Shopify Partner
4 0 0

Hello there,

 

i have fixed this using following changes

 

Step 1 import : npm install @remix-run/node

 

Step 2 do small chnages in webhooks file as per given link.

 

This will work for me.

appaza
Shopify Partner
6 0 1

can you share code?

appaza
Shopify Partner
6 0 1
import crypto from "node:crypto";

import type { ActionFunctionArgs } from "@remix-run/node"; // or cloudflare/deno
import { json } from "@remix-run/node"; // or cloudflare/deno

export const action = async ({
  request,
}: ActionFunctionArgs) => {
  if (request.method !== "POST") {
    return json({ message: "Method not allowed" }, 405);
  }
  const payload = await request.json();

  /* Validate the webhook */
  const signature = request.headers.get(
    "X-Hub-Signature-256"
  );
  const generatedSignature = `sha256=${crypto
    .createHmac("sha256", process.env.GITHUB_WEBHOOK_SECRET)
    .update(JSON.stringify(payload))
    .digest("hex")}`;
  if (signature !== generatedSignature) {
    return json({ message: "Signature mismatch" }, 401);
  }

  /* process the webhook (e.g. enqueue a background job) */

  return json({ success: true }, 200);
};