Solved

How to process webhooks in updated Node and React Shopify app?

nandezer
Tourist
12 2 8

Hey!

A month ago I developed a node and react Shopify app following the "Build a Shopify App with Node and React" tutorial. Since then they seem to have updated/changed the way to register and process webhooks.

The first time I had it the code was something like this:

 

/* Register webhook for cart update */
const registrationCartUpdate = await registerWebhook({
	address: `${HOST}/webhooks/carts/update`,
	topic: 'CARTS_UPDATE',
	accessToken,
	shop,
	apiVersion: ApiVersion.October20
});
if (registrationCartUpdate.success) {
	console.log('Successfully registered cart update webhook!');
} else {
	console.log('Failed to register cart update webhook', registrationOrderPaid.result);
}
const webhook = receiveWebhook({secret: SHOPIFY_API_SECRET});
    
/*Receive webhook carts update*/
 router.post('//webhooks/carts/update', webhook, (ctx) => {
         console.log('received cart update webhook: ');
         //Print all cart items
          console.log(ctx.state.webhook.payload.line_items);
});

 

 

To something like this:

 

/* Register webhook for cart update*/
const registrationCartUpdate = await Shopify.Webhooks.Registry.register({
  shop,
  accessToken,
  path: '/webhooks',
  topic: 'CARTS_UPDATE',
  webhookHandler: async (_topic, shop, body) => {
	console.log('received cart update webhook: ');
	//Print body
	console.log(body);
  },
});
if (registrationCartUpdate.success) {
	console.log('Successfully registered cart update webhook!');
} else {
	console.log('Failed to register cart update webhook', registrationOrderPaid.result);
}
router.post("/webhooks", async (ctx) => {
	try {
                //Process webhook
		await Shopify.Webhooks.Registry.process(ctx.req, ctx.res);
		console.log(`Webhook processed, returned status code 200`);
	} catch (error) {
		console.log(`Failed to process webhook: ${error}`);
	}
});

 

 

My problem is that the body that I receive in the new way of doing it is a string (see console.log):

 

┃ received cart update webhook:
┃ {"id":"b28c0ef9ff6ce6b4b1261ab7f6f4ed76","token":"b28c0ef9ff6ce6b4b1261ab7f6f4ed76","line_items":[{"id":39266914205850,"properties":null,"quantity":1,"variant_id":39266914205850,"key":"39266914205850:2dae68072231626227fe28e192723de1","discounted_price":"25.00","discounts":[],"gift_card":false,"grams":0,"line_price":"25.00","original_line_price":"25.00","original_price":"25.00","price":"25.00","product_id":6541117784218,"sku":"","taxable":true,"title":"T-shirt","total_discount":"0.00","vendor":"A","discounted_price_set":{"shop_money":{"amount":"25.0","currency_code":"SEK"},"presentment_money":{"amount":"25.0","currency_code":"SEK"}},"line_price_set":{"shop_money":{"amount":"25.0","currency_code":"SEK"},"presentment_money":{"amount":"25.0","currency_code":"SEK"}},"original_line_price_set":{"shop_money":{"amount":"25.0","currency_code":"SEK"},"presentment_money":{"amount":"25.0","currency_code":"SEK"}},"price_set":{"shop_money":{"amount":"25.0","currency_code":"SEK"},"presentment_money":{"amount":"25.0","currency_code":"SEK"}},"total_discount_set":{"shop_money":{"amount":"0.0","currency_code":"SEK"},"presentment_money":{"amount":"0.0","currency_code":"SEK"}}},{"id":37335165829274,"properties":{},"quantity":1,"variant_id":37335165829274,"key":"37335165829274:677f0f916861c76dd6c1cf0be5719fd7","discounted_price":"0.00","discounts":[],"gift_card":false,"grams":0,"line_price":"0.00","original_line_price":"0.00","original_price":"0.00","price":"0.00","product_id":6010213040282,"sku":"","taxable":true,"title":"Movesgood T-shirt","total_discount":"0.00","vendor":"Test Store","discounted_price_set":{"shop_money":{"amount":"0.0","currency_code":"SEK"},"presentment_money":{"amount":"0.0","currency_code":"SEK"}},"line_price_set":{"shop_money":{"amount":"0.0","currency_code":"SEK"},"presentment_money":{"amount":"0.0","currency_code":"SEK"}},"original_line_price_set":{"shop_money":{"amount":"0.0","currency_code":"SEK"},"presentment_money":{"amount":"0.0","currency_code":"SEK"}},"price_set":{"shop_money":{"amount":"0.0","currency_code":"SEK"},"presentment_money":{"amount":"0.0","currency_code":"SEK"}},"total_discount_set":{"shop_money":{"amount":"0.0","currency_code":"SEK"},"presentment_money":{"amount":"0.0","currency_code":"SEK"}}}],"note":null,"updated_at":"2021-03-14T11:40:41.373Z","created_at":"2021-03-08T05:27:18.177Z"}
┃ Webhook processed, returned status code 200

 

