Documentation menu

Documentation / Start here

Commerce Quickstart

This quickstart stands up a product catalog and a storefront: the Commerce block pack (product cards, grids, pricing tables) on the front, the @wabbit/tome-catalog layer holding products underneath, and the @wabbit/tome-economy layer for orders and checkout. It assumes you have the block loop from Get Started working.

The Commerce block pack ships as @wabbit/tome-blocks-catalog-pack (its buyer-facing name is "Commerce Blocks"). It's a gated pack, so installing it needs an install token whose scope includes it.

What the starter already gives you

If you forked tome-starter, Commerce is already wired — the fork ships with all three pieces in package.json and composed in src/payload.config.ts:

  • @wabbit/tome-catalog — the five product collections and the product-type registry.
  • @wabbit/tome-economy — orders, payments, prices, and the checkout adapters.
  • @wabbit/tome-blocks-catalog-pack — the storefront blocks, bridged in src/blocks/catalog-pack-bridge.*.

So on a starter fork you can skip to seeding products. The wiring sections below are the reference for how it's assembled — read them if you're adding Commerce to a bare Payload app, or to understand what your fork already did.

Install the packages

Make sure your .npmrc has the registry line and an install token (see Get Started) — the catalog pack is gated, so it needs the token. Then:

npm install @wabbit/tome-blocks-core @wabbit/tome-blocks-catalog-pack @wabbit/tome-catalog @wabbit/tome-economy

@wabbit/tome-catalog is an optional peer of the block pack — the blocks render with static props even without it. Install it anyway: it's what lets the blocks read live product data, and it's the collection home for everything you sell. @wabbit/tome-economy adds the order and checkout machinery.

Wire the catalog layer

The catalog layer is a set of collection factories plus a product-type registry. Register the product types you sell before building the product collection (the type select reads them), then add the collections:

import {
  createProductCollection,
  createProductAttributeCollection,
  createProductAttributeValueCollection,
  createCategoryCollection,
  createProductMediaCollection,
  createEntitlementsCollection,
  productTypeRegistry,
} from '@wabbit/tome-catalog'

// Register the product types your store uses (runs before createProductCollection):
productTypeRegistry.register({ value: 'physical', label: 'Physical', source: 'my-store' })

// ...then add these to your config's `collections` array:
createCategoryCollection({ maxDepth: 3, mediaCollection: 'media' }),
createProductAttributeCollection(),
createProductAttributeValueCollection(),
createProductCollection({ mediaCollection: 'media' }),
createProductMediaCollection({ mediaCollection: 'media' }),
createEntitlementsCollection({ usersSlug: 'members', membersSlug: 'members' }),

This gives you the catalog-products, catalog-categories,catalog-attributes, catalog-attribute-values, and catalog-media collections. createEntitlementsCollection is what a completed order writes to — it's how a purchase grants access.

Wire the economy layer

Call initEconomy() once at module load (it registers the admin sidebar grouping), wire your order-complete handler, and add the collection factories:

import {
  initEconomy,
  createOrdersCollection,
  createPaymentsCollection,
  createPricesCollection,
  createVendorEarningsCollection,
} from '@wabbit/tome-economy'

initEconomy()

// ...add to your config's `collections` array:
createOrdersCollection(),
createPaymentsCollection(),
createPricesCollection(),
createVendorEarningsCollection(),

The economy layer ships two payment adapters: FreeAdapter (a zero-cost checkout that completes orders immediately — the default the starter demos with) and StripeAdapter (lazy-loads the Stripe SDK; only used when you add Stripe keys). The order-complete event is where you grant the entitlement — the starter wires this in src/lib/economy/orderHandlers.ts via registerStarterOrderHandlers(), which turns a completed order into a catalog entitlement (and an LMS enrollment for course products).

Register the Commerce blocks

The pack exposes six blocks: product-card, product-grid,category-strip, price-table, inventory-badge, and featured-product. Register them the same way as any pack — the one-call form:

import { blockRegistry, bundleRegistry } from '@wabbit/tome-blocks-core'
import { register } from '@wabbit/tome-blocks-catalog-pack'

register(blockRegistry, bundleRegistry)
// blockRegistry.resolveAll() now includes the six catalog blocks

The starter uses its bridge form instead: the config half (src/blocks/catalog-pack-bridge.config.ts) imports the block descriptors and spreads CatalogPackBlocks into the Pages layout field; the render half (src/blocks/catalog-pack-bridge.tsx) imports the components from @wabbit/tome-blocks-catalog-pack/render and folds catalogPackComponents into the master render map. When the @wabbit/tome-catalog layer is present, the blocks detect it and light up their rich data path; when it's absent they fall back to static props.

Seed or create products

The starter has a real seed. Running the demo seeds populate the catalog-products collection with live rows:

npm run demo:dev

That runs with DEMO_SEED=1 SANDBOX_SEED=1 and creates a set of demo products (physical goods, course products, and block-bundle products), categories, and attributes — all idempotent, so re-running is safe. Prefer to start clean? Create a product by hand in /admin under the catalog-products collection — pick a type you registered, set it active, and it's live.

Render the storefront

With the pack registered, drop a product-grid or featured-product block onto a page's layout in /admin and publish. RenderBlocks paints it through the component map you wired in the previous step. The starter's auto-gallery route /blocks/catalog-pack shows every catalog block and variant.

Heads up — blocks vs. live data. In the starter, the seeded storefront blocks are fed hand-written demo product props, and the auto-gallery uses fabricated demo props — neither is bound to the seeded catalog-products rows out of the box. The starter demonstrates live product data on a separate /products route that queries catalog-products directly with @wabbit/tome-ui card primitives, not the pack's ProductGrid. Wiring the pack blocks to live rows (via the block's @wabbit/tome-catalog integration) is the step you own — budget for it, and treat "blocks render" and "blocks show my real products" as two separate checkpoints.

Checkout & the managed tier

The economy layer's FreeAdapter gives you an end-to-end checkout with no payment processor — good for course sign-ups, comps, and proving the order → entitlement flow. Add Stripe by installing the stripe SDK and configuring the StripeAdapter; the lazy import means you carry no Stripe dependency until you opt in.

The fully-managed Commerce capability tier — where we run the payment-flow and its liability with you — is set up through a request-access conversation, not a checkout button. If that's where you're headed, reach out. For what a subscription keeps buying you after install, see Versioning & updates.