Quickstart
Go from zero to a working JavaScript integration in five minutes.
This walkthrough takes a one-line product on your server and turns it into a working hosted-checkout iframe in the browser. By the end you'll have collected a real (sandbox) payment.
You'll need two pieces of code: a tiny server endpoint that mints a checkout session, and a browser page that mounts the iframe.
@easylabs/browser 0.2.0 ships three payment surfaces — Embedded Checkout, Elements, and Wallet buttons. This quickstart uses Embedded Checkout because it's the fastest path to a working payment. If you'd rather render your own card form, jump to the Elements page; the bootstrap (steps 1 and 2 below) is identical.
1. Get your API keys
Sign in to the Easy Labs dashboard and copy your sandbox publishable key — it starts with pk_test_. The browser SDK auto-detects sandbox vs. production from this prefix; no environment flags needed.
This quickstart's Embedded Checkout flow also uses a server-side secret key (sk_test_… / sk_live_…) on the server endpoint that mints the checkout session — that secret must never ship to the browser. The server hands the resulting client_secret (a short-lived, single-use credential) to the browser; the browser only ever sees the publishable key and the client_secret.
2. Initialize the SDK
On your server (using @easylabs/node or any HTTP client), create a checkout session:
// server.ts
import { createClient } from "@easylabs/node";
const easy = await createClient({ apiKey: process.env.EASY_API_KEY! });
app.post("/checkout/session", async (_req, res) => {
const { data } = await easy.createEmbeddedCheckoutSession({
mode: "payment",
line_items: [{ price_id: "price_...", quantity: 1 }],
success_url: "https://example.com/success",
cancel_url: "https://example.com/cancel",
});
res.json({ clientSecret: data.client_secret });
});In the browser, you don't need to construct an EasyApiClient at all for Embedded Checkout — the iframe authenticates against the short-lived client_secret your server just minted. Just import the mount helper directly:
// app.ts
import { mountEmbeddedCheckout } from "@easylabs/browser";Never ship a secret key (
sk_test_…/sk_live_…) to the browser. Anything that runs in a browser tab is publicly readable. Pass your publishable key (pk_test_…/pk_live_…) tocreateEasyClient— these are the only Easy Labs keys safe for client-side use, and the browser SDK throws anEasyConfigErrorif you hand it a secret key. Do all secret-key work server-side with@easylabs/node, and hand the browser aclient_secret(Embedded Checkout) or a Basis Theory token reference (Elements).You only need
createEasyClientin the browser when you want the typedEasyApiClientsurface for read-only utility flows — see Client for the trade-offs.
3. Make your first call
Fetch the session from your server, then mount the iframe into a container element:
const res = await fetch("/checkout/session", { method: "POST" });
const { clientSecret } = await res.json();
const handle = mountEmbeddedCheckout("#checkout", {
clientSecret,
onReady: () => console.log("checkout ready"),
onCryptoConfirmed: (event) => console.log("crypto confirmed", event),
});Drop a target into your HTML:
<div id="checkout" style="max-width: 480px; margin: 0 auto;"></div>4. Handle the response
The iframe handles card entry, validation, 3DS, and confirmation internally. When the customer pays, Easy Labs redirects the iframe to your success_url (or cancel_url). For crypto payments specifically, the SDK also fires onCryptoConfirmed so you can react in-page without a redirect round-trip.
For everything else — fulfillment, receipts, downstream automations — listen for the checkout.session.completed webhook on your server. The browser is never the source of truth for "payment succeeded".
If createEasyClient throws, the API key is invalid or the network is unreachable. If mountEmbeddedCheckout throws synchronously, the selector or element you passed couldn't be resolved. Errors that come back from API calls on the client are instances of EasyApiError — check err.status and err.code to branch on them.
import { EasyApiError } from "@easylabs/browser";
try {
await easy.getCustomer("cus_does_not_exist");
} catch (err) {
if (err instanceof EasyApiError && err.status === 404) {
// not found
} else {
throw err;
}
}What's next
- Embedded Checkout — the full option surface for
mountEmbeddedCheckout. - Elements — render your own card form with iframed PCI-isolated inputs and
tokenize. - Wallet Checkout — drop-in Apple Pay and Google Pay buttons.
- Client — every typed API method you can call from the browser.
- Examples › Vanilla JS — a complete runnable example covering Embedded Checkout, Elements, and wallets.