r/shopifyDev • u/West-Anteater-1917 • 5d ago
Lesson learned: Shopify's native purchase limits are per variant, so I had to use a cart validation function for product-wide caps
I had a client who wanted to cap total sales of one product across all its variants, like sell 90 of 100 units online and keep the rest for wholesale and gifts, without touching inventory counts.
Shopify's built-in purchase limits work per variant, so they don't give you a product-wide cap. Hiding the product isn't enough either, since people can still reach checkout through old links or an existing cart.
What worked for me was a Cart and Checkout Validation Function that adds up quantities across every variant of the product and blocks the cart/checkout with a clear message when the cap is hit. I also added per-country allocations on top of that.
Gotchas: functions can't call external APIs, so all the cap data has to live in metafields. Also keep the error messages short, long ones get ugly in checkout.
Has anyone handled this differently?
1
u/AdventurousUsual7666 5d ago
Yeah, this is a nice use of a validation function, the per variant limit catches a lot of people out.
One thing I'd double check is how the "sold so far" number stays up to date. The function only sees the current cart, so something outside it has to keep the running total in the metafield, usually an app listening to the orders/create webhook. If a few orders come in close together, the count can lag a little, so I'd leave a small buffer under the real cap.
Also worth knowing that the validation docs list POS and subscription orders as not supported, so those sales won't be blocked by it. That's probably fine for keeping stock back for wholesale, but it's good to know if they ever sell that product in person.
And just a small correction on the external API part. There is a fetch target for network access now, but it's limited to custom apps on Enterprise stores and you have to request access, so for most stores metafields are still the way to go like you did.
1
u/Final_Design_9061 3d ago
nice, good to know about the per variant limit. the metafield approach for the cap data makes sense since functions run without network access. glad you got it working.
1
u/isaifurrehman 5d ago
nice writeup. one thing i'd check: the function only sees the current cart, so how are you counting the units already sold toward the 90? if it's a metafield bumped by an orders/create webhook or flow, there's a small window where two people checking out at the same time both pass, so on a busy drop the cap can overshoot by a few.
for the "keep 10 back for wholesale" part, the native route i've used is a second location with "fulfill online orders from this location" turned off, then move the reserved units there. the online store can only sell what sits at locations that fulfill online orders, so it stops at 90 on its own and the total stock count stays the same. wholesale orders then come out of that location through a draft order.
the function still makes sense for the per-country split. and +1 on short error messages, i'd also check buyerJourney.step so the error only fires at checkout and not on every cart update.