Skip to content

JavaScript / TypeScript

@shoppable/checkout is isomorphic (browser + Node), ships ESM, CJS, and types, and has no required dependencies (it uses the platform fetch).

Install

Terminal window
npm install @shoppable/checkout

Quick example

import { ShoppableCheckout } from "@shoppable/checkout";
const checkout = new ShoppableCheckout({ token: "pk_live_…" });
const config = await checkout.getCartConfiguration(); // mints the session token
const products = await checkout.lookupProducts(["012345678905"]);
const order = await checkout.prepareCheckout(cartContent);
const { clientSecret } = await checkout.createIntent(order._id);
// confirm `clientSecret` with Stripe.js in your UI, then:
await checkout.completeCheckout(order._id);

Constructor

new ShoppableCheckout(options: ShoppableCheckoutOptions)
OptionTypeDefaultDescription
tokenstring—Publishable cart token (pk_live_… / pk_test_…). Safe in a browser bundle. Required unless you pass sessionToken.
sessionTokenstring—A session token you already hold. Skips the initial handshake. Pass token too to enable auto-refresh.
baseUrlstringhttps://ps.shoppable.comAPI base. Use https://ps.staging.shoppable.com for pk_test_.
originstringderived in browserSent as the Parent header; must be on the cart’s allowlist. Required in Node.
onSessionToken(token: string) => void—Called whenever a session token is minted or refreshed (sync it to your store).
timeoutnumber30000Per-request timeout in ms. 0 disables it.
maxRetriesnumber2Max retries for transient failures.
apiVersionstringbuilt-inPinned API version, sent as Shoppable-Version.
fetchtypeof fetchglobalCustom fetch for older runtimes.

Methods

Each mutating method also accepts a trailing options?: RequestOptions ({ signal, timeout, idempotencyKey, headers }).

MethodReturnsNotes
getCartConfiguration()Promise<CartConfiguration>Fetches config and captures the session token.
getSessionToken()string | undefinedThe current session token, if any.
getCustomer()Promise<object>Customer context for the cart.
lookupProducts(upcs, opts?)Promise<ProductAvailability[]>opts.includeVariations expands size/color variants.
prepareCheckout(cart)Promise<Checkout>Starts a checkout. Carries an idempotency key.
createIntent(checkoutId, body?)Promise<{ clientSecret }>Creates the Stripe PaymentIntent.
updateCheckout(checkoutId, patch)Promise<Checkout>Patches an in-progress checkout.
completeCheckout(checkoutId)Promise<Checkout>Finalizes after payment confirmation.
validateCoupon(input)Promise<CouponResult>Normalizes the API’s { status: 0 } failure into { valid, message, raw }.
lander.getSession(sid)Promise<object>AI cart lander session context.
lander.getRecommendations(sid)Promise<object>Cross-sell + complementary recommendations.
lander.recordCartItem(sid, upc)Promise<object>Records a lander-side add onto the shared AI cart.

Reliability

  • Retries — network errors, timeouts, 429 (honoring Retry-After), and 5xx are retried with exponential backoff. Only safe requests retry: reads, and mutations that carry an idempotency key.
  • Idempotency — every mutation sends an auto-generated Idempotency-Key, so a retry can’t double-create an order or charge. Override per call with { idempotencyKey } to make a user-triggered retry idempotent too.
  • Timeouts & cancellation — timeout plus a per-call AbortSignal.
const controller = new AbortController();
await checkout.prepareCheckout(cart, {
idempotencyKey: stableKey,
signal: controller.signal,
});

Errors

Every API failure throws a subclass of ShoppableApiError, each carrying status, the encrypted support ref, the server requestId, and the raw body.

ErrorWhen
ShoppableAuthError401 token missing / invalid / expired
ShoppableValidationError400 / 422 invalid request
ShoppableNotFoundError404
ShoppableRateLimitError429 (has retryAfter ms)
ShoppableServerError5xx
ShoppableNetworkErrorno response (offline, DNS, TLS)
ShoppableTimeoutErrorexceeded the timeout
ShoppableConfigErrorSDK misconfiguration (not an API response)
import { ShoppableApiError, ShoppableRateLimitError } from "@shoppable/checkout";
try {
await checkout.prepareCheckout(cart);
} catch (err) {
if (err instanceof ShoppableRateLimitError) {
// back off and retry later
} else if (err instanceof ShoppableApiError) {
showError(err.message, err.ref); // quote err.ref / err.requestId to support
}
}

Verifying downloads

Release zips are signed with Sigstore cosign (keyless):

Terminal window
cosign verify-blob \
--certificate checkout-0.1.0.zip.pem \
--signature checkout-0.1.0.zip.sig \
--certificate-identity-regexp 'https://github.com/72Lux/shoppable-developers/.*' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
checkout-0.1.0.zip