shopify-app-template-node icon indicating copy to clipboard operation
shopify-app-template-node copied to clipboard

APP_UNINSTALLED hook not being registered

Open omkaark opened this issue 3 years ago • 8 comments

Issue summary

Expected behavior

I am expecting that when I uninstall my test app, the APP_UNINSTALLED WEBHOOK gets fired.

Actual behavior

I get nothing, I get neither an error nor my console.log. I am pretty sure Shopify's backend fires the webhook, and the fact that the webhook is registered on my side as I get this response when I register it - {"APP_UNINSTALLED":{"success":true,"result":{}}}

Steps to reproduce the problem

I installed a new app to check if the mistake is on my side. I used shopify app create node. I uninstalled the default app and tried to reinstall, I got the same error. This leads me to believe the problem is on the shopify node app's side.

Reduced test case

Try uninstalling and reinstalling a new node app. The webhook is not registered.

Specifications

  • Browser: Chrome
  • Device: Mac Book Pro
  • Operating System: Apple

omkaark avatar Apr 29 '22 20:04 omkaark

Here is the relevant code:

  • In auth.js
const response = await Shopify.Webhooks.Registry.register({
        path: "/webhooks",
        topic: "APP_UNINSTALLED",
        shop: session.shop,
        accessToken: session.accessToken,
      });

      console.log(JSON.stringify(response));
      if (!response["APP_UNINSTALLED"].success) {
        console.log(
          `Failed to register APP_UNINSTALLED webhook: ${response.result}`
        );
      }
  • index.js
Shopify.Webhooks.Registry.addHandler("APP_UNINSTALLED", {
  path: "/webhooks",
  webhookHandler: async (topic, shop, body) => {
    console.log(topic, shop, body);
    delete ACTIVE_SHOPIFY_SHOPS[shop];
  },
});
app.post("/webhooks", async (req, res) => {
    try {
      await Shopify.Webhooks.Registry.process(req, res);
      console.log(`Webhook processed, returned status code 200`);
    } catch (error) {
      console.log(`Failed to process webhook: ${error}`);
      res.status(500).send(error.message);
    }
  });

omkaark avatar Apr 29 '22 20:04 omkaark

Actually, if I use Postman, the /webhooks endpoint works. This means Shopify doesn't fire a APP_UNINSTALLED webhook?

omkaark avatar Apr 29 '22 21:04 omkaark

@omkaark I tried the following steps and have been unable to reproduce the issue.

  1. shopify app create node -n issue807
  2. cd issue807 and modify the /webhooks handler to print a debug line
Shopify.Webhooks.Registry.addHandler("APP_UNINSTALLED", {
  path: "/webhooks",
  webhookHandler: async (topic, shop, body) => {
    console.log(`DEBUG: ${topic}, ${shop}, ${body}`);
    delete ACTIVE_SHOPIFY_SHOPS[shop];
  },
});
  1. shopify app serve
  2. Installed the app in my development store, then delete the app
  3. I get the console.log output
DEBUG: APP_UNINSTALLED, <shopname>.myshopify.com, {<body contents>}
Webhook processed, returned status code 200
  1. Install it again, delete it again
  2. I get the console.log DEBUG output again

mkevinosullivan avatar May 03 '22 16:05 mkevinosullivan

I got the exact same bug as @omkaark The console.log in the POST handler never gets fired inside the try.

  • Console log outside of the Try when Shopify Call the registered Webhook is triggered as expected - This means Shopify call the Webhook url.
// SHOPIFY WEBHOOKS
Shopify.Webhooks.Registry.addHandler('APP_UNINSTALLED', {
	path: '/api/webhooks',
	webhookHandler: async(topic, shop, body) => {
		console.log('webhookHandler', topic, shop, body); <== Shows in Terminal
		return security.deleteAppSession();
	}
});
// OAuth code
// Registry webhooks
const responses = await Shopify.Webhooks.Registry.registerAll({
	shop: session.shop,
	accessToken: session.accessToken,
});

// Process responses
Object.entries(responses).map(([topic, response]) => {
	console.log('Webhooks.Registry.registerAll:', topic, response); <== Shows in Terminal
});
// Router
router.post('/api/webhooks', async (req, res) => {
	console.log('/api/webhooks'); // <== Shows in terminal 
	try {
		const p = await Shopify.Webhooks.Registry.process(req, res);
		console.log('/api/webhooks:', p); // <== Never Shows
	} catch (err) {
		console.log('/api/webhooks:ERROR:', err); // <== Never Shows either
	}
});

bluedge avatar Jul 12 '22 01:07 bluedge

Solution: This is happening because one should not use bodyParser.json() or express.json() on the Shopify Webhook queries... Solution found here: https://github.com/Shopify/shopify-api-node/issues/167#issuecomment-1174948817

bluedge avatar Jul 12 '22 02:07 bluedge

This issue is stale because it has been open for 90 days with no activity. It will be closed if no further action occurs in 14 days.

github-actions[bot] avatar Sep 30 '22 02:09 github-actions[bot]

Hi, same error here. Any solution?

I am also wondering if the path should be "/api/webhooks" or only "/webhooks".

alekscarrese avatar Oct 14 '22 16:10 alekscarrese

I think the issue might be related to the APP_UNINSTALLED webhook callback address out-dated.

My question:

  1. Are there any suggested approach to update webhook address by shopify?
  2. Are there any shopify pre-written code or functions to do the auto update jobs?
  3. Are developers supposed to handle all webhook address update stuff, including update app uninstall webhook?
  4. Will there be contradictions with shopify pre-implemented code if developers implement our own logic?

TL;DR: I know is the issue of out-dated webhook address, how should I update it?


  1. I have found the following issue when using ngrok tunnel server for app development.

    • The callback address(URL) of the app uninstall webhook will not update for each time running npm run dev.
    • However, for each time we run again the dev server, a new address is generated.
    • Therefore, the webhook will call a deprecated address but not the current server handler endpoint.
  2. The following is the registered app/uninstalled webhook object. Once the topic is trigger, shopify will send a post request to the registered address e.g. https://aa12-123-12-123-123ap.ngrok.io/api/webhooks

{
    "id": 2258278463553,
    "address": "https://aa12-123-12-123-123ap.ngrok.io/api/webhooks",
    "topic": "app/uninstalled",
    ...
 }
  1. However, the domain part aa12-123-12-123-123ap will remain unchanged until we update it whereas the app will not update it by default. And, the current server address might not be the same as the registered one.

  2. As a result, when we uninstall the app, shopify will call a deprecated address. That's why nothing happens.

  3. I see the following code in auth.js and I understand it will create or update the webhooks, but I found that it never runs

app.get("/api/auth/callback", async (req, res)=>{
       const responses = await Shopify.Webhooks.Registry.registerAll({
            shop: session.shop,
            accessToken: session.accessToken,
    });
})
  1. Are there any ways to auto update the address for app uninstall or we should implement the code ourself to do so?

  2. Should there be any mistakes or misconceptions in my statements, please point out and correct me. Thanks

See also for docs: https://github.com/Shopify/shopify-api-node/blob/main/docs/usage/webhooks.md#note-regarding-use-of-body-parsers

See also for the issue: https://community.shopify.com/c/shopify-apis-and-sdks/uninstalled-webhook-not-firing/m-p/1141119#M65605

lalalam123 avatar Dec 05 '22 19:12 lalalam123