Enforcement

Why a direct cart link walks straight past hidden prices

Prime Lock logo

Prime Lock Team

18 February 2026 · 9 min read

ENFORCEMENT
Contents

Here is a test you can run on your own store in two minutes. It is the single most useful thing in this article, so do it before reading further.

  1. 1Log in as a customer who can see your restricted prices. Open a restricted product.
  2. 2Open devtools, find the variant ID in the markup, or just load /products/<handle>.js and read the id field of the first variant.
  3. 3Log out. Open a private window.
  4. 4Go to https://your-store.myshopify.com/cart/<variant_id>:1

If you land on a cart page with the product in it and a working checkout button, your price hiding is decoration. Most stores using a theme snippet, or an app without checkout enforcement, will land exactly there.

Cart permalinks are a documented, first-party Shopify feature. They exist so you can put a "buy this bundle" link in an email or an ad and have it prefill the cart. The format is simple:

Cart permalink formats
/cart/44192837465:1                      one variant, quantity 1
/cart/44192837465:2,44192837466:1        two variants
/cart/44192837465:1?discount=TRADE20     with a discount code applied

Notice what is not in that request: your product page, your collection page, your theme's price snippet, and any JavaScript your app embed injected. Shopify's cart endpoint receives a variant ID and a quantity and does its job. It has no opinion about whether the storefront would have shown that product to this visitor, because the storefront was not consulted.

Variant IDs are not a secret

The bypass only matters if variant IDs are obtainable, and they are, from several directions at once:

  • /products/<handle>.js returns every variant with its ID and price, on every plan, for any visitor.
  • /collections/<handle>/products.json paginates a whole collection the same way.
  • The rendered product page includes variant IDs in the form and in the product JSON script tag.
  • Anyone who was ever an approved buyer has the IDs in their browser history and old order pages.
  • IDs are stable. Revoking a customer's tag does not change them.

Why theme-level gating cannot fix this

This is not a flaw in any particular app or snippet. It follows from where the code runs. A Liquid conditional runs when a template renders. An app embed runs when a page loads in a browser. A cart permalink causes neither of those things to happen for the product in question. You cannot enforce a rule from inside a page that was never requested.

The same reasoning covers the other bypasses people find: posting directly to /cart/add.js, reusing an old checkout URL, or querying the Storefront API with the public token your theme already uses. All of them go around the storefront rendering path.

Shopify Functions: the layer that actually stops it

Shopify replaced Plus-only Scripts with Functions: small WebAssembly modules that run inside Shopify's own infrastructure at defined extension points. Two of those points matter here: cart validation and checkout validation. They receive the cart, the buyer identity and their attributes, and return a list of errors. If the list is non-empty, checkout does not proceed.

The important, frequently-misunderstood detail: validation functions are available on every Shopify plan. Basic, Grow, Advanced, Plus. This is not the old Scripts situation. A merchant on the cheapest plan can enforce catalog access server-side.

LayerRuns whereStops a permalink?
Liquid conditional in the themeShopify's renderer, at template renderNo
CSS or JavaScript hidingThe visitor's browserNo
App embed rule evaluationThe visitor's browserNo
Cart validation functionShopify's serversYes, blocks the cart
Checkout validation functionShopify's serversYes, blocks payment
Where each layer runs, and what it can therefore stop.

What a blocked checkout looks like to the buyer

It is deliberately undramatic. The buyer reaches the cart or checkout as normal. Above the line items, an error appears with the text you configured, for example "Wool Throw is available to approved trade accounts. Log in or apply for access." The pay button is disabled while the offending line is present. Removing the item clears the error and the rest of the cart checks out normally.

Write that message as though a legitimate customer will read it, because usually one will. The most common trigger is not a competitor; it is a genuine buyer whose approval lapsed, or someone who clicked a link shared by a colleague.

When cosmetic gating is genuinely fine

This article argues for enforcement, so it should be equally clear about when you do not need it. A display-only gate is enough if all of the following are true:

  • Your prices are the same for everyone, and hiding them is about presentation or lead capture rather than confidentiality.
  • You would happily fulfil an order placed by anyone who found the price, at that price.
  • You have no MAP or distribution agreement that constrains who may see a price.
  • Your restricted items are not meaningfully cheaper than the public equivalents.

A gift-registry store hiding prices for aesthetic reasons does not need a validation function. A distributor whose trade price is forty percent below retail absolutely does. Be honest about which one you are; the answer changes what you should spend money on.

If you are in the second group, the requirement is short: display rules in the theme so the store reads correctly, plus a validation function so the rules mean something. Prime Lock ships both, and the function is on the free plan, because a lock that does not lock is not a feature.

Frequently asked

Prime Lock logo

Prime Lock Team

Prime Lock is built by Aspedan, the team behind Discount Prime and Prime MOQ. We write about the parts of Shopify that are harder than they look.

Decide who gets to see your store.

Early access opens with the free plan. Unlimited locks, enforced at checkout.

No newsletter. One email when Prime Lock is live.