Repository navigation
Basket and Checkout
Baskets are the shopping carts of a Selldone shop. Each customer (or guest) has one open basket per product type: PHYSICAL, VIRTUAL, FILE, SERVICE and SUBSCRIPTION. This page shows how to read the baskets, add and remove items with window.$storefront.basket, keep guest baskets working, and which XAPI endpoints the official Storefront uses for the rest of the checkout.
Open baskets come with the shop bootstrap call:
const { shop, baskets } = await window.$storefront.shop.fetchShop();
const physicalBasket = baskets?.find((b) => b.type === "PHYSICAL");
console.log(physicalBasket?.items, physicalBasket?.bill);The official Storefront calls fetchShop() on start-up, after login, and whenever a basket call asks for a refresh.
basket.addItem(
product_id: number,
variant_id: number | null,
count: number,
options?: {
currency?: keyof typeof Currency; // default: window.$storefront.currency.code
preferences?: BasketItem.IPreferences | null; // service preferences, area/volume dimensions…
vendor_product_id?: number | null; // 🟣 Marketplace: the vendor offer to buy from
price_id?: number | null; // 🎗️ Subscription: the selected subscription price
},
): Promise<{
success: boolean;
error: boolean;
error_msg: string | null;
refresh?: boolean;
basket: Basket;
bill: Basket.ICalculatedBill;
}>count is the quantity of this product (and variant) you want in the basket. The official basket item spinner sends the new total each time the customer changes it.
window.$storefront.basket
.addItem(product_id, variant_id, count, {
preferences: preferences,
vendor_product_id: vendor_product_id, // 🟣 Marketplace 🟣
price_id: subscription_price_id, // 🎗️ Subscription
})
.then(({ basket, bill, refresh, error, error_msg }) => {
if (basket) {
setBasket(basket); // update your state
setBasketBill(basket, bill);
}
// The backend can return an error AND a corrected basket at the same time
if (error) showError(error_msg);
// Important: re-fetch shop and baskets (e.g. an item was removed automatically)
if (refresh) window.$storefront.shop.fetchShop().then(updateBaskets);
})
.catch((error) => showError(error));Unlike most calls, addItem resolves (instead of rejecting) when the response contains error: true, because the server still returns the up-to-date basket and bill. Always check error and error_msg.
Endpoint: PUT /shops/@{shop}/basket/{product_id} with body { currency, variant_id, count, preferences, vendor_product_id, price_id }.
window.$storefront.basket
.deleteItem(product_id, variant_id) // optional 3rd arg: { currency }
.then(({ success, basket, bill }) => {
setBasket(basket);
setBasketBill(basket, bill);
})
.catch((error) => showError(error));Endpoint: DELETE /shops/@{shop}/basket/{product_id} with { currency, variant_id }.
Shoppers do not need to log in to fill a basket (unless the shop disables guest checkout). Guest baskets are identified by a guest code:
-
fetchShop()returnsshop.guest_codefor a guest. - Save it and send it as the
S-Guestheader on every request. -
StorefrontSDK.Setup()reads the saved code on the next page load and adds the header for you.
This is what the official Storefront does after fetchShop():
import { StorefrontLocalStorages } from "@selldone/core-js/helper/local-storage/StorefrontLocalStorages";
const { shop } = await window.$storefront.shop.fetchShop();
if (shop.guest_code) {
// Must be saved in localStorage to be reused in all request headers
StorefrontLocalStorages.SetShopGuestCode(shop.guest_code);
window.axios.defaults.headers.common["S-Guest"] = shop.guest_code;
} else {
// Signed-in customer: no guest header
delete window.axios.defaults.headers.common["S-Guest"];
}After the customer signs in, call fetchShop() again so the backend can return their baskets.
bill (Basket.ICalculatedBill) is calculated by the server and is the source of truth for totals. Render subtotal, discounts, coupon, shipping, tax and the final payable amount from it instead of computing them in the browser. Basket and bill models are documented in Selldone Core.
The SDK wraps adding and removing items. The rest of the checkout (receiver info, shipping, coupon, payment) is done with window.XAPI, the URL builder the SDK creates, and window.axios, which already carries all required headers. Each builder only returns a URL; you send the request.
// Example: reload the bill of the physical basket
const url = window.XAPI.GET_BASKET_BILL(window.$storefront.name, "PHYSICAL");
const { data } = await window.axios.get(url);| Step | URL builder | Endpoint |
|---|---|---|
| Item message / preferences |
POST_BASKET_ITEM_MESSAGE, PUT_BASKET_ITEM_PREFERENCES
|
POST /shops/@{shop}/basket/{product_id}/message, PUT …/preferences
|
| Basket config | PUT_SET_BASKET_CONFIG(shop, basket_id) |
PUT /shops/@{shop}/baskets/{basket_id}/config |
| Customer location | POST_SET_MY_LOCATION(shop, type) |
POST /shops/@{shop}/baskets/{type}/my-location |
| Apply a coupon | PUT_SET_BASKET_COUPON(shop) |
PUT /shops/@{shop}/coupon |
| Apply a discount code | PUT_DISCOUNT_CODE(shop) |
PUT /shops/@{shop}/discount-code |
| Apply a lottery prize | PUT_SET_BASKET_LOTTERY(shop) |
PUT /shops/@{shop}/lottery |
| Calculated bill | GET_BASKET_BILL(shop, type) |
GET /shops/@{shop}/basket/{type}/bill |
| Pay | POST_BUY_BASKET(shop, type, gateway) |
POST /shops/@{shop}/basket/{type}/buy/{gateway_code} |
| Submit a service request | POST_SUBMIT_SERVICE_BASKET(shop, type) |
POST /shops/@{shop}/basket/{type}/submit |
| Pay a seller-priced bill | POST_PAY_BILL(shop, basket_id, bill_id, gateway) |
POST /shops/@{shop}/basket/{basket_id}/bills/{bill_id}/pay/{gateway_code} |
| Order detail | GET_ORDER_BASKET_INFO(shop, basket_id) |
GET /shops/@{shop}/baskets/{basket_id} |
| Order history | GET_MY_ORDERS_HISTORY_PHYSICAL(shop, type) |
GET /shops/@{shop}/basket/orders-{type} |
The full list of more than 150 URL builders, with path parameters and auth notes, is in apis/XAPI.ts. Request body fields are not wrapped by the SDK; take them from the official implementation:
-
ai-guideline/storefront/04-basket-checkout-payment.md: checkout state machine, basket types and payment paths. -
ai-guideline/storefront/11-checkout-payment-account-deep-dive.md: checkout form, shipping, payment modes and order history. - The cart and payment pages of the Storefront app and the Components library.
- Load the basket from
fetchShop()(or refresh it). - Validate items: stock, variant, vendor, required preferences.
- Collect receiver and shipping information and save the basket config.
- Load the bill and apply coupon, discount code, offers or lottery prize.
- Let the customer choose a payment gateway, then call the buy endpoint (or submit a service basket).
- Follow the payment redirect, then show the order page.
For shops that use Avocado (request-based orders where customers describe the items they want), the SDK wraps the main calls under window.$storefront.avocado: addItem(hash, title, message, link, photo, count), updateReceiverInfo(hash, receiver_info), submitOrder(hash, currency) and getOrder(hash). See the API Reference.
- Coupons, Offers, Cashback and Lottery
- Customer Account
- API Reference
- Storefront wiki: the complete production checkout.