Install
@fanfare-io/fanfare-sdk-core is a required peer dependency. react and react-dom are optional
peers — install them only if you use the ./react subpath.
Two import surfaces
The package has two entry points so a server-only consumer can install it without React.
The resolver, verifier, and claimer are internal —
FanfareShopifyProvider assembles them from
your store config, so you configure one provider rather than wiring loose factories.
Configure the provider
Wrap your storefront once. In production,shopify.proxyBaseUrl points at the Fanfare Shopify app
proxy; that is the only required store fact.
mode defaults to "production" (real verify + claim via the proxy) and experiences defaults to
{ source: "metafield" }. Production without a proxyBaseUrl throws FanfareConfigurationError at
mount — it never silently degrades.
Resolve a product to an experience
useFanfareExperience(product) maps a Shopify product to the experience it gates, or null when the
product is not gated. Pass a ShopifyProductRef ({ handle, id }).
experienceId === null means not gated — render your native purchase UI. It is SSR-safe: any miss
or failure yields null, never a throw.
Metafield primitives
If you already have the rawexperienceIds value, parse it directly. Both helpers are pure and never
throw.
Connect the drop widget
useFanfareWidgetBridge() returns the onJourneyChange handler you spread onto your
<ExperienceWidget>. That spread feeds the cleared-drop grant and the routed journey snapshot into
the provider, so the gate and the checkout can read them.
Gate checkout entry
<FanfareCheckoutGate> is the drop-in guard for the checkout route: it renders children only when
the grant for the cart’s gated items is valid, otherwise onDenied.
useFanfareGrant() returns { state: "checking" | "allowed" | "blocked", reason? }.
Run the gated checkout (recommended)
useFanfareCheckout(adapter) is the recommended on-buy path. Its gatedCheckout owns the ordering
that keeps Shopify’s gate validation happy — create an empty cart, claim it (mint the gate token
and reserve the slot), then add the line(s) to that same cart. You supply the cart I/O; the SDK owns
the order, so the protected line can never be added before the claim.
The line identifier
GatedLine carries two Shopify ids, both required, branded so a transposition fails to compile:
Build them with the
productGid / variantGid constructors (they throw if handed the opposite
entity’s GID).
Result codes
gatedCheckout never rejects — every outcome is a FanfareGatedCheckoutResult. Branch on
retryable (or the presence of retry), never on code alone.
Advanced: bring-your-own-cart claim
If you own your cart system and want to add lines yourself, use the low-levelclaim (also returned
by useFanfareCheckout, and available standalone as useFanfareClaim). You materialize an empty
cart, claim it, then add your gated line — in that order.
claim(cartId) (a bare string) stays as the no-purchase-context escape hatch. On
cart_not_materialized (recoverable), re-materialize the cart and claim again.
Notes and gotchas
- The root barrel is React-free and SSR-safe. It imports zero React, so a server-only consumer installs and uses it (the metafield primitives + the checkout vocabulary) without React.
gatedCheckoutowns the ordering. YourcreateCartmust return an EMPTY cart; the SDK adds the gated line only after the claim writes the token. Adding the line insidecreateCartreintroduces the line-before-token bug the hook exists to prevent.- Never re-claim on
add_line_failed. The slot is already reserved; useres.retry()to resume the add on the same cart. A blind freshgatedCheckoutdouble-reserves (oversell). - The gate is enforced server-side.
GrantRecordis UX/continuity state; admission is enforced through the proxy using the admission credential, and the platform gate token is server-minted into the cart metafield.
Next steps
- Follow the Shopify integration guide for app installation and theme embedding.
- Read the Shopify types reference for the full exported type list.
- See Core SDK Quickstart for the journey model the resolver feeds from.