E-commerce Store
Build a cart-first storefront with products, quotes, checkout, and payments
Arky storefronts are cart-first. The browser should not keep its own checkout model in local storage; it should use the SDK storefront store, load the server cart, and let checkout create the order from that cart.
Store Setup
Create one store for the app and call setContext when the storefront boots or when locale/market context changes.
import { initialize, mountCheckoutAction } from "arky-sdk/storefront";
export const arky = initialize(import.meta.env.PUBLIC_ARKY_PUBLISHABLE_KEY, {
locale: "en",
market: "us",
});
arky.setContext({ locale: "en" });
await arky.action.track({
key: "page.view",
payload: { path: location.pathname },
});
await arky.eshop.cart.load();
eshop.cart.load creates or resumes the current arky_vst_... visitor session
and loads the backend cart. Public catalog reads do not create a visitor
session. Anonymous visitors still receive a contact record when they begin a
stateful flow, so the cart is always tied to visitor context.
Product Listing
Use the e-shop module on the store. The call updates arky.eshop.state, so a page can either use the returned value or subscribe to the store state.
Variant order is semantic: variants[0] is the default configured by the store operator.
const { items: products } = await arky.eshop.product.list({
limit: 24,
});
const first = products[0];
const variant = first.variants[0];
const price = arky.utils.formatPrice(variant.prices);
Product Detail
Adding a product should update the server cart immediately. The local UI gets refreshed from the cart response.
const product = await arky.eshop.product.get({ id: productId });
const variant = product.variants[0];
await arky.eshop.cart.addProduct(product, variant, 1);
Cart UI
The cart store exposes Nano Store atoms, so React, Svelte, Vue, Astro islands, or plain browser code can subscribe without a framework-specific adapter.
const unsubscribe = arky.eshop.cart.snapshot.subscribe((snapshot) => {
console.log(snapshot.item_count);
console.log(snapshot.product_items);
console.log(snapshot.booking_items);
});
const status = arky.eshop.cart.status.get();
const currentQuote = arky.eshop.cart.quote_result.get();
unsubscribe();
Common cart methods live directly under eshop.cart.
await arky.eshop.cart.setProductQuantity(item.id, 2);
await arky.eshop.cart.removeProduct(item.id);
await arky.eshop.cart.applyPromoCode("SUMMER20");
await arky.eshop.cart.selectShippingMethod("standard");
Quote
Quote calculation also goes through the cart. Pass addresses, promo codes, forms, and shipping method when they are available.
const quote = await arky.eshop.cart.quote({
shipping_method_id: "standard",
promo_code: "SUMMER20",
shipping_address: {
name: "Jane Doe",
street1: "456 Contact Ave",
city: "New York",
state: "NY",
postal_code: "10001",
country: "US",
},
});
console.log(quote?.money.total);
Money And Tax
Quote and checkout totals come from the backend cart. Storefronts should render the returned quote/order values instead of recalculating tax in the browser.
The current flow is:
item unit price * quantity
minus promo-code discount
= taxable base
plus item tax when the market is exclusive
plus shipping
plus shipping tax when the selected shipping method is taxable
= order total
Markets choose the tax mode:
exclusive: prices are net prices and tax is added on top.inclusive: prices already include tax and the tax amount is backed out for reporting.
Zones provide the configured tax rate and available payment/shipping methods. Promo codes are discounts; they are not stored value.
Checkout
Checkout creates the order from the cart. Storefronts should not call an order checkout endpoint directly.
const result = await arky.eshop.cart.checkout({
payment_method_key: "credit_card",
return_url: window.location.href,
});
console.log(result.order_id, result.number);
if (result.payment_action.type === "stripe_embedded_checkout") {
const mounted = await mountCheckoutAction(
result.payment_action,
"#stripe-checkout",
);
// Call mounted.destroy() when the checkout container is removed.
}
Card checkout stays inside the storefront. The server returns a short-lived,
closed stripe_embedded_checkout action containing the connected account’s
publishable key and Checkout client secret. Mount it with the shared SDK
controller; never construct Stripe account context yourself. Arky confirms
payment from the connected account’s signed webhook. The browser completion
callback is navigation and presentation state, not proof of payment.
Order State
Orders expose separate lifecycle and fulfillment summary, while payment is an explicit child:
status: order lifecycle:pending,confirmed,partially_cancelled, orcancelled.payment_id: the identity of the one separate OrderPayment; use the order-scoped payment endpoint when payment state is needed.fulfillment_status: delivery/work summary, such asunfulfilled,in_progress,partially_fulfilled,fulfilled, ornot_required.
Physical products use shipping_lines[] on the order for the buyer-selected
shipping charge. Product, booking, digital-product, payment, fulfillment,
shipment, refund, and dispute records are fetched through explicit order-scoped
APIs. Those resources have independent identity or lifecycle and are never
silently hydrated onto a normal Order response.
Booking availability is read from a compact ProviderSchedule record for the
provider’s local day. OrderBooking remains authoritative; the schedule is a
transactionally maintained, rebuildable capacity projection so storefronts do
not scan orders or bookings to render availability.
Mixed Carts
Products and scheduled service lines share the same cart. Product storefronts usually use eshop.cart.addProduct, while service pages can use the service controller and checkout through the same cart.
await arky.eshop.service.initialize();
await arky.eshop.service.select(service);
await arky.eshop.service.findFirstAvailable();
const serviceState = arky.eshop.service.state.get();
await arky.eshop.service.selectTimeSlot(serviceState.slots[0]);
await arky.eshop.service.addToCart();
const order = await arky.eshop.cart.checkout({
payment_method_key: "cash",
});
Storefront Rules
- Use
initializeas the storefront entry point. - Use the backend cart as the source of truth.
- Use
eshop.cart.quotebefore presenting totals. - Use
eshop.cart.checkoutto create orders. - Use
arky.clientonly for unusual low-level cases that are not represented by the store yet.