Skip to content

State, recovery, and errors ​

The SDK separates safe render state from persisted capabilities. Understanding that boundary makes reloads, retries, SSR, and error handling predictable.

Cart snapshot ​

ts
type CartSnapshot = {
    cart: Cart | null;
    hasCart: boolean;
    isBusy: boolean;
    lastError: PublicError | null;
};
FieldMeaning
hasCartA persisted cart capability exists
cartLast loaded authoritative cart, or null before restore
isBusyCart restoration or mutation is in flight
lastErrorSafe to render; excludes capabilities and API internals

Checkout snapshot ​

ts
type CheckoutSnapshot = {
    order: Order | null;
    paymentAttemptId: string | null;
    paymentAttemptStatus: string | null;
    isBusy: boolean;
    lastError: PublicError | null;
};

The completed public order remains available in the checkout snapshot after the SDK clears its cart. A later cart mutation resets the completed order so the same storefront instance can handle another purchase.

Public errors ​

ts
const snapshot = storefront.cart.getSnapshot();

switch (snapshot.lastError?.kind) {
    case 'network':
        showRetryMessage();
        break;
    case 'capability-lost':
        showCartExpiredMessage();
        break;
    case 'api':
        showCheckoutMessage(snapshot.lastError.message);
        break;
}
kindTypical causeLocal capability
networkTransport failure; no HTTP statusKept so the buyer can retry
capability-lostCart/order no longer valid (HTTP 404)Cleared automatically
apiValidated business or request errorDepends on the operation

Operations also reject with ZynoSalesError for imperative error handling:

ts
import { ZynoSalesError } from '@zyno-io/zynosales-ecommerce-sdk';

try {
    await storefront.cart.refresh();
} catch (error) {
    if (error instanceof ZynoSalesError && error.isNetworkError) {
        showRetryButton();
    } else {
        throw error;
    }
}

ZynoSalesError exposes status, isNetworkError, and isCapabilityLost. Failed operations also update lastError on the relevant snapshot, so subscription-driven UIs and try/catch paths stay aligned.

Persistence defaults ​

StateDefault storageLifetime
Cart and order capabilitieslocalStorageAcross browser reloads and sessions
Payment attempt recoverysessionStorageCurrent browser tab/session
Completed-order markersessionStorageCurrent browser tab/session, bounded to one order
Product list cacheMemoryCurrent storefront instance
Runtime config cacheMemoryCurrent storefront instance

Storage keys are isolated by API base and publishable key before configuration, then by the Sales tenant once configuration is known. The SDK reconciles and rekeys cart and payment state when getConfig() resolves.

When browser storage throws or is unavailable, the defaults fall back to memory. Inject ZynoSalesStorage implementations to integrate a different synchronous storage policy.

Startup sequence ​

ts
const config = await storefront.getConfig();
const cart = await storefront.cart.restore();

renderCapabilities(config.capabilities);
renderCart(cart.cart);

Loading configuration first scopes persisted state to the resolved storefront tenant before cart restoration. Cart and checkout operations are serialized internally per storefront instance.

On a Stripe return route, follow restoration with payment recovery as shown in Stripe and order completion.

SSR behavior ​

Creating the SDK during SSR is safe: when window is absent, default storage is in memory. That server instance cannot restore the browser's saved cart. Create or reuse the browser storefront during hydration and call cart.restore() there.

Do not serialize the SDK instance, cart access, client secrets, or storage contents into HTML. Serialize only render-safe catalog/cart/order data that your application intentionally exposes.

Capability safety ​

Cart and order keys grant access; they are not identifiers. The SDK deliberately omits them from snapshots and hooks other than the explicit beforePayment handoff. Avoid reading or copying SDK storage directly. Use withServerAccess() only for a short same-origin merchant-server request.

Next steps ​

Membership fulfillment ​

A public order’s membership lines include membership.id, membership.status, and the accepted membership.terms. Render the returned status for membership activation. afterOrderCompleted remains an order-completion hook; it does not create memberships. Refresh the order when fulfillment is pending.

Payment recovery stores the intent kind and its identifier, including SetupIntent references for free recurring starts. It never persists the Stripe client secret. A confirmed payment whose local settlement is interrupted is retried through the same durable attempt. Recover that attempt before starting another payment.

MIT Licensed