I’m facing an issue with the APP_UNINSTALLED webhook for my Shopify app.
- My webhook endpoint
/webhooks/app/uninstalledworks perfectly in local and staging environments. - I’m using a Shopify Remix app hosted on Google Cloud Platform (GCP).
- The webhook is registered properly using
shopify.server.jsand set to:
APP_UNINSTALLED: {
deliveryMethod: DeliveryMethod.Http,
callbackUrl: "/webhooks/app/uninstalled"
}
and this is my webhook with hook code in shopify.server.js file:
webhooks: {
APP_UNINSTALLED: {
deliveryMethod: DeliveryMethod.Http,
callbackUrl: "/webhooks/app/uninstalled",
// callback: async (topic, shop, body) => {
// await prisma.shops.deleteMany({
// where: { myshopify_domain: shop },
// });
// await ShopTrial.updateOne(
// { shop },
// { $set: { uninstalledAt: new Date() } },
// );
// },
},
APP_SUBSCRIPTIONS_UPDATE: {
deliveryMethod: DeliveryMethod.Http,
callbackUrl: "/webhooks/app/subscriptions",
callback: async (topic, shop, body) => {
const payload = JSON.parse(body);
const subscription = payload.app_subscription;
if (subscription.status === "CANCELLED") {
// Clear chargeId on cancellation
await ShopTrial.updateOne({ shop }, { $set: { chargeId: null } });
}
},
},
},
hooks: {
afterAuth: async ({ session, admin }) => {
try {
console.log("afterAuth Hook Triggered");
// Register necessary webhooks
await shopify.registerWebhooks({ session });
// Fetch and save shop data via GraphQL
await saveShopData(session, admin);
await setLegacyCustomerMetafield(session.shop, session.accessToken);
await assignFreePlan(session.shop, session.accessToken);
await ShopTrial.updateOne(
{ shop: session.shop },
{
$setOnInsert: { shop: session.shop, trialUsed: false },
$set: { uninstalledAt: null },
},
{ upsert: true },
);
console.log("Shop Data Saved Successfully");
const shop = await Shop.findOne({ myshopify_domain: session.shop });
} catch (error) {
console.error("Error in afterAuth Hook:", error);
}
},
},
and this is my webhooks.app.uninstall code:
export const action = async ({ request }) => {
await connectDB();
const { shop, session, topic } = await authenticate.webhook(request);
console.log(`Received ${topic} webhook for ${shop}`);
if (session) {
await db.session.deleteMany({ where: { shop } });
}
return new Response();
}
- After deploying the Remix app to GCP, I added the deployed webhook URL to my older Shopify app (which was built using Node.js, React, and ScriptTag).
- However, when uninstalling the app from a store, the webhook doesn’t seem to trigger.
Has anyone experienced something similar? What could be the reason it’s not working in production but works fine locally and in staging?
Any help would be greatly appreciated! (my app already live, please if any help possible then suggest me)