Core Concept

Sale Locks

Sale-locks are the synchronous, cross-channel guard that prevents the same card from selling twice. When a buyer enters checkout on Storefront, every other channel that has the same card listed gets a temporary lock. Lock holds until the checkout resolves (paid or abandoned).

The problem they solve

A card listed on Storefront AND ToT simultaneously. Buyer A starts a Storefront checkout; before they pay, Buyer B starts a ToT Pull commit including the same card. Without coordination, both might pay and the operator owes one of them a refund + apology. Sale-locks make this race impossible.

The flow

  1. Buyer enters checkout on Channel A (e.g. Storefront). The route generates a fresh lockRef (random 16 chars).
  2. Channel A calls acquireSaleLockBatch() — POSTs to SlabTrack /api/sale-locks/acquire with { cardIds, channelSlug, lockRef, ttlSeconds: 600 }
  3. SlabTrack atomically inserts rows into the sale_locks table. If any card already has an active lock from a different lockRef, the whole batch fails — Channel A returns 409 to the buyer ("someone else is buying this right now").
  4. Channel A creates Stripe Checkout Session, sends buyer to Stripe-hosted page
  5. Buyer payscheckout.session.completed webhook fires → Channel A calls completeSaleLockBatch() on the locks → SlabTrack flips card_channels.status='sold' on Channel A's rows AND fires bulk-disengage to all OTHER channels for those cards
  6. Buyer abandonscheckout.session.expired after 24h → Channel A calls releaseSaleLockBatch() → SlabTrack deletes the lock rows → cards available again everywhere

The schema

CREATE TABLE sale_locks (
  id            SERIAL PRIMARY KEY,
  card_id       INTEGER NOT NULL,
  channel_slug  TEXT NOT NULL,        -- which channel holds the lock
  lock_ref      TEXT NOT NULL,        -- per-checkout token (matches across batch)
  acquired_at   TIMESTAMP DEFAULT NOW(),
  expires_at    TIMESTAMP NOT NULL,   -- TTL — auto-cleanup if hangs
  UNIQUE (card_id)                    -- one active lock per card, period
);
⚠ TTL is the safety net

If a webhook fails to fire (network blip, Stripe outage), the lock would hang forever and the card would be permanently unbuyable. The 10-minute TTL + a cron sweep clears stale locks. Choose TTL longer than your slowest reasonable checkout (10 min for Stripe Checkout; 60 min if you support ACH).

The bulk-disengage cascade

When a card sells on Channel A, the OTHER channels need to know so they pull their listings. SlabTrack's completeSaleLockBatch:

  1. Sets card_channels.status='sold' on Channel A's row
  2. Queries for OTHER active rows for those cards
  3. Fires POST /api/webhook/bulk-disengage at each affected satellite (ToT, Storefront, Showcase, etc.)
  4. Each satellite removes its local listing for those cards
  5. Sets card_channels.status='inactive' on the disengaged rows

Cross-system + same-system locks

Same logic works inside a single repo too. ToT's Pull commit acquires locks before captureing payment intent, releases them on cancel. Storefront's checkout does the same. The locks are namespaced by channel_slug so a Storefront lock doesn't block a Pull commit if they're on different channels (only when the SAME card is dual-listed).

The DUAL_LISTING_ENABLED flag

Storefront has a feature flag DUAL_LISTING_ENABLED. When false (default), Storefront skips sale-lock acquisition entirely (since same card can't be on two channels in the first place — channels exclusivity prevents it). When true, full sale-lock dance fires on every checkout.

Code references