Storefront SDKyour design, the listing's facts.

Design the product page and even the checkout form your own way, bound live to a Wakato listing, while the payment always runs on Wakato.

The Storefront SDK lets a seller's page look however they want while the facts come from the listing. You never type a price, a stock count, a title or a photo address into a page. You mark where they go, the SDK fills them, and when the seller changes the listing in Commerce every page showing it redraws within a second. Nothing to republish.

Every page Wakato serves for a Space (its sites, a custom domain, /product/<id>) already carries the SDK as window.Wakato. It is ready before your own scripts run.

There are four ways to use it, from no code to a fully custom checkout.

1. Bind with marks

<section data-wakato-listing="PRODUCT_ID" class="drop">
  <img data-wakato-field="photo" class="hero">
  <h1 data-wakato-field="title"></h1>
  <p class="price" data-wakato-field="price"></p>
  <span data-wakato-field="stock"></span>
  <div data-wakato-field="options"></div>
  <button data-wakato-checkout class="buy">Take it home</button>
</section>

data-wakato-listing="<product id>" marks a block. Inside it, data-wakato-field is filled from that listing:

FieldWhat it holds
title, description, long_descriptionThe listing's words
priceThe label: "$12", "From $40", "$9/month", "Free". Follows the picked option
price_amount, currencyThe number and the currency
stock, stock_count"3 left", "Sold out", or empty when stock is not counted
shipping, shipping_from, free_shippingThe shop's shipping facts
refund_policy, shipping_policy, shop_nameThe shop's own words, empty when unset
buy_label"Buy now", "Book now", "Subscribe", "Sold out"…
rating"★ 4.8 (12)", empty until reviewed
photoOn an <img> sets src; elsewhere a background. data-wakato-index="2" = the third photo
photosFilled with one <img> per photo for your own gallery
variantOn a <select>: one option per variant, sold-out ones disabled
optionsOne group of buttons per option, aria-pressed on the picked one

An empty data-wakato-checkout inside the block becomes that listing's buy button, carrying the picked variant. It is disabled when sold out.

The block itself wears data-wakato-available, data-wakato-stage (live or draft) and data-wakato-ready for your CSS:

[data-wakato-available="false"] .buy { opacity: .4; }

A price elsewhere on the page can be bound on its own:

<a href="#buy">Get yours for <span data-wakato-listing="PRODUCT_ID" data-wakato-field="price"></span></a>

2. Read it in your own script

const p = await Wakato.store.product(PRODUCT_ID);
// { id, title, price, price_label, currency, photos, available, stock,
//   option_types, variants, shipping, shop, buy_label, rating, url, … }

const stop = Wakato.store.onChange(PRODUCT_ID, (p) => draw(p)); // now, and after every change

await Wakato.store.products([ID1, ID2]);
Wakato.store.select(blockOrId, variantId);   // drive the options from your own picker
Wakato.store.bind(element);                  // bind blocks you add later (also automatic)

The page also hears wakato:listing-change and wakato:variant-change on window.

3. Your own checkout form

Quote

Wakato.checkout.quote() is the checkout's own arithmetic: the variant's price, stock, where the seller ships, the discount and the tax. Show its numbers; never compute a total in the page.

const q = await Wakato.checkout.quote({
  productId: PRODUCT_ID,
  variantId,
  quantity: 2,
  address: { name, line1, city, state, postalCode, country: "US" },
  code: "SPRING10",
});
// { ok, missing: ["address"], problem, needsAccount, test, free, currency,
//   subtotal, discount, shipping, tax, total, cents: {…}, labels: {…} }

problem is a sentence you can show as is ("This seller does not ship to Mali."). Call quote() again whenever the form changes.

Keep the total live

When the seller changes the listing, the SDK asks your last question again. Bind the numbers with marks and they follow with no code:

<button class="pay">Pay <span data-wakato-quote="total"></span></button>
Shipping <span data-wakato-quote="shipping"></span> · Tax <span data-wakato-quote="tax"></span>
<p data-wakato-quote="problem"></p>

data-wakato-quote is one of total, subtotal, shipping, tax, discount or problem, anywhere on the page. Or draw them yourself with Wakato.checkout.onQuote(cb) or the wakato:quote-change event. Both fire only when the numbers moved.

The card field

Wakato.checkout.paymentField("#card", {
  color: "#F2F0EA", background: "#17171B", border: "1px solid #33333A",
  primary: "#C6F432", radius: "10px",
  font: "Instrument Sans, sans-serif",
  fontUrl: "https://fonts.googleapis.com/css2?family=Instrument+Sans",
});

This places Wakato's secure card field inside your element, in your colours. The card number is typed into that frame and never reaches the page's code. It is the one piece of the checkout that is not yours. Never collect card details in your own inputs.

Pay

const r = await Wakato.checkout.pay({
  productId: PRODUCT_ID, variantId, quantity: 2, address, code,
  buyer: { email: "ada@example.com", name: "Ada" },   // email is required
});

switch (r.status) {
  case "paid":          showThanks(r.orderId); break;
  case "price_changed": refreshQuote(); break;      // nothing was charged
  default:              showError(r.message);       // declined, problem, error
}

pay() quotes once more and charges exactly that quote through Wakato's checkout. If the total moved since the page showed it, nothing is charged and the status is price_changed. The order, the held money, the receipt, any job and automations, and the payout then happen as for any other sale. The page also hears wakato:checkout-paid with the orderId.

A free order needs no card field. When quote() answers needsAccount (a subscription, or something delivered into a Wakato account), open the ready-made checkout instead with Wakato.checkout(PRODUCT_ID).

Drafts are tests

A listing still in Draft is a rehearsal: the card field accepts only Wakato's test cards and no money moves. Make it Live when you want real payments.

4. The ready-made checkout

Any element with data-wakato-checkout="<product id>" (and optional data-wakato-variant), or Wakato.checkout(id, { variantId }) from your script, opens Wakato's own checkout over the page: address, card, everything. The page hears wakato:checkout-open, wakato:checkout-close and wakato:checkout-paid. Colour it with the set_checkout_branding tool.

<button data-wakato-checkout="PRODUCT_ID">Buy now</button>

The public API behind it

The SDK reads from storefront-api, a public door that needs no key and answers any origin. It only reads; nothing is created.

curl -X POST https://api.wakato.io/functions/v1/storefront-api \
  -H "Content-Type: application/json" \
  -d '{"action":"products","ids":["PRODUCT_ID"]}'

curl -X POST https://api.wakato.io/functions/v1/storefront-api \
  -H "Content-Type: application/json" \
  -d '{"action":"quote","product_id":"PRODUCT_ID","quantity":1,
       "shipping_address":{"country":"US","postal_code":"30303"}}'

products returns each listing's public facts (live and draft listings only). quote returns the same arithmetic as Wakato.checkout.quote(). Paying is only possible through the card field or the ready-made checkout.

Rules that keep a page honest

  • The listing is the truth. Never hard-code what a mark can give.
  • Never promise free shipping, a discount, a return policy or a delivery time the shop has not set. Bind free_shipping, refund_policy and shipping_policy; empty means say nothing.
  • Never put a price or stock in <title> or meta tags: they cannot follow the listing.
  • Product card and product page templates can use all of this too: data-wakato-listing="{{id}}" works inside a template.

Agents building a page read the same rules with the MCP tool get_storefront_sdk_reference. See the commerce tools and Payments.

On this page