Skip to main content
In this guide you’ll add an overlay checkout to your site: a button that opens the Bachs hosted checkout in a modal on top of your page, takes the payment, and tells you the result in real time. By the end you’ll have a drop-in payment flow where the customer never leaves your site. The overlay loads the Bachs-hosted checkout inside an iframe, so your page never touches card data. Bachs owns the card form, tokenization, and the payment; you decide what to sell and what happens after.
The Bachs overlay checkout open on a merchant page: payment methods and a local-currency picker on the hosted checkout, with the sandbox test-card panel in the corner
Try it before you build. snapkit.bachs.io is a demo storefront built on this SDK. Open one-time, subscription, free-trial, and pay-what-you-want checkouts in the overlay or as the hosted page, all against the sandbox with a test card.

Quick start

That is the whole client. The steps below walk through it properly, server side included.

How it works

1

Create a checkout session (server-side)

Your backend calls the Bachs API with your secret key and gets back a checkout_url. The amount and currency are fixed here, on your server.
2

Open the overlay (browser)

Your frontend loads bachs.js and calls Bachs.Checkout.open() with that session. The customer pays in the modal.
3

Fulfil on the webhook

Bachs sends the checkout.completed and collection.succeeded webhooks to your server. Those, not the browser, are your signal to grant access or ship the order.

Before you start

Steps

1

Create a checkout session on your server

Call POST /v1/checkout-sessions from your backend with your secret key, exactly as in Accept a payment. You get back a checkout_url. Return it to your frontend.
Create sessions server-side only. Your secret key must never reach the browser, and creating the session on the server is what keeps the amount tamper-proof.
2

Add bachs.js to your page

Add the SDK with a script tag, or install the npm package if you use a bundler (Vite, Next.js, webpack). Both load the same overlay.
With the script tag, the SDK is available as the global Bachs. With the npm package, import the loader:
npm import
3

Initialize the SDK

Initialize once when your app loads, not on every click. Pass a global onEvent handler for checkout lifecycle events.
There is no test/live switch on the SDK. A checkout session already carries its environment: a session created with a sandbox key is a sandbox checkout, and its checkout_url says which checkout serves it. Pass that URL through and the client works the same in every environment. Only if you open bare tokens with open({ token }) do you point the SDK somewhere with baseUrl (the sandbox checkout origin while you build).
4

Open the overlay

On a button click, fetch a fresh checkout_url from your server, then open the overlay with it. open() returns a promise that resolves once the modal is mounted.
You can pass the whole checkout_url as checkoutUrl, or only its token segment as token. A checkoutUrl must be on your Bachs checkout origin; the SDK rejects any other host.

Handle checkout events

Pass an onEvent callback to Initialize (global) or to open (this checkout only). Each event is { type, data }. These events drive your UI, not fulfilment.

Fulfil the order with a webhook

The overlay tells you when a payment succeeds, but the browser is not the source of truth. When the checkout is paid, Bachs sends the checkout.completed and collection.succeeded webhooks to your endpoint. Grant access or ship the order on whichever arrives first, once.
Never fulfil an order from the overlay’s browser event, also named checkout.completed. A customer can close the overlay after paying but before your code runs, so the browser event can be missed. Treat the webhooks your server receives as the source of truth, and verify their signatures before trusting them. See Set up webhooks.
collection.succeeded

Test it

Use a sandbox key (sk_sandbox_...) and mode: "test" to run the whole flow against test-mode products without moving real funds. Open the overlay, pay with a test card, and confirm your webhook receives collection.succeeded. See Sandbox.

Go live

When you’re ready, swap one thing: create sessions with a live secret key (sk_live_...) against https://api.bachs.io. The live session’s checkout_url points at the live checkout, so the client code does not change at all. See Go live.

Methods

Bachs.Initialize(options)

Configure the SDK once, when your app loads. Returns Bachs for chaining.

Bachs.Checkout.open(args)

Opens the overlay. Returns a promise that resolves once the modal is mounted and rejects on a bad token or a checkoutUrl from the wrong origin.

Bachs.Checkout.close() and Bachs.Checkout.isOpen()

Close the overlay programmatically, and check whether it is currently open.

How the overlay stays secure

  • No card data on your page. The card form lives in the Bachs iframe on checkout.bachs.io. Your site and the SDK never see the card number, CVV, or PIN.
  • Server-set amounts. The token references a session whose amount and currency were fixed on your server. The browser cannot change what the customer pays.
  • Origin-locked messaging. The SDK only accepts messages from the exact Bachs checkout origin it loaded, and only from its own iframe. Anything else is ignored.

Overlay or hosted page?

Both run the same checkout session; they differ in where the customer pays. The live demo has a toggle that opens every scenario both ways, on identical sessions.

Troubleshooting

Check the browser console for a checkout.error event. The usual causes: the SDK was not loaded before calling open(), the token is invalid, or the checkoutUrl is on a different origin than the SDK expects (set baseUrl to match where the session was created).
Confirm onEvent is set in Initialize() or passed to open(), and that no other message listener on your page stops propagation. Events only flow while the overlay is open.
The session is probably expired or the token malformed; create a fresh session and open it promptly. Sessions default to a 60 minute lifetime.

Next steps