checkout_url. Bachs resolves pricing, handles currency conversion, selects payment methods, and processes the payment. You send the customer to the URL and listen for the result.
customer is optional. Pass one and it’s attached at creation; omit it and the hosted page collects the buyer’s email and name before they can pay. See Attach a customer.
You price a session in one of two ways, and supply exactly one:
product_cart: one or more catalog products (each item may override its price withpricing).pricing: a raw amount and currency, with no product. See Charge a raw amount below.
Before you start
- A sandbox API key (
sk_sandbox_...). See Authentication. - At least one product to sell. See Create a product.
- A webhook endpoint to receive the result. See Set up webhooks.
Steps
1
Create the checkout session
Call The total charged is the sum of
POST /v1/checkout-sessions with the products the customer is buying and their details. You get back a checkout_url.unit_amount × quantity across the cart. All products in the cart must share the same primary currency.2
Redirect the customer
Send the customer to the When the customer finishes, Bachs redirects them to your
checkout_url from the response. Bachs renders the hosted page, collects payment, and handles currency conversion.Redirect
success_url with ?checkout_id= appended. If they cancel or abandon the checkout, they are sent to your cancel_url.3
Confirm the payment with a webhook
Don’t rely on the redirect alone. When the payment resolves, Bachs sends a
collection.succeeded event to your webhook endpoint. Use it to fulfill the order.collection.succeeded
Provide exactly one pricing source:
product_cart or pricing. Never both, and never neither.Charge a raw amount
When your pricing is computed at order time and you have no product to reference, pass apricing object instead of a cart. Bachs creates the checkout from the amount alone, with no products and no catalog. It’s the right choice when your pricing is dynamic, computed at order time, or when you want to accept a payment without defining products in Bachs first.
checkout_url, redirect the customer, and confirm with the collection.succeeded webhook.
Pricing intent
Setpricing.currency to USD and Bachs converts to the customer’s local currency at the prevailing FX rate, letting them pay in any currency you support. Set it to any other supported fiat currency instead and the customer pays in that currency only.
Add currency_options to lock in exact amounts for specific currencies instead of relying on the live FX rate. Bachs uses the override when the customer selects that currency, and falls back to FX for any currency not listed. Keys must be fiat currency codes you support, and cannot include currency itself.
pricing field list, including per-currency minimums.
Override a product price
To sell a catalog product at a different price for one checkout, setpricing on the cart item. Bachs charges the override instead of the catalog price, without creating a new product. It works on any product, including fixed-price ones, and supports the same price types as catalog prices (fixed, custom, and free). The override is in the product’s primary currency and applies to that checkout only.
custom override lets the buyer pick the amount on the hosted page, within your bounds. On a custom ui_mode (server-to-server) checkout there is no hosted page, so supply the buyer’s chosen amount as the cart item’s amount.
For a one-time product the override is snapshotted on the checkout, so it never enters your price list. For a recurring product it becomes the subscription’s price for its whole life; Bachs mints an archived, one-off price the subscription ties to when it activates, kept out of your catalog price list.
Attach a customer
Pass a new customer by email (Bachs creates or matches one), an existing customer by ID, or omitcustomer entirely and let the buyer identify themselves on the hosted page.
- New customer
- Existing customer
- No customer (guest)
customer is required for recurring products. A cart containing a product with a billing_cycle becomes a subscription checkout, which needs a durable identity to bill on renewal, and there is no opportunity to collect one later. Omitting customer returns 400.
Guest checkout
Omitcustomer on a standard hosted checkout and Bachs collects the buyer’s email and name on the checkout page itself. By default they do not join your customer directory: you receive their email and name on customer_details, and customer stays null. Send customer_creation: "always" if you want them on file as a customer.
Until the buyer supplies their identity, GET /v1/checkout-sessions/{checkout_id} and the list endpoint return "customer": null and "customer_details": null, and the hosted page’s own pricing and payment calls return 400, since there is no one to bill yet. A malformed email returns 400 with error_code: "VALIDATION_ERROR".
Whether a guest becomes a customer: customer_creation
customer_creation decides whether collecting an identity on the hosted page also creates a customer record. Send it when you create the checkout.
always when you want every buyer on file, for example to look them up through the API or to bill them again by hand.
Under
always, a buyer whose email already matches a customer you hold attaches to that record rather than creating a second one, so their payment history stays in one place. Matching is on the email alone, and an email typed on the checkout page is not verified, so anyone who knows one of your customers’ addresses can have their purchase recorded against that customer. Under if_required this cannot happen: a buyer who shares an email with one of your customers stays entirely separate from them.customer vs customer_details for exactly which of the two you get in each case.
Recurring products
You don’t create subscriptions directly. If a product in the cart has abilling_cycle, Bachs turns the checkout into a subscription checkout automatically: when the customer pays, it saves their card, bills the first cycle (or defers it for a trial), and creates the subscription.
On completion you receive collection.succeeded and customer.subscription.created + invoice.paid. Subscriptions are USD card only today. See Subscriptions for how they renew and how to manage them.
Common options
Restrict payment methods
By default a checkout offers every payment method your account is enabled for. Passpayment_method_types to show less than that.
Each entry is an exact payment-method corridor, not a payment type. Card, bank transfer, and mobile money are each split into one corridor per currency: USD_CARD and NGN_CARD are separate corridors, as is each of the nine mobile money corridors. You restrict to a currency by choosing which corridors to list, not by filtering a shared card entry. A corridor you leave out is not offered at all.
NGN_CARD is not listed. Mobile money and crypto do not appear at all, because no corridor for them is named.
A restriction only ever narrows. It cannot offer a corridor your account is not enabled for, and listing only corridors you cannot accept leaves the checkout with no way to pay — the request fails with CHECKOUT_RESTRICTION_LEAVES_NO_PAYMENT_METHOD.
The corridors
Do not guess which corridors your account can accept. Payment method support lists every corridor and whether it is enabled for you.
CRYPTO is one corridor covering every asset and network we support. The customer picks the one they want to pay with; whichever they choose, you are credited in USD.
Naming a currency the corridor cannot process is rejected with 422, as is an empty currencies array. To turn a corridor off, leave its key out rather than passing an empty list.
The same field works on payment links, where it restricts every checkout the link creates.
Testing
Use a sandbox key (sk_sandbox_...) to run the whole flow against test-mode products without moving real funds. See Sandbox.
Next steps
- Live demo: a storefront running this exact flow on the sandbox, overlay and hosted page both.
- Charge in any currency: price in a currency you do not hold, and see what settles.
- The checkout session object: every field the API returns and accepts.
- Manage customers: the customer object, and when a checkout creates one for you.
- Set up webhooks: receive and verify the result.
- Subscriptions: sell recurring products.
- Refunds: refund a completed payment.