So I can't do body.line_items as then I get an undefined error.

 

The ctx variable seems to have changed also, as ctx.state.webhook.payload.line_items for example is also undefined.

 

How can I read the items in the cart now? (same problem applies when I try to do order/paid!)

Accepted Solution (1)

nandezer
Tourist
12 2 8

This is an accepted solution.

By adding the parse function of JSON, I get the string into an object.

const obj = JSON.parse(body);

 

View solution in original post

Replies 10 (10)

nandezer
Tourist
12 2 8

This is an accepted solution.

By adding the parse function of JSON, I get the string into an object.

const obj = JSON.parse(body);

 

scole954387
Shopify Partner
20 1 10

I'm new to programming and been tearing my hair out trying to find out how to register multiple webhooks.  I'm using similiar code as yours that's created with the shopify cli node app generator. 

Do you have code to register multiple webhooks?

Thanks!

nandezer
Tourist
12 2 8

Hey @scole954387

you basically have to put one const after the other.

 

                         /*Register webhook for uninstalled app */
			const registrationUninstalled = await Shopify.Webhooks.Registry.register({
			  shop,
			  accessToken,
			  path: "/webhooks",
			  topic: "APP_UNINSTALLED",
			  webhookHandler: async (topic, shop, body) => {
				console.log('App uninstalled');
				const obj = JSON.parse(body);
				console.log(obj);
				delete ACTIVE_SHOPIFY_SHOPS[shop];
			  },
			});
			 if (registrationUninstalled.success) {
			  console.log('Successfully registered uninstalled app webhook!');
			} else {
			  console.log('Failed to register uninstalled app webhook', registrationUninstalled.result);
			}
			
			/* Register webhook for orders paid */
			const registrationOrderPaid = await Shopify.Webhooks.Registry.register({
			  shop,
			  accessToken,
			  path: '/webhooks',
			  topic: 'ORDERS_PAID',
			   webhookHandler: (_topic, shop, body) => {
				  console.log('received order paid webhook: ');
				  const obj = JSON.parse(body);
			  },
			});
			 if (registrationOrderPaid.success) {
			  console.log('Successfully registered Order Paid webhook!');
			} else {
			  console.log('Failed to register Order Paid webhook', registrationOrderPaid.result);
			}
				
			/* Register webhook for cart create */
			const registrationCartCreate = await Shopify.Webhooks.Registry.register({
			  shop,
			  accessToken,
			  path: '/webhooks',
			  topic: 'CARTS_CREATE',
			  webhookHandler: (_topic, shop, body) => {
				console.log('received cart created webhook: ');
				const obj = JSON.parse(body);
				console.log(obj);
			  },
			});
			if (registrationCartCreate.success) {
				console.log('Successfully registered cart create webhook!');
			} else {
				console.log('Failed to register cart create webhook', registrationCartCreate.result);
			}
			
			/* Register webhook for cart update*/
			const registrationCartUpdate = await Shopify.Webhooks.Registry.register({
			  shop,
			  accessToken,
			  path: '/webhooks',
			  topic: 'CARTS_UPDATE',
			  webhookHandler: async (_topic, shop, body) => {
				console.log('received cart update webhook: ');
				const obj = JSON.parse(body);
				 console.log(obj);
			  },
			});
			if (registrationCartUpdate.success) {
				console.log('Successfully registered cart update webhook!');
			} else {
				console.log('Failed to register cart update webhook', registrationCartUpdate.result);
			}
				
scole954387
Shopify Partner
20 1 10

Perfect thanks!   That's what I was thinking but couldn't seem to fit all the pieces together to get it to work.  If you don't mind can you show me the routes processing the individual webhooks received?

Thanks again!

nandezer
Tourist
12 2 8

The webhook handler in each individual webhook is what processes it:

webhookHandler: async (_topic, shop, body) => {
     console.log('webhook handler: ');
     const obj = JSON.parse(body);
     console.log(obj);
 },

 

The route for any of the webhooks is what I have posted on my question:

router.post("/webhooks", async (ctx) => {
     //.....
});

This will make sure to call the right webhookHandler function.

 

So basically the code you need is this:

			/*Register webhook for uninstalled app */
			const registrationUninstalled = await Shopify.Webhooks.Registry.register({
			  shop,
			  accessToken,
			  path: "/webhooks",
			  topic: "APP_UNINSTALLED",
			  webhookHandler: async (topic, shop, body) => {
				console.log('App uninstalled');
				const obj = JSON.parse(body);
				console.log(obj);
				delete ACTIVE_SHOPIFY_SHOPS[shop];
			  },
			});
			 if (registrationUninstalled.success) {
			  console.log('Successfully registered uninstalled app webhook!');
			} else {
			  console.log('Failed to register uninstalled app webhook', registrationUninstalled.result);
			}
			
			/* Register webhook for orders paid */
			const registrationOrderPaid = await Shopify.Webhooks.Registry.register({
			  shop,
			  accessToken,
			  path: '/webhooks',
			  topic: 'ORDERS_PAID',
			   webhookHandler: (_topic, shop, body) => {
				  console.log('received order paid webhook: ');
				  const obj = JSON.parse(body);
			  },
			});
			 if (registrationOrderPaid.success) {
			  console.log('Successfully registered Order Paid webhook!');
			} else {
			  console.log('Failed to register Order Paid webhook', registrationOrderPaid.result);
			}
				
			/* Register webhook for cart create */
			const registrationCartCreate = await Shopify.Webhooks.Registry.register({
			  shop,
			  accessToken,
			  path: '/webhooks',
			  topic: 'CARTS_CREATE',
			  webhookHandler: (_topic, shop, body) => {
				console.log('received cart created webhook: ');
				const obj = JSON.parse(body);
				console.log(obj);
			  },
			});
			if (registrationCartCreate.success) {
				console.log('Successfully registered cart create webhook!');
			} else {
				console.log('Failed to register cart create webhook', registrationCartCreate.result);
			}
			
			/* Register webhook for cart update*/
			const registrationCartUpdate = await Shopify.Webhooks.Registry.register({
			  shop,
			  accessToken,
			  path: '/webhooks',
			  topic: 'CARTS_UPDATE',
			  webhookHandler: async (_topic, shop, body) => {
				console.log('received cart update webhook: ');
				const obj = JSON.parse(body);
				 console.log(obj);
			  },
			});
			if (registrationCartUpdate.success) {
				console.log('Successfully registered cart update webhook!');
			} else {
				console.log('Failed to register cart update webhook', registrationCartUpdate.result);
			}
				


			router.post("/webhooks", async (ctx) => {
				try {
					//Process webhook
					await Shopify.Webhooks.Registry.process(ctx.req, ctx.res);
					console.log(`Webhook processed, returned status code 200`);
				} catch (error) {
					console.log(`Failed to process webhook: ${error}`);
				}
			});

 

scole954387
Shopify Partner
20 1 10

Awesome!  Thanks.  I actually figured it out after I replied once I got back on my computer and analyzed the code more.  Your code had the missing pieces that I needed to put it together!    I was thinking the route handled the actual processing of the specific webhook but now I see that the webhookHandler (makes sense now, LOL) does the processing.  

webhookHandler: (_topic, shop, body) => {
				console.log('received cart created webhook: ');
				const obj = JSON.parse(body);
				console.log(obj);

 

I REALLY appreciate your help.  I was hours and hours googling just to find an example of multiple webhooks being processed.  Such a simple thing that took me forever to figure out.  

Easy to tell just how new I am to this! LOL

Thanks again!

nandezer
Tourist
12 2 8

You and me both (in the hours and the being new!) haha

 

Happy I was able to help 🙂

rohit_martires
Shopify Partner
49 3 7

hey i was trying the same thing but in the topic fields i was adding "carts/create" and "carts/update" then i saw this answer and switched the topic to CARTS_CREATE and CARTS_UPDATE and it worked, but my question is from where did u find these topics names ? 

i've been using this link https://shopify.dev/api/admin/rest/reference/events/webhook are u referring to some other docs ?


- Cheers

nandezer
Tourist
12 2 8

Hey, 

I've used the WebhookSubscriptionTopic enum. You can find it here.

ConspireAgency
Shopify Expert
35 3 24

Instead of writing redundant code to register the webhooks, you may want to write a function that just iterates through an array of all the topics for which you'd like to register webhooks.

Conspire Agency //
Los Angeles, California //
https://conspireagency.com
View my showreel: https://www.youtube.com/watch?v=E9upo48FQUg