Files
rudder-js-sdk/skills/rudder-web-sdk/reference/stores.md
T
edmand46 7271874cac
CI / check (push) Successful in 16s
CI / publish (push) Has been skipped
2.0.0: changelog and skill docs for slugs and environments
Claude-Session: https://claude.ai/code/session_01SMCvdwDmuxoaqGgvGBLk1V
2026-09-06 22:39:03 +03:00

2.8 KiB

Stores / purchases — client.stores

StoresDomain (source: src/domains/StoresDomain.ts, handles in src/state/shops.ts). The observable store list plus the purchase executor. Synced under revision key stores. This is the SDK's economy surface — there is no separate economy service.

Surface

client.stores.data       // ShopHandle[] | undefined
client.stores.onChange(cb);
await client.stores.load() / .reload();

// Direct purchase (bypassing handles):
await client.stores.purchase(storeSlug, offerSlug, options?);

Handles

class ShopHandle {
  readonly slug: string;
  readonly name?: string;
  readonly description?: string;
  readonly data?: { [key: string]: unknown };
  readonly offers: OfferHandle[];
}

class OfferHandle {
  readonly id: string;    // authoring id, differs between staging and prod
  readonly slug: string;  // stable across environments — what purchases use
  readonly name?: string;
  readonly price?: OfferPrice;        // { amount?: number; currency?: string }
  readonly contents: OfferContent[];  // { amount?: number; itemId?: string }[]
  readonly maxPurchases?: number;

  buy(options?: BuyOptions): Promise<PurchaseOfferResponse>;
}

interface BuyOptions { idempotencyKey?: string }

interface PurchaseOfferResponse {
  error?: string;
  purchaseId?: string;
  success?: boolean;
}

Purchase semantics

  • stores.purchase(storeSlug, offerSlug, options?) and offer.buy(options?) are the same executor; handles just bind the store slug and the offer slug.
  • When options.idempotencyKey is omitted the SDK generates crypto.randomUUID() per call — safe retries require passing your own key.
  • On success the executor invalidates player, inventory, and stores, so subscribers observe fresh wallet/inventory/store data immediately.
  • Check response.success / response.error — a failed purchase is a resolved response, not necessarily a thrown error.
  • Purchase metrics (purchase.offer:<offerSlug>, purchase.item:<itemId>) are reported to quests automatically server-side — do not report them manually (see reference/quests.md).

Wire types

interface Store {
  createdAt?: string; data?: { [key: string]: unknown }; description?: string;
  environment?: string; id?: string; name?: string; offers?: Offer[];
  projectId?: string; scenarioId?: string; slug?: string; status?: string;
  updatedAt?: string;
}
interface Offer {
  contents?: OfferContent[]; createdAt?: string; id?: string;
  maxPurchases?: number; name?: string; price?: OfferPrice; slug?: string;
  updatedAt?: string;
}

ShopHandle/OfferHandle throw a plain Error if constructed from a store without slug / an offer without id or slug — in practice the domain only builds handles from server data that has all of them.