Has anyone else experienced same issue? or i am doing something wrong?
Any help in this regard is appreciated.
Topics covering webhook creation & management, event handling, Pub/Sub, and Eventbridge, in Shopify apps.
Has anyone else experienced same issue? or i am doing something wrong?
Any help in this regard is appreciated.
Solved! Go to the solution
This is an accepted solution.
Hey there,
I believe it was within the last year that we started to send webhooks when a product is sold, and this behaviour is intentional. If memory serves, it's the inventory of the product being touched that triggers the webhook.
Josh | Shopify
- Was my reply helpful? Click Like to let me know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more visit the Shopify Help Center or the Shopify Blog
This is an accepted solution.
Hey there,
I believe it was within the last year that we started to send webhooks when a product is sold, and this behaviour is intentional. If memory serves, it's the inventory of the product being touched that triggers the webhook.
Josh | Shopify
- Was my reply helpful? Click Like to let me know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more visit the Shopify Help Center or the Shopify Blog
This is an old thread but its still relevant . Has there been any talk internally around a webhook that fires when a product is updated via the admin and not a sale?
something like - product/update_admin
There is a very big difference between and admin adjusting the body / title / images etc., compared to a sale happening.
I have the same issue
Ditto. QuickBooks does this same thing and its annoying and causes a lot of unnecessary overhead. I get Inventory Levels have changed, but that should be a separate webhook just as it is a separate object in the Shopify database.
Is there any fix for this behavior? It really doesn't make any sense. If you want a product hook to fire on product inventory change (which will be every time a product is soled, which hopefully is very often) why not put this is in a specific product_stock_change or something similar?
As it works now the webhooks are unusable. I've set up a SSG fore storefront in Netlify... problem is, every time someone makes a purchase the site rebuilds, eating away att build hours when nothing except stock has changed (which is checked individually anyway).
So I ask again, is there any workaround on this or do we just have to wait until someone on the dev team understands that firing the product update/change hook every time a sale is made probably isn't how most people are expecting it to work and also is easily fixable by simple adding a product stock specific hook for those that want/need that type of behavior.
I agree. We already have an inventory hook which is suitable for this.
Product creation/update/delete hooks are clearly meant for the changes by the admin. If any customer can trigger this update hook then what is even the point of it? It's absolutely unusable.
I was also very annoyed by this, obviously we don't want to rebuild our static pages on every sale but there is also no other way to be notified of actual product changes (title, image, description, price, etc), than using this webhook.
Thankfully, at least the webhook posts some details of the product. So I managed to work around this by generating a checksum of that details, after removing the parts I'm not interested in: inventory_quantity, old_inventory_quantity and updated_at (which obviously changes with inventory updates as well). I then store that in a simple Redis store keyed by GID, and prevent revalidation if it is unchanged.
For reference, here is the implementation for Next.js using Vercel KV:
import { kv } from '@vercel/kv'
import { revalidate } from 'lib/shopify'
import { NextRequest, NextResponse } from 'next/server'
export const runtime = 'edge'
function ignoreInventoryChanges(item: any) {
const { updated_at, variants, ...rest } = item
return {
...rest,
variants: variants?.map((v: any) => {
const { updated_at, inventory_quantity, old_inventory_quantity, ...variant } = v
return variant
}),
}
}
async function createHash(json: any) {
const text = new TextEncoder().encode(JSON.stringify(json))
const digest = await crypto.subtle.digest('SHA-256', text)
return arrayBufferToBase64(digest)
}
function arrayBufferToBase64(buffer: ArrayBuffer) {
var binary = ''
var bytes = new Uint8Array(buffer)
var len = bytes.byteLength
for (var i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]!)
}
return btoa(binary)
}
export async function POST(req: NextRequest): Promise<NextResponse> {
const body = await req.json()
const hash = await createHash(ignoreInventoryChanges(body))
const previous = await kv.get<string>(body.admin_graphql_api_id)
if (previous === hash) {
return NextResponse.json({ status: 200, revalidated: false, now: Date.now() })
}
await kv.set(body.admin_graphql_api_id, hash)
return revalidate(req)
}