/examples/commercelouise-toolkit/commerce/square test mode · no data leaves your browserlouise-toolkit/commerce/square wraps the Square API for Workers. The browser tokenizes the card into a sourceId (raw card data never touches your Worker); the server prices the item from the live catalog and charges the token with createPayment. Sandbox test cards approve for free.
Cortado
Single origin · 6oz
import { createPayment, listCatalogItems, type SquareConfig } from "louise-toolkit/commerce/square";
interface CheckoutEnv {
SQUARE_TOKEN: string; // server-only access token (secret)
SQUARE_LOCATION: string; // your Square location id
SQUARE_ENV: "sandbox" | "production"; // sandbox honors test cards, moves no real money
}
// POST { sourceId, variationId } — charge the tokenized card for one catalog item.
export async function handleCheckout(request: Request, env: CheckoutEnv): Promise<Response> {
const { sourceId, variationId } = (await request.json()) as {
sourceId: string;
variationId: string;
};
const config: SquareConfig = { accessToken: env.SQUARE_TOKEN, environment: env.SQUARE_ENV };
// Price server-side from the live catalog — never trust an amount from the client.
const catalog = await listCatalogItems(config);
const variation = catalog.flatMap((item) => item.variations).find((v) => v.id === variationId);
if (!variation) return Response.json({ error: "Unknown item" }, { status: 404 });
// Charge the card token. `amountMoney` is the smallest unit (cents for USD); in
// sandbox, Square's test cards approve instantly and no real money moves.
const payment = await createPayment(config, {
sourceId,
locationId: env.SQUARE_LOCATION,
amountMoney: { amount: variation.priceCents, currency: variation.currency },
});
return Response.json({ status: payment.status, id: payment.id, receipt: payment.receiptUrl });
}<div class="louise-commerce-demo grid gap-4" data-price-cents={PRICE_CENTS}>
<div class="flex items-center gap-3 rounded-xl border border-base-300 p-3">
<div
class="flex h-12 w-12 items-center justify-center rounded-lg bg-base-200 text-2xl"
aria-hidden="true"
>
<i class="ph ph-coffee"></i>
</div>
<div class="flex-1">
<p class="font-semibold">Cortado</p>
<p class="text-sm text-base-content/60">Single origin · 6oz</p>
</div>
<span class="font-mono font-semibold">{price}</span>
</div>
<label class="grid gap-1 text-sm">
<span class="text-base-content/70">Card — Square sandbox test card</span>
<input
class="input input-bordered w-full font-mono"
name="card"
value="4111 1111 1111 1111"
inputmode="numeric"
/>
</label>
<div class="grid grid-cols-2 gap-3">
<input class="input input-bordered w-full font-mono" name="exp" value="12/26" />
<input class="input input-bordered w-full font-mono" name="cvv" value="111" />
</div>
<button class="louise-pay-btn btn btn-primary btn-sm w-fit" type="button">
Pay {price} · sandbox
</button>
<p class="louise-pay-status text-sm" role="status" aria-live="polite"></p>
</div>
<script>
// Simulate the real Square flow without loading the SDK or calling the server:
// 1. Web Payments SDK tokenizes the card → sourceId
// 2. POST sourceId → the handler runs createPayment (see square-checkout.ts)
// Sandbox test cards approve for free; here the whole thing is faked in-page.
document.querySelectorAll<HTMLElement>(".louise-commerce-demo").forEach((root) => {
const btn = root.querySelector<HTMLButtonElement>(".louise-pay-btn");
const status = root.querySelector<HTMLElement>(".louise-pay-status");
const cents = Number(root.dataset.priceCents ?? "0");
const amount = `$${(cents / 100).toFixed(2)}`;
const wait = (ms: number) => new Promise((r) => setTimeout(r, ms));
const say = (text: string, kind: "step" | "ok" = "step") => {
if (!status) return;
status.textContent = text;
status.style.color = kind === "ok" ? "var(--color-success)" : "var(--color-base-content)";
};
btn?.addEventListener("click", async () => {
btn.disabled = true;
say("Tokenizing card with the Web Payments SDK…");
await wait(500);
const sourceId = `cnon:card-nonce-demo-${Math.random().toString(36).slice(2, 8)}`;
say(`Got token ${sourceId} — charging via createPayment…`);
await wait(650);
const paymentId = `sqpmt_${Math.random().toString(36).slice(2, 12)}`;
say(`Payment COMPLETED · ${amount} · ${paymentId}`, "ok");
btn.disabled = false;
});
});
</script>Stripe and Fourthwall follow the same shape — a client token or hosted checkout, then a server call — via louise-toolkit/commerce/stripe andlouise-toolkit/commerce/fourthwall.