Getting Started
Install the Better Store packages, load products, and render checkout.
Getting Started
This guide shows the shortest path from an empty TypeScript project to a working Better Store integration.
You will:
- Install the packages you need.
- Create a server-side SDK client.
- Load products.
- Create a checkout session.
- Render checkout in React.
- Use the cart hook for client-side cart state.
Install
Use the SDK on the server:
bun add @betterstore/sdkUse the React package when you need cart state or embedded checkout UI:
bun add @betterstore/reactUse helpers when you need price and currency formatting:
bun add @betterstore/helpersCreate A Server Client
Create the Better Store client in server-side code only.
import { createBetterStore } from "@betterstore/sdk";
export const betterstore = createBetterStore(process.env.BETTERSTORE_SECRET!);Never use BETTERSTORE_SECRET in browser code. The secret key should live in .env.local, server environment variables, server actions, route handlers, or another server-only runtime.
Load Products
import { betterstore } from "./betterstore";
export async function getProducts() {
return betterstore.products.list({
columns: {
id: true,
title: true,
description: true,
images: true,
priceInCents: true,
},
});
}If you need variants, request them through the query with option:
import type { ListProductsParams } from "@betterstore/sdk";
import { betterstore } from "./betterstore";
const productQuery = {
columns: {
id: true,
title: true,
description: true,
images: true,
priceInCents: true,
},
with: {
productVariants: {
columns: {
id: true,
title: true,
priceInCents: true,
variantOptions: true,
},
},
},
} as const satisfies ListProductsParams;
export async function getProductsWithVariants() {
return betterstore.products.list(productQuery);
}Create A Checkout Session
Create checkout sessions from the server.
const checkout = await betterstore.checkout.create({
currency: "usd",
lineItems: [
{
productId: "product-id",
quantity: 1,
variantOptions: [],
},
],
});
const checkoutId = checkout.checkoutSession.id;
const checkoutSecret = checkout.checkoutSession.checkoutSecret;Send only checkoutId and checkoutSecret to the checkout page. Keep BETTERSTORE_SECRET private.
If your storefront supports markets, resolve the buyer country first and pass the resolved market context into checkout creation:
const market = await betterstore.markets.resolve({ countryCode: "CZE" });
const checkout = await betterstore.checkout.create({
currency: market.currency,
marketId: market.marketId ?? undefined,
buyerCountryCode: market.countryCode ?? "CZE",
lineItems,
});Product queries can also receive marketId so the API can apply market availability, market pricing, and market currency consistently.
Render Embedded Checkout
"use client";
import { CheckoutEmbed } from "@betterstore/react";
export function CheckoutPage({
checkoutId,
checkoutSecret,
}: {
checkoutId: string;
checkoutSecret: string;
}) {
return (
<CheckoutEmbed
checkoutId={checkoutId}
config={{
checkoutSecret,
cancelUrl: "/cart",
successUrl: "/success",
locale: "en",
}}
/>
);
}| Option | Meaning |
|---|---|
checkoutSecret | Secret for one checkout session. |
successUrl | Where the customer goes after payment succeeds. |
cancelUrl | Where the customer goes if they leave checkout. |
locale | Checkout language. |
clientProxy | Optional proxy URL if checkout API calls should go through your app. |
The checkout-specific checkoutSecret is browser-safe for that checkout session. Your private BETTERSTORE_SECRET is not browser-safe.
Hosted checkout collects a shipping address first. Billing defaults to "same as shipping", and customers can enter a separate billing address with company and tax ID fields when needed. For backwards compatibility, the React package still sends the legacy address alias as the shipping address.
Use The Cart Hook
Use useCart for simple client-side cart state.
"use client";
import { useCart } from "@betterstore/react";
type Product = {
id: string;
title: string;
priceInCents: number;
};
export function AddToCartButton({ product }: { product: Product }) {
const { lineItems, addItem, clearCart } = useCart<Product>();
return (
<div>
<button type="button" onClick={() => addItem(product, { quantity: 1 })}>
Add to cart
</button>
<button type="button" onClick={() => clearCart()}>
Clear cart ({lineItems.length})
</button>
</div>
);
}The cart lives in the browser. Checkout creation should still happen on the server.
Environment Variables
| Variable | Required | Used by | Description |
|---|---|---|---|
BETTERSTORE_SECRET | Yes | Server only | Private API secret for Better Store. Never expose this in browser code. |
BETTERSTORE_CURRENCY | Optional | Server only | Currency used by your storefront, such as usd or czk. |
Security Checklist
- Keep
BETTERSTORE_SECRETserver-side. - Send only checkout-specific
checkoutSecretvalues to browser checkout UI. - Keep real credentials in
.env.local. - Keep committed
.env.examplefiles placeholder-only.