@lockerverse/sdk
v0.2.141
Published
Stateless browser clients for Lockerverse checkout, signup, public event listings, and public Event attendee APIs. Checkout and signup validate responses and return immutable snapshots. The generated public Event client supplies request and response types
Readme
Lockerverse SDK
Stateless browser clients for Lockerverse checkout, signup, public event listings, and public Event attendee APIs. Checkout and signup validate responses and return immutable snapshots. The generated public Event client supplies request and response types, but it does not validate responses at runtime or freeze them. These clients never render UI or own host application state.
This README ships with the package and describes that release. For an installed
project, use its local README, package.json exports, and public .d.ts files.
Online documentation can describe a newer release. Import public package entry
points, not private dist files. For ready-made React UI, use the README shipped
with @lockerverse/react.
Usage guide
- Checkout and payment recovery
- Runtime configuration
- Public Event attendees
- Signup
- Auctions and purchase recovery
- Public event listings
- Security and observability
Checkout
import { createLockerverseCheckoutClient } from "@lockerverse/sdk/checkout";
const client = createLockerverseCheckoutClient({
communitySlug: "auburn",
widgetSlug: "auburn-tailgate-party",
onError(error) {
console.error(error.code, error.operation);
},
});
// The host owns Product selection.
const selection = [{ productSlug: "adult", quantity: 2 }];
const pricing = {
email: "[email protected]",
includeServiceFee: true,
tipCents: 500,
};
// One request returns the catalog and the initial authoritative quote.
const { catalog, checkout } = await client.load({ selection });
// Render Stripe Elements with checkout.payment.publishableKey,
// checkout.payment.connectedAccountId, and checkout.totalCents. The prepared
// checkout also binds the normalized items and pricing used for this total.
// Recalculate with the final email and other customer-controlled pricing
// fields before confirmation.
const finalCheckout = await client.calculateTotal({
catalog,
selection,
...pricing,
});
const payment = await client.submitPayment({
checkout: finalCheckout,
submission: {
confirmationToken: "ctoken_...",
customFieldAnswers: [
{ fieldId: "guest-name", value: "Ada Lovelace" },
{ fieldId: "interests", value: ["meetups", "merch"] },
],
email: "[email protected]",
metadata: { source: "community-site" },
},
});
// Safe after a refresh or an interrupted confirmation response.
const recovered = await client.getPaymentStatus(finalCheckout.paymentReference);Load a catalog and its initial authoritative quote in one GET request:
const { catalog, checkout } = await client.load({
selection,
});If the encoded selection is too long for a safe URL, the client uses a catalog GET request and then a quote POST request instead.
Deploy the backend support for selected widget GET requests before publishing
an SDK version that uses this flow. The selected response must include quote.
If the host already has a catalog, it can show an immediate local estimate and refresh the authoritative quote without another catalog request:
import { estimateLockerverseCheckout } from "@lockerverse/sdk/checkout";
const estimate = estimateLockerverseCheckout(catalog, {
includeServiceFee: catalog.widget.fields.serviceFee,
selection,
});
const checkout = await client.calculateTotal({
catalog,
includeServiceFee: catalog.widget.fields.serviceFee,
selection,
});The estimate supports catalog prices, quantities, custom amounts, tips, and the published service-fee rate. It does not apply discounts. Do not use it for payment submission. Only the authoritative quote can create a payment.
createLockerverseCheckoutClient() exposes only load, calculateTotal,
submitPayment, and getPaymentStatus. The client has no selection store,
lifecycle state, recurring billing lifecycle, or Stripe state. React
applications can use @lockerverse/react for the complete payment UI and
orchestration.
Select one fixed recurring Product without a quantity. Lockerverse returns its monthly or annual cadence in the prepared checkout:
const recurringCheckout = await client.calculateTotal({
catalog,
email: "[email protected]",
items: [{ productId: monthlyProduct.id }],
});One-time fixed Products use quantity. One-time custom Products use
amountCents. A recurring checkout contains exactly one fixed Product.
Fixed prices in the catalog are display data. calculateTotal() sends Product
IDs and selections to Lockerverse, which resolves the payable amount. The host
returns them together as one immutable prepared checkout. Pass that prepared
checkout to submitPayment() so a host cannot accidentally submit different
inputs from the ones Lockerverse quoted. It owns one stable payment reference
so Lockerverse can prevent duplicate checkout creation and recover ambiguous
outcomes.
If submitPayment() throws an error with recoveryRecommended: true, the
request may still have reached Lockerverse. Query getPaymentStatus() with the
prepared checkout's payment reference before allowing another payment. Other
failures are definitive and do not require recovery.
Runtime configuration
Production is the default endpoint and environment. Checkout, signup, auctions,
and public event listings share these options. A custom endpoint requires an
explicit environment; development also requires an explicit apiBaseUrl.
Include /api in this URL. The generated attendee client uses a separate
baseUrl option with the origin only, as shown in its section below.
const client = createLockerverseCheckoutClient({
apiBaseUrl: "https://portal-dev.lockerverse.com/api",
communitySlug: "auburn",
environment: "development",
widgetSlug: "auburn-tailgate-party",
});Custom endpoints must be HTTP(S) URLs without credentials, query strings, or
fragments. HTTPS is required. Local HTTP endpoints are accepted only with
environment: "development" and a host of localhost, 127.0.0.1, [::1],
or a .localhost subdomain. Requests have a 20-second deadline by default;
override it with a positive requestTimeoutMs value. The same deadline includes
reading successful JSON responses and supported payment or signup error bodies.
Public Event attendees
Use the generated public API client. It already owns the route, parameters, response type, and production origin:
import { getPublicEventAttendees } from "@lockerverse/sdk";
try {
const { data } = await getPublicEventAttendees({
path: {
communitySlug: "auburn",
eventId: "11111111-1111-4111-8111-111111111111",
},
throwOnError: true,
});
console.log(data.registrations);
} catch (error) {
console.error("Could not load attendees", error);
}data.registrations contains each public name and party size. Pass
data.nextCursor as query.cursor to load the next page. For development,
create the same generated client with the development origin and pass it to
each request:
import { createLockerversePublicApiClient } from "@lockerverse/sdk";
const client = createLockerversePublicApiClient({
baseUrl: "https://portal-dev.lockerverse.com",
});
const firstPage = await getPublicEventAttendees({
client,
path: {
communitySlug: "auburn",
eventId: "11111111-1111-4111-8111-111111111111",
},
throwOnError: true,
});
if (firstPage.data.nextCursor) {
await getPublicEventAttendees({
client,
path: {
communitySlug: "auburn",
eventId: "11111111-1111-4111-8111-111111111111",
},
query: { cursor: firstPage.data.nextCursor },
throwOnError: true,
});
}Signup
import { createLockerverseSignupClient } from "@lockerverse/sdk/signup";
const signup = createLockerverseSignupClient({
communitySlug: "auburn",
signupSlug: "tailgate-guests",
});
const definition = await signup.load();
const submissionReference = crypto.randomUUID();
const submission = await signup.submit({
answers: [{ fieldId: "guest-type", value: "student" }],
email: "[email protected]",
name: "Ada Lovelace",
participantCount: 2,
}, { submissionReference });The signup client is stateless. Calls to load and submit are independent.
Signup definitions always have an enabled, required name field. For all other
built-in fields, a disabled field is never required. The SDK rejects a response
that breaks these rules before it reaches the UI.
Create a submission reference before a request. If an error has
recoveryRecommended: true, reuse that reference with the same values so
Lockerverse can return the original submission.
Auctions
Use the auction client without React or Stripe UI dependencies:
import { createLockerverseAuctionClient } from "@lockerverse/sdk/auctions";
const auctions = createLockerverseAuctionClient({ communitySlug: "auburn" });
const catalog = await auctions.list();
const auction = await auctions.load("community-auction");
const item = auction.items[0];The default environment is production. For development, set both apiBaseUrl
and environment: "development", as shown in
runtime configuration. load returns the auction,
enabled items, bid amounts, inventory, fees, and payment configuration. The payment
configuration supplies the Stripe publishable key and connected account; no
secret key belongs in a browser. Older backends can omit this configuration;
update the backend before using the built-in auction checkout.
The following functions show the raw mutation API. They do not run until your application calls them after the customer confirms their choice:
import type {
LockerverseAuctionBidSubmission,
LockerverseAuctionPurchaseSubmission,
} from "@lockerverse/sdk/auctions";
function submitBid(itemId: string, submission: LockerverseAuctionBidSubmission) {
return auctions.createBid(auction.path, itemId, submission);
}
function submitPurchase(itemId: string, submission: LockerverseAuctionPurchaseSubmission) {
return auctions.createPurchase(auction.path, itemId, submission);
}Create a Stripe confirmation token with the payment configuration from the
auction. Bids use card setup for later off-session settlement. Buy-now uses
immediate payment. Both submissions include the token, email, and E.164 phone.
Amounts are integer USD cents. Send the expected subtotal, service fee, and total
that the customer accepted. Handle requiresAction with Stripe
handleNextAction and the returned clientSecret.
- Never automatically retry a bid. The backend does not deduplicate bids and has no public bid-status recovery endpoint. An unknown result needs support follow-up; a submitted bid is not a winning bid or a payment receipt.
- Create one
clientRequestIdbefore a buy-now request. Keep that ID and the same purchase details for recovery. CallgetPurchaseStatus(auctionSlug, itemId, clientRequestId)to check the existing purchase without creating another payment. It returns a purchase result ornulland can reconcile Stripe status and deliver the purchase notification.nullmeans no purchase was found at that moment; it does not prove an original in-flight request has ended. If you retry a submission, use the same original request ID and details. Usestatus === "paid"to establish payment completion; a pending result is not completion. getPurchaseReceipt(auctionSlug, itemSlug, purchaseId)takes the item slug, while mutations take its ID. The receipt contains amounts and quantity, not payment status. It is not proof of successful payment.
For card-only buy-now Elements configured with paymentMethodTypes: ["card"],
pass the same paymentMethodTypes: ["card"] in createPurchase. Omit this field
when collecting payment details with automatic payment methods. Stripe requires
the client and server payment-method configuration to match. Include a
return_url when creating the confirmation token.
The built-in React auction checkout accepts cards only. The core client accepts the confirmation tokens supported by the backend; a custom UI must obey its payment-method rules.
For ready-made browsing and checkout, use @lockerverse/react/auctions and
@lockerverse/react/auction-item.
Public event listings
Use @lockerverse/sdk/public-events to read a community's publicly listed
Events. This API is separate from the ticket attendee API at the package root.
It does not require a user account or expose ticket data.
import { createLockerversePublicEventsClient } from "@lockerverse/sdk/public-events";
const client = createLockerversePublicEventsClient({ communitySlug: "auburn" });
const upcoming = await client.list();
const past = await client.listPast({ limit: 20, offset: 0 });
const selected = await client.search({
from: "2026-09-01T00:00:00.000Z",
to: "2026-10-01T00:00:00.000Z",
timeScope: "all",
sort: "startAtAsc",
limit: 20,
offset: 0,
});
if (past.hasMore) {
const nextPage = await client.listPast({ limit: 20, offset: past.events.length });
}list() returns Events that are happening now or start in the future.
listPast() returns { events, hasMore } for Events that have ended; its defaults
are limit: 20 and offset: 0. Increment the offset by the number of records
already loaded. Each Event has id, name, description, imageUrl, location,
link, startAt, and endAt. The description, image, location, and link can be
null. The backend controls public visibility. The SDK validates responses and
returns immutable snapshots. Use @lockerverse/react/public-events for the
styled list with Upcoming and Past views.
search() returns the same page shape and defaults to timeScope: "current".
The Event must overlap the window:
it must end after from and start before to. timeScope can be
current, past, or all. Sort with startAtAsc, startAtDesc, endAtAsc,
or endAtDesc. Follow hasMore with the next offset to read the full range.
Security and observability
- No Stripe or Lockerverse secret is accepted by the public API.
- Successful catalog, quote, confirmation, and status responses are validated before being returned.
- Public operation inputs are validated with private Valibot schemas.
- Backend response bodies and validation internals never escape in SDK errors.
- Expected input errors are
reportable: false; unexpected network and server failures arereportable: true. - Reportable failures are sent directly to Lockerverse Sentry with allowlisted
request context.
sentryDsn: nulldisables loading and sending telemetry. - The Sentry runtime is an isolated failure-only chunk and does not replace or mutate the host application's Sentry client.
- Stripe's shared publishable key is selected by environment. The authoritative quote supplies the community's connected account.
- Analytics is intentionally out of scope.
Commands
Run these from the repository root:
pnpm dev:react
pnpm typecheck
pnpm test
pnpm build
pnpm --filter @lockerverse/sdk pack:check