App reviews, troubleshooting, and recommendations
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!)
Solved! Go to the solution
This is an accepted solution.
By adding the parse function of JSON, I get the string into an object.
const obj = JSON.parse(body);
This is an accepted solution.
By adding the parse function of JSON, I get the string into an object.
const obj = JSON.parse(body);
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!
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);
}
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!
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}`);
}
});
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!
You and me both (in the hours and the being new!) haha
Happy I was able to help 🙂
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
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.
Hey Community! As the holiday season unfolds, we want to extend heartfelt thanks to a...
By JasonH Dec 6, 2024Dropshipping, a high-growth, $226 billion-dollar industry, remains a highly dynamic bus...
By JasonH Nov 27, 2024Hey Community! It’s time to share some appreciation and celebrate what we have accomplis...
By JasonH Nov 14, 2024