# Bolt Help > Bolt's documentation for developers, merchants, and shoppers, as one file: every prose page in navigation order. > > 153 pages, 991 KB. Check that against your model's context window before loading the whole file. > > The API reference is not here. It is generated from OpenAPI specs and is several times the size of everything else, so each endpoint is published on its own at https://help.boltapp.com/llms/api-reference/... and indexed in https://help.boltapp.com/llms.txt. # Documentation: Start here --- # Developers > Choose how you want to accept payments with Bolt. Source: https://help.boltapp.com/getting-started/choose-integration Bolt's fully managed checkout solution. Keep your checkout, enhanced with Bolt's shopper network. Checkout links and scannable QR codes with Bolt checkout. Managed sub-merchant payouts with streamlined onboarding. ## Choose by what you need [#choose-by-what-you-need] | Your goal | Start here | | ----------------------------------------------- | ---------------------------------------------------- | | Let Bolt provide your checkout interface | [Managed Checkout](/products/checkout) | | Keep your own checkout interface | [Embedded API](/products/embeddable-checkout) | | Sell through a link or QR code | [Checkout Everywhere](/products/checkout-everywhere) | | Onboard sellers and manage sub-merchant payouts | [Marketplace Connect](/products/connect) | | Bill shoppers on a recurring schedule | [Subscriptions](/products/subscriptions) | **Billing on a recurring schedule?** Bolt Subscriptions prices each plan from a catalog. If your recurring amount is computed per shopper, a rate that varies with seat count or a first period prorated to the signup date, read [How prices are set](/products/subscriptions#how-prices-are-set) before choosing a path. **Using a commerce platform?** Choose [Shopify, BigCommerce, Adobe Commerce, Salesforce Commerce Cloud, WooCommerce, or Volusion](/use-cases/ecommerce#platform-setup). Every Bolt product, add-on, and use case in one place. Client and server libraries, once you know which path you are building. Markdown: https://help.boltapp.com/llms/getting-started/choose-integration.md # Documentation: Products --- # Managed Checkout Quickstart > Accept one-time payments for physical products in your existing web app. - **Bolt handles** the checkout interface, shipping details, and payment collection. - **Your app owns** the cart, pricing, and order records. Source: https://help.boltapp.com/products/checkout ## Your integration steps [#your-integration-steps] ## Before you begin [#before-you-begin] Have these ready before starting the estimated integration time. | Requirement | What you need | | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Existing app | An online store with a cart, customer sign-in, and saved orders. The examples use Node.js 22 and Express. | | Sandbox account | [Create a merchant account](https://merchant.boltapp.com/onboarding/get-started), accept your sandbox invitation, and ask Bolt for a **custom-cart division** with split shipping/tax callbacks and pre-authorization order creation. | **Platform support** This guide works for any custom web app. The examples use Node.js and Express, but every server step is a plain HTTPS call to Bolt with a standard HMAC check that you can write in any language, and the browser steps load Bolt's hosted scripts from any front end. The [Web checkout](/platforms/direct-api) reference documents that browser client. Hosted commerce platforms and native mobile apps use their own guides instead. JS Embed iOS Android REST API Python ## 1. Set up [#1-set-up] 1. Open [sandbox Payments](https://merchant-sandbox.boltapp.com/payments), select the Bolt Payments processor, and configure **Immediate Capture** for your USD sandbox division. 2. Open [Administration > API](https://merchant-sandbox.boltapp.com/administration/api-keys#apiKeys). Copy the **API key** and **Publishable key** for that division, then copy the [Signing secret](https://merchant-sandbox.boltapp.com/administration/api-keys#signingSecrets). 3. Open [Administration > Trusted Domains](https://merchant-sandbox.boltapp.com/administration/trusted-domains). Select the same division as your credentials, select **+ Add Domain**, enter your app's public HTTPS address, and select **Update**. 4. Add these values to your app's server environment using its existing secrets configuration: ```bash BOLT_API_KEY=YOUR_SANDBOX_API_KEY BOLT_PUBLISHABLE_KEY=YOUR_SANDBOX_PUBLISHABLE_KEY BOLT_SIGNING_SECRET=YOUR_SANDBOX_SIGNING_SECRET ``` These are names used by this guide's code. Expose only the publishable key through your app's public configuration. Keep the API key and signing secret on the server and out of source control. ### Validate setup [#validate-setup] * Reopen [sandbox Payments](https://merchant-sandbox.boltapp.com/payments) and confirm **Immediate Capture** is saved. * Confirm all three credentials belong to the same sandbox division and your HTTPS development origin is saved in Trusted Domains. * Restart your backend with the updated environment. Check that all three values are present without logging their contents. Do not continue if a value is missing. ## 2. Connect orders [#2-connect-orders] Connect Bolt to your app's existing order lifecycle. Your backend must price the current cart, save an unpaid order, and use that order's identifier as Bolt's `order_reference`. For this walkthrough, use one **$10.00 USD** physical item with free shipping, no tax, and no discounts. ### Map your cart and order storage [#map-your-cart-and-order-storage] Provide an `orders` adapter with these four operations. These are functions you implement using your app's services, not Bolt SDK methods: | Operation | Required behavior | | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `prepare(req)` | Load the authenticated shopper's cart, calculate its price on the server, and persist an unpaid checkout snapshot. Return that snapshot as `{ id, cart, payment }`, including its current payment if one exists. Reuse the same unpaid snapshot for repeat requests with an unchanged cart. | | `find(id)` | Return the saved snapshot, or `null`. Use this for signed callbacks and transaction notifications. | | `findForShopper(id, req)` | Return a snapshot only if it belongs to the requesting shopper, otherwise `null`. | | `recordPayment(id, payment)` | Persist `{ reference, status }` against that order atomically. Repeated deliveries must update the same payment, never create another order. Reject a different payment reference once a successful payment is recorded. This guide does not implement fulfillment. | Map your cart fields to this shape. Use your own order identifier and product reference; prices are integers in cents. Store this snapshot before returning it from `prepare`: ```javascript title="JavaScript" const checkoutSnapshot = { id: 'YOUR_ORDER_ID', cart: { order_reference: 'YOUR_ORDER_ID', display_id: 'YOUR_ORDER_ID', currency: 'USD', total_amount: 1000, tax_amount: 0, items: [{ reference: 'YOUR_PRODUCT_ID', name: 'Test product', type: 'physical', unit_price: 1000, total_amount: 1000, quantity: 1, }], }, payment: null, }; ``` The adapter is required application work: implement and validate it before proceeding. The snippets do not provide a database, a cross-tab payment lock, receipt delivery, or fulfillment. Keep the checkout snapshot immutable while payment is in progress. A changed cart needs a new snapshot and token. Do not accept prices or an arbitrary shopper's order identifier from the browser. ### Add the Bolt handlers [#add-the-bolt-handlers] Add this module to your backend. It creates an order token, answers checkout callbacks, and records payment only after retrieving the transaction from Bolt. The browser success callback never marks an order paid. ```javascript title="bolt-checkout.mjs" import { createHmac, timingSafeEqual } from 'node:crypto'; export function createBoltHandlers(orders) { for (const name of ['BOLT_API_KEY', 'BOLT_PUBLISHABLE_KEY', 'BOLT_SIGNING_SECRET']) { if (!process.env[name]) throw new Error(`Set ${name} in your server environment`); } async function bolt(path, body) { const response = await fetch(`https://api-sandbox.boltapp.com${path}`, { method: body ? 'POST' : 'GET', headers: { 'X-API-Key': process.env.BOLT_API_KEY, 'Content-Type': 'application/json' }, ...(body ? { body: JSON.stringify(body) } : {}), }); if (!response.ok) throw new Error(`Bolt returned HTTP ${response.status}`); return response.json(); } function signedBody(req) { if (!Buffer.isBuffer(req.body)) throw new Error('Bolt routes need the raw request body'); const expected = createHmac('sha256', process.env.BOLT_SIGNING_SECRET) .update(req.body).digest(); // During rotation, the incoming secret signs the Pending header. const valid = ['X-Bolt-Hmac-Sha256', 'X-Bolt-Hmac-Sha256-Pending'].some(name => { const received = Buffer.from(req.get(name) || '', 'base64'); return expected.length === received.length && timingSafeEqual(expected, received); }); if (!valid) return null; return JSON.parse(req.body.toString('utf8')); } const handle = fn => async (req, res, next) => { res.set('Cache-Control', 'no-store'); try { await fn(req, res); } catch (error) { next(error); } }; return { createOrder: handle(async (req, res) => { const order = await orders.prepare(req); if (!order || order.cart.order_reference !== order.id || order.payment?.status === 'completed') { return res.status(409).json({ error: 'No unpaid checkout snapshot' }); } const result = await bolt('/v1/merchant/orders', { cart: order.cart, channel: 'browser' }); if (!result.token) throw new Error('No order token returned'); res.json({ id: order.id, token: result.token }); }), orderStatus: handle(async (req, res) => { const order = await orders.findForShopper(req.params.id, req); if (!order) return res.status(404).json({ error: 'Order not found' }); res.json({ id: order.id, payment: order.payment }); }), callbacks: handle(async (req, res) => { const message = signedBody(req); if (!message) return res.status(401).json({ error: 'Invalid signature' }); const { event, data } = message; let reply; if (event === 'order.shipping') { reply = { shipping_options: [{ service: 'Free sandbox shipping', cost: 0, reference: 'free', signature: 'free-v1' }] }; } else if (event === 'order.tax') { reply = { tax_result: { subtotal_amount: 0 }, taxes_included: false }; } else if (event === 'order.create') { const cart = data?.order?.cart; const order = cart?.order_reference ? await orders.find(cart.order_reference) : null; if (!order || cart.total_amount?.amount !== order.cart.total_amount || cart.currency?.currency !== order.cart.currency) { return res.status(422).json({ event, status: 'failure', errors: [ { code: 2001, data: [{ reason: 'Unknown or changed order' }] }, ] }); } reply = { display_id: order.cart.display_id }; } else { return res.status(422).json({ event, status: 'failure', error: { code: 2319, message: 'This walkthrough does not enable additional checkout features.', } }); } res.json({ event, status: 'success', data: reply }); }), webhooks: handle(async (req, res) => { const message = signedBody(req); if (!message) return res.status(401).json({ error: 'Invalid signature' }); if (message.object === 'transaction' && message.data?.reference) { const transaction = await bolt(`/v1/merchant/transactions/${encodeURIComponent(message.data.reference)}`); // This walkthrough accepts card purchases only. Credits/refunds are separate transactions. if (transaction.type !== 'cc_payment') return res.json({ status: 'success' }); if (transaction.reference !== message.data.reference) throw new Error('Transaction reference mismatch'); const id = transaction.order?.cart?.order_reference; const order = id ? await orders.find(id) : null; if (!order) throw new Error('No matching local order for this transaction'); if (transaction.amount?.amount !== order.cart.total_amount || transaction.amount?.currency !== order.cart.currency) { throw new Error('Transaction does not match the saved order'); } await orders.recordPayment(id, { reference: transaction.reference, status: transaction.status }); } res.json({ status: 'success' }); }), }; } ``` Register these routes in your existing Express app. `orders`, `requireShopper`, and `requireCsrf` below refer to your app's adapter, authentication middleware, and CSRF middleware. Connect those existing functions before using this snippet. ```javascript title="JavaScript" import express from 'express'; import { createBoltHandlers } from './bolt-checkout.mjs'; const bolt = createBoltHandlers(orders); // Register BEFORE your existing JSON parser and shopper-login middleware. // Bolt sends signed server requests, without a shopper session or CSRF token. app.post('/bolt/callbacks', express.raw({ type: 'application/json', limit: '64kb' }), bolt.callbacks); app.post('/bolt/webhooks', express.raw({ type: 'application/json', limit: '64kb' }), bolt.webhooks); // Add to your app's authenticated routes after its normal request parsers. app.post('/api/bolt/order', requireShopper, requireCsrf, bolt.createOrder); app.get('/api/bolt/orders/:id', requireShopper, bolt.orderStatus); ``` Signature verification needs the original bytes, so [Express's raw body parser](https://expressjs.com/en/5x/api/express/#express.raw) must run before a JSON parser consumes the body. Keep your existing server error handler after these routes. Log failures on the server, return a generic error body to clients, and use non-2xx responses for failed notification processing so Bolt can retry. ### Validate orders [#validate-orders] 1. From your app's authenticated API client, send `POST /api/bolt/order` using its normal CSRF protection. Expect HTTP `200` with a nonempty `id` and `token`. 2. Find that `id` in your order storage. Confirm the saved cart has a total of `1000`, currency `USD`, and no completed payment. 3. Repeat the request with the unchanged cart. Confirm it uses the same local unpaid order rather than creating another one. 4. Request `GET /api/bolt/orders/{id}` as the same shopper. Expect that order's ID and `payment: null`. A different shopper must not receive its details. Keep this order ID for the checkout test. Stop here if the token request or ownership check fails. ## 3. Add checkout [#3-add-checkout] Add a checkout container and status message to your **existing cart page**: ```html title="HTML"

``` Run this function once when the cart page mounts and call its returned cleanup function on unmount. Use this module as the only loader for the two Bolt scripts; keep one active checkout mount and one sandbox division per page. Pass the publishable key from your app's public configuration and its existing authenticated JSON request helper. The helper must send the app's CSRF token on POST, reject non-2xx responses, and return parsed JSON. It is application code, not a Bolt SDK method. ```javascript title="JavaScript" const boltScripts = new Map(); export function mountBoltCheckout({ publishableKey, request }) { const status = document.querySelector('#bolt-status'); const container = document.querySelector('#bolt-checkout'); const cdn = 'https://connect-sandbox.boltapp.com'; let active = true; let timer; function show(message) { if (active && status.textContent !== message) status.textContent = message; } function loadScript(id, src) { if (!boltScripts.has(id)) { const promise = new Promise((resolve, reject) => { const script = document.createElement('script'); script.id = id; script.src = src; script.dataset.publishableKey = publishableKey; script.onload = resolve; script.onerror = () => { script.remove(); boltScripts.delete(id); reject(new Error('Bolt script could not load')); }; document.head.append(script); }); boltScripts.set(id, promise); } return boltScripts.get(id); } async function start() { show('Preparing checkout…'); try { const { id, token } = await request('/api/bolt/order', { method: 'POST' }); if (!active) return; container.dataset.tid = 'instant-bolt-checkout-button'; const button = document.createElement('object'); button.data = `${cdn}/v1/checkout_button?publishable_key=${encodeURIComponent(publishableKey)}`; button.setAttribute('aria-label', 'Open Bolt Checkout'); container.replaceChildren(button); await loadScript('bolt-track', `${cdn}/track.js`); if (!active) return; await loadScript('bolt-connect', `${cdn}/connect.js`); if (!active) return; BoltCheckout.configure({ orderToken: token }, {}, { success: (_transaction, callback) => { show('Payment submitted. Waiting for server confirmation. Do not pay again.'); callback(); }, }); show(`Order ${id}: ready for checkout.`); async function poll() { try { const order = await request(`/api/bolt/orders/${encodeURIComponent(id)}`); if (!active) return; if (order.payment) { show(`Order ${id}: ${order.payment.status}. Transaction: ${order.payment.reference}`); } if (['completed', 'failed', 'cancelled', 'rejected_irreversible'].includes(order.payment?.status)) return; timer = setTimeout(poll, 2000); } catch { show(`Cannot confirm order ${id} yet. Check this order in your account or contact support before paying again.`); } } poll(); } catch { if (active) container.replaceChildren(); show('Checkout could not load. Return to your cart and try again.'); } } start(); // Returned immediately, so navigation can cancel pending setup as well as polling. return () => { active = false; clearTimeout(timer); container.replaceChildren(); }; } ``` `track.js` collects fraud signals; `connect.js` launches checkout. The server omits `order_received_url` so the shopper stays in your app and sees the server-verified status. Keep the cart fixed for this first test. ### Validate checkout [#validate-checkout] 1. Open your cart page through its HTTPS development URL. Confirm the Bolt button appears and the status shows your order ID. 2. In browser developer tools, confirm `track.js` and `connect.js` load successfully from `connect-sandbox.boltapp.com`. 3. Check the page source and network responses: only the publishable key may appear, never the private API key or signing secret. Do not open checkout or submit payment yet. Register its receiving URLs next. ## 4. Connect Bolt [#4-connect-bolt] Use the same HTTPS development origin that serves your cart and backend. For example, `https://your-app.example` below represents **your own development URL**. 1. Open [Administration > API > Merchant Callbacks](https://merchant-sandbox.boltapp.com/administration/api-keys#merchantCallbacks). Select **Universal API**, enter your development origin followed by `/bolt/callbacks`, and select **Add merchant callback**. 2. Open [Administration > Webhooks](https://merchant-sandbox.boltapp.com/administration/webhooks) and register the same origin followed by `/bolt/webhooks`. If Universal API or Webhooks is unavailable, ask Bolt to confirm your custom-cart division and settings permissions. Merchant callbacks and transaction webhooks are separate registrations. ### Validate connections [#validate-connections] 1. Reopen the linked settings and confirm the saved domain and both endpoint URLs. Update existing registrations if the development URL changes. 2. Send an unsigned request to each public endpoint. Replace the example origin with yours: ```bash curl -i https://your-app.example/bolt/callbacks \ -H 'Content-Type: application/json' -d '{"event":"order.shipping","data":{}}' curl -i https://your-app.example/bolt/webhooks \ -H 'Content-Type: application/json' -d '{"object":"transaction","data":{}}' ``` Both must return HTTP `401` from your handler, without a login redirect or proxy error. 3. Open Bolt checkout from your cart, enter a test email and US shipping address, then choose **Free sandbox shipping**. Confirm your server receives signed `order.shipping` and `order.tax` calls, answers with HTTP `200`, and checkout displays **$10.00 USD**, free shipping, and zero tax. Leave checkout open for the final step. ## 5. Verify payment [#5-verify-payment] Submit the open checkout using the [Bolt Payments approved test card and card details](/getting-started/test-cards#bolt-payments). ### Validate payment [#validate-payment] 1. Confirm your server receives `order.create`, finds the saved order, and answers successfully. Then confirm `/bolt/webhooks` receives a signed transaction notification and completes the server-side transaction lookup. 2. Wait for your app to show `completed` with a transaction reference. `pending` and `authorized` are not the completion checkpoint. 3. Look up the original order ID in your app's storage. Confirm its payment reference matches the page and its recorded status is `completed`. 4. Open [sandbox Transactions](https://merchant-sandbox.boltapp.com/transactions). Find that reference and confirm the amount is **$10.00 USD** and the order number matches your app. 5. Restart your backend, then reload `GET /api/bolt/orders/{id}` using your authenticated API client. Confirm the result persists. Reprocess the same notification in your development test harness and confirm it updates the existing record without creating another order. The quickstart does not create fulfillment actions or receipts. **You are done when:** your existing app opens Bolt checkout, completes a sandbox payment, and persists the verified transaction against the correct order. ## If a checkpoint fails [#if-a-checkpoint-fails] * **No order token:** check the sandbox API key, saved cart totals, and USD payment enablement. Inspect the backend's Bolt HTTP error. * **Checkout does not open:** confirm the current host is in [Trusted Domains](https://merchant-sandbox.boltapp.com/administration/trusted-domains) and both sandbox scripts loaded. * **Callbacks fail signature verification:** ensure `express.raw` runs before your JSON parser and the [Signing secret](https://merchant-sandbox.boltapp.com/administration/api-keys#signingSecrets) belongs to the configured division. * **Payment exists but your app still waits:** check the saved [Webhooks URL](https://merchant-sandbox.boltapp.com/administration/webhooks), server errors, and original order ID. Investigate the existing payment before attempting another purchase. For callback payloads, token errors, and other setup failures, use [custom-cart troubleshooting](/custom-cart/troubleshooting) and [callback conventions](/api-reference/merchant-callback/conventions). ## After your first payment [#after-your-first-payment] Run the [Managed Checkout test plan](/products/checkout-test-plan) to check payment failures, recovery, and the experience your app controls. Its first five checks map to the checkpoints in this guide. The scope table identifies which later checks verify these snippets, your required adapters, or additional launch behavior. Replace the fixed free-shipping and zero-tax responses with your app's shipping and tax calculations. Add safe retry controls, cross-tab payment locking, cart changes, confirmation-page recovery, refunds, and later risk-state changes. The card-only handler acknowledges credit/refund transactions without recording them; implement a separate refund lifecycle before offering refunds. Make payment updates transactional and fulfillment safe to repeat. Review your adapter's behavior for concurrent or out-of-order notifications before launch. Complete [Bolt Payments onboarding](https://help.boltapp.com/add-ons/bolt-payments/bolt-pay-setup/) with your Bolt representative. Submit company, owner, bank, and verification details through [Payments](https://merchant.boltapp.com/payments) and wait for activation before accepting live payments. Continue with [production readiness](https://help.boltapp.com/developers/production-readiness-guides/). Use the [transaction lookup reference](/api-reference/endpoints/transactions/gettransactiondetails) for additional operations and [webhook reference](/custom-cart/transaction-webhooks) for later state changes. Markdown: https://help.boltapp.com/llms/products/checkout.md --- # Test your Managed Checkout integration > Verify payments, order state, recovery, and the shopper experience in your app before launch. Source: https://help.boltapp.com/products/checkout-test-plan Test the [Managed Checkout quickstart](/products/checkout). Copy these tables into your test report and record evidence against each result. All cases start **Not run**. ## What this plan verifies [#what-this-plan-verifies] | Coverage | Tests | Implementation being tested | | -------------------------- | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Quickstart outcome | Q1–Q5 | Follow the five guide checkpoints in order. Real sandbox execution required. | | Supplied handlers | F8; F10 unhandled events; E3 script/status failure; E4 pending mount cleanup | Signature and transaction checks, non-purchase filtering, generic browser errors, and teardown. Controlled tests can verify these without making a payment. | | Required app integration | Q2/Q5; F6–F10 storage, access, replay, and restart | Your `orders` adapter, session/CSRF middleware, persistence, and error handler. The guide defines their contract; it does not implement your database or concurrency controls. | | Additional launch behavior | F1–F5; F7 concurrent ordering; E1–E8 beyond the supplied UI; L1–L4 | Safe retries, payment locking, cart changes, full confirmation/recovery UI, focus behavior, fulfillment, receipts, and enabled features. Implement these in your app before expecting those tests to pass. | The quickstart verifies a card purchase and a persisted payment. It does not build a fulfillment system, send receipts, or implement refunds. Count those effects only when your launch includes them; record the omission otherwise. Additional launch checks remain required when applicable, but are not evidence that the minimal quickstart already supplies those features. ## Setup and inputs [#setup-and-inputs] | Input | Value / preparation | | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Run details | Record build, date, tester, sandbox division, browser/device versions, and enabled features. | | Configuration | Same division in [API settings](https://merchant-sandbox.boltapp.com/administration/api-keys), [Payments](https://merchant-sandbox.boltapp.com/payments), [Trusted Domains](https://merchant-sandbox.boltapp.com/administration/trusted-domains), and [Webhooks](https://merchant-sandbox.boltapp.com/administration/webhooks). Keep the HTTPS development URL running. | | Cart | Shopper A: one physical product, **$10.00 USD**, quantity one, free shipping, zero tax, no discounts. Create shopper B for access tests. | | Evidence | App order/payment records, backend logs, [sandbox Transactions](https://merchant-sandbox.boltapp.com/transactions), and screenshots. If enabled, send fulfillment/emails to test destinations so you can count them. | | Harness | Ability to delay/fail your endpoints, replay signed notifications, and substitute transaction responses. Use actual sandbox payments for Q1–Q5. | | Reset | Fresh order per independent case. Preserve order/transaction IDs during retries and recovery. Never repay just to resolve an unknown result. | | Payment input | Card | Expiration / CVV | | ------------------------ | ---------------------------------------------------------------------------------------------------------- | ------------------------------ | | Approved | `4111 1111 1111 1111` | `03/30` / `737` | | Processor decline | `4111 1111 1111 1012` | Future date / any three digits | | Pre-auth rejection | `4100 2003 0003 0001` | Future date / any three digits | | Review / later rejection | Use the [Bolt Payments test-card set](/getting-started/test-cards#bolt-payments) for your onboarding date. | As listed in the reference | ## 1. Quickstart checkpoints [#1-quickstart-checkpoints] Run Q1–Q5 in order in the real sandbox. Keep the same order through Q2–Q5. | Test | Input | Steps | Expected output | Result | | ----------------------- | ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ------- | | **Q1 · Set up** | Sandbox credentials; USD; Immediate Capture | 1. Reopen Payments/API settings.
2. Inspect browser content. | Same division throughout; capture setting and development Trusted Domain saved; no private keys or signing secret in the browser. | Not run | | **Q2 · Connect orders** | Shopper A’s $10 cart; shopper B | 1. Request a token twice as A.
2. Retrieve A’s order as A, then B. | Token returned; one unpaid order at `1000 USD`; B cannot read it. | Not run | | **Q3 · Add checkout** | A’s saved cart | 1. Open the cart.
2. Inspect scripts and status without opening checkout. | One button; both sandbox scripts loaded; correct order ID shown; no blocking browser error. | Not run | | **Q4 · Connect Bolt** | Unsigned requests; US shipping address | 1. POST unsigned bodies to both receivers.
2. Open checkout; verify product/quantity/$10 total.
3. Enter a US address. | Unsigned requests: `401`. Signed shipping/tax callbacks: `200`. Checkout: free shipping, zero tax. | Not run | | **Q5 · Verify payment** | Approved card; original order ID | 1. Pay.
2. Match app and dashboard records.
3. Restart and retrieve order. | Successful order callback; verified transaction; persisted `completed` payment; matching order/reference and $10 amount. | Not run | ## 2. Payment failures and retries [#2-payment-failures-and-retries] Use a fresh order per case. Within each retry scenario, retain the original order and check for duplicate charges. | Test | Input | Steps | Expected output | Result | | ------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------- | ------- | | **F1 · Decline** | Decline card, then approved card | 1. Submit decline.
2. Retry through the app. | Decline: unpaid, cart preserved. Retry: one completed purchase, one fulfillment/notification if those features are enabled. | Not run | | **F2 · Rejection** | Pre-auth rejection card | 1. Submit payment.
2. Check app and order. | Unpaid; no fulfillment or success message; no internal fraud-rule details exposed. | Not run | | **F3 · Close checkout** | Unpaid checkout; submitted payment awaiting confirmation | 1. Close before payment and reopen.
2. Repeat after payment submission. | Cart survives. After submission, app checks original payment state before allowing another attempt. | Not run | | **F4 · Repeated actions** | One checkout snapshot; two tabs | 1. Double-click checkout.
2. Reload/open a second tab.
3. Retry after a timeout. | No overlapping payment attempts; at most one successful charge, paid order, and fulfillment for the purchase. | Not run | | **F5 · Cart changes** | Checkout open in one tab | 1. Change quantity, item, or price in another tab.
2. Resume checkout. | App locks or refreshes the cart visibly. No silent stale charge; new snapshot/token for a changed cart. | Not run | ## 3. Payment delivery and order integrity [#3-payment-delivery-and-order-integrity] Use your development harness for fault injection. Label manual replays and mocked responses in the evidence. | Test | Input | Steps | Expected output | Result | | ------------------------------------ | ------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | | **F6 · Delayed confirmation** | Receiver returns `503`; separate transaction-lookup failure | 1. Complete payment.
2. Restore receiver and replay.
3. Repeat with lookup failure. | Before verification: pending, no second payment. Failure: non-2xx. Recovery: original order completes once. | Not run | | **F7 · Duplicate / old events** | Captured signed notifications | 1. Replay twice.
2. Replay concurrently.
3. Deliver an older event last. | Authoritative state retained; no regression, duplicate order, fulfillment, or success notification. | Not run | | **F8 · Invalid confirmation** | Bad signature; unknown order; wrong amount/currency/reference; `cc_credit`; browser success only | 1. Submit each invalid fixture.
2. Inspect stored payment and logs.
3. Send a valid shipping callback with only the pending rotation signature, then with valid primary and invalid pending signatures. | Either matching signature accepts the callback; two invalid signatures return 401. Invalid confirmation cannot mark paid. `cc_credit` is acknowledged without replacing the sale. Failed processing is observable; browser success alone cannot confirm payment. | Not run | | **F9 · Access / price** | A/B sessions; missing authentication/CSRF; modified browser prices | 1. Request another shopper’s order.
2. Submit unauthorized or altered requests. | No leaked order details or unauthorized order creation. Server owns cart prices and shopper identity. | Not run | | **F10 · Persistence / other events** | Completed order; unrelated signed event | 1. Restart backend.
2. Replay payment.
3. Send an unhandled object type. | Payment persists; no duplicate effects. Deliberately unhandled event gets 2xx without changing payment state. | Not run | ## 4. Merchant-controlled experience [#4-merchant-controlled-experience] Run on supported desktop browsers and a real mobile device. Include keyboard/screen-reader use and supported in-app browsers. Test merchant-owned screens and transitions; report Bolt-hosted UI defects to Bolt. | Test | Input | Steps | Expected output | Result | | ----------------------- | ---------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | | **E1 · Clear purchase** | Cart, checkout, confirmation | 1. Compare item, quantity, currency, and total.
2. Inspect checkout action. | Values agree; shipping/tax assumptions visible; clearly labeled action, unobscured by merchant overlays. | Not run | | **E2 · Loading** | Slow network; own endpoints delayed 10 seconds | 1. Delay token creation.
2. Delay payment status. | Visible progress; no duplicate submission; cart preserved. Pending confirmation has a safe next step, not endless spinning. | Not run | | **E3 · Errors** | Blocked Bolt script; token failure; offline status request | 1. Trigger each failure.
2. Follow recovery action. | Readable message; no stack traces/secrets. Safe retry, or check existing order when payment is uncertain. | Not run | | **E4 · Navigation** | Open checkout; mobile browser | 1. Navigate away during delayed token/script loading.
2. Remount the cart.
3. Close/reopen and resume on mobile. | No late checkout mount after unmount; no duplicate scripts after remount. Merchant navigation restores cart, controls, and focus without another payment. | Not run | | **E5 · Accessibility** | Keyboard and screen reader | 1. Reach/open/close checkout.
2. Observe app status and errors. | Named controls; visible focus; useful return focus; status announced without repeated interruptions; color is not the only cue. | Not run | | **E6 · Mobile layout** | Narrow screen; 200% zoom; on-screen keyboard | 1. Rotate, zoom, and open keyboard.
2. Complete merchant-owned screens. | Totals, actions, and errors remain readable/reachable; no obstructing overlays or horizontal scrolling hiding actions. | Not run | | **E7 · Confirmation** | Completed order; receipt enabled if offered | 1. Refresh confirmation.
2. Open order/help links.
3. Count receipts. | Persisted, correct order result; clear next step and support route; one receipt matching the order; pending is not complete. | Not run | | **E8 · Session expiry** | Shopper session expires | 1. Expire before checkout.
2. Repeat during confirmation.
3. Sign in again. | Clear sign-in; original order recoverable; no cross-shopper disclosure or second payment. | Not run | ## 5. Launch features [#5-launch-features] Run after replacing quickstart fixtures with real behavior. Use Not applicable only for features outside your launch, with a reason. | Test | Input | Steps | Expected output | Result | | --------------------------- | -------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | ------- | | **L1 · Real totals** | Offered shipping, tax, quantities, discounts, currencies; unavailable item | 1. Test each supported combination.
2. Attempt unavailable combinations. | App/Bolt totals agree; unsupported purchases blocked with a useful recovery action. | Not run | | **L2 · Review / rejection** | Account-appropriate review/rejection cards | 1. Pay.
2. Observe initial and later states. | Order/fulfillment follow payment and risk policy. Review is not final success; rejection does not trigger fulfillment. | Not run | | **L3 · Post-purchase** | Supported refund, void/cancellation, later payment changes | 1. Perform each supported operation.
2. Reconcile records and messages. | Amounts/references, inventory, order state, and messages agree. Refund tracked separately; never mistaken for a new sale. | Not run | | **L4 · Entry points** | Each supported cart/product/app entry | 1. Run purchase and recovery cases from each entry point. | Same verified order outcome and merchant-experience checks pass; exclusions recorded. | Not run | ## Results and completion [#results-and-completion] For each result, attach the actual outcome, order/transaction IDs, and redacted evidence. Record an issue, owner, and retest for failures. Duplicate rows for required browser/device runs. | Result | Meaning | | -------------- | ------------------------------------------------------------------------------------------------------ | | Pass | Expected output observed; evidence attached. Label sandbox, mocked, and replayed evidence. | | Fail | Output differs; issue and owner recorded. Merchant-experience failures count even if payment succeeds. | | Blocked | Missing access, configuration, or fixtures. Prevents sign-off when required. | | Not run | Outstanding. Prevents sign-off when required. | | Not applicable | Feature outside launch scope; reason recorded. | | Gate | Required output | | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Quickstart verified | Q1–Q5 pass in the real sandbox, including the required app adapter. Fixed shipping/tax fixtures remain unsuitable for production. | | Test plan complete | All Q, F, E, and applicable L cases pass. No required Blocked/Not run cases. Technical and merchant-experience owners review evidence and retest fixes. | | Production review | Test plan complete plus [production readiness](https://help.boltapp.com/developers/production-readiness-guides/). Sandbox tests do not establish live processing or settlement. | ## Test notes [#test-notes] | Topic | Rule | | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Callback failure | Generic custom-cart callback errors may not stop payment. Use the [documented deliberate-decline envelope](/api-reference/merchant-callback/conventions#when-your-endpoint-fails) and verify the actual payment outcome. | | Replay | Use the helper below for F6/F7/F10. Inspect stored state and downstream counts, not just HTTP responses. Record manual replay separately from actual Bolt retry; no retry schedule is assumed. | | Timing | E2's 10-second delay is a test input, not a Bolt response-time promise. | | Test cards | If the intended payment state is not triggered, record Blocked or Fail and investigate; do not mark Pass. | | Evidence | Never include credentials or shopper personal data. This page is an unexecuted plan, not a passed-test report. | | Retest | Rerun affected cases after cart, checkout, payment-handler, or shopper-journey changes. | ## Replay a sandbox notification [#replay-a-sandbox-notification] For F6, F7, and F10, capture a notification received for your own sandbox transaction as `sandbox-notification.json`. Keep its original transaction reference. Load your sandbox signing secret into the server environment and run this helper against your development receiver: ```javascript title="replay-webhook.mjs" import { readFileSync } from 'node:fs'; import { createHmac } from 'node:crypto'; const [endpoint, file] = process.argv.slice(2); if (!endpoint || !file || !process.env.BOLT_SIGNING_SECRET) { throw new Error('Provide a development endpoint, payload file, and sandbox signing secret'); } const body = readFileSync(file); const signature = createHmac('sha256', process.env.BOLT_SIGNING_SECRET) .update(body).digest('base64'); const response = await fetch(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Bolt-Hmac-Sha256': signature }, body, }); console.log(`Receiver returned HTTP ${response.status}`); if (!response.ok) process.exitCode = 1; ``` ```bash node replay-webhook.mjs https://your-development-host.example/bolt/webhooks sandbox-notification.json ``` For the concurrent case, start two instances against the same endpoint and payload. For the older-event case, retain an earlier payload and replay it after a newer state is confirmed. A `200` response alone is not a pass: inspect the stored order, transaction reference, fulfillment count, and notification count. Markdown: https://help.boltapp.com/llms/products/checkout-test-plan.md --- # Custom cart overview > Use Bolt-hosted checkout for a cart your application controls, with the same backend integration across web, iOS, and Android. Source: https://help.boltapp.com/custom-cart/overview ## How checkout works [#how-checkout-works] At runtime, your client, backend, and Bolt exchange the cart and checkout updates in this order: ## 1. Requirements [#1-requirements] Before you build, [confirm your merchant account, Custom Cart division, payment processor, sandbox credentials, and Trusted Domains](/custom-cart/prerequisites). ## 2. Orders API [#2-orders-api] Your backend sends the current cart to Bolt and receives an order token. The client uses that token to open the corresponding cart in Bolt Checkout. Keep the API key on your backend. [Create an order token](/custom-cart/create-order-token) to start the server integration. ## 3. Merchant Callbacks [#3-merchant-callbacks] During checkout, Bolt sends synchronous requests to your backend for information such as shipping options, tax, discounts, and final order creation. Checkout waits for these responses. [Choose and build the callbacks](/api-reference/merchant-callback/overview) your checkout needs. ## 4. Register callback URLs [#4-register-callback-urls] Register each callback URL for the same division as your publishable key. Registration options depend on the callback type. [Register your callback URLs](/platforms/how-to-integrate/set-up-hook-urls/) and confirm that Bolt can reach them. ## 5. Transaction webhooks [#5-transaction-webhooks] Bolt sends asynchronous webhooks when a transaction changes state. Webhooks use a separate endpoint from Merchant Callbacks and do not block checkout. [Configure transaction webhooks](/custom-cart/transaction-webhooks) for the events your backend processes. ## 6. Checkout client [#6-checkout-client] Bolt Checkout is the shopper-facing interface. Your web or mobile client receives an order token from your backend and uses it to open checkout. Install the [web client](/platforms/direct-api), [iOS checkout SDK](/developers/sdks/ios-swift/checkout), or [Android checkout SDK](/developers/sdks/android-kotlin/checkout). Use [Custom Cart troubleshooting](/custom-cart/troubleshooting) when a step does not produce its expected result. Markdown: https://help.boltapp.com/llms/custom-cart/overview.md --- # Prerequisites and readiness > Confirm that your account, division, processor, credentials, and storefront are ready for a Custom Cart integration. Source: https://help.boltapp.com/custom-cart/prerequisites ## Readiness checklist [#readiness-checklist] Complete these requirements in sandbox before writing integration code. | Requirement | Owner | Ready when | | ------------------------------------- | ------------ | ---------------------------------------------------------------------------------- | | Merchant account and sandbox access | You and Bolt | You can sign in to the intended merchant account in sandbox | | Custom Cart division and capabilities | Bolt | Bolt has confirmed the division and the capabilities required by your flow | | Payment processor and currencies | Bolt | The processor accepts every currency you will test | | Sandbox credentials | You | You have an API key, signing secret, and publishable key for the intended division | | Trusted Domains for web | You | Every host that will open Bolt Checkout is registered | ## Merchant account and sandbox access [#merchant-account-and-sandbox-access] Sign in to the [Sandbox Merchant Dashboard](https://merchant-sandbox.boltapp.com) and select the merchant account you will use for development. If your company does not have a Bolt merchant account, [start merchant onboarding](https://merchant.boltapp.com/onboarding/get-started). ## Division and processor readiness [#division-and-processor-readiness] ### Custom Cart capabilities [#custom-cart-capabilities] Ask your Bolt onboarding or Customer Success contact to confirm that the division is configured for Custom Cart and has the capabilities your checkout will use: * Merchant Callbacks, including the Universal Merchant API if one URL will handle multiple events * Transaction webhooks * Split shipping and tax, if you will calculate them with separate callbacks * Pre-authorization order creation, if your flow requires it There is no API-key-authenticated endpoint that reports the division's platform or enabled capabilities. Do not infer readiness from an API key or a working sandbox order. ### Processor and currencies [#processor-and-currencies] Ask your Bolt onboarding contact to confirm that a payment processor is connected to the division and configured for every currency your storefront accepts. A connected processor is not necessarily configured for every purchase currency. If order creation later returns `currency_not_supported` for a currency Bolt supports, ask Bolt to check the processor configuration before changing your cart. ## Credentials and division ID [#credentials-and-division-id] Follow [API keys](/getting-started/api-keys) to obtain these values from **Administration > API**: * **API key:** Authenticates server requests. * **Signing secret:** Verifies Merchant Callback and transaction webhook requests. * **Publishable key:** Initializes the web or mobile client and identifies its division. Keep the API key and signing secret in your backend secret manager. Do not put either secret in browser or mobile code. ### Find your division ID [#find-your-division-id] The division ID used by later callback and webhook requests is the second dot-separated segment of the publishable key: ```text title="Text" EXAMPLEKEY01.DIVISIONID01.0123456789abcdef0123456789abcdef ^^^^^^^^^^^^ division ID ``` At this stage, confirm that all three credentials come from sandbox and that the publishable key identifies the intended division. You will verify registered callbacks and webhooks after configuring them in the later server integration guides. ## Trusted Domains [#trusted-domains] For a web storefront, register every host that will open Bolt Checkout under **Administration > Trusted Domains**. Include staging hosts, CNAMEs, regional domains, and every production host. Follow [Register Trusted Domains](/dashboard/account/trusted-domains/) for the dashboard steps. Register production domains before launch rather than relying on a checkout that works from a sandbox development host. ## Environments and hostnames [#environments-and-hostnames] Start in sandbox. Sandbox and production credentials, transactions, and client resources are separate. | Environment | Dashboard | API | Checkout scripts | | ----------- | ------------------------------ | ------------------------- | ----------------------------- | | Sandbox | `merchant-sandbox.boltapp.com` | `api-sandbox.boltapp.com` | `connect-sandbox.boltapp.com` | | Production | `merchant.boltapp.com` | `api.boltapp.com` | `connect.boltapp.com` | See [Environments](/api-reference/environments) for the complete environment reference. ## Next [#next] When every checklist row is ready, [create an order token](/custom-cart/create-order-token) in sandbox. Markdown: https://help.boltapp.com/llms/custom-cart/prerequisites.md --- # Create an order token > Exchange your cart for the order token that Bolt Checkout requires. Source: https://help.boltapp.com/custom-cart/create-order-token Bolt Checkout will not open without an order token. Your backend creates one by sending the current cart to `POST /v1/merchant/orders`, then returns only the token to your web or mobile client. This request is server-side only because it uses your secret API key. Never call the Bolt endpoint directly from browser or mobile application code. If your backend uses Python, the beta [Bolt Python SDK](https://github.com/BoltApp/Bolt-Python-SDK/blob/main/docs/sdks/orders/README.md#create_order_token) wraps this endpoint as `orders.create_order_token()`. Pin the package version. The TypeScript, Go, and C# generated SDKs target `/v3/orders`, which does not create this Custom Cart token; use the REST request below for those languages. ## Request [#request] Authenticate with your API key in the `X-API-Key` header. The TypeScript and JavaScript examples run in a server-side Node.js environment. Amounts are integers in the currency's minor units, so `41200` is $412.00. TypeScript JavaScript cURL ```typescript type OrderTokenResponse = { token: string }; const baseUrl = process.env.BOLT_API_BASE_URL ?? "https://api-sandbox.boltapp.com"; const apiKey = process.env.BOLT_API_KEY; if (!apiKey) throw new Error("Missing BOLT_API_KEY"); const response = await fetch(`${baseUrl}/v1/merchant/orders`, { method: "POST", headers: { "Content-Type": "application/json", "X-API-Key": apiKey, }, body: JSON.stringify({ cart: { order_reference: "order_100", display_id: "displayid_100", currency: "USD", total_amount: 41200, tax_amount: 900, items: [ { name: "Bolt Coffee Mug", reference: "sku-mug-01", sku: "MUG-01", unit_price: 20150, total_amount: 40300, quantity: 2, image_url: "https://example.com/img/mug.png", }, ], }, channel: "browser", }), }); if (!response.ok) { throw new Error(`Order token request failed: ${response.status} ${await response.text()}`); } const { token } = (await response.json()) as OrderTokenResponse; ``` ```javascript const baseUrl = process.env.BOLT_API_BASE_URL ?? "https://api-sandbox.boltapp.com"; const apiKey = process.env.BOLT_API_KEY; if (!apiKey) throw new Error("Missing BOLT_API_KEY"); const response = await fetch(`${baseUrl}/v1/merchant/orders`, { method: "POST", headers: { "Content-Type": "application/json", "X-API-Key": apiKey, }, body: JSON.stringify({ cart: { order_reference: "order_100", display_id: "displayid_100", currency: "USD", total_amount: 41200, tax_amount: 900, items: [ { name: "Bolt Coffee Mug", reference: "sku-mug-01", sku: "MUG-01", unit_price: 20150, total_amount: 40300, quantity: 2, image_url: "https://example.com/img/mug.png", }, ], }, channel: "browser", }), }); if (!response.ok) { throw new Error(`Order token request failed: ${response.status} ${await response.text()}`); } const { token } = await response.json(); ``` ```bash curl -X POST https://api-sandbox.boltapp.com/v1/merchant/orders \ -H "Content-Type: application/json" \ -H "X-API-Key: $BOLT_API_KEY" \ -d '{ "cart": { "order_reference": "order_100", "display_id": "displayid_100", "currency": "USD", "total_amount": 41200, "tax_amount": 900, "items": [ { "name": "Bolt Coffee Mug", "reference": "sku-mug-01", "sku": "MUG-01", "unit_price": 20150, "total_amount": 40300, "quantity": 2, "image_url": "https://example.com/img/mug.png" } ] }, "channel": "browser" }' ``` `cart.total_amount` is the amount the shopper will be charged. Bolt does not derive it for you, and it must match the sum of the other cart amounts, which [How the amounts are validated](#how-the-amounts-are-validated) sets out exactly. In this example, the items total `40300` and tax is `900`, so the cart total is `41200`. ### Required on every request [#required-on-every-request] | Field | Notes | | ---------------------- | ------------------------------------------------------------------------ | | `channel` | Where the order originated: `browser` or `mobile_app` | | `cart.order_reference` | Your identifier for the order. Must be unique per successful transaction | | `cart.currency` | Three letter ISO code, for example `USD` | | `cart.total_amount` | Total to charge, in minor units | `cart.display_id` is optional. If you omit it, Bolt uses `cart.order_reference` as the display ID. Send a separate value only when the order identifier shown to shoppers or support teams should differ from your order reference. When you send `cart.items`, send these fields on each item: | Field | Notes | | ------------------------------------- | ---------------------------------------------------------------- | | `cart.items[].reference` | Your identifier for the line item | | `cart.items[].name` | Shopper-facing item name | | `cart.items[].sku` | Your stock identifier for the item | | `cart.items[].total_amount` | The line total. This is the figure that has to add up, see below | | `cart.items[].unit_price`, `quantity` | Price per unit and quantity. Not checked against `total_amount` | The API contract marks all six required. Today only a missing `name` is actually rejected, so the others can be omitted without an error, but do not build on that: they are required by the contract and the other consumers described below read them. ### How the amounts are validated [#how-the-amounts-are-validated] Bolt checks the cart total against the sum of everything you sent, and for **line items** it uses each item's `total_amount`. It never multiplies a line item's `unit_price` by its `quantity`. So there is one arithmetic rule to satisfy: ```text title="Validation rule" sum(items[].total_amount) + sum(items[].gift_option.cost) + shipments + in_store_cart_shipments + fees + tax - discounts - gift cards == cart.total_amount ``` `cart.fees[]` are the exception to the no-multiplication rule: each one contributes `(unit_price + unit_tax_amount) * quantity`, because a fee carries no `total_amount` of its own. Get that wrong and the request is rejected before any money moves: HTTP `422` with error code `26` on field `cart.total_amount`, whose message ends with `cart provided amount X does not match computed amount Y`. It can arrive alongside other validation errors in the same `errors` array, so read the array rather than only its first entry. There is one case, applying to every merchant, where the sum is allowed not to match. A cart whose discounts or gift cards come to more than its contents may send `cart.total_amount` of `0`, even though the arithmetic above produces a negative number. A fully gift-carded order is the usual way to reach this. A line item whose `unit_price` times `quantity` does not equal its `total_amount` is accepted without error or warning, and the shopper is charged your `cart.total_amount` either way. Do not read that as permission to leave `unit_price` inconsistent. Bolt forwards it to several consumers that do their own arithmetic, Klarna and the PayPal breakdown among them, and none of their failures points back at your cart. Two are worth knowing about in detail. **Level 3 card data recalculates the line total** as `unit_price` times `quantity`. An inconsistent `unit_price` therefore makes the Level 3 breakdown disagree with the amount actually charged, and because item tax and discount amounts are apportioned from that same figure, they inherit the error. **PayPal line item breakdowns** are patched only when the discrepancy is under 100 minor units, where Bolt inserts a synthetic adjustment to make PayPal's own check pass. At or above 100 minor units the discrepancy is passed through untouched, and PayPal may reject the order at payment time. **For an amount that is not really a unit price**, such as a prorated charge, a computed fee, or anything without a per-unit figure behind it, send `quantity: 1` with `unit_price` equal to `total_amount`, and describe the real calculation in `description`. That keeps every downstream consumer consistent. For example, two memberships at `18900` prorated to 17 of 30 days is a single line of `21420`, not `2 x 18900`. Two more things worth knowing, because neither is rejected at creation time: * `quantity` is only bounded above, at `15000`. A `0` or negative quantity is accepted, but it is not harmless: it zeroes or drops the line in every consumer described in the warning above, and a zero quantity can corrupt the `unit_price` those consumers compute. * `unit_price` defaults to `0` when omitted, silently. It is not treated as absent. Two fields are commonly described as required but are not: `description` and `image_url`. Omitting them is valid, and omitting `image_url` is the simplest way to work around the restriction below during local development. For digital goods, set `cart.items[].type` to `digital` explicitly. The contract also accepts `physical`, `bundled`, and `unknown`. Do not rely on an undocumented default to choose the checkout experience. Every URL in the cart, including `image_url`, must be resolvable from the public internet. A cart pointing at `localhost` is rejected at the edge before it reaches the API, and the response is an HTML error page rather than JSON, so clients that expect JSON fail with a parse error instead of a Bolt error code. In local development, either omit `image_url` or serve images through a tunnel. ## Response [#response] The token is what you need. `cart` echoes back what Bolt stored. ```json title="Response" { "token": "9c8e1f3a2b7d4e5f6a0b1c2d3e4f5a6b", "cart": { "order_reference": "order_100", "display_id": "displayid_100", "currency": { "currency": "USD", "currency_symbol": "$" }, "total_amount": { "amount": 41200, "currency": "USD", "currency_symbol": "$" }, "tax_amount": { "amount": 900, "currency": "USD", "currency_symbol": "$" }, "items": [ { "name": "Bolt Coffee Mug", "reference": "sku-mug-01", "sku": "MUG-01", "unit_price": { "amount": 20150, "currency": "USD", "currency_symbol": "$" }, "total_amount": { "amount": 40300, "currency": "USD", "currency_symbol": "$" }, "quantity": 2 } ] } } ``` The response is not the request. Amounts you sent as integers come back as objects of `amount`, `currency`, and `currency_symbol`, and the `currency` string comes back as an object too. A single type reused for both directions will fail to deserialize the response. The same split applies to the Merchant Callback events, where which form you receive depends on the event. See [Merchant Callback conventions](/api-reference/merchant-callback/conventions#amounts-and-currency). ## Next [#next] Return `token` to the web or mobile client. Next, [choose and build the Merchant Callback events](/api-reference/merchant-callback/overview) that your checkout needs for order creation, shipping, tax, and discounts. Markdown: https://help.boltapp.com/llms/custom-cart/create-order-token.md --- # Register callback URLs > Register the Merchant Callback endpoints that Bolt calls during checkout. Source: https://help.boltapp.com/platforms/how-to-integrate/set-up-hook-urls Register each callback only after its endpoint returns the envelope described in [Merchant Callback conventions](/api-reference/merchant-callback/conventions). Merchant Callbacks and transaction webhooks use separate URLs. Complete this page under **Administration > API > Merchant Callbacks**. Configure transaction webhooks in the next server-integration task. ## Register callback URLs [#register-callback-urls] 1. Sign in to the Bolt Merchant Dashboard for the environment you are testing. 2. Go to **Administration > API** and find **Merchant Callbacks**. 3. Select the division used by your publishable key. 4. Select **Add Callback**, then choose the callback type for the endpoint you implemented. 5. Enter the public HTTPS URL and save it. 6. Repeat for each callback your checkout needs. The dashboard shows only the callback types enabled for the selected division. If a type you need is missing, ask Bolt to confirm the division's callback configuration. ## Choose a registration method [#choose-a-registration-method] Use one of the methods available for your division: * **Universal Merchant API:** Register one URL for every Merchant Callback. Route each request using its top-level `event` value. * **Individual callbacks:** Register a separate URL for **Create Order**, **Shipping**, **Tax**, or another callback type exposed for the division. In either case, verify the HMAC signature over the raw request body and return the matching `{ event, status, data }` envelope. ### Universal API URL [#universal-api-url] When **Universal Merchant API** is available, one public HTTPS URL can receive all Merchant Callback events. The public callback API identifies this registration type as `universal_merchant_api`. The combined shipping-and-tax URL is different. It is a legacy division setting exposed as **Shipping and Tax** in the dashboard. The public `POST /v1/merchant/callbacks` contract does not accept `shipping_and_tax`, and `GET /v1/merchant/callbacks` does not return it. Configure and verify that URL in the dashboard. If your backend uses Python, the beta [Bolt Python SDK](https://github.com/BoltApp/Bolt-Python-SDK/blob/main/docs/sdks/configuration/README.md#set_merchant_callbacks) wraps the standard callback API as `configuration.set_merchant_callbacks()` and `configuration.get_merchant_callbacks()`. It cannot configure or retrieve the combined shipping-and-tax URL because that URL is outside the public callback API. ## Verify registration [#verify-registration] 1. Send a test request to each public endpoint and confirm it returns the expected response envelope. 2. Confirm signature verification uses the raw request body and your signing secret. 3. Call `GET /v1/merchant/callbacks` with the division ID and confirm each standard callback URL appears. 4. For a combined shipping-and-tax URL, confirm the saved value in the dashboard instead. The [prerequisites guide](/custom-cart/prerequisites#find-your-division-id) explains how to find the division ID and query the registered callbacks. Markdown: https://help.boltapp.com/llms/platforms/how-to-integrate/set-up-hook-urls.md --- # Transaction webhooks > Receive asynchronous transaction notifications and tell them apart from Merchant Callbacks. Source: https://help.boltapp.com/custom-cart/transaction-webhooks Merchant Callbacks tell Bolt what to do during checkout. Transaction webhooks tell your server what happened afterwards. They are configured in different places and do not share a URL. ## Callbacks are not webhooks [#callbacks-are-not-webhooks] | | Merchant Callbacks | Transaction webhooks | | ------------- | --------------------------------------------- | ------------------------------------------ | | Direction | Bolt asks, checkout waits for your answer | Bolt informs, nothing waits | | Timing | Synchronous, blocking | Asynchronous, retried | | Failure | Can break the checkout | Retried until acknowledged | | Configured at | **Administration > API > Merchant Callbacks** | **Administration > Developers > Webhooks** | | Stored as | Callback URLs on the division | Webhook subscriptions | They are stored separately, so a webhook endpoint does not fall back to your Universal Merchant API URL. Configuring callbacks and expecting webhooks to arrive at the same URL results in a checkout that works and an order record that never appears. Do not look for transaction webhooks under **Administration > API > Merchant Callbacks**. The callback type dropdown there has no transaction webhook option. Webhooks live under **Administration > Developers > Webhooks**. ## If you cannot see the Webhooks page [#if-you-cannot-see-the-webhooks-page] The page is filtered out of navigation rather than returning an error, so an account without access sees nothing at all rather than a permission message. Three things restrict it: * Write access to webhook settings. Without it the navigation item is hidden. * Marketplace sub-merchant accounts cannot configure webhooks. * Without the v2 webhooks capability a division is limited to a single endpoint. If the controls your integration needs are missing, ask Bolt to confirm the webhook capabilities for the division. See [prerequisites and readiness](/custom-cart/prerequisites). ## Choose the events for an endpoint [#choose-the-events-for-an-endpoint] In the Merchant Dashboard, subscribe an endpoint to all events or select the events it should receive. When using the API, provide exactly one of `event_group` or `events`. An endpoint subscribed to all events must switch on **both** the notification type and the object it concerns, and acknowledge events it does not process. | Category | Public event types | | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Transaction | `payment`, `credit`, `capture`, `void`, `auth`, `pending`, `rejected_irreversible`, `rejected_reversible`, `failed_payment`, `risk_insights` | | Shopper account | `newsletter_subscription`, `credit_card_deleted` | | Subscription | `subscription_created`, `subscription_renewed`, `subscription_canceled`, `subscription_payment_failed`, `subscription_paused`, `subscription_unpaused`, `subscription_ended` | These values come from the public webhook contract. Subscribe only to events your integration understands, or use `event_group: "all"` and make the handler tolerate new event types. Return HTTP 200 after accepting a delivery. An unacknowledged delivery is retried, so processing must be idempotent. ## Configuring over the API [#configuring-over-the-api] Webhooks can be created without the dashboard: If your backend uses Python, the beta [Bolt Python SDK](https://github.com/BoltApp/Bolt-Python-SDK/blob/main/docs/sdks/webhooks/README.md#create_webhook) wraps these endpoints as `webhooks.create_webhook()`, `query_webhooks()`, `get_webhook()`, and `delete_webhook()`. It does not receive or process webhook deliveries; that handler remains part of your application. ```bash title="Request" curl -X POST https://api-sandbox.boltapp.com/v1/webhooks \ -H "Content-Type: application/json" \ -H "X-API-Key: $BOLT_API_KEY" \ -d '{ "division_id": "'"$BOLT_DIVISION_ID"'", "url": "https://example.com/bolt/webhooks", "event_group": "all" }' ``` List what is registered with `GET /v1/webhooks`, and callbacks with `GET /v1/merchant/callbacks`. Both require `division_id`, which is the second segment of your publishable key. See [prerequisites](/custom-cart/prerequisites#find-your-division-id). `POST /v1/merchant/callbacks` can set a callback URL but cannot clear one. An empty URL is rejected rather than treated as a removal, so recovering from a bad callback URL means overwriting it with a working one or using the dashboard. Webhook subscriptions are not affected by this. ## Verifying webhooks [#verifying-webhooks] Webhooks are signed the same way callbacks are. See [verifying the request came from Bolt](/api-reference/merchant-callback/conventions#verifying-the-request-came-from-bolt). ## Verify the completed integration [#verify-the-completed-integration] Complete a sandbox checkout and confirm that: 1. The client opens checkout with a token created by your backend. 2. Bolt reaches the Merchant Callback endpoints needed by the checkout. 3. Your webhook endpoint receives the events you subscribed to. 4. Replaying a delivery does not create a duplicate update in your system. If one of these checks fails, continue to [Custom cart troubleshooting](/custom-cart/troubleshooting). Markdown: https://help.boltapp.com/llms/custom-cart/transaction-webhooks.md --- # Web checkout > Load Bolt Checkout on a web storefront and open it with an order token from your backend. Source: https://help.boltapp.com/platforms/direct-api Your storefront loads Bolt's hosted browser runtime and requests a fresh order token from your backend when a shopper opens checkout. ## Before you add the client [#before-you-add-the-client] You need: | Requirement | Where it belongs | | ---------------------------------------------------------------- | ------------------ | | Publishable key for the selected division and environment | Browser | | An endpoint in your application that returns a fresh order token | Your backend | | The storefront host registered as a Trusted Domain | Merchant Dashboard | Complete [prerequisites and readiness](/custom-cart/prerequisites) and [create an order token](/custom-cart/create-order-token) before connecting a real cart. Never put a Bolt API key or signing secret in browser code. The browser calls your backend for an order token; your backend calls Bolt with the API key. ## Load the hosted scripts [#load-the-hosted-scripts] Add both scripts to pages that can open checkout. Use the hosts for the same environment as your publishable key. Sandbox Production ```html ``` ```html ``` Keep `async` and `defer` off the `connect.js` tag and load it before the application code that calls Bolt. The script creates `window.BoltCheckout` before the browser continues to the next script. `track.js` can remain asynchronous because checkout does not call it directly. ## Open checkout from your button [#open-checkout-from-your-button] Add a button that your application owns: ```html title="HTML" ``` The example below verifies that the hosted script initialized, requests a token from your backend, configures checkout once for that attempt, and opens the modal. ```javascript title="JavaScript" async function initializeBoltButton() { const checkoutButton = document.querySelector("#bolt-checkout-button"); const BoltCheckout = window.BoltCheckout; if (!BoltCheckout?.configure) { throw new Error("Bolt Checkout did not load"); } checkoutButton.disabled = false; checkoutButton.addEventListener("click", async () => { checkoutButton.disabled = true; try { const response = await fetch("/api/checkout/order-token", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ cartId: "CURRENT_CART_ID" }), }); if (!response.ok) { throw new Error(`Order token request failed: ${response.status}`); } const { token } = await response.json(); const configuredCheckout = BoltCheckout.configure( { orderToken: token }, {}, { success: (_transaction, complete) => complete(), }, ); if (!configuredCheckout?.open) { throw new Error("Bolt Checkout could not be configured"); } configuredCheckout.open(); } finally { checkoutButton.disabled = false; } }); } initializeBoltButton().catch(console.error); ``` `/api/checkout/order-token` is an endpoint in your application, not a Bolt URL. It should build the current cart and call `POST /v1/merchant/orders` from your server as shown in [Create an order token](/custom-cart/create-order-token). The browser `success` callback is useful for interface updates, but it is not a trusted payment record. Reconcile orders from your verified server-side callback and webhook handling. ## Browser callback behavior [#browser-callback-behavior] The callback object passed to `configure` is optional. If you provide `success`, call its completion function after your client-side work finishes so checkout can continue. [Frontend events](/developers/frontend-events#bolt-checkout) lists every callback, when Bolt calls it, and the `onNotify` events checkout sends. Do not make the browser callback your only record of an order. When an `order.create` Merchant Callback response contains `order_received_url`, Bolt redirects the shopper there and the browser `success` and `close` callbacks do not run. Use server-side callbacks and webhooks for durable processing. ## Verify the web client [#verify-the-web-client] In sandbox: 1. Load the page and confirm `track.js` and `connect.js` return successfully. 2. Select your checkout button and confirm your backend returns an order token. 3. Confirm the Bolt modal opens and its total matches the cart sent by your backend. 4. Complete a checkout with a [sandbox test card](/getting-started/test-cards), then confirm your Merchant Callback handlers receive the expected events. If the modal does not open or reports `cart_error`, continue to [Custom cart troubleshooting](/custom-cart/troubleshooting). ## SDK boundaries [#sdk-boundaries] The web client does not install an npm package. In particular: * `@boltpay/bolt-js` supports Bolt Gaming payment-link flows, not this Custom Cart integration. * `@boltpay/bolt-typescript-sdk` is a server-side generated API client. Its Orders API is not the Custom Cart `POST /v1/merchant/orders` order-token contract. Use the hosted scripts above for the browser and keep the Custom Cart REST call on your backend. Markdown: https://help.boltapp.com/llms/platforms/direct-api.md --- # iOS checkout > Open Bolt Checkout from an iOS app with an order token from your backend. Source: https://help.boltapp.com/developers/sdks/ios-swift/checkout ## Before you begin [#before-you-begin] Complete [steps 1 through 5 of the Custom Cart integration](/custom-cart/overview#1-requirements). Your app also needs: * The [iOS SDK installed and initialized](/developers/sdks/ios-swift#installation) with the publishable key, division, and environment for this integration. * An application backend endpoint that returns a fresh order token for the current cart. Never call `POST /v1/merchant/orders` or put a Bolt API key in the app. Follow [Create an order token](/custom-cart/create-order-token) for the server implementation. ## 1. Get an order token [#1-get-an-order-token] When the shopper selects checkout, send the current cart identifier to your backend. Wait for your backend to return the `token` from Bolt before opening checkout. Create a new order token when the cart changes or the shopper starts another checkout attempt. ## 2. Open checkout [#2-open-checkout] The completion handler works from UIKit and SwiftUI without requiring a presenting view controller: ```swift title="Swift" import BoltCheckoutSDK func openBoltCheckout(orderToken: String) { let config = BoltCheckoutConfig(orderToken: orderToken) BoltCheckout.shared.startCheckout(config: config) { outcome in switch outcome { case .success(let result): print("Bolt transaction: \(result.reference)") case .canceled: // Return the shopper to the cart. break case .failed(let errorReason): // Show a retry action and record the error. print("Bolt Checkout failed: \(errorReason)") } } } ``` The app callback controls client-side state only. Use verified Merchant Callbacks and transaction webhooks as the source of truth for order and payment state. ### Choose another presentation method [#choose-another-presentation-method] These are alternatives to the completion handler. | Method | Use it when | | --------------- | -------------------------------------------------- | | Delegate | A coordinator or view model owns checkout results. | | View controller | UIKit code must control presentation directly. | #### Delegate [#delegate] Implement the three required `BoltSimpleCheckoutDelegate` methods, then pass the delegate when checkout starts: ```swift title="Swift" import BoltCheckoutSDK final class CheckoutCoordinator: BoltSimpleCheckoutDelegate { func open(orderToken: String) { let config = BoltCheckoutConfig(orderToken: orderToken) BoltCheckout.shared.startCheckout(config: config, delegate: self) } func onCheckoutSuccess(result: BoltCheckoutResult) { print("Bolt transaction: \(result.reference)") } func onCheckoutError(errorReason: String) { print("Bolt Checkout failed: \(errorReason)") } func onCheckoutCancel() { // Return the shopper to the cart. } } ``` #### View controller [#view-controller] Present `BoltCheckoutViewController` with the same configuration and delegate: ```swift title="Swift" let config = BoltCheckoutConfig(orderToken: orderToken) let checkout = BoltCheckoutViewController(config: config, delegate: delegate) present(checkout, animated: true) ``` ### Prefill shopper details [#prefill-shopper-details] Pass optional hints when your app already has shopper information: ```swift title="Swift" let hints = Bolt.HintsBuilder() .withFirstName("Ada") .withLastName("Lovelace") .withEmail("ada@example.com") .withCountry("US") .build() let config = BoltCheckoutConfig(orderToken: orderToken, hints: hints) ``` Do not send placeholder or stale values. The shopper can review the information in checkout. ## 3. Verify the iOS client [#3-verify-the-ios-client] In sandbox: 1. Start checkout from a cart and confirm your backend returns a new order token. 2. Confirm Bolt Checkout opens and shows the same items and total as the cart. 3. Complete a purchase with a [sandbox test card](/getting-started/test-cards). 4. Confirm the app receives a success result and your backend receives the expected callback and webhook events. 5. Cancel another attempt and confirm the app returns the shopper to the cart without treating it as a completed order. If checkout does not open or the cart does not match, continue to [Custom Cart troubleshooting](/custom-cart/troubleshooting). Markdown: https://help.boltapp.com/llms/developers/sdks/ios-swift/checkout.md --- # Android checkout > Open Bolt Checkout from an Android app with an order token from your backend. Source: https://help.boltapp.com/developers/sdks/android-kotlin/checkout ## Before you begin [#before-you-begin] Complete [steps 1 through 5 of the Custom Cart integration](/custom-cart/overview#1-requirements). Your app also needs: * The [Android SDK installed and initialized](/developers/sdks/android-kotlin#installation) with the publishable key and environment for this integration. * An application backend endpoint that returns a fresh order token for the current cart. Never call `POST /v1/merchant/orders` or put a Bolt API key in the app. Follow [Create an order token](/custom-cart/create-order-token) for the server implementation. ## 1. Get an order token [#1-get-an-order-token] When the shopper selects checkout, send the current cart identifier to your backend. Wait for your backend to return the `token` from Bolt before opening checkout. Create a new order token when the cart changes or the shopper starts another checkout attempt. ## 2. Register the result launcher [#2-register-the-result-launcher] Register an `ActivityResultLauncher` while the activity is being created. The delegate handles the three terminal results: ```kotlin title="Kotlin" import android.content.Intent import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.result.ActivityResultLauncher import com.bolt.checkout.BoltCheckout import com.bolt.checkout.intl.BoltCheckoutDelegate import com.bolt.checkout.intl.BoltCheckoutResult class MainActivity : ComponentActivity() { private lateinit var checkoutLauncher: ActivityResultLauncher override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) checkoutLauncher = BoltCheckout.get().getBoltCheckoutActivityResultLauncher( activity = this, boltCheckoutDelegate = object : BoltCheckoutDelegate { override fun onCheckoutSuccess(result: BoltCheckoutResult) { println("Bolt transaction: ${result.transactionReference}") } override fun onCheckoutError(errorReason: String) { println("Bolt Checkout failed: $errorReason") } override fun onCheckoutCancel() { // Return the shopper to the cart. } }, ) } } ``` The app callback controls client-side state only. Use verified Merchant Callbacks and transaction webhooks as the source of truth for order and payment state. ### Register from a fragment [#register-from-a-fragment] Use the fragment overload when a `Fragment` owns the checkout result: ```kotlin title="Kotlin" checkoutLauncher = BoltCheckout.get().getBoltCheckoutActivityResultLauncher( fragment = this, boltCheckoutDelegate = checkoutDelegate, ) ``` ## 3. Open checkout [#3-open-checkout] After your backend returns the order token, configure checkout and pass the registered launcher: ```kotlin title="Kotlin" import com.bolt.checkout.model.BoltCheckoutConfig fun openBoltCheckout(orderToken: String) { val config = BoltCheckoutConfig(orderToken = orderToken) BoltCheckout.get().startCheckout( checkoutConfig = config, context = this, checkoutActivityResultLauncher = checkoutLauncher, ) } ``` ### Prefill shopper details [#prefill-shopper-details] Pass optional hints when your app already has shopper information: ```kotlin title="Kotlin" import com.bolt.checkout.model.BoltCheckoutConfig import com.bolt.checkout.model.BoltMerchantHints val hints = BoltMerchantHints( firstName = "Ada", lastName = "Lovelace", email = "ada@example.com", country = "US", ) val config = BoltCheckoutConfig(orderToken = orderToken, hints = hints) ``` Do not send placeholder or stale values. The shopper can review the information in checkout. ## 4. Verify the Android client [#4-verify-the-android-client] In sandbox: 1. Start checkout from a cart and confirm your backend returns a new order token. 2. Confirm Bolt Checkout opens and shows the same items and total as the cart. 3. Complete a purchase with a [sandbox test card](/getting-started/test-cards). 4. Confirm the app receives a success result and your backend receives the expected callback and webhook events. 5. Cancel another attempt and confirm the app returns the shopper to the cart without treating it as a completed order. If checkout does not open or the cart does not match, continue to [Custom Cart troubleshooting](/custom-cart/troubleshooting). Markdown: https://help.boltapp.com/llms/developers/sdks/android-kotlin/checkout.md --- # Troubleshooting > Diagnose a custom cart integration from the symptom you can actually see. Source: https://help.boltapp.com/custom-cart/troubleshooting ## Start here [#start-here] | What you see | Check first | | ----------------------------------------------------- | -------------------------------------------------------------- | | Order-token request returns an error | HTTP status and response body from `POST /v1/merchant/orders` | | Checkout does not open | Hosted scripts, your order-token endpoint, and Trusted Domains | | `cart_error` or `undefined_cart` | The token or cart passed to `BoltCheckout.configure` | | Checkout cannot calculate shipping, tax, or discounts | The corresponding Merchant Callback request and response | | Payment succeeds but your system has no order | The `order.create` callback and transaction webhook logs | | Transaction webhooks do not arrive | Webhook registration, subscribed events, and response status | Work from the first failing stage. A browser error can hide a more specific response from your backend or a Bolt API. ## Order-token request fails [#order-token-request-fails] Call `POST /v1/merchant/orders` from your backend and record its HTTP status and response body. Do not diagnose this request only from the browser. Check these items in order: 1. Confirm the request uses the sandbox API key with `api-sandbox.boltapp.com`, or the production API key with `api.boltapp.com`. 2. Confirm the request includes `X-API-Key` and that the key belongs to the intended merchant account. 3. Compare the payload with [Create an order token](/custom-cart/create-order-token), including cart totals, item totals, currency, and required fields. 4. Confirm every URL in the cart is reachable from the public internet. During local development, omit optional image URLs or serve them from a public host. A successful request returns HTTP 200 and an order token. If the response says `Merchant Primary Processor not found`, ask Bolt to confirm that a payment processor is connected to the division. ### Order creation fails with an unsupported currency [#order-creation-fails-with-an-unsupported-currency] If the response contains `currency_not_supported`, confirm that `cart.currency` is a three-letter ISO currency code. If it is valid, ask Bolt to confirm that the division's processor accepts that purchase currency. A connected processor might not support every currency. See [prerequisites and readiness](/custom-cart/prerequisites#division-and-processor-readiness). ## Checkout does not open [#checkout-does-not-open] Use your browser's developer tools to check the client in this order: 1. Confirm `track.js` and `connect.js` load successfully from the same environment as the publishable key. 2. Confirm your application's order-token endpoint returns a fresh token without exposing the Bolt API key to the browser. 3. Confirm the storefront host is registered under **Administration > Trusted Domains**. 4. Confirm your code calls `BoltCheckout.configure` with the returned token before calling `open()`. Compare the implementation with [Web checkout](/platforms/direct-api). For iOS or Android, confirm the app uses the same environment for SDK initialization and order creation. ### Modal does not open in production but worked in sandbox [#modal-does-not-open-in-production-but-worked-in-sandbox] Confirm every production host is registered under **Administration > Trusted Domains**, including CNAMEs, regional domains, and alternate storefront hosts. Then confirm the production page uses production scripts and a production publishable key. See [Register Trusted Domains](/dashboard/account/trusted-domains/). ## `cart_error` names no cause [#cart_error-names-no-cause] `cart_error` means checkout could not load the configured cart. Inspect your application's order-token request and the underlying Bolt API response before changing the browser integration. `undefined_cart` means checkout did not receive a cart. Confirm that the token returned by your backend reaches `BoltCheckout.configure` and that you configure checkout after the token request succeeds. ### Check that every URL in the cart is publicly reachable [#check-that-every-url-in-the-cart-is-publicly-reachable] If an optional `image_url` points to `localhost` or a private host, remove it and create a new token. If checkout then opens, host the image at a public URL before restoring the field. ## Callbacks never arrive [#callbacks-never-arrive] Confirm the callbacks registered for the division: ```bash title="Request" curl -G https://api-sandbox.boltapp.com/v1/merchant/callbacks \ -H "X-API-Key: $BOLT_API_KEY" \ --data-urlencode "division_id=$BOLT_DIVISION_ID" ``` Then check that: 1. The expected callback type points to the correct public HTTPS endpoint. 2. Your application logs the incoming request and its response. 3. The handler returns a 2xx response with the required response envelope. 4. The handler verifies the Bolt signature before processing the request. The division ID is the second segment of your publishable key. See [Find your division ID](/custom-cart/prerequisites#find-your-division-id) and [Merchant Callback conventions](/api-reference/merchant-callback/conventions). If the URL is registered but Bolt never sends the expected event, ask Bolt to confirm the callback capabilities enabled for the division. ### Check your callbacks are succeeding [#check-your-callbacks-are-succeeding] For shipping, tax, and discount failures, find the request in your server logs and inspect the exact response your handler returned. Confirm the event name, HTTP status, JSON syntax, and required `data` object against the event's API reference. ## Payment succeeded and no order was created [#payment-succeeded-and-no-order-was-created] Check whether your server received `order.create`, what it returned, and whether a transaction webhook arrived for the same order reference. Fix any non-2xx response or invalid response envelope from the callback. Do not use the browser `success` callback as your only order record. Log Merchant Callback requests and responses, and reconcile orders with verified transaction webhooks. See [browser callback behavior](/platforms/direct-api#browser-callback-behavior) and [Transaction webhooks](/custom-cart/transaction-webhooks). ## Webhooks never arrive [#webhooks-never-arrive] Confirm the webhook subscriptions registered for the division: ```bash title="Request" curl -G https://api-sandbox.boltapp.com/v1/webhooks \ -H "X-API-Key: $BOLT_API_KEY" \ --data-urlencode "division_id=$BOLT_DIVISION_ID" ``` Confirm that the returned subscription has the expected URL and includes the event you are testing. Then check that your handler logs the delivery, verifies its signature, and returns HTTP 200 after accepting it. Check that you registered the endpoint under **Administration > Developers > Webhooks** and not under Merchant Callbacks, which is a separate system. See [transaction webhooks](/custom-cart/transaction-webhooks). If the subscription is correct and no delivery appears in your logs, ask Bolt to confirm webhook access for the division. ## Sandbox hostnames look inconsistent [#sandbox-hostnames-look-inconsistent] Use `api-sandbox.boltapp.com` for API requests and `connect-sandbox.boltapp.com` for checkout scripts, as shown throughout this guide. Bolt client resources can report a corresponding `bolt.com` hostname in browser messages; this alone does not indicate an environment mismatch. ## Contact support [#contact-support] If the checks above do not identify the failure, [contact Bolt Support](/support) with: * Sandbox or production environment * Division ID and your order reference * Approximate timestamp and time zone * Failing stage and steps to reproduce it * HTTP status and sanitized response body * Relevant browser, callback, or webhook logs Remove API keys, signing secrets, payment details, and shopper personal information before sharing logs. Markdown: https://help.boltapp.com/llms/custom-cart/troubleshooting.md --- # Embedded API Quickstart > Accept card payments inside your existing checkout page. - **Bolt handles** secure card fields and payment processing. - **Your app owns** the checkout layout, cart totals, and order records. Source: https://help.boltapp.com/products/embeddable-checkout ## Your integration steps [#your-integration-steps] **This flow:** browser JavaScript, Node.js 22 / Express, and a guest payment through the v1 direct-payment API with Bolt Payments. Shopper login and saved cards are follow-on work. ## Before you begin [#before-you-begin] Have these ready before the estimate starts. | Requirement | What you need | | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Existing app | Your own checkout page, customer sessions, and saved orders. The examples use Node.js 22 and Express. | | Sandbox account | Ask Bolt to confirm that your division supports **v1 direct payments with Bolt Payments**. This path requires enablement; do not substitute v3 endpoints. Account setup is separate from this estimate. | **Platform support** This guide works for any custom web checkout. The examples use Node.js and Express, but the server steps are plain HTTPS calls to Bolt that you can write in any language, and the browser steps load Bolt's hosted embed script from any front end. Native mobile apps and Salesforce Commerce Cloud use their own guides instead. JS Embed React Native REST API Python Salesforce ## 1. Set up [#1-set-up] 1. Open [sandbox Payments](https://merchant-sandbox.boltapp.com/payments) and confirm Bolt Payments is configured for USD test payments. 2. Open [Administration > API](https://merchant-sandbox.boltapp.com/administration/api-keys#apiKeys). Copy the **API key**, **Publishable key**, and **Merchant Division Public ID** from the same division. 3. Open [Trusted Domains](https://merchant-sandbox.boltapp.com/administration/trusted-domains) and save your app's HTTPS origin for that division. 4. Add these values to your existing server secrets configuration: ```bash title="Server environment" BOLT_API_KEY=YOUR_SANDBOX_API_KEY BOLT_PUBLISHABLE_KEY=YOUR_SANDBOX_PUBLISHABLE_KEY BOLT_DIVISION_ID=YOUR_MERCHANT_DIVISION_PUBLIC_ID ``` These names configure this example, not the SDK. Only the publishable key belongs in browser configuration. ### Validate setup [#validate-setup] Confirm the keys and domain belong to the enabled sandbox division. Restart the backend and check that all three values are available without logging them. Stop if Bolt has not confirmed this payment path for your account. ## 2. Connect orders [#2-connect-orders] Use your existing order service to save an unpaid checkout snapshot. For this first test, use one **$10.00 USD digital item**, no tax, and no discounts. Prices below are cents. Replace the order ID with your app's unique, saved order ID: ```javascript title="Checkout snapshot" const order = { id: 'YOUR_SAVED_ORDER_ID', cart: { order_reference: 'YOUR_SAVED_ORDER_ID', display_id: 'YOUR_SAVED_ORDER_ID', currency: 'USD', total_amount: 1000, tax_amount: 0, items: [{ reference: 'test-download', name: 'Test download', type: 'digital', quantity: 1, unit_price: 1000, total_amount: 1000 }], }, billing: { first_name: 'Test', last_name: 'Shopper', email: 'shopper@example.com', phone: '+14155550199', street_address1: '535 Mission St', locality: 'San Francisco', region: 'CA', country_code: 'US', postal_code: '94105', }, payment: null, }; ``` The `orders` adapter below is your application code. Implement these operations before continuing: | Operation | Required behavior | | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `findForShopper(id, req)` | Return the saved order only for its owning session, otherwise `null`. Read prices and billing details from server-validated storage. | | `claim(id)` | Atomically reserve the order's only payment attempt. Return `false` if already claimed or paid. Keep this reservation across restarts and uncertain network failures. | | `saveReference(id, reference)` | Persist the returned reference as `order.payment.reference` (for example, `payment: { reference }`) so `findForShopper` returns it. Preserve other payment fields and reject a different existing reference atomically. | | `recordPayment(id, payment)` | Atomically persist `{ reference, status }`. Never overwrite a completed payment with a different reference or an older state. | Keep the snapshot unchanged during payment. A new cart needs a new snapshot. These snippets do not implement your database, sessions, fulfillment, or safe resubmission after an uncertain payment. Add these handlers to your backend: ```javascript title="embedded-payments.mjs" export function createEmbeddedHandlers(orders) { for (const key of ['BOLT_API_KEY', 'BOLT_PUBLISHABLE_KEY', 'BOLT_DIVISION_ID']) { if (!process.env[key]) throw new Error(`Missing server configuration: ${key}`); } async function bolt(path, body, id) { const response = await fetch(`https://api-sandbox.boltapp.com${path}`, { method: body ? 'POST' : 'GET', headers: { 'X-API-Key': process.env.BOLT_API_KEY, 'X-Publishable-Key': process.env.BOLT_PUBLISHABLE_KEY, 'Content-Type': 'application/json', ...(id ? { 'Idempotency-Key': `embedded-${id}` } : {}), }, ...(body ? { body: JSON.stringify(body) } : {}), }); if (!response.ok) throw new Error(`Bolt HTTP ${response.status}`); return response.json(); } const handle = fn => async (req, res, next) => { res.set('Cache-Control', 'no-store'); try { await fn(req, res); } catch (error) { next(error); } }; return { pay: handle(async (req, res) => { const order = await orders.findForShopper(req.params.id, req); if (!order) return res.status(404).json({ error: 'Order not found' }); const card = req.body; if (!['token', 'bin', 'last4', 'expiration'].every(k => typeof card?.[k] === 'string' && card[k].length > 0)) { return res.status(400).json({ error: 'Card token required' }); } if (order.cart.order_reference !== order.id || !await orders.claim(order.id)) { return res.status(409).json({ error: 'Check the existing payment before trying again' }); } const address = order.billing; const result = await bolt('/v1/merchant/transactions/authorize', { division_id: process.env.BOLT_DIVISION_ID, source: 'direct_payments', auto_capture: true, create_bolt_account: false, user_identifier: { email: address.email, phone: address.phone }, user_identity: { first_name: address.first_name, last_name: address.last_name }, cart: { ...order.cart, billing_address: address }, credit_card: { token: card.token, bin: card.bin, last4: card.last4, expiration: card.expiration, postal_code: address.postal_code, token_type: 'bolt', billing_address: address, save: false }, }, order.id); if (!result.transaction?.reference) throw new Error('Missing payment reference'); await orders.saveReference(order.id, result.transaction.reference); res.status(202).json({ id: order.id }); }), status: handle(async (req, res) => { const order = await orders.findForShopper(req.params.id, req); if (!order) return res.status(404).json({ error: 'Order not found' }); const reference = order.payment?.reference; if (!reference) return res.json({ id: order.id, status: 'unconfirmed' }); const transaction = await bolt(`/v1/merchant/transactions/${encodeURIComponent(reference)}`); if (transaction.reference !== reference || transaction.type !== 'cc_payment' || transaction.order?.cart?.order_reference !== order.id || transaction.amount?.amount !== order.cart.total_amount || transaction.amount?.currency !== order.cart.currency) { throw new Error('Payment does not match the saved order'); } await orders.recordPayment(order.id, { reference, status: transaction.status }); res.json({ id: order.id, reference, status: transaction.status }); }), }; } ``` Register the handlers after your existing JSON parser and before your error handler. Connect your session and CSRF middleware; these identifiers are app-owned, not Bolt APIs: ```javascript title="Express routes" import { createEmbeddedHandlers } from './embedded-payments.mjs'; const embedded = createEmbeddedHandlers(orders); app.post('/api/embedded/orders/:id/pay', requireShopper, requireCsrf, embedded.pay); app.get('/api/embedded/orders/:id', requireShopper, embedded.status); ``` Return generic browser errors from your error handler; keep diagnostic details in server logs without card tokens or credentials. A timeout after submission is uncertain, not permission to submit another charge. ### Validate orders [#validate-orders] 1. Save the test snapshot under your shopper's session. Expose its ID to that shopper's checkout page. 2. Request `GET /api/embedded/orders/{id}` through the app. Expect `unconfirmed`. 3. Repeat as another shopper. Expect `404`, with no order details. 4. Verify `claim` permits only one concurrent attempt and survives a restart before connecting payment submission. ## 3. Add fields [#3-add-fields] Load the sandbox embed script once in your existing page shell. Replace the publishable key: ```html title="Page shell" ``` Add these elements inside your existing payment section. Keep your app's price and billing-address display visible: ```html title="Payment section"

``` Call this function on page mount, with the saved `orderId`, public key, and your authenticated JSON `request` helper. The helper must send CSRF protection on POST, reject non-2xx responses, and parse JSON. Call the returned cleanup on unmount; keep one active payment component per page. ```javascript title="embedded-checkout.js" export function mountEmbeddedPayment({ orderId, publishableKey, request }) { const pay = document.querySelector('#embedded-pay'); const refresh = document.querySelector('#embedded-refresh'); const status = document.querySelector('#embedded-status'); const script = document.querySelector('#bolt-embed'); const path = `/api/embedded/orders/${encodeURIComponent(orderId)}`; let active = true, fields, submitted = false; const show = text => { if (active && status.textContent !== text) status.textContent = text; }; const check = async () => { try { const result = await request(path); if (result.status === 'completed') { submitted = true; pay.disabled = true; show(`Order ${orderId} paid. Payment ${result.reference}.`); } else show(`Order ${orderId}: ${result.status}. Check again before paying again.`); } catch { show('Cannot confirm payment. Check this order with support before paying again.'); } }; async function start() { if (!active) return; try { Bolt.initialize(publishableKey); fields = Bolt.create('credit_card_input'); await fields.mount('#embedded-fields'); if (!active) { await fields.unmount(); return; } pay.disabled = submitted; show('Enter your test card.'); } catch { show('Payment fields could not load. Reload this page to try again.'); } } pay.onclick = async () => { if (submitted || pay.disabled) return; pay.disabled = true; try { const card = await fields.tokenize(); if (!active) return; if (!card.token) { show('Check your card details.'); pay.disabled = false; return; } submitted = true; show('Submitting payment…'); await request(`${path}/pay`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(card) }); await check(); } catch { show(submitted ? 'Payment is unconfirmed. Check its status before trying again.' : 'Check your card details.'); if (active) pay.disabled = submitted; } }; refresh.onclick = check; const loadFailed = () => show('Payment fields could not load. Reload this page to try again.'); if (window.Bolt) start(); else { script.addEventListener('load', start, { once: true }); script.addEventListener('error', loadFailed, { once: true }); } return () => { active = false; pay.onclick = null; refresh.onclick = null; script.removeEventListener('load', start); script.removeEventListener('error', loadFailed); if (fields) Promise.resolve(fields.unmount()).catch(() => {}); }; } ``` ### Validate fields [#validate-fields] Open checkout and confirm the card fields load inside Bolt-hosted frames. The Pay button enables after mounting. Confirm the page includes only the publishable key, and card numbers do not pass through your app's inputs or backend. ## 4. Take payment [#4-take-payment] 1. Confirm the order is **$10.00 USD** and the displayed billing ZIP is `94105`. 2. Enter `4111 1111 1111 1111`, expiration `03/30`, and CVV `737` in the hosted fields. 3. Select **Pay $10.00** once. The browser sends a token to your backend, not the card number. 4. Use **Check payment status** while capture is pending. Automatic capture is asynchronous. ### Validate submission [#validate-submission] The pay route returns `202` and your saved order has one transaction reference. `authorized`, `pending`, and `unconfirmed` are not completed payments. If submission fails after it starts, check that order in the dashboard before attempting another payment. ## 5. Verify order [#5-verify-order] 1. Select **Check payment status** until the backend reports `completed`. 2. Reopen the order in your app's storage. Confirm its payment reference and completed status survived a page refresh. 3. Open [sandbox Transactions](https://merchant-sandbox.boltapp.com/transactions). Match the order ID, reference, **$10.00 USD**, and completed payment. ### Validate completion [#validate-completion] You are done when your checkout collects card details through Bolt fields and your server verifies and stores the matching payment. The guest is not enrolled in Bolt and no card is saved by this sample. ## Test before launch [#test-before-launch] Run the [Embedded API test plan](/products/embedded-api-test-plan). Before production, add asynchronous payment reconciliation using [verified webhooks](/api-reference/guides/verify-webhooks), safe recovery for uncertain payments, and your order confirmation/fulfillment behavior. Add [shopper login and saved-card flows](/api-reference/guides/embeddable-checkout) as separate, explicitly enabled work; this guest-payment quickstart does not implement them. Markdown: https://help.boltapp.com/llms/products/embeddable-checkout.md --- # Test your Embedded API integration > Verify the quickstart, failure recovery, and the experience your app or store controls. Source: https://help.boltapp.com/products/embedded-api-test-plan Run these cases against the [Embedded API quickstart](/products/embeddable-checkout). Copy the tables into your test report and attach redacted evidence. Every case starts **Not run**. ## Scope and setup [#scope-and-setup] | Scope | What is covered | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Quickstart | Hosted fields, one payment attempt per claimed order, server transaction lookup, and status display. | | Merchant integration | Session/CSRF enforcement, immutable pricing, atomic claim, reference persistence, monotonic payment updates. | | Additional launch work | Webhooks/background reconciliation, safe retry policy, full recovery/confirmation, fulfillment, receipts, and optional shopper login/saved cards. | | Inputs | One saved $10.00 USD digital-item order, no tax/discounts; shopper A and unrelated shopper B; billing ZIP 94105. Use the guide’s Bolt Payments sandbox division. | | Evidence | Record build/date, tester, sandbox division, device/browser, case result, order/transaction/seller or link IDs as applicable, and redacted logs/screenshots. Never attach tokens, credentials, or personal banking data. | Use real sandbox execution for Q cases. Label controlled response substitutions, outages, and replay tests separately; mocks do not establish live payment acceptance. Use a fresh order for each payment test and reset store/fixture changes afterward. Do not retry an uncertain payment until it is reconciled. ## 1. Quickstart checkpoints [#1-quickstart-checkpoints] | Test | Input | Steps | Expected output | Result | | ------------------- | ------------------------------- | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | ------- | | Q1 · Set up | Enabled v1 sandbox division | 1. Confirm Bolt Payments and credentials. 2. Reopen Trusted Domains. | All settings belong to the same enabled division; private key stays server-side. | Not run | | Q2 · Connect orders | Shopper A/B; saved order | 1. GET the order as A. 2. Repeat as B. 3. Exercise two concurrent claim calls. | A gets unconfirmed; B gets 404; exactly one claim succeeds and survives restart. | Not run | | Q3 · Add fields | Existing checkout page | 1. Open payment section. 2. Inspect loaded scripts/frames. | Hosted fields mount; Pay enables; only the publishable key is exposed; no raw card fields in merchant DOM. | Not run | | Q4 · Take payment | 4111 1111 1111 1111; 03/30; 737 | 1. Enter card and ZIP 94105. 2. Select Pay once. | POST returns 202; one transaction reference is saved; no claim of completion while capture is pending. | Not run | | Q5 · Verify order | Saved reference | 1. Check status until completed. 2. Refresh/restart app. 3. Compare Bolt Transactions. | Same order/reference, $10.00 USD, and completed payment in app storage and Bolt. | Not run | ## 2. Functional integrity [#2-functional-integrity] | Test | Input | Steps | Expected output | Result | | ------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------- | ------- | | F1 · Incomplete card | Missing card fields | 1. Submit incomplete fields. 2. Correct them. | Readable correction; no authorization request before tokenization succeeds; submission can then continue. | Not run | | F2 · Duplicate submission | Same order; two simultaneous requests | 1. Submit from two tabs. 2. Refresh checkout and submit again. | At most one authorization attempt; later claims return 409. Merchant adapter enforces this atomically. | Not run | | F3 · Price tampering | Browser payload with changed amount | 1. Add amount/currency fields to the pay request. 2. Inspect outbound request. | Server uses the saved snapshot; browser-supplied prices have no effect. | Not run | | F4 · Transaction mismatch | Controlled Bolt response: other order, amount, currency, reference, or refund type | 1. Substitute each response at transaction lookup. 2. Request status. | No completed state saved; safe error. A credit/refund never becomes a new purchase. | Not run | | F5 · Uncertain submission | Drop authorization response after Bolt accepts it | 1. Submit. 2. Reload and attempt again. 3. Reconcile via dashboard. | Claim remains reserved; no second charge; app directs shopper to status/support. Recovery requires merchant implementation. | Not run | | F6 · Capture pending | Controlled authorized/pending response | 1. Check status. 2. Change response to completed and check again. | Pending is not shown as paid; only verified completion updates the order. | Not run | | F7 · Access and CSRF | Other shopper; missing/invalid CSRF token | 1. Try GET as B. 2. POST without valid session/CSRF. | Access rejected before a Bolt request; no order data disclosed. | Not run | ## 3. Merchant-owned experience [#3-merchant-owned-experience] | Test | Input | Steps | Expected output | Result | | ------------------------- | ----------------------------------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | ------- | | E1 · Clear purchase | Checkout total and digital product | 1. Review checkout before paying. | Product, $10.00 USD, billing details, and Pay action are clear; hosted fields fit the merchant layout. | Not run | | E2 · Load/recovery | Block embed.js; reload | 1. Block script. 2. Restore it and reload. | Pay stays disabled on load failure; helpful message; successful reload mounts one component. | Not run | | E3 · Navigation | Slow script/tokenization; unmount | 1. Navigate away during setup and tokenization. 2. Return. | No late payment submission after leaving; no duplicate controls/listeners; hosted fields clean up. | Not run | | E4 · Accessibility/mobile | Keyboard, screen reader, 390px, 200% zoom | 1. Tab through merchant controls. 2. Trigger errors/status. 3. Rotate/zoom. | Visible focus, named controls, useful announcements, no inaccessible actions. Record Bolt-owned frame issues separately. | Not run | | E5 · Unavailable status | Fail GET after submission | 1. Check payment status. 2. Follow the recovery message. | No false success or raw exception; shopper can recover the existing order without another payment. | Not run | ## 4. Additional launch requirements [#4-additional-launch-requirements] | Test | Input | Steps | Expected output | Result | | ----------------------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------- | ------- | | L1 · Background updates | Shopper closes page after payment | 1. Complete payment then close browser. 2. Process authenticated notification or reconciliation. | Order eventually updates without an open page; duplicate notifications do not duplicate fulfillment. | Not run | | L2 · Lifecycle | Decline/review; refund; receipt/fulfillment | 1. Run configured failure and post-purchase flows. 2. Reconcile records. | Correct risk/payment states and exactly-once business effects; refunds separate from sales. | Not run | | L3 · Expanded checkout | Real totals; saved cards/login if offered | 1. Exercise each offered flow. 2. Confirm consent and pricing. | Enabled features pass their own cases; guest baseline does not claim account creation or saved cards. | Not run | ## Completion gates [#completion-gates] | Gate | Required evidence | | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Quickstart verified | All Q cases pass in the real sandbox, including merchant-owned integration work. | | Ready for launch review | All Q, F, E and applicable L cases pass; no required Blocked or Not run cases. Record exclusions with a reason and obtain technical/experience-owner review. | | Failed or blocked case | Record actual output, issue/owner, and retest evidence. A successful payment does not excuse a broken shopper or seller experience. | Use **Pass**, **Fail**, **Blocked**, **Not run**, or **Not applicable** with a reason. A build or mocked test result does not verify production processing, delivery, or settlement. Markdown: https://help.boltapp.com/llms/products/embedded-api-test-plan.md --- # Add shopper accounts to your checkout > Extend a working Embedded API integration with Bolt login and saved shopper details. Source: https://help.boltapp.com/api-reference/guides/embeddable-checkout Start with the [Embedded API quickstart](/products/embeddable-checkout) to verify a guest payment. Account login, consent, and saved payment methods are additional integration work. ## Plan the account flow [#plan-the-account-flow] | Task | Integration reference | | ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | | Prompt the shopper to sign in | [Login modal](https://help.boltapp.com/products/checkout/embeddable-checkout/api-implementation/components/login-modal/) | | React to login, logout, and account checks | [Frontend events](/developers/frontend-events#embedded-api) | | Exchange the login result on your server | [Bolt OAuth](https://help.boltapp.com/developers/bolt-oauth/) | | Retrieve and update authorized shopper data | [Embeddable API v1](/api-embedded/) | | Collect account-creation consent | [Account checkbox](https://help.boltapp.com/products/checkout/embeddable-checkout/api-implementation/components/account-checkbox/) | | Add fields within your checkout | [Payment fields](https://help.boltapp.com/products/checkout/embeddable-checkout/api-implementation/components/payment-fields/) | Confirm the enabled API version and account features with Bolt. Use the v1 contracts for this documentation path; a v3 payload is not interchangeable with the v1 payment example. A recognized Bolt shopper can use saved details for one-click checkout. ## Keep account and payment state separate [#keep-account-and-payment-state-separate] * Exchange authorization codes on your backend and associate tokens with the correct shopper session. * Obtain consent before creating an account or saving a card. The guest quickstart sets `create_bolt_account: false` and `save: false`. * For payments made as a logged-in Bolt shopper, include that shopper's OAuth access token as required by the authorization API. Never reuse one shopper's token for another session. * Continue to verify payment on your server; successful sign-in does not mean an order was paid. ## Verify the extension [#verify-the-extension] Add cases to your [Embedded API test plan](/products/embedded-api-test-plan) for existing/new shoppers, declined consent, expired sessions, login cancellation, saved-card selection, and logout. Verify that another shopper cannot access the previous shopper's data. Markdown: https://help.boltapp.com/llms/api-reference/guides/embeddable-checkout.md --- # Checkout Everywhere Quickstart > Sell a product from a checkout link or a scannable QR code. - **Bolt handles** the checkout page and payment collection. - **Your store owns** the product catalog, order records, and fulfillment. Source: https://help.boltapp.com/products/checkout-everywhere ## Your integration steps [#your-integration-steps] **This flow:** BigCommerce test store and Bolt Payments sandbox. ## Before you begin [#before-you-begin] Have these ready before the estimated integration time begins. | Requirement | What you need | | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Test store | BigCommerce administrator access and one available physical product with a single variant. | | Sandbox account | A [Bolt merchant account](https://merchant.boltapp.com/onboarding/get-started) with Checkout Everywhere and Bolt Payments enabled for sandbox testing. Account setup is separate from this estimate. | **Platform support** The checkout link, QR code, and order verification steps work the same for any store. The store connection steps use BigCommerce; other platforms are linked below, and a store without a supported platform uses the [Custom API setup](https://help.boltapp.com/products/checkout-everywhere/set-up/custom-api/). Shopify WooCommerce Adobe Commerce Salesforce ## 1. Connect store [#1-connect-store] 1. In your BigCommerce test store, open **Settings > Store-level API accounts > Create API Account**. Select **V2/V3 API Token** and name it **Bolt sandbox**. 2. Give it **Carts: modify**, **Orders: modify**, and **Products: read-only** access, then save. Copy the **Access Token** and the store hash from the API path. Keep the token private. 3. Open [Bolt sandbox Integrations](https://merchant-sandbox.boltapp.com/integrations), select **BigCommerce**, and enter that store hash and access token. 4. Select **Connect BigCommerce** and wait for the product catalog to import. The store hash is the value after `/stores/` in BigCommerce's API path. See [BigCommerce API accounts](https://docs.bigcommerce.com/developer/docs/overview/api-fundamentals/api-accounts) for account permissions. ### Validate connection [#validate-connection] Confirm the catalog count increases and your test product is available. Check that you connected the test store, not your live store. If the catalog stays empty, correct the connection before creating a link. ## 2. Configure checkout [#2-configure-checkout] 1. Open [sandbox Payments](https://merchant-sandbox.boltapp.com/payments). Select **Bolt Payments** and configure **Immediate Capture** for the product's currency. 2. Open [Business Information](https://merchant-sandbox.boltapp.com/administration/business-info). Save the shopper support email and phone number under **Support Contact Information**. 3. Open [Checkout settings](https://merchant-sandbox.boltapp.com/checkout/configuration), add your store logo, and publish the change. 4. In BigCommerce, confirm your product is in stock and can ship to your test address. Record its price, currency, and expected shipping/tax amounts. ### Validate settings [#validate-settings] Reopen Payments and confirm Immediate Capture is saved. Confirm the support details and logo belong to the test store. Do not continue if Bolt Payments or the product currency is unavailable for your division. ## 3. Create link [#3-create-link] 1. Open [Checkout Everywhere](https://merchant-sandbox.boltapp.com/checkout-everywhere) and select **Create Campaign**. 2. Name it **Sandbox quickstart** and select **Create campaign**. 3. Inside that campaign, select **Create Links**. Select the campaign and your intended channel. 4. Choose **Direct to checkout**, search for the test product, and select its variant. Leave promotional codes and optional tracking fields empty for this first test. 5. Select **Create Link** and copy the generated URL from its table row. ### Validate link [#validate-link] Open the copied link in a new tab. Confirm the product, variant, quantity, price, currency, branding, and support details. Keep the generated URL unchanged and private while testing. ## 4. Create QR [#4-create-qr] 1. Return to the campaign and open your link's row. 2. Under **Share your link**, select **Create** beside the QR code icon. 3. Select **Download QR code** to save the PNG. 4. Scan it with a phone camera and open the destination. ### Validate QR [#validate-qr] The scan must open the same product and variant as the copied link. Try the QR code at its intended display size; confirm it is readable without zooming. Keep this checkout open for the next step. ## 5. Verify order [#5-verify-order] 1. In the checkout opened from the QR code, enter your test shopper's details and supported shipping address. 2. Check the shipping, tax, currency, and final total against the store's settings. 3. Use the [Bolt Payments approved test card](/getting-started/test-cards#bolt-payments): `4111 1111 1111 1111`, expiration `03/30`, CVV `737`. Submit one payment and save the confirmation details. 4. Open [sandbox Transactions](https://merchant-sandbox.boltapp.com/transactions). Find the purchase and confirm its completed payment, amount, currency, and order number. 5. Open **Orders** in BigCommerce. Find the matching order and compare the product, variant, quantity, shopper, and total. ### Validate purchase [#validate-purchase] You are done when the link and QR code reach the correct checkout, the payment completes, and the matching order exists in BigCommerce. A generated link or checkout confirmation alone is not enough. If payment exists but the store order is missing, investigate that transaction before trying another purchase. ## Test before sharing [#test-before-sharing] Run the [Checkout Everywhere test plan](/products/checkout-everywhere-test-plan). To disable a test link, open its row, select **Deactivate Link**, and confirm; test both the copied URL and QR code afterward. Before a real campaign, configure your production store and Bolt Payments account, generate production links/QR codes, and complete [production readiness](https://help.boltapp.com/developers/production-readiness-guides/). Sandbox links do not establish live payment or fulfillment readiness. Markdown: https://help.boltapp.com/llms/products/checkout-everywhere.md --- # Test your Checkout Everywhere integration > Verify the quickstart, failure recovery, and the experience your app or store controls. Source: https://help.boltapp.com/products/checkout-everywhere-test-plan Run these cases against the [Checkout Everywhere quickstart](/products/checkout-everywhere). Copy the tables into your test report and attach redacted evidence. Every case starts **Not run**. ## Scope and setup [#scope-and-setup] | Scope | What is covered | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Quickstart | Dashboard workflow to connect the store, configure checkout, generate a direct link/QR, and verify a store order. | | Merchant integration | Store catalog, stock, shipping/tax settings, support details, and the installed connector’s order permissions. | | Additional launch work | Live campaign destinations, printed QR assets, fulfillment, receipts, refunds, and reporting. | | Inputs | BigCommerce test store, one available physical product/variant, one sandbox campaign and link, downloaded QR PNG, test phone, and expected shipping/tax totals. | | Evidence | Record build/date, tester, sandbox division, device/browser, case result, order/transaction/seller or link IDs as applicable, and redacted logs/screenshots. Never attach tokens, credentials, or personal banking data. | Use real sandbox execution for Q cases. Label controlled response substitutions, outages, and replay tests separately; mocks do not establish live payment acceptance. Use a fresh order for each payment test and reset store/fixture changes afterward. Do not retry an uncertain payment until it is reconciled. ## 1. Quickstart checkpoints [#1-quickstart-checkpoints] | Test | Input | Steps | Expected output | Result | | ----------------------- | ------------------------------------------- | --------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | ------- | | Q1 · Connect store | BigCommerce test hash/token | 1. Connect the test store with the documented scopes. 2. Locate the product. | Catalog imports from the intended store; no live store connected by mistake. | Not run | | Q2 · Configure checkout | Bolt Payments; store branding/support | 1. Reopen payment settings. 2. Inspect logo and contact details. | Immediate Capture and correct currency saved; store identity/support correct. | Not run | | Q3 · Create link | Sandbox quickstart campaign; chosen variant | 1. Create Direct to checkout link. 2. Open copied URL. | Correct product, variant, quantity, price, and currency; no unexpected promotion. | Not run | | Q4 · Create QR | Downloaded PNG; phone camera | 1. Scan at intended size. 2. Compare with copied link. | Same checkout/product/variant; readable scan and usable mobile destination. | Not run | | Q5 · Verify order | 4111 1111 1111 1111; 03/30; 737 | 1. Pay once from QR destination. 2. Match Bolt transaction and BigCommerce order. | Completed payment and matching product, shopper, amount, currency, and order number in both systems. | Not run | ## 2. Functional integrity [#2-functional-integrity] | Test | Input | Steps | Expected output | Result | | ------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | ------- | | F1 · Disabled destination | Deactivate test link | 1. Open old URL. 2. Scan saved QR. 3. Reactivate and repeat. | Inactive destination cannot complete a new purchase; reactivation restores the intended destination. | Not run | | F2 · Product availability | Make test variant unavailable | 1. Update store availability. 2. Wait for catalog sync. 3. Open link. | Unavailable item cannot be sold; restore inventory after the test. Record observed sync delay. | Not run | | F3 · Store totals | Supported shipping/tax address | 1. Compare final checkout total with store configuration. 2. Complete purchase. | Shipping/tax included consistently in payment and store order. | Not run | | F4 · Payment/order gap | Missing or delayed connector order in a controlled test | 1. Observe paid transaction without store order. 2. Follow recovery process. | Support can identify and reconcile the original transaction; no blind repeat charge. | Not run | | F5 · Cancellation | Close checkout before payment | 1. Start from link. 2. Close. 3. Reopen. | No paid order from abandonment; correct variant remains available when reopened. | Not run | ## 3. Merchant-owned experience [#3-merchant-owned-experience] | Test | Input | Steps | Expected output | Result | | --------------------- | ---------------------------------------------- | -------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | ------- | | E1 · Campaign promise | Actual email/post/QR artwork | 1. Compare copy, price, and variant with destination. | Campaign promise matches what the shopper can buy; no misleading price or product. | Not run | | E2 · QR readability | Intended printed/display size and lighting | 1. Scan with supported phones. 2. Try at normal viewing distance. | Reliable scan without cropping; destination clear and usable. Record asset dimensions. | Not run | | E3 · Branding/support | Checkout logo/contact details | 1. Review on desktop and phone. 2. Follow support route. | Recognizable merchant and reachable support; no test placeholder branding. | Not run | | E4 · Accessibility | Merchant campaign page; keyboard/screen reader | 1. Reach link and read campaign. 2. Use text-link alternative to QR. | Descriptive accessible link and readable layout; QR is not the only way to reach checkout. | Not run | | E5 · Confirmation | Completed order | 1. Read confirmation. 2. Find order/support details. | Clear purchase result, expected delivery, and recovery/support route without requiring a second purchase. | Not run | ## 4. Additional launch requirements [#4-additional-launch-requirements] | Test | Input | Steps | Expected output | Result | | ---------------------- | ---------------------------------------- | --------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ------- | | L1 · Live destinations | Production campaign and final artwork | 1. Confirm production store/payment setup. 2. Scan every final asset. | Each asset uses the intended production URL; no sandbox destinations distributed. | Not run | | L2 · Order lifecycle | Receipt, inventory, fulfillment, refund | 1. Run each supported operation. 2. Reconcile store/Bolt records. | Business effects occur once; refund and inventory behavior agree with merchant policy. | Not run | | L3 · Decline/review | Account-appropriate failure/review cards | 1. Complete sandbox scenarios. 2. Check store and payment states. | No fulfillment on an unapproved/unpaid purchase; merchant-owned recovery is clear. | Not run | ## Completion gates [#completion-gates] | Gate | Required evidence | | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Quickstart verified | All Q cases pass in the real sandbox, including merchant-owned integration work. | | Ready for launch review | All Q, F, E and applicable L cases pass; no required Blocked or Not run cases. Record exclusions with a reason and obtain technical/experience-owner review. | | Failed or blocked case | Record actual output, issue/owner, and retest evidence. A successful payment does not excuse a broken shopper or seller experience. | Use **Pass**, **Fail**, **Blocked**, **Not run**, or **Not applicable** with a reason. A build or mocked test result does not verify production processing, delivery, or settlement. Markdown: https://help.boltapp.com/llms/products/checkout-everywhere-test-plan.md --- # Manage checkout-link campaigns > Expand a verified checkout link into campaigns, bulk links, and reporting. Source: https://help.boltapp.com/api-reference/guides/checkout-everywhere Start with the [Checkout Everywhere quickstart](/products/checkout-everywhere) to connect a store and verify one link, QR code, and order. ## Choose the next task [#choose-the-next-task] | Task | Guide | | -------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | | Organize links by campaign | [Create and manage campaigns](https://help.boltapp.com/products/checkout-everywhere/checkout-links/campaigns/) | | Let shoppers choose product variants | [Product detail page links](https://help.boltapp.com/products/checkout-everywhere/checkout-links/single-link/) | | Create many links | [Bulk links](https://help.boltapp.com/products/checkout-everywhere/checkout-links/bulk-links/) | | Disable or reactivate destinations | [Manage links](https://help.boltapp.com/products/checkout-everywhere/checkout-links/manage-links/) | | Inspect campaign performance | [Analytics](https://help.boltapp.com/products/checkout-everywhere/checkout-links/analytics/) | | Supply products/orders from your own backend | [Custom API integration](https://help.boltapp.com/products/checkout-everywhere/set-up/custom-api/) | ## Verify campaign changes [#verify-campaign-changes] Run the relevant [Checkout Everywhere tests](/products/checkout-everywhere-test-plan) after changing a destination, product, promotion, or QR asset. Match campaign copy to the product, price, variant, and availability at checkout. Test both the copied link and downloaded QR code; they share a destination. Markdown: https://help.boltapp.com/llms/api-reference/guides/checkout-everywhere.md --- # Marketplace Connect Quickstart > Onboard a seller and accept a payment for their product in your marketplace. - **Bolt handles** seller onboarding, checkout, and payment processing. - **Your app owns** seller identities, product assignments, and order records. Source: https://help.boltapp.com/products/connect ## Your integration steps [#your-integration-steps] **This flow:** an existing JavaScript / Node.js marketplace, Bolt Connect SDK 2.0.0 or later, Bolt Payments sandbox, and one seller with zero commission. ## Before you begin [#before-you-begin] Have these ready before the estimated integration time begins. Seller review and account provisioning are separate. | Requirement | What you need | | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Existing marketplace | Seller sign-in, a buyer cart, and saved seller/order records. The examples use Node.js 22 and Express. | | Sandbox access | Ask Bolt to enable Marketplace Connect, Bolt Payments, custom-cart callbacks, and seller splits. Obtain approved sandbox onboarding details for one seller; do not use invented identity or bank information to bypass review. | **Platform support** This guide is written for a JavaScript marketplace. The examples use Node.js and Express. The order, callback, and webhook steps are plain HTTPS calls to Bolt that you can write in any language, but the seller onboarding steps use the Bolt Connect SDK for JavaScript in both the browser and the server; another server language onboards sellers through the [Merchant Onboarding API](/api-merchant-onboarding/). Sellers who take payments on their own storefronts use [Bolt Connect checkout](https://help.boltapp.com/products/bolt-connect/set-up-bolt-connect-sdk/bolt-connect-checkout/). Bolt Connect REST API Python ## 1. Set up [#1-set-up] 1. Open [sandbox API settings](https://merchant-sandbox.boltapp.com/administration/api-keys#apiKeys) for your **marketplace division**. Copy its API key, publishable key, and [signing secret](https://merchant-sandbox.boltapp.com/administration/api-keys#signingSecrets). 2. In [sandbox Payments](https://merchant-sandbox.boltapp.com/payments), configure Bolt Payments for USD with **Immediate Capture**. 3. Save your app's public HTTPS address in [Trusted Domains](https://merchant-sandbox.boltapp.com/administration/trusted-domains). 4. Install the SDK in your existing app and backend package: ```bash title="Terminal" npm install @boltpay/bolt-connect-sdk@^2.0.0 ``` Configure the server using your existing secrets setup: ```bash title="Server environment" BOLT_API_KEY=YOUR_SANDBOX_MARKETPLACE_API_KEY BOLT_PUBLISHABLE_KEY=YOUR_SANDBOX_MARKETPLACE_PUBLISHABLE_KEY BOLT_SIGNING_SECRET=YOUR_SANDBOX_MARKETPLACE_SIGNING_SECRET ``` ### Validate setup [#validate-setup] Confirm the installed version is `2.0.0` or later, the credentials belong to the marketplace division, and that division has Bolt Payments and seller splits enabled. Only the publishable key may reach the browser. ## 2. Onboard seller [#2-onboard-seller] Choose one saved seller in your app. Reuse that seller's stable ID throughout onboarding and checkout. The SDK calls it `marketplaceSubMerchantID`; order payloads call it `seller_id`. Connect these two app-owned adapter operations: | Operation | Required behavior | | ------------------------------------ | ------------------------------------------------------------------------------------------------------------ | | `sellers.current(req)` | Return the authenticated seller's saved `{ id }`. Never accept another seller ID from the browser. | | `sellers.saveOnboarding(id, status)` | Persist the server-retrieved status against that seller. Do not treat the browser success event as approval. | Add this authenticated route to your backend. `requireSeller` is your seller-session middleware: ```javascript title="Seller route" import { CheckoutHelpers } from '@boltpay/bolt-connect-sdk'; app.get('/api/marketplace/seller', requireSeller, async (req, res, next) => { res.set('Cache-Control', 'no-store'); try { const seller = await sellers.current(req); if (!seller) return res.status(404).json({ error: 'Seller not found' }); const info = await CheckoutHelpers.getSellerInfo({ publishableKey: process.env.BOLT_PUBLISHABLE_KEY, marketplaceSubMerchantID: seller.id, isTestEnv: true, }); await sellers.saveOnboarding(seller.id, info.onboardingStatus); res.json({ sellerId: seller.id, status: info.onboardingStatus, marketplaceKey: process.env.BOLT_PUBLISHABLE_KEY }); } catch (error) { next(error); } }); ``` Add an onboarding button and status message to your existing seller settings page: ```html title="Seller settings"

``` Call this function on page mount with your authenticated JSON `request` helper; call its returned cleanup on unmount. The helper rejects non-2xx responses and returns parsed JSON. Keep one seller onboarding session per page. ```javascript title="seller-onboarding.js" import { Onboarding } from '@boltpay/bolt-connect-sdk'; export function mountSellerOnboarding({ request }) { const button = document.querySelector('#seller-onboard'); const refresh = document.querySelector('#seller-refresh'); const status = document.querySelector('#seller-status'); let active = true, configured = false; const show = text => { if (active && status.textContent !== text) status.textContent = text; }; const check = async () => { try { const seller = await request('/api/marketplace/seller'); if (!active) return; if (!configured) { Onboarding.configure({ publishableKey: seller.marketplaceKey, marketplaceSubMerchantID: seller.sellerId, isTestEnv: true }, { onSuccess: () => { if (active) check(); }, onFailure: () => { show('Onboarding could not finish. Check your status before reopening it.'); }, onNotify: () => { show('Follow the instructions in the onboarding form.'); }, }); configured = true; } button.disabled = seller.status !== 'unconfigured'; show(seller.status === 'completed' ? 'Seller onboarding complete.' : seller.status === 'under_review' ? 'Seller is under review. Check again after review.' : 'Set up payments to continue.'); } catch { show('Cannot retrieve seller status. Try checking again.'); } }; button.onclick = async () => { button.disabled = true; try { await Onboarding.start(); } catch { show('Onboarding could not open. Check your status and try again.'); } }; refresh.onclick = check; check(); return () => { active = false; button.onclick = null; refresh.onclick = null; if (configured) Onboarding.end(); }; } ``` ### Validate seller [#validate-seller] 1. Sign in as your test seller and select **Set up payments**. Complete the sandbox form with the details supplied by Bolt. 2. Select **Check onboarding status**. Confirm your backend saves `completed` for that same seller ID. 3. If the result is `under_review`, wait for review. Do not force it to `completed`. A second seller must not receive this seller's record. ## 3. Connect orders [#3-connect-orders] Save one unpaid order for a **$10.00 USD physical item**, free shipping, no tax, and no discounts. Assign the item's `seller_id` from your catalog's seller record, not a browser-supplied value: ```javascript title="Checkout snapshot" const order = { id: 'YOUR_SAVED_ORDER_ID', sellerId: 'YOUR_ONBOARDED_SELLER_ID', payment: null, cart: { order_reference: 'YOUR_SAVED_ORDER_ID', display_id: 'YOUR_SAVED_ORDER_ID', currency: 'USD', total_amount: 1000, tax_amount: 0, items: [{ reference: 'test-mug', name: 'Test mug', type: 'physical', seller_id: 'YOUR_ONBOARDED_SELLER_ID', quantity: 1, unit_price: 1000, total_amount: 1000 }], }, }; ``` Implement the `orders` adapter using your existing store. These are app functions, not SDK methods: | Operation | Required behavior | | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `prepare(req)` | Load the buyer's cart, price it on the server, resolve its seller from your catalog, and save the snapshot above. Reuse an unchanged unpaid snapshot. | | `find(id)` | Return that saved snapshot, or `null`, for signed callbacks. | | `findForShopper(id, req)` | Return it only for its owning buyer, otherwise `null`. | | `recordPayment(id, payment)` | Atomically save `{ reference, status }`. Replays update the same record; reject another reference for a completed payment and prevent stale state from replacing completion. | Keep the seller and cart fixed during checkout. The example below uses the **marketplace publishable key**, with the seller assigned through `seller_splits`. Do not replace that key with the seller's storefront key. Zero commission and fixed totals are fixtures, not a production fee policy. ```javascript title="marketplace-checkout.mjs" import { CheckoutHelpers } from '@boltpay/bolt-connect-sdk'; import { createHmac, timingSafeEqual } from 'node:crypto'; export function createMarketplaceHandlers(orders) { for (const name of ['BOLT_API_KEY', 'BOLT_PUBLISHABLE_KEY', 'BOLT_SIGNING_SECRET']) { if (!process.env[name]) throw new Error(`Set ${name} in your server environment`); } async function bolt(path, body) { const response = await fetch(`https://api-sandbox.boltapp.com${path}`, { method: body ? 'POST' : 'GET', headers: { 'X-API-Key': process.env.BOLT_API_KEY, 'Content-Type': 'application/json' }, ...(body ? { body: JSON.stringify(body) } : {}), }); if (!response.ok) throw new Error(`Bolt returned HTTP ${response.status}`); return response.json(); } function signedBody(req) { if (!Buffer.isBuffer(req.body)) throw new Error('Bolt routes need the raw request body'); const expected = createHmac('sha256', process.env.BOLT_SIGNING_SECRET) .update(req.body).digest(); // During rotation, the incoming secret signs the Pending header. const valid = ['X-Bolt-Hmac-Sha256', 'X-Bolt-Hmac-Sha256-Pending'].some(name => { const received = Buffer.from(req.get(name) || '', 'base64'); return expected.length === received.length && timingSafeEqual(expected, received); }); if (!valid) return null; return JSON.parse(req.body.toString('utf8')); } const handle = fn => async (req, res, next) => { res.set('Cache-Control', 'no-store'); try { await fn(req, res); } catch (error) { next(error); } }; return { createOrder: handle(async (req, res) => { const order = await orders.prepare(req); if (!order || order.cart.order_reference !== order.id || order.payment?.status === 'completed') { return res.status(409).json({ error: 'No unpaid checkout snapshot' }); } if (!order.sellerId || order.cart.items.some(item => item.seller_id !== order.sellerId)) { return res.status(409).json({ error: 'Order must belong to one seller' }); } const seller = await CheckoutHelpers.getSellerInfo({ publishableKey: process.env.BOLT_PUBLISHABLE_KEY, marketplaceSubMerchantID: order.sellerId, isTestEnv: true, }); if (seller.onboardingStatus !== 'completed') { return res.status(409).json({ error: 'Seller onboarding is not complete' }); } const result = await bolt('/v1/merchant/orders', { cart: order.cart, channel: 'browser', seller_splits: [{ seller_id: order.sellerId, amounts: { merchant_subtotal: order.cart.total_amount, discount_total: 0, shipping_total: 0, tax_total: 0, gross_total: order.cart.total_amount }, marketplace_commission_fee: { bps: 0, flat_cents: 0 } }], }); if (!result.token) throw new Error('No order token returned'); res.json({ id: order.id, token: result.token }); }), orderStatus: handle(async (req, res) => { const order = await orders.findForShopper(req.params.id, req); if (!order) return res.status(404).json({ error: 'Order not found' }); res.json({ id: order.id, payment: order.payment }); }), callbacks: handle(async (req, res) => { const message = signedBody(req); if (!message) return res.status(401).json({ error: 'Invalid signature' }); const { event, data } = message; let reply; if (event === 'order.shipping') { reply = { shipping_options: [{ service: 'Free sandbox shipping', cost: 0, reference: 'free', signature: 'free-v1' }] }; } else if (event === 'order.tax') { reply = { tax_result: { subtotal_amount: 0 }, taxes_included: false }; } else if (event === 'order.create') { const cart = data?.order?.cart; const order = cart?.order_reference ? await orders.find(cart.order_reference) : null; if (!order || cart.total_amount?.amount !== order.cart.total_amount || cart.currency?.currency !== order.cart.currency) { return res.status(422).json({ event, status: 'failure', errors: [ { code: 2001, data: [{ reason: 'Unknown or changed order' }] }, ] }); } reply = { display_id: order.cart.display_id }; } else { return res.status(422).json({ event, status: 'failure', error: { code: 2319, message: 'This walkthrough does not enable additional checkout features.', } }); } res.json({ event, status: 'success', data: reply }); }), webhooks: handle(async (req, res) => { const message = signedBody(req); if (!message) return res.status(401).json({ error: 'Invalid signature' }); if (message.object === 'transaction' && message.data?.reference) { const transaction = await bolt(`/v1/merchant/transactions/${encodeURIComponent(message.data.reference)}`); // This walkthrough accepts card purchases only. Credits/refunds are separate transactions. if (transaction.type !== 'cc_payment') return res.json({ status: 'success' }); if (transaction.reference !== message.data.reference) throw new Error('Transaction reference mismatch'); const id = transaction.order?.cart?.order_reference; const order = id ? await orders.find(id) : null; if (!order) throw new Error('No matching local order for this transaction'); if (transaction.amount?.amount !== order.cart.total_amount || transaction.amount?.currency !== order.cart.currency) { throw new Error('Transaction does not match the saved order'); } await orders.recordPayment(id, { reference: transaction.reference, status: transaction.status }); } res.json({ status: 'success' }); }), }; } ``` Register the signed endpoints before your JSON parser or buyer-login middleware. Register the buyer endpoints after your normal parsers; `requireShopper` and `requireCsrf` are your app's protections: ```javascript title="Express routes" import express from 'express'; import { createMarketplaceHandlers } from './marketplace-checkout.mjs'; const marketplace = createMarketplaceHandlers(orders); app.post('/marketplace/callbacks', express.raw({ type: 'application/json', limit: '64kb' }), marketplace.callbacks); app.post('/marketplace/webhooks', express.raw({ type: 'application/json', limit: '64kb' }), marketplace.webhooks); app.post('/api/marketplace/order', requireShopper, requireCsrf, marketplace.createOrder); app.get('/api/marketplace/orders/:id', requireShopper, marketplace.orderStatus); ``` Keep your error handler last: log diagnostic details without secrets, send generic browser errors, and return non-2xx for failed notification processing. Raw-body HMAC checks require the original request bytes. ### Validate orders [#validate-orders] 1. From your authenticated buyer session, request `POST /api/marketplace/order`. Expect an order ID and token, with the seller saved against the order. 2. Confirm the outgoing payload assigns both the item and the split to the onboarded seller. The split is `1000` cents gross with zero commission. 3. Use an unconfigured test seller: expect `409` and no order request to Bolt. As a different buyer, request the saved order status: expect `404`. ## 4. Add checkout [#4-add-checkout] Add a container and status message to your existing buyer cart page: ```html title="Buyer checkout"

``` Mount this code with the **marketplace** publishable key and your authenticated JSON request helper. The helper sends CSRF protection for POST and rejects failed responses. Use this as the only Bolt script loader, with one active checkout per page. Call the returned cleanup on unmount: ```javascript title="marketplace-browser.js" const boltScripts = new Map(); export function mountMarketplaceCheckout({ publishableKey, request }) { const status = document.querySelector('#bolt-status'); const container = document.querySelector('#bolt-checkout'); const cdn = 'https://connect-sandbox.boltapp.com'; let active = true; let timer; function show(message) { if (active && status.textContent !== message) status.textContent = message; } function loadScript(id, src) { if (!boltScripts.has(id)) { const promise = new Promise((resolve, reject) => { const script = document.createElement('script'); script.id = id; script.src = src; script.dataset.publishableKey = publishableKey; script.onload = resolve; script.onerror = () => { script.remove(); boltScripts.delete(id); reject(new Error('Bolt script could not load')); }; document.head.append(script); }); boltScripts.set(id, promise); } return boltScripts.get(id); } async function start() { show('Preparing checkout…'); try { const { id, token } = await request('/api/marketplace/order', { method: 'POST' }); if (!active) return; container.dataset.tid = 'instant-bolt-checkout-button'; const button = document.createElement('object'); button.data = `${cdn}/v1/checkout_button?publishable_key=${encodeURIComponent(publishableKey)}`; button.setAttribute('aria-label', 'Open Bolt Checkout'); container.replaceChildren(button); await loadScript('bolt-track', `${cdn}/track.js`); if (!active) return; await loadScript('bolt-connect', `${cdn}/connect.js`); if (!active) return; BoltCheckout.configure({ orderToken: token }, {}, { success: (_transaction, callback) => { show('Payment submitted. Waiting for server confirmation. Do not pay again.'); callback(); }, }); show(`Order ${id}: ready for checkout.`); async function poll() { try { const order = await request(`/api/marketplace/orders/${encodeURIComponent(id)}`); if (!active) return; if (order.payment) { show(`Order ${id}: ${order.payment.status}. Transaction: ${order.payment.reference}`); } if (['completed', 'failed', 'cancelled', 'rejected_irreversible'].includes(order.payment?.status)) return; timer = setTimeout(poll, 2000); } catch { show(`Cannot confirm order ${id} yet. Check this order in your account or contact support before paying again.`); } } poll(); } catch { if (active) container.replaceChildren(); show('Checkout could not load. Return to your cart and try again.'); } } start(); // Returned immediately, so navigation can cancel pending setup as well as polling. return () => { active = false; clearTimeout(timer); container.replaceChildren(); }; } ``` 1. Open [Merchant Callbacks](https://merchant-sandbox.boltapp.com/administration/api-keys#merchantCallbacks), select **Universal API**, and register your HTTPS origin followed by `/marketplace/callbacks`. 2. Open [Webhooks](https://merchant-sandbox.boltapp.com/administration/webhooks) and register that origin followed by `/marketplace/webhooks`. 3. Open the buyer cart and select the Bolt checkout button. ### Validate checkout [#validate-checkout] Confirm the modal shows the expected item, **$10.00 USD**, free shipping, and zero tax. Confirm signed shipping/tax/order callbacks reach your server without shopper-session or CSRF errors. No private API key or signing secret may appear in the browser. ## 5. Verify payment [#5-verify-payment] 1. Pay once with `4111 1111 1111 1111`, expiration `03/30`, and CVV `737`. 2. Wait for the app's server-verified completed payment, not just the browser success event. 3. Open [sandbox Transactions](https://merchant-sandbox.boltapp.com/transactions). Match the transaction reference, order ID, amount, and currency. 4. In your order storage, confirm the same order still belongs to the seller from step 2 and contains the completed payment. Reload the buyer page and inspect that saved record again. ### Validate completion [#validate-completion] You are done when your marketplace onboards one seller, assigns that seller to the order, and persists a verified card payment. This proves the checkout integration, **not settlement into the seller's bank account**. Confirm payout configuration, fees, and settlement separately before launch. ## Test before launch [#test-before-launch] Run the [Marketplace Connect test plan](/products/connect-test-plan). Your app still needs production fee/tax/shipping rules, concurrency controls, recovery, fulfillment, and refund handling. Confirm seller review, bank-account setup, and payout reconciliation with Bolt before [production readiness](https://help.boltapp.com/developers/production-readiness-guides/). Markdown: https://help.boltapp.com/llms/products/connect.md --- # Test your Marketplace Connect integration > Verify the quickstart, failure recovery, and the experience your app or store controls. Source: https://help.boltapp.com/products/connect-test-plan Run these cases against the [Marketplace Connect quickstart](/products/connect). Copy the tables into your test report and attach redacted evidence. Every case starts **Not run**. ## Scope and setup [#scope-and-setup] | Scope | What is covered | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Quickstart | SDK onboarding/status, single-seller order payload, signed callbacks, card-payment verification, and buyer status display. | | Merchant integration | Seller/session ownership, immutable catalog-to-seller assignment, order persistence, monotonic payment updates, and concurrency controls. | | Additional launch work | Seller eligibility enforcement, real commission/tax/shipping, refunds, fulfillment, payout setup and settlement reconciliation. | | Inputs | Enabled marketplace sandbox; seller A with Bolt-provided onboarding details; unconfigured seller B; buyer A/B; one $10.00 USD item assigned to A, free shipping/tax, zero commission. | | Evidence | Record build/date, tester, sandbox division, device/browser, case result, order/transaction/seller or link IDs as applicable, and redacted logs/screenshots. Never attach tokens, credentials, or personal banking data. | Use real sandbox execution for Q cases. Label controlled response substitutions, outages, and replay tests separately; mocks do not establish live payment acceptance. Use a fresh order for each payment test and reset store/fixture changes afterward. Do not retry an uncertain payment until it is reconciled. ## 1. Quickstart checkpoints [#1-quickstart-checkpoints] | Test | Input | Steps | Expected output | Result | | ------------------- | ----------------------------------- | -------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | ------- | | Q1 · Set up | Marketplace credentials; SDK 2.0.0+ | 1. Confirm division enablement, payments, and Trusted Domains. | Marketplace key and signing secret configured; SDK installed; seller splits enabled. | Not run | | Q2 · Onboard seller | Seller A sandbox details | 1. Complete onboarding. 2. Check status through backend. 3. Inspect seller record. | Same stable seller ID saved with server-retrieved completed status; no approval inferred from browser callback alone. | Not run | | Q3 · Connect orders | A’s item; B not onboarded | 1. Request order for A. 2. Inspect item/split seller IDs and amounts. 3. Repeat for B. | A gets token; both seller IDs match A; gross 1000 and commission 0. B receives 409 before Bolt order creation. | Not run | | Q4 · Add checkout | Buyer page; registered signed URLs | 1. Open checkout. 2. Observe shipping/tax/order callbacks. | Correct item and $10.00 USD total; callbacks authenticate; no secrets in browser. | Not run | | Q5 · Verify payment | 4111 1111 1111 1111; 03/30; 737 | 1. Pay once. 2. Match app/Bolt records. 3. Reload and inspect saved seller. | Verified completed payment remains attached to the original seller/order. No claim of bank settlement. | Not run | ## 2. Functional integrity [#2-functional-integrity] | Test | Input | Steps | Expected output | Result | | ------------------------- | --------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------- | | F1 · Seller isolation | Seller A session; guessed seller B ID | 1. Request onboarding/config as A with B’s ID appended. | Server resolves A from session; never returns or changes B’s record. | Not run | | F2 · Review gate | under\_review or unconfigured status fixture | 1. Request a new buyer order. | 409; no checkout token generated for a seller who has not completed onboarding. | Not run | | F3 · Seller tampering | Browser cart assigns different seller | 1. Tamper with seller ID. 2. Inspect saved/outgoing cart. | Server-owned catalog assignment wins; mixed/mismatched seller carts rejected. | Not run | | F4 · Signed callbacks | Invalid signature; rotation headers; altered order amount | 1. Send invalid raw signature. 2. Send signed order.create with wrong total. 3. Send a shipping callback with a valid pending signature and invalid primary signature, then reverse them. | Either matching signature accepts the callback; 401 when neither matches; failure for changed order; no payment or order mutation. | Not run | | F5 · Verified transaction | Controlled wrong reference/order/amount/currency; credit/refund | 1. Replay a signed transaction notification against each response. | Mismatch never marks order paid; non-card-purchase transactions ignored by this sample. | Not run | | F6 · Replay/restart | Same signed payment notification twice | 1. Deliver twice. 2. Restart backend. 3. Read order. | One persisted payment reference and original seller assignment; no duplicate business effects. | Not run | | F7 · Buyer isolation | Buyer B; invalid CSRF | 1. Read A’s order as B. 2. Request token without valid CSRF/session. | 404 or authorization rejection; no unauthorized token or order disclosure. | Not run | ## 3. Merchant-owned experience [#3-merchant-owned-experience] | Test | Input | Steps | Expected output | Result | | ---------------------- | ---------------------------------------- | -------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | ------- | | E1 · Seller progress | Onboarding success/review/failure | 1. Exercise each state. 2. Check status and retry controls. | Plain-language state and safe next action; under review is not presented as ready. | Not run | | E2 · Buyer clarity | Single-seller cart | 1. Review product, merchant identity, totals, and confirmation. | Correct seller/product throughout; payment status is server-backed. | Not run | | E3 · Navigation | Delayed seller/status/script response | 1. Leave during onboarding/setup. 2. Return. | No late UI updates or duplicate active checkout; seller form cleans up and can reopen safely. | Not run | | E4 · Access/mobile | Keyboard, screen reader, narrow viewport | 1. Open/close seller form and buyer checkout. 2. Verify merchant controls. | Usable focus, status announcements, and recovery controls; record Bolt-owned UI issues separately. | Not run | | E5 · Uncertain payment | Fail buyer status endpoint after payment | 1. Observe error. 2. Recover the existing order. | No false success, raw exception, or invitation to blindly pay again. | Not run | ## 4. Additional launch requirements [#4-additional-launch-requirements] | Test | Input | Steps | Expected output | Result | | -------------------------- | -------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | ------- | | L1 · Fees/allocations | Real commissions, shipping, discounts, multi-seller if offered | 1. Run offered combinations. 2. Reconcile allocations. | Seller and marketplace amounts match the approved fee policy; fixture-only sample is not launch evidence. | Not run | | L2 · Payout settlement | Approved bank setup and agreed payout schedule | 1. Confirm seller eligibility and payout configuration with Bolt. 2. Reconcile actual settlement evidence. | Expected seller receives the expected net amount; payment completion alone does not pass this test. | Not run | | L3 · Lifecycle/concurrency | Two simultaneous checkouts; refund/void; fulfillment | 1. Exercise each supported operation. 2. Reconcile buyer, seller, and marketplace records. | No duplicate charges/fulfillment; refunds and fees settle to the correct parties; merchant recovery works. | Not run | ## Completion gates [#completion-gates] | Gate | Required evidence | | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Quickstart verified | All Q cases pass in the real sandbox, including merchant-owned integration work. | | Ready for launch review | All Q, F, E and applicable L cases pass; no required Blocked or Not run cases. Record exclusions with a reason and obtain technical/experience-owner review. | | Failed or blocked case | Record actual output, issue/owner, and retest evidence. A successful payment does not excuse a broken shopper or seller experience. | Use **Pass**, **Fail**, **Blocked**, **Not run**, or **Not applicable** with a reason. A build or mocked test result does not verify production processing, delivery, or settlement. Markdown: https://help.boltapp.com/llms/products/connect-test-plan.md --- # Install Bolt Connect SDK > Seller onboarding with Bolt Connect APIs. Source: https://help.boltapp.com/api-reference/guides/connect-onboarding For the complete integration, start with the [Marketplace Connect quickstart](/products/connect). Use this page to look up SDK methods and options. Install the SDK using one of the following methods: npm pnpm yarn bun ```bash npm install @boltpay/bolt-connect-sdk ``` ```bash pnpm add @boltpay/bolt-connect-sdk ``` ```bash yarn add @boltpay/bolt-connect-sdk ``` ```bash bun add @boltpay/bolt-connect-sdk ``` #### Direct download from npm registry [#direct-download-from-npm-registry] Alternatively, you can download the package directly from the npm registry: **Package info**: `https://registry.npmjs.org/@boltpay/bolt-connect-sdk` ## Quick Start [#quick-start] Follow these steps to onboard sellers using the Bolt Connect SDK. The SDK ships TypeScript definitions, so types resolve automatically in a TypeScript project. The examples below are plain JavaScript and run unchanged in both. ### Step 1: Import the SDK [#step-1-import-the-sdk] ```javascript title="JavaScript" import { Onboarding, CheckoutHelpers } from '@boltpay/bolt-connect-sdk' ``` ### Step 2: Configure Onboarding [#step-2-configure-onboarding] #### API Reference [#api-reference] ```typescript title="Signature" Onboarding.configure(clientProps, eventHandlers): void ``` **Description**\ Set up the onboarding configuration with your credentials and event callbacks. **Parameters** | Parameter | Type | Required | Description | | ----------------------------- | ---------------------------------------- | -------- | ----------------------------------------------------------------------------- | | **clientProps (ClientProps)** | | | Object containing client-specific configuration parameters. | | `publishableKey` | `string` | ✅ | Your Bolt publishable key. You can find this value in the Merchant Dashboard. | | `marketplaceSubMerchantID` | `string` | ✅ | The identifier of the seller in your platform. | | `isTestEnv` | `boolean` | ❌ | `true` for sandbox; `false` for production (default `false`). | | **eventHandlers** | | | Object containing optional callback functions. | | `onSuccess` | `() => void` | ❌ | Triggered when onboarding completes successfully. | | `onFailure` | `(payload: { message: string }) => void` | ❌ | Triggered when onboarding fails. | | `onNotify` | `(payload: { message: string }) => void` | ❌ | Triggered for status updates during onboarding. | **Example** ```javascript title="JavaScript" Onboarding.configure( { publishableKey: 'your-publishable-key', marketplaceSubMerchantID: 'your-sub-merchant-id', isTestEnv: true, // Set to false for production }, { onSuccess: () => { console.log('Onboarding completed successfully!') // Handle successful onboarding (e.g., redirect, show success message) }, onFailure: payload => { console.error('Onboarding failed:', payload.message) // Handle onboarding failure (e.g., show error message) }, onNotify: payload => { console.log('Onboarding notification:', payload.message) // Handle notifications during onboarding process }, } ) ``` ### Step 3: Start Onboarding [#step-3-start-onboarding] #### API Reference [#api-reference-1] ```typescript title="Signature" Onboarding.start(): Promise ``` **Description**\ Starts the onboarding form as a full-page iframe within your site. **Example** ```javascript title="JavaScript" // Start the onboarding process const startOnboarding = async () => { try { await Onboarding.start() // The iframe will be displayed full-screen } catch (error) { console.error('Failed to start onboarding:', error) } } // Attach to a button click document .getElementById('onboarding-button') ?.addEventListener('click', startOnboarding) ``` ### Step 4: Get Seller Information and Onboarding Status [#step-4-get-seller-information-and-onboarding-status] #### API Reference [#api-reference-2] ```typescript title="Signature" CheckoutHelpers.getSellerInfo(params): Promise ``` **Description**\ Fetches seller information and current onboarding status. **Parameters** | Parameter | Type | Required | Description | | -------------------------- | --------- | -------- | ----------------------------------------------------------------------------- | | **GetSellerInfoParams** | | | Parameters used to retrieve seller information. | | `publishableKey` | `string` | ✅ | Your Bolt publishable key. You can find this value in the Merchant Dashboard. | | `marketplaceSubMerchantID` | `string` | ✅ | The identifier of the seller in your platform. | | `isTestEnv` | `boolean` | ❌ | `true` for sandbox; `false` for production (default `false`). | **Returns** | Name | Type | Description | | ---------------------- | -------- | ------------------------------------------------------------------- | | **SellerInfoResponse** | | The response object containing seller information. | | `publishableKey` | `string` | The publishable key of the seller, to be used to initiate checkout. | | `onboardingStatus` | `string` | The onboarding status: `completed`, `unconfigured`, `under_review`. | **Example** ```javascript title="JavaScript" // Fetch seller information const getSellerInfo = async () => { try { const sellerInfo = await CheckoutHelpers.getSellerInfo({ publishableKey: 'your-publishable-key', marketplaceSubMerchantID: 'your-sub-merchant-id', isTestEnv: true // Set to false for production }) console.log('Seller info:', sellerInfo) // sellerInfo.onboardingStatus: 'completed' | 'unconfigured' | 'under_review' } catch (error) { console.error('Failed to fetch seller info:', error) } } ``` ## Caching [#caching] The SDK includes intelligent caching to improve performance: * Seller information is cached for improving performance. ## Error Handling [#error-handling] The SDK provides comprehensive error handling: ```javascript title="JavaScript" try { await Onboarding.start() } catch (error) { console.error('Onboarding error:', error) } try { const sellerInfo = await CheckoutHelpers.getSellerInfo(params) } catch (error) { console.error('API error:', error) } ``` ## Browser Support [#browser-support] The SDK supports all modern browsers with ES6+ support: * Chrome 60+ * Firefox 55+ * Safari 12+ * Edge 79+ Markdown: https://help.boltapp.com/llms/api-reference/guides/connect-onboarding.md --- # Transfer funds to a seller > Pay sellers on your own schedule with the Transfers API, independently of any order. Source: https://help.boltapp.com/api-reference/guides/transfers The Transfers API moves funds between your marketplace account and a seller's, without tying the movement to an order or a payment. Use it to pay sellers after fulfilment, after a returns window, or on a weekly payout run. Sellers are identified by the seller ID you registered with Bolt when you onboarded them, the same value you pass as `X-Bolt-Connect-Seller-Id` and in `seller_splits`. For the full endpoint reference, see [Create a transfer](/api-reference/transfers/transfers/createtransfer) and the rest of the Transfers reference. ## When to use Transfers [#when-to-use-transfers] Bolt offers two ways to move money to a seller. They differ in what triggers the movement. | | Seller splits at checkout | Transfers API | | ----------- | ---------------------------------------- | ---------------------------------------------------- | | Trigger | A shopper payment | Your own API call, at any time | | Amount | Fixed at authorization, tied to one cart | Any amount within the seller's per-transfer limit | | Timing | Follows the order's capture | Whenever you decide to pay | | Use it when | The split is part of paying for a cart | The payment to the seller is independent of any cart | Use seller splits when a seller's share is decided at checkout. Use Transfers when you decide later what a seller is owed. ## Before you begin [#before-you-begin] Three things must be true before a transfer is accepted. All three are checked on every create, and none are self-serve. | Requirement | What you need | | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Transfers enabled for the seller | Enable transfers for each seller in the Bolt Merchant Dashboard, with an optional per-transfer limit. Permissions are **directional**: enabling payouts to a seller does not enable collections from that seller. Ask for both if you need both. | | Both accounts set up for payments | An account Bolt cannot pay out cannot receive a transfer. | | A shared settlement currency | There is no currency conversion. A transfer must be in a currency both sides settle in. | Enabling transfers for a new seller is a Bolt-side step with a lead time. Raise the sellers you need early. You can build and test the integration while provisioning is in progress. ## Choose a direction [#choose-a-direction] Your marketplace account is always one side of a transfer. The `direction` field says which way the money moves. | `direction` | Money moves | Use it for | | ------------ | ------------------------------- | ------------------------------------------------------ | | `payout` | From your account to the seller | Paying a seller what they are owed | | `collection` | From the seller to your account | Recovering an overpayment or a chargeback you absorbed | `direction` is required and has no default. An implied direction on a money movement is a costly thing to get wrong, so the API asks you to say it every time. ## Pay a seller [#pay-a-seller] Send the direction, the seller ID, the amount, and your own `reference`. ```bash title="Request" curl -X POST https://api-sandbox.boltapp.com/v3/transfers \ -H "X-API-Key: $BOLT_API_KEY" \ -H "X-Publishable-Key: $BOLT_PUBLISHABLE_KEY" \ -H "Content-Type: application/json" \ -d '{ "direction": "payout", "seller_id": "seller_4471", "amount": { "units": 4000, "currency": "USD" }, "reference": "payout-2026-10-14-seller4471", "transfer_group": "PAYOUT_2026_10_W2", "description": "October payout, orders 95 to 112" }' ``` ```json title="Response" { "id": "trf_9f8e7d6c5b", "created_at": "2026-10-14T17:32:28Z", "updated_at": "2026-10-14T17:32:28Z", "status": "pending", "direction": "payout", "seller_id": "seller_4471", "reference": "payout-2026-10-14-seller4471", "amount": { "units": 4000, "currency": "USD" }, "amount_reversed": { "units": 0, "currency": "USD" }, "reversed": false, "description": "October payout, orders 95 to 112", "transfer_group": "PAYOUT_2026_10_W2", "payout_reference": null, "failure_reason": null, "reversals": [], "metadata": {} } ``` **Expected result:** a `200` with `status: "pending"`. The funds are committed against your balance and are waiting for the seller's next payout. The seller does not have the money yet. `reference` is required, and it is what stops a retry from paying a seller twice. Reusing a `reference` returns the transfer you already created instead of creating a second one, and that protection does not expire. Derive it from something stable in your own system, such as the payout run and the seller: ```javascript title="Node.js" const reference = `payout-${payoutRunId}-${sellerId}` ``` If a request times out and you do not know whether it landed, retry with the same `reference`, or look the transfer up: ```bash title="Terminal" curl -G https://api-sandbox.boltapp.com/v3/transfers \ -H "X-API-Key: $BOLT_API_KEY" \ -H "X-Publishable-Key: $BOLT_PUBLISHABLE_KEY" \ -d reference=payout-2026-10-14-seller4471 ``` The `Idempotency-Key` header also works on every `POST` and `PATCH`, but it expires after 24 hours. Use `reference` as the real safeguard. A transfer passes through these states. | Status | What it means | Can cancel | Can reverse | | ------------ | -------------------------------------------------------------------------------- | ---------- | ----------- | | `pending` | Accepted and committed against your balance, waiting for the next payout. | Yes | No | | `in_transit` | Included in a payout and sent to the bank. | No | No | | `paid` | The funds have arrived. | No | Yes | | `failed` | The bank rejected the payout and the funds were returned to your balance. Final. | No | No | | `cancelled` | Withdrawn before it was sent. Final. | No | No | Subscribe to webhooks rather than polling. Payout timing follows the **seller's** payout cycle, not yours, so do not assume a fixed delay. | Event | Fires when | | -------------------- | ---------------------------------------------------- | | `transfer_created` | A transfer is created. | | `transfer_paid` | The funds have arrived. | | `transfer_failed` | The payout was rejected and the funds were returned. | | `transfer_cancelled` | A transfer is cancelled before it is sent. | | `transfer_reversed` | A reversal is created, full or partial. | | `transfer_updated` | The `description` or `metadata` changed. | **Expected result:** a `transfer_paid` delivery carrying the transfer with `status: "paid"` and a `payout_reference` you can reconcile against your statement. A `200` on create means the transfer was accepted, not that the seller was paid. Marking a seller paid on the create response will overstate what you have sent them. Wait for `transfer_paid`. A transfer can reach `failed` if the bank rejects the payout, for example when the seller's account has been closed. The funds return to your balance and `failure_reason` says what happened. ```json title="Response" { "id": "trf_9f8e7d6c5b", "status": "failed", "direction": "payout", "seller_id": "seller_4471", "amount": { "units": 4000, "currency": "USD" }, "payout_reference": "po_20261015_0031", "failure_reason": "account_closed" } ``` `failed` is final. There is nothing to reverse, because nothing reached the seller. Once the underlying problem is resolved, create a new transfer with a new `reference`. Reusing the old `reference` returns the failed transfer rather than creating a new one. Treat the set of `failure_reason` values as open, and handle an unrecognized value as a generic failure. **Expected result:** a `transfer_failed` delivery, and your balance restored by the transfer amount. ## Group a payout run [#group-a-payout-run] Set the same `transfer_group` on every transfer in a run, then retrieve the whole run in one call. `transfer_group` cannot be changed after creation. ```bash title="Request" curl -G https://api-sandbox.boltapp.com/v3/transfers \ -H "X-API-Key: $BOLT_API_KEY" \ -H "X-Publishable-Key: $BOLT_PUBLISHABLE_KEY" \ -d transfer_group=PAYOUT_2026_10_W2 \ -d page_size=100 ``` ```json title="Response" { "transfers": [ { "id": "trf_9f8e7d6c5b", "status": "paid", "seller_id": "seller_4471" }, { "id": "trf_1c2d3e4f5a", "status": "pending", "seller_id": "seller_5512" } ], "pagination": { "page": 1, "page_size": 100, "total_count": 2 } } ``` Each entry is a complete transfer object. The response above is shortened for readability. Create one transfer per seller. There is no batch create endpoint. ## Take money back [#take-money-back] How you undo a transfer depends on where it is in its lifecycle. ### Cancel a transfer that has not been sent [#cancel-a-transfer-that-has-not-been-sent] While a transfer is `pending`, cancel it. This releases the hold on your balance and nothing ever reaches the seller. ```bash title="Request" curl -X POST https://api-sandbox.boltapp.com/v3/transfers/trf_1c2d3e4f5a/cancel \ -H "X-API-Key: $BOLT_API_KEY" \ -H "X-Publishable-Key: $BOLT_PUBLISHABLE_KEY" ``` **Expected result:** a `200` with `status: "cancelled"`. If the transfer has already moved on, you get `transfer_not_cancelable` and the message names the current status. Once a transfer is `paid`, reverse it instead. ### Reverse a transfer that has been paid [#reverse-a-transfer-that-has-been-paid] Once a transfer is `paid`, create a reversal. Omit `amount` to reverse the full remaining amount, or supply one to reverse part of it. You can reverse in several steps, up to the transfer's unreversed remainder. ```bash title="Request" curl -X POST https://api-sandbox.boltapp.com/v3/transfers/trf_9f8e7d6c5b/reversals \ -H "X-API-Key: $BOLT_API_KEY" \ -H "X-Publishable-Key: $BOLT_PUBLISHABLE_KEY" \ -H "Content-Type: application/json" \ -d '{ "amount": { "units": 1500, "currency": "USD" }, "reference": "reversal-return-8831", "description": "Partial return, 1 of 3 items" }' ``` ```json title="Response" { "id": "trr_4a3b2c1d0e", "created_at": "2026-10-20T11:02:44Z", "transfer_id": "trf_9f8e7d6c5b", "status": "pending", "amount": { "units": 1500, "currency": "USD" }, "reference": "reversal-return-8831", "description": "Partial return, 1 of 3 items", "payout_reference": null, "metadata": {} } ``` **Expected result:** a `200` with the reversal in `pending`. A reversal is a money movement in its own right: it goes out on a payout and settles like any transfer, so it is not instantaneous. A transfer in `in_transit` can be neither cancelled nor reversed. Wait for `paid`. A reversal moves money between you and the seller. It does not refund the shopper. Refunding a shopper is a credit against the original transaction, and the two are separate operations. A customer return usually needs both. ## Collect from a seller [#collect-from-a-seller] To bring funds back from a seller outside of a specific transfer, for example to recover an overpayment, create a transfer with `direction` set to `collection`. ```bash title="Request" curl -X POST https://api-sandbox.boltapp.com/v3/transfers \ -H "X-API-Key: $BOLT_API_KEY" \ -H "X-Publishable-Key: $BOLT_PUBLISHABLE_KEY" \ -H "Content-Type: application/json" \ -d '{ "direction": "collection", "seller_id": "seller_4471", "amount": { "units": 2500, "currency": "USD" }, "reference": "recovery-2026-10-14-seller4471" }' ``` A collection follows the same lifecycle as a payout, in the opposite direction. Collections need their own permission. Enabling payouts to a seller does not enable collections from that seller. ## Handle errors [#handle-errors] Validation failures return `invalid_input_parameter` with a `field` naming the problem. Branch on `.tag` and `field`, not on the message text. | `field` | What to fix | | ---------------------- | -------------------------------------------------------------------------------------------------------- | | `transfer_permissions` | Transfers are not enabled for this seller in this direction. Enable them in the Bolt Merchant Dashboard. | | `seller_id` | The seller is not one of yours, cannot be paid out, or does not settle in the currency. | | `direction` | Missing, or not `payout` or `collection`. | | `amount` | The amount is not greater than zero, or is above the per-transfer limit set for the seller. | | `amount.currency` | The currency is not supported, or is not one both sides settle in. | | `reference` | Missing, or longer than 255 characters. | State errors use their own tags. | `.tag` | What it means | | ----------------------------------- | ------------------------------------------------------------------------------------------- | | `transfer_not_cancelable` | The transfer is past `pending`. Reverse it instead once it is `paid`. | | `transfer_not_reversible` | The transfer is not `paid`. Cancel it, or wait. A `failed` transfer has nothing to reverse. | | `transfer_already_reversed` | The transfer has been reversed in full. | | `reversal_amount_exceeds_remaining` | The reversal is larger than the unreversed remainder. | **Retry only `409`, `429`, and `5xx`**, with backoff and the original `reference`. Every `400`, `401`, `403`, `404`, and `422` is deterministic: an identical retry fails in exactly the same way. ## Next steps [#next-steps] Every endpoint, field, and error in the Transfers API. Onboard a seller and take your first marketplace payment. How the `Idempotency-Key` header behaves across Bolt APIs. Check the `X-Bolt-Hmac-Sha256` signature before acting on a delivery. Markdown: https://help.boltapp.com/llms/api-reference/guides/transfers.md --- # Sell subscriptions with Bolt Subscriptions > Generate checkout links for recurring billing and digital product subscriptions. Source: https://help.boltapp.com/products/subscriptions Use **Bolt Subscriptions** to let shoppers purchase or subscribe to digital products through hosted checkout links. ## Who this is for [#who-this-is-for] Merchants selling digital goods, memberships, or recurring plans who want Bolt-hosted subscription checkout without building billing infrastructure. ## How it works [#how-it-works] 1. Create subscription products and plans via the Subscriptions API. 2. Bolt returns checkout links for each product variant. 3. Shoppers complete purchase or subscribe through the hosted link. 4. Manage subscriptions and view transactions in the Merchant Dashboard. ## How prices are set [#how-prices-are-set] Bolt Subscriptions is catalog-shaped. You create a product with a fixed `unit_price`, give it one or more plans, and each plan sets a billing frequency. The price belongs to the product rather than the plan, so a monthly plan and a yearly plan on the same product bill the same amount. Bolt returns a checkout link per plan, and every shopper who buys through it pays that price, less any discount you attach to the plan. Changing a product's `unit_price` later sets the price for new subscribers only: each renewal is billed from a copy of that subscriber's last successful order, so existing subscribers keep the amount they signed up at. That fits a published price list: tiers, memberships sold at a set rate, digital plans. The documented API has no field that sets a different recurring amount for one shopper, so a rate that varies with seat count, a first period prorated to the signup date, or usage billed in arrears does not fit the catalog. `initial_period` sets how long the first period lasts, not what it costs. ### If your price is computed [#if-your-price-is-computed] You can still take recurring payments through Bolt. Charge the first period through [Custom Cart](/custom-cart/overview), which accepts any cart total. Then charge each renewal against the shopper's saved card using the `merchant_credit_card_authorization_recharge` flow in [Delayed capture](/api-reference/guides/delayed-capture), passing the `consumer_id` and `credit_card_id` from the first transaction together with a cart carrying that period's amount. Set `processing_initiator` to `stored_merchant_initiated` on each renewal charge. That is what marks the payment as merchant-initiated against a stored credential, and it is not inferred from the request: leave it out and the charge goes to the networks unflagged, which costs you authorization rates and the chargeback protection a correctly flagged recurring payment carries. Bolt finds the original transaction to chain it to, so you do not have to pass one. Bolt takes every payment. You own the schedule and the retry policy, and you do not get the rest of Bolt Subscriptions: plan management, the renewal engine, and subscription records in your dashboard. ## Get started [#get-started] 1. [Create a merchant account](https://merchant.boltapp.com/onboarding/get-started) 2. Copy API and publishable keys from **Administration** > **API** in the dashboard 3. Follow the [Subscriptions integration guide](/api-reference/guides/subscriptions) 4. Create products with `POST /v1/subscriptions/products` ## API and configuration [#api-and-configuration] | Task | Link | | --------------------- | ----------------------------------------------------------------------------------------------------- | | Implementation guide | [Guide](/api-reference/guides/subscriptions) | | Subscriptions API | [API reference](/api-reference/endpoints/subscriptions/getsubscriptions) | | Manage subscriptions | [Subscriptions setup](/products/bolt-subscriptions/subscriptions-setup/) | | Shopper notifications | [Confirmation emails](/products/bolt-subscriptions/subscriptions-setup/#review-shopper-notifications) | ## Verify in dashboard [#verify-in-dashboard] After a test subscription, open **Transactions** in the [Merchant Dashboard](https://merchant.boltapp.com) and confirm the payment appears with the expected status. ## Related [#related] * [Checkout Everywhere](/products/checkout-everywhere): one-time checkout links * [Bolt Checkout](/products/checkout): storefront checkout replacement * [Webhooks](/api-reference/guides/webhooks): subscription events * [API keys](/getting-started/api-keys) Markdown: https://help.boltapp.com/llms/products/subscriptions.md --- # Bolt Subscriptions Setup > Set up Bolt Subscriptions in your Merchant Dashboard. Source: https://help.boltapp.com/products/bolt-subscriptions/subscriptions-setup Use Bolt Subscriptions to sell digital goods on a recurring basis, from an external storefront or inside your app or game. ## Prerequisites [#prerequisites] Create a [Bolt Merchant account](https://merchant.boltapp.com/onboarding/get-started) and finish onboarding first. ## Quickstart [#quickstart] To get started with Bolt Subscriptions, complete the following steps: 1. [Create and activate your Bolt Merchant account](#create-and-activate-your-bolt-merchant-account) 2. [Complete Merchant Dashboard setup](#complete-merchant-dashboard-setup) 3. [Configure products and plans](#configure-products-and-plans) 4. [Get checkout links](#get-checkout-links) 5. [Manage subscriptions](#manage-subscriptions) 6. [Configure failed payment recovery](#configure-failed-payment-recovery) 7. [Review shopper notifications](#review-shopper-notifications) 8. [Configure a tax provider](#configure-a-tax-provider) ## Create and Activate Your Bolt Merchant Account [#create-and-activate-your-bolt-merchant-account] 1. Create a [Bolt Merchant account](https://merchant.boltapp.com/onboarding/get-started). 2. Provide your company **Website URL**. 3. Provide your company and personal details. 4. Accept the terms and submit the form. 5. Open the activation email from Bolt and follow the link to complete your setup. After activation, sign in to your [Bolt Merchant account](https://merchant.boltapp.com/). ## Complete Merchant Dashboard Setup [#complete-merchant-dashboard-setup] Sign in to your [Bolt Merchant account](https://merchant.boltapp.com/) and finish the remaining setup tasks in the dashboard. Dashboard setup tasks ### Upload a logo [#upload-a-logo] 1. Go to **Checkout** settings. 2. Under **Configuration**, click **Browse Files** to upload your logo. Company logo upload ### Provide Customer Support Contact [#provide-customer-support-contact] 1. Go to **Administration** settings. 2. Select **Business Info**. 3. Find the **Shopper Support Contact Information** section and enter your support contact info. Shopper support contact ## Configure Products and Plans [#configure-products-and-plans] To enable a digital subscription: 1. In the **Commerce** section of the left navigation, click **Subscriptions**. 2. Click the **Products** tab, then click **Create Product**. 3. Fill in the product form and select at least one plan. Create product button The form collects information about the product you will sell and the subscription plans you want to offer. Each product can have multiple plans (for example, monthly and yearly), but at least one plan is required. Product form Plan example You can choose weekly, monthly, or yearly subscriptions. Changing the Frequency value allows extra flexibility; for example, `Frequency = 3` and `Unit = monthly` yields a quarterly subscription. Quarterly plan example ## Get Checkout Links [#get-checkout-links] After creating a product: 1. Open the **Product** view to see all plans associated with it. 2. Navigate to each **Plan** to get the Bolt checkout link for the subscription. 3. Add those checkout links to buttons wherever shoppers should subscribe. Product view Checkout link on plan ## Manage Subscriptions [#manage-subscriptions] Subscriptions appear on the **Subscriptions** tab. The table shows active and canceled subscriptions across products. * Use the search box to find a specific subscription. * Click a subscription to view details, orders, and the next scheduled charge. Subscriptions table The subscription view shows all orders associated with the subscription, including the next scheduled charge. Subscription details ### Cancel a Subscription [#cancel-a-subscription] 1. Open the subscription details. 2. Click **Cancel Subscription** and confirm. ## Configure Failed Payment Recovery [#configure-failed-payment-recovery] Bolt automatically retries failed renewal payments and reminds subscribers before each renewal. Configure this from the **Payment Recovery** tab: 1. In the **Commerce** section of the left navigation, click **Subscriptions**. 2. Click the **Payment Recovery** tab . 3. Set **Retry schedule (hours after failure)**: a comma-separated list of hour offsets from the initial payment failure, for example `24, 72, 168, 336`. Up to 10 entries, strictly increasing, each at most 2160 hours (90 days). 4. Set **Renewal reminder lead days**: a comma-separated list of days before renewal to email subscribers, for example `30, 7, 3`. Up to 10 entries, each at most 90 days, no duplicates. 5. Choose **After final retry fails**: **Cancel subscription** or **Pause subscription**, the action Bolt takes once every retry in the schedule has failed. 6. Turn on **SMS notifications** to also text subscribers when a renewal payment fails, in addition to email. 7. Click **Save Settings**. If you haven't configured these settings yet, Bolt applies defaults: retries at 24, 72, 168, and 336 hours after failure, reminders at 30, 7, and 3 days before renewal, and cancellation as the final action. ## Review Shopper Notifications [#review-shopper-notifications] Bolt sends transactional emails for subscription events (created, renewed, canceled). See examples below. ### Subscription Created Email [#subscription-created-email] Subscription created email ### Subscription Renew Email [#subscription-renew-email] Subscription renew email ### Subscription Payment Update Email [#subscription-payment-update-email] Subscription payment update email When a shopper updates their payment method using the link in this email, Bolt retries the failed payment immediately. ### Subscription Cancel Email [#subscription-cancel-email] Subscription cancel email ## Configure a Tax Provider [#configure-a-tax-provider] The instructions below apply only for digital goods and subscriptions: 1. In your [Bolt Merchant account](https://merchant.boltapp.com/), go to **Integrations**. Integrations tab 2. In **Tax Compliance Provider**, choose your provider and enter provider credentials: * **TaxJar**: API Key * **Avalara**: Account ID and License Key Tax provider configuration 3. Click **Update credentials** to save and activate tax calculations. Ensure your account with the selected provider is active and properly configured before connecting. ## Related [#related] * [Bolt Subscriptions](/products/subscriptions) * [Subscriptions integration guide](/api-reference/guides/subscriptions) * [API keys](/getting-started/api-keys) Markdown: https://help.boltapp.com/llms/products/bolt-subscriptions/subscriptions-setup.md --- # Steps to Implement Bolt Subscriptions > Subscriptions API implementation guide. Source: https://help.boltapp.com/api-reference/guides/subscriptions ## 1. Get Keys [#1-get-keys] 1. Log in to the [sandbox Merchant Dashboard](https://merchant-sandbox.boltapp.com/) for the sandbox keys these examples use, or the [Bolt Merchant Dashboard](https://merchant.boltapp.com/) for production keys. See [Environments](/api-reference/environments) 2. Navigate to **Administration** → **API**. 3. Copy the following: * Api Key * Publishable Key * The publishable key is a long string of lower and upper case letters and numbers that consists of three sections. get keys Every endpoint in this guide requires **both** keys, including the ones you call from your server. They do different jobs. The API key identifies your merchant account; the publishable key selects which **division** of that account the call operates on. It is also not only a credential: the product endpoints copy it into the checkout links they return, so it is data that ends up inside the response. Authentication fails if it is missing, and equally if it belongs to a different merchant or to a division that is not active. The examples below use the sandbox host, `https://api-sandbox.boltapp.com`. Switch to `https://api.boltapp.com` when you go live, and use the keys that belong to the environment you are calling. A key from the wrong environment fails authentication. If either key is missing or invalid, these endpoints return `401` with error code `63` and the message `Unauthorized`. The response does not say which key was at fault, so check both. Go to the Integrations tab on Merchant Dash to find more information on how to use Bolt Subscriptions APIs We leverage Bolt Subscriptions APIs to generate checkout links that allow shoppers to purchase or subscribe to digital products seamlessly. ## 2. Creating a New Product [#2-creating-a-new-product] ```bash title="Request" curl --request POST 'https://api-sandbox.boltapp.com/v1/subscriptions/products' \ --header 'Content-Type: application/json' \ --header 'X-API-Key: YOUR_API_KEY' \ --header 'X-Publishable-Key: YOUR_PUBLISHABLE_KEY' \ --data '{ "name": "Bolt+ Membership", "description": "Get All the money", "brand": "Bolt", "sku": "BOLTPLUS", "images": [ "https://user-images.githubusercontent.com/40345645/229246491-bb572a18-7e09-4457-ac20-b51e0fb05923.png" ], "plans": [ { "sku": "BOLTPLUSMIN", "name": "Bolt+ membership - monthly", "frequency": 3, "frequency_unit": "month", "unit_price": 999, "images": [ "https://user-images.githubusercontent.com/40345645/229246491-bb572a18-7e09-4457-ac20-b51e0fb05923.png" ] }, { "sku": "BOLTPLUSWEEK", "name": "Bolt+ membership - 1 year", "frequency": 1, "frequency_unit": "year", "unit_price": 6999, "images": [ "https://user-images.githubusercontent.com/40345645/229246491-bb572a18-7e09-4457-ac20-b51e0fb05923.png" ] } ] }' ``` ### Request Parameters [#request-parameters] * **POST**: Specifies that the request method is POST. * **application/json'**: Specifies that the request body will be sent as JSON. * **YOUR\_API\_KEY'**: Replace **YOUR\_API\_KEY** with the API key provided by Bolt. * **YOUR\_PUBLISHABLE\_KEY'**: Replace **YOUR\_PUBLISHABLE\_KEY** with the publishable key provided by Bolt. * **`data-raw '{ ... }'`**: Specifies the JSON payload containing product details such as **name, description, brand, SKU, images, and associated subscription plans with pricing and frequency.** ```json title="Response" { "id": 52, "name": "Bolt+ Membership", "description": "Get All the monies", "created_at": "2025-04-15T22:35:45.723863Z", "updated_at": "2025-04-15T22:35:45.723863Z", "plans": [ { "id": 86, "name": "Bolt+ membership - monthly", "created_at": "2025-04-15T22:35:45.725395Z", "updated_at": "2025-04-15T22:35:45.725395Z", "frequency": 3, "product_id": 52, "frequency_unit": "month", "unit_price": 999, "images": [ "https://user-images.githubusercontent.com/40345645/229246491-bb572a18-7e09-4457-ac20-b51e0fb05923.png" ], "checkout_link": "https://test-bolt.c.boltapp.com/g?merchant_product_id=52&merchant_variant_id=86&publishable_key=HjztBWNROK5B.v5RNH2UWU-lQ.5aabe855e3674df5fcace9f5ac812c9ccb6e46d1626df6afeef178447a6a8c0c" }, { "id": 87, "name": "Bolt+ membership - 1 year", "created_at": "2025-04-15T22:35:45.726496Z", "updated_at": "2025-04-15T22:35:45.726496Z", "frequency": 1, "product_id": 52, "frequency_unit": "year", "unit_price": 6999, "images": [ "https://user-images.githubusercontent.com/40345645/229246491-bb572a18-7e09-4457-ac20-b51e0fb05923.png" ], "checkout_link": "https://test-bolt.c.boltapp.com/g?merchant_product_id=52&merchant_variant_id=87&publishable_key=HjztBWNROK5B.v5RNH2UWU-lQ.5aabe855e3674df5fcace9f5ac812c9ccb6e46d1626df6afeef178447a6a8c0c" } ], "images": [ "https://user-images.githubusercontent.com/40345645/229246491-bb572a18-7e09-4457-ac20-b51e0fb05923.png" ] } ``` **NOTE**: The checkout links returned in the response can be embedded anywhere a shopper needs to complete their purchase, serving as a universal checkout point for each product. **More Info: [Subscriptions API](https://help.boltapp.com/api-subscriptions/)** ## 3. The checkout link directs you to Bolt Checkout to complete your order [#3-the-checkout-link-directs-you-to-bolt-checkout-to-complete-your-order] ## 4. Once the checkout is completed, you should see [#4-once-the-checkout-is-completed-you-should-see] * [Transaction](/products/bolt-subscriptions/subscriptions-setup/#manage-subscriptions) on Merchant Dashboard * **Get a subscription** Endpoint Retrieves the Subscription by its ID and its Status. * Shopper Receive a [confirmation email](/products/bolt-subscriptions/subscriptions-setup/#review-shopper-notifications) with the purchase and subscription details ## Other Useful Endpoints [#other-useful-endpoints] ### Get all Products [#get-all-products] Retrieves a list of all products. ```bash title="Request" curl --location 'https://api-sandbox.boltapp.com/v1/subscriptions/products' \ --header 'X-API-Key: YOUR_API_KEY' \ --header 'X-Publishable-Key: YOUR_PUBLISHABLE_KEY' ``` ```json title="Response" [ { "id": 33, "name": "Test Wework", "description": "Come here", "created_at": "2025-04-14T16:55:35.827132Z", "updated_at": "2025-04-14T16:55:35.827132Z", "plans": [ { "id": 63, "name": "Monthly", "created_at": "2025-04-14T16:55:35.833266Z", "updated_at": "2025-04-14T16:55:35.833266Z", "frequency": 5, "product_id": 33, "frequency_unit": "month", "unit_price": 777, "images": [ "https://img.tipser.com/image/fetch/t_medium/https://api.dev.tipser.com/image/i/67f6a562e3290a22691f8726/eOM%252Br0B%252FxJHOVFcdH1rHFqau5tT%252FyaAO801Hy%252FRmpUtZEjtTEMlosMOO1iMYeoajQ1FWgUmTHACbDEJGeV2%252FnJ5u9Sx%252BrOOzFCVnDJDPKfscPgDbi3toPU12nQVUv3SOP6%252FdFXJ8mTq4sTov5t%252FBPw%253D%253D" ], "checkout_link": "https://test-bolt.c.boltapp.com/g?merchant_product_id=33&merchant_variant_id=63&publishable_key=HjztBWNROK5B.v5RNH2UWU-lQ.5aabe855e3674df5fcace9f5ac812c9ccb6e46d1626df6afeef178447a6a8c0c" } ], "images": [ "https://img.tipser.com/image/fetch/t_medium/https://api.dev.tipser.com/image/i/67f6a562e3290a22691f8726/eOM%252Br0B%252FxJHOVFcdH1rHFqau5tT%252FyaAO801Hy%252FRmpUtZEjtTEMlosMOO1iMYeoajQ1FWgUmTHACbDEJGeV2%252FnJ5u9Sx%252BrOOzFCVnDJDPKfscPgDbi3toPU12nQVUv3SOP6%252FdFXJ8mTq4sTov5t%252FBPw%253D%253D" ] } ] ``` ### Get a Product [#get-a-product] Retrieves a product by its ID. ```bash title="Request" curl --location 'https://api-sandbox.boltapp.com/v1/subscriptions/products/{product_id}' \ --header 'X-API-Key: YOUR_API_KEY' \ --header 'X-Publishable-Key: YOUR_PUBLISHABLE_KEY' ``` ```json title="Response" [ { "id": 10, "name": "Test Streamlabs", "description": "This product does everything", "created_at": "2025-04-11T12:24:54.801497Z", "updated_at": "2025-04-11T12:24:54.801497Z", "plans": [ { "id": 31, "name": "Monthly", "created_at": "2025-04-11T12:24:54.810461Z", "updated_at": "2025-04-11T12:24:54.810461Z", "frequency": 1, "product_id": 10, "frequency_unit": "month", "unit_price": 450, "images": null, "checkout_link": "https://test-bolt.c.boltapp.com/g?merchant_product_id=10&merchant_variant_id=31&publishable_key=HjztBWNROK5B.v5RNH2UWU-lQ.5aabe855e3674df5fcace9f5ac812c9ccb6e46d1626df6afeef178447a6a8c0c" } ], "images": [ "https://img.tipser.com/image/fetch/t_medium/https://api.dev.tipser.com/image/i/67f6a562e3290a22691f8726/eOM%252Br0B%252FxJHOVFcdH1rHFqau5tT%252FyaAO801Hy%252FRmpUtZEjtTEMlosMOO1iMYeoajQ1FWgUmTHACbDEJGeV2%252FnJ5u9Sx%252BrOOzFCVnDJDPKfscPgDbi3toPU12nQVUv3SOP6%252FdFXJ8mTq4sTov5t%252FBPw%253D%253D" ] } ] ``` ### Get a Subscription [#get-a-subscription] Retrieves a subscription by its ID. ```bash title="Request" curl --location 'https://api-sandbox.boltapp.com/v1/subscriptions/{subscription_id}' \ --header 'X-API-Key: YOUR_API_KEY' \ --header 'X-Publishable-Key: YOUR_PUBLISHABLE_KEY' ``` ```json title="Response" { "id": 38, "created_at": "2025-04-16T20:02:04.380541Z", "updated_at": "2025-04-16T20:02:04.380541Z", "consumer_id": 14598189, "subscription_plan_id": 90, "quantity": 1, "status": "active", "plan": { "id": 90, "name": "BoltPremium membership - months", "created_at": "2025-04-16T19:56:59.365954Z", "updated_at": "2025-04-16T19:56:59.365954Z", "frequency": 2, "product_id": 54, "frequency_unit": "month", "unit_price": 999 } } ``` ### Get All Subscription [#get-all-subscription] Retrieves a list of all subscriptions. ```bash title="Request" curl --location 'https://api-sandbox.boltapp.com/v1/subscriptions' \ --header 'X-API-Key: YOUR_API_KEY' \ --header 'X-Publishable-Key: YOUR_PUBLISHABLE_KEY' ``` ```json title="Response" [ { "id": 9, "created_at": "2025-04-12T13:08:50.867688Z", "updated_at": "2025-04-12T13:08:50.867688Z", "consumer_id": 25817508, "subscription_plan_id": 34, "quantity": 1, "status": "canceled", "plan": { "id": 34, "name": "Bolt Member - Monthly", "created_at": "2025-04-11T12:42:16.145519Z", "updated_at": "2025-04-11T12:42:16.145519Z", "frequency": 1, "product_id": 13, "frequency_unit": "month", "unit_price": 700 } }, { "id": 38, "created_at": "2025-04-16T20:02:04.380541Z", "updated_at": "2025-04-16T20:02:04.380541Z", "consumer_id": 14598189, "subscription_plan_id": 90, "quantity": 1, "status": "active", "plan": { "id": 90, "name": "BoltPremium membership - months", "created_at": "2025-04-16T19:56:59.365954Z", "updated_at": "2025-04-16T19:56:59.365954Z", "frequency": 2, "product_id": 54, "frequency_unit": "month", "unit_price": 999 } } ] ``` ### Cancel a Subscription [#cancel-a-subscription] Cancels a subscription by its ID ```bash title="Request" curl --request POST 'https://api-sandbox.boltapp.com/v1/subscriptions/{subscription_id}/cancel' \ --header 'X-API-Key: YOUR_API_KEY' \ --header 'X-Publishable-Key: YOUR_PUBLISHABLE_KEY' ``` ```json title="Response" { "success": true } ``` **More Info: [Subscriptions API](https://help.boltapp.com/api-subscriptions/)** Markdown: https://help.boltapp.com/llms/api-reference/guides/subscriptions.md --- # Introduction To Bolt Gaming > Introduction To Bolt Gaming Source: https://help.boltapp.com/gaming/guide/bolt-introduction We are [Bolt Gaming](https://www.boltapp.com/gaming), the future of **Web Checkout for Gaming**. Our mission is to help developers maximize revenue, reduce costs, and deliver seamless checkout experiences for game players. ## Bolt for Game Developers [#bolt-for-game-developers] Bolt uses a transparent pricing model of **2.1% + $0.30 per transaction**. Unlike traditional app stores that charge up to 30% of revenue, Bolt’s approach ensures developers retain more control over their earnings, set sustainable pricing models, and reinvest directly into their games and communities. Info Bolt’s fees are subject to change but will remain highly competitive. For the latest rates, see [Bolt Pricing](https://www.boltapp.com/pricing). For details, review the [End User Terms and Conditions](https://www.boltapp.com/end-user-terms). ## The Power of Web: Epic Games vs. Apple [#the-power-of-web-epic-games-vs-apple] The **Epic Games vs. Apple** ruling marked a significant shift for game developers: * **Freedom of Choice** – U.S. courts barred Apple from preventing developers from linking to external payment systems, removing the mandatory 30% App Store commission on external purchases. * **Stricter Oversight** – Apple was found to have willfully violated a prior injunction, resulting in stronger enforcement measures. * **Developer Control** – Developers can now direct users to external stores or webshops, retain more revenue, set flexible pricing, and design custom payment flows. * **Open Ecosystem** – The decision reduces Apple’s gatekeeping power and supports a more competitive marketplace, enabling greater earnings and direct player engagement. As a result, developers gain **payment flexibility, lower fees, more control, and greater leverage** in the in-app purchase ecosystem. [Learn more here](https://www.boltapp.com/gaming). ## Quickstart [#quickstart] Ready to build? Start with the [Checkout Quickstart](/gaming/guide/quickstart) to create a sandbox account, a payment link, and a webhook. Markdown: https://help.boltapp.com/llms/gaming/guide/bolt-introduction.md --- # Checkout Quickstart > Set up Bolt checkout links in your game in 15 minutes. Source: https://help.boltapp.com/gaming/guide/quickstart ⏱️ 15-minute integration This Quickstart helps you set up Bolt Checkout Links in a flash ⚡ By the end of this guide you will be able to process payment links anywhere in your app. Sections should take no more than a few minutes: * [Create a Bolt Sandbox Account](/gaming/guide/quickstart#create-a-bolt-sandbox-account) * [Install an SDK](/gaming/guide/quickstart#install-an-sdk) * [Get Your API Keys](/gaming/guide/quickstart#get-your-api-keys) * [Create a Payment Link](/gaming/guide/quickstart#create-a-payment-link) * [Enable Webhooks](/gaming/guide/quickstart#enable-webhooks) Review the [Process Flow Overview](/gaming/guide/process-flow-overview) page to see how the overall end-to-end integration flow works. ## Create a Bolt Sandbox Account [#create-a-bolt-sandbox-account] If you don't have one, create a [Bolt Merchant Sandbox account](https://merchant-sandbox.boltapp.com/onboarding/get-started/gaming) by completing the following steps: * **Enter your company website URL** – Use the public website for your game studio or company. * **Provide company details** – Add your legal business name, address, and contact information. * **Add sandbox user information** – Create a user profile for yourself (and optionally register additional team member). * **Review and confirm** – Check that all details are correct, confirm the pricing information, and select **Submit**. * **Activate your account** – Open the activation email from Bolt and follow the link to complete setup. After activation, you can access your sandbox account here: [Bolt Merchant Sandbox](https://merchant-sandbox.boltapp.com/). ## Install an SDK [#install-an-sdk] We provide lightweight SDKs to help you get started: * [Unity SDK](/gaming/platforms/unity/intro/overview) · [GitHub](https://github.com/BoltApp/bolt-unity-sdk) * [JavaScript SDK](/gaming/platforms/javascript/intro/overview) · [GitHub](https://github.com/BoltApp/bolt-frontend-sdk) More SDKs are coming soon. ## Get Your API Keys [#get-your-api-keys] Once you create a [Bolt Merchant account](https://merchant-sandbox.boltapp.com/), [sign in](https://merchant-sandbox.boltapp.com/) and navigate to: * In the left-side menu, **Administration** → **API**. Copy the following into your request headers: * **API Key** - for authentication * **Publishable Key** - for identifying your public-facing integration ```bash title="Bash" --header 'X-API-KEY: YOUR_API_KEY' \ --header 'X-PUBLISHABLE-KEY: YOUR_PUBLISHABLE_KEY' \ ``` ## Create a Payment Link [#create-a-payment-link] A **Payment Link** is a hosted checkout page for your item. POST /v1/gaming/payment\_linksYou can create a payment link by providing the following parameters: * **Item details** - name, price, currency, and optionally an image * **Game identifiers** - your game\_id and user\_id * **Redirect URL** - a redirect\_url for post-checkout * **Metadata** (optional) - custom values The example below shows a minimal request payload in the **Sandbox** environment. For complete request and response examples, see [Create Payment Link](/gaming/guide/bolt-api/create-payment-link) in the Bolt API. ```bash title="Request" curl --location 'https://api-sandbox.boltapp.com/v1/gaming/payment_links' \ --header 'X-API-KEY: YOUR_API_KEY' \ --header 'X-PUBLISHABLE-KEY: YOUR_PUBLISHABLE_KEY' \ --header 'Content-Type: application/json' \ --data '{ "game_id": "YOUR_GAME_ID", "user_id": "USER_ID", "redirect_url": "bubble-merge://bolt-webcheckout/success", "item": { "price": 699, "name": "Gems Pack", "image_url": "https://yourcdn.com/image.png", "currency": "USD" }, "metadata": { "beamable_id": "9876543210", "greeting": "Hello World!" } }' ``` ## Enable Webhooks [#enable-webhooks] To receive transaction updates in your backend, configure a webhook endpoint in your [Bolt Merchant account](https://merchant-sandbox.boltapp.com/): * In the left-side menu, go to **Administration** → **Webhooks**. * Enter the URL of the endpoint that will receive Bolt webhook requests. See how to [Validate Webhook Authenticity](/gaming/guide/bolt-webhooks/validate-authenticity) in the Bolt Webhooks. ## Next Steps [#next-steps] You now have a working sandbox environment, an SDK installed, and your first payment link configured. Next, you can: * [Check payment link status](/gaming/guide/bolt-api/check-payment-link) * [Consume payment link](/gaming/guide/bolt-api/consume-payment-link) * [Check transaction status](/gaming/guide/bolt-api/check-transaction) **Production** When you are done with your testing you need to get ready for production. * [Create a Production Merchant Account](https://merchant.boltapp.com/onboarding/get-started/gaming) * Swap your API keys for your production build * Test and start accepting payments with Bolt web checkout! Markdown: https://help.boltapp.com/llms/gaming/guide/quickstart.md --- # Process Flow Overview > End-to-end Bolt Gaming checkout link flow from payment link to webhook. Source: https://help.boltapp.com/gaming/guide/process-flow-overview After completing the [Checkout Quickstart](/gaming/guide/quickstart), review the end-to-end integration flow. ## Steps [#steps] 1. **Create payment link**: Your web server calls `POST /v1/gaming/payment_links` with item, `game_id`, `user_id`, and `redirect_url`. 2. **Open checkout**: The game opens the returned URL in a browser or web view. 3. **Complete payment**: The shopper pays on Bolt Checkout. 4. **Webhook**: Bolt notifies your server; validate authenticity before fulfilling. 5. **Deep link**: Return the player to the game with transaction metadata. ## Related [#related] * [Web server setup](/gaming/guide/web-server/setup) * [Create payment link](/gaming/guide/bolt-api/create-payment-link) * [Validate webhooks](/gaming/guide/bolt-webhooks/validate-authenticity) Markdown: https://help.boltapp.com/llms/gaming/guide/process-flow-overview.md --- # Interactive Ads Quickstart > Interactive Ads Quickstart Source: https://help.boltapp.com/gaming/guide/interactive-ads/quickstart ⏱️ 15-minute integration Bolt Gaming has partnered with [ToffeePay](https://docs.toffee.com/) to deliver the future of **Ads for Gaming**. You will likely see some co-branding throughout the integration. This Quickstart helps you set up Bolt Interactive Ads in a flash ⚡ By the end of this guide you will be able to fetch and display personalized offers to users in your app. Sections should take no more than a few minutes: * [Create a Bolt Sandbox Account](/gaming/guide/interactive-ads/quickstart#create-a-bolt-sandbox-account) * [Install an SDK](/gaming/guide/interactive-ads/quickstart#install-an-sdk) * [Get Your API Keys](/gaming/guide/interactive-ads/quickstart#get-your-api-keys) * [Create Personalized Ad Offer](/gaming/guide/interactive-ads/quickstart#create-personalized-ad-offer) * [Enable Webhooks](/gaming/guide/interactive-ads/quickstart#enable-webhooks) Review the [Process Flow Overview](/gaming/guide/process-flow-overview) page to see how the overall end-to-end integration flow works. ## Create a Bolt Sandbox Account [#create-a-bolt-sandbox-account] If you don't have one, create a [Bolt Merchant Sandbox account](https://merchant-sandbox.boltapp.com/onboarding/get-started/gaming) by completing the following steps: * **Enter your company website URL** – Use the public website for your game studio or company. * **Provide company details** – Add your legal business name, address, and contact information. * **Add sandbox user information** – Create a user profile for yourself (and optionally register additional team member). * **Review and confirm** – Check that all details are correct, confirm the pricing information, and select **Submit**. * **Activate your account** – Open the activation email from Bolt and follow the link to complete setup. After activation, you can access your sandbox account here: [Bolt Merchant Sandbox](https://merchant-sandbox.boltapp.com/). ## Install an SDK [#install-an-sdk] We provide lightweight SDKs to help you get started: * [JavaScript SDK](/gaming/platforms/javascript/intro/overview) · [GitHub](https://github.com/BoltApp/bolt-frontend-sdk) More SDKs are coming soon for Unity and other platforms. ### Usage [#usage] Use the SDK to display interactive ads with `BoltSDK.gaming.openAd()`. Here's a sample implementation: ```javascript title="JavaScript" // Initialize the Bolt SDK BoltSDK.initialize({ gameId: 'your-game-id', publishableKey: 'YOUR_PUBLISHABLE_KEY' }); // Open an interactive ad. openAd takes options only; the ad link comes back in the result. const adOfferResult = await BoltSDK.gaming.openAd({ onClaim: () => { // fires when the player claims the reward } }); // Handle the result if (adOfferResult.status === 'success') { // TODO – validate the ad offer in the backend for a secure confirmation // now you can display a success modal console.log('Ad displayed successfully:', adOfferResult.data.adLink); } else { // user exited early – display a failure modal console.error('Failed to display ad:', adOfferResult.error); } ``` **Sample Link for Testing:** Use this sample link to test the integration: [https://show.sandbox.toffee.com/offer\_01k5y8wdbk5b390mmwdz5ja7cd](https://show.sandbox.toffee.com/offer_01k5y8wdbk5b390mmwdz5ja7cd) ## Get Your API Keys [#get-your-api-keys] Once you create a [Bolt Merchant account](https://merchant-sandbox.boltapp.com/), [sign in](https://merchant-sandbox.boltapp.com/) and navigate to: * In the left-side menu, **Administration** → **API**. Copy the following into your request headers: * **API Key** - for authentication * **Publishable Key** - for identifying your public-facing integration ```bash title="Bash" --header 'X-API-KEY: YOUR_API_KEY' \ --header 'X-PUBLISHABLE-KEY: YOUR_PUBLISHABLE_KEY' \ ``` ## Create Personalized Ad Offer [#create-personalized-ad-offer] To get personalized ads for your users you will need to hit the Bolt API. Note: these apis are still in beta. For now the api will always return a static result but by mid October it will generate unique ad offers per request. Thanks for your patience while we production-ize our new ad tech technology. ```bash title="Request" curl --location 'https://api-sandbox.boltapp.com/v1/gaming/beta/ad_offer' \ --header 'X-API-KEY: YOUR_API_KEY' \ --header 'X-PUBLISHABLE-KEY: YOUR_PUBLISHABLE_KEY' \ --header 'Content-Type: application/json' \ --data '{ "user_id": "user-123" }' ``` ```json title="Response" { "id": "offer_01k6gs6gxv08j8zm9zmwcq9spm", "url": "https://show.sandbox.toffee.com/offer_01k6gs6gxv08j8zm9zmwcq9spm", "status": "available", "image": "https://cdn.toffee.com/offers/example.png", "expires": 3600 } ``` #### Metadata [#metadata] Optionally you can include a user\_metadata field to be permanently associated with this ad offer. Bolt does not rely on this field but it can be useful if you have external IDs you use for analytics. It will be returned on all future requests for this ad offer. ```bash title="Request" curl --location 'https://api-sandbox.boltapp.com/v1/gaming/beta/ad_offer' \ --header 'X-API-KEY: YOUR_API_KEY' \ --header 'X-PUBLISHABLE-KEY: YOUR_PUBLISHABLE_KEY' \ --header 'Content-Type: application/json' \ --data '{ "user_id": "user-123", "user_metadata": { "google-analytic-id": "your-example-id", "offer-placement": "game-over-screen", "user-tier": "free-user", "reward_type": "bolt-buckaroos", "reward_amount": "500" } }' ``` ```json title="Response" { "id": "offer_01k6gs6gxv08j8zm9zmwcq9spm", "url": "https://show.sandbox.toffee.com/offer_01k6gs6gxv08j8zm9zmwcq9spm", "status": "available", "image": "https://cdn.toffee.com/offers/example.png", "expires": 3600 } ``` ## Verify an Ad Offer [#verify-an-ad-offer] Once an ad is completed by the user, you will need to do a server verification before issuing a reward. You can do this by checking the status of the ad offer. ```bash title="Request" curl --location GET 'https://api-sandbox.boltapp.com/v1/gaming/beta/ad_offer/YOUR_AD_OFFER_ID' \ --header 'X-API-KEY: YOUR_API_KEY' \ --header 'X-PUBLISHABLE-KEY: YOUR_PUBLISHABLE_KEY' ``` You will get a response with status marked redeemed if the ad was completed by the user. This means the reward is ready to be issued to the user: ```json title="Response" { "ad_offer_properties": { "id": "offer_01k6gs6gxv08j8zm9zmwcq9spm", "url": "https://show.sandbox.toffee.com/offer_01k6gs6gxv08j8zm9zmwcq9spm", "status": "redeemed", "metadata": { "google-analytic-id": "your-example-id", "offer-placement": "game-over-screen", "user-tier": "free-user", "reward_type": "bolt-buckaroos", "reward_amount": "500" }, "image": "https://cdn.toffee.com/offers/example.png" } } ``` If the status code comes back as available or expired then a reward should not be issued. The user did not complete the ad offer. See the possible status codes for reference. **Possible status codes** * available: ad offer is ready to go and not yet completed * expired: ad offers automatically expire after 24hrs and can no longer be viewed * redeemed: user finished the ad and you should issue a reward * consumed: reward has been issued to the user Once an ad offer is finished by the user it will automatically be marked as redeemed by our system. It will stay in that state until consumed (see next section on how to do that). Redeemed ad offers cannot expire so you can rely on that status to know when to issue rewards to the user. ## Consume an Ad Offer [#consume-an-ad-offer] When an ad offer comes back as redeemed it means it's time to issue the reward to the user. You are expected to keep track of the reward type and amount. It's recommended to put this into the user\_metadata object for easy reference. ```bash title="Request" curl --location --request PUT 'https://api-sandbox.boltapp.com/v1/gaming/beta/ad_offer/consume/YOUR_AD_OFFER_ID' \ --header 'X-API-KEY: YOUR_API_KEY' \ --header 'X-PUBLISHABLE-KEY: YOUR_PUBLISHABLE_KEY' ``` ```json title="Response" { "ad_offer_properties": { "id": "offer_01k6gs6gxv08j8zm9zmwcq9spm", "url": "https://show.sandbox.toffee.com/offer_01k6gs6gxv08j8zm9zmwcq9spm", "status": "consumed", "image": "https://cdn.toffee.com/offers/example.png" } } ``` You can only consume redeemed ad offers. You will get an error if you try to consume an offer with any other status. Ensure you mark an offer as consumed only after issuing the reward to the user. There is no way to set the status back. ## Enable Webhooks [#enable-webhooks] To receive transaction updates in your backend, configure a webhook endpoint in your [Bolt Merchant account](https://merchant-sandbox.boltapp.com/): * In the left-side menu, go to **Administration** → **Webhooks**. * Enter the URL of the endpoint that will receive Bolt webhook requests. See how to [Validate Webhook Authenticity](/gaming/guide/bolt-webhooks/validate-authenticity) in the Bolt Webhooks. ## Next Steps [#next-steps] You now have a working sandbox environment, an SDK installed, and your first ad integration configured. ### Production [#production] When you are done with your testing you need to get ready for production. * [Create a Production Merchant Account](https://merchant.boltapp.com/onboarding/get-started/gaming) * Set up your production ToffeePay account * Swap your API keys for your production build * Test and start accepting payments with interactive ads! Markdown: https://help.boltapp.com/llms/gaming/guide/interactive-ads/quickstart.md --- # Web Server Setup > Set up a trusted backend to create gaming payment links and handle webhooks. Source: https://help.boltapp.com/gaming/guide/web-server/setup Your game needs a backend service to connect securely with the Bolt API. The web server is a trusted intermediary between your game client and Bolt. ## Requirements [#requirements] Your web server should: * **Authenticate with Bolt**: Use server-side API keys (never expose the signing secret in the client). * **Create payment links**: Call `POST /v1/gaming/payment_links` on behalf of the game. * **Persist transactions**: Store transaction data in your game schema. * **Handle webhooks**: Accept transaction status updates from Bolt. * **Validate authenticity**: Verify webhook signatures before fulfilling purchases. ## Payment link flow [#payment-link-flow] The game requests a payment link from your web server. Your server calls `POST /v1/gaming/payment_links` and returns the `payment_link` URL to the client. See [Create Payment Link](/gaming/guide/bolt-api/create-payment-link) for request and response details. ## Reference implementation [#reference-implementation] * [Bolt Game Server Sample](https://github.com/BoltApp/bolt-gameserver-sample/tree/main): complete web server example * [Demo storefront](https://gaming.staging-boltapp.com/store): try the checkout flow ## Related [#related] * [Checkout quickstart](/gaming/guide/quickstart) * [Validate webhooks](/gaming/guide/bolt-webhooks/validate-authenticity) Markdown: https://help.boltapp.com/llms/gaming/guide/web-server/setup.md --- # Validate Webhook Authenticity > Validate Webhook Authenticity Source: https://help.boltapp.com/gaming/guide/bolt-webhooks/validate-authenticity ## 1. Configure a Webhook Endpoint [#1-configure-a-webhook-endpoint] In your [Bolt Merchant account](https://merchant-sandbox.boltapp.com/): * In the left-side menu, go to **Administration** → **Webhooks**. * Enter the URL of the endpoint on your server that will receive webhook requests. ## 2. Get Your Signing Secret [#2-get-your-signing-secret] In your [Bolt Merchant account](https://merchant-sandbox.boltapp.com/): * In the left-side menu, go to **Administration** → **API**. * Copy the **Signing Secret**. You will need it to validate requests. ## 3. Validate Incoming Webhooks [#3-validate-incoming-webhooks] Each webhook request includes a signature in the X-Bolt-Hmac-Sha256 header. To verify the request: * Take the raw request body. * Hash it with your **Signing Secret** using HMAC + SHA-256. * Base64-encode the result. * Compare it to the signature in the header. If they match, the webhook is valid. TypeScript JavaScript Python PHP ```typescript import { createHmac, timingSafeEqual } from "node:crypto"; export function isFromBolt( rawBody: Buffer, headerValue: string, signingSecret: string, ): boolean { const expected = createHmac("sha256", signingSecret).update(rawBody).digest("base64"); const a = Buffer.from(expected); const b = Buffer.from(headerValue); // timingSafeEqual throws unless both buffers are the same length. return a.length === b.length && timingSafeEqual(a, b); } ``` ```javascript const { createHmac, timingSafeEqual } = require("node:crypto"); function isFromBolt(rawBody, headerValue, signingSecret) { const expected = createHmac("sha256", signingSecret).update(rawBody).digest("base64"); const a = Buffer.from(expected); const b = Buffer.from(headerValue); // timingSafeEqual throws unless both buffers are the same length. return a.length === b.length && timingSafeEqual(a, b); } ``` ```python import base64, hashlib, hmac def is_from_bolt(raw_body: bytes, header_value: str, signing_secret: str) -> bool: digest = hmac.new(signing_secret.encode(), raw_body, hashlib.sha256).digest() expected = base64.b64encode(digest).decode() return hmac.compare_digest(expected, header_value) ``` ```php Pass the raw request body exactly as received. Parsing and re-serializing the JSON first changes the bytes, and the signature will not match. `headerValue` is the value of the `X-Bolt-Hmac-Sha256` header; read it the way your framework exposes headers, which is usually lowercased. See more information about [Bolt Webhooks](https://help.boltapp.com/developers/webhooks/) for additional information. Markdown: https://help.boltapp.com/llms/gaming/guide/bolt-webhooks/validate-authenticity.md --- # Unity Overview > Unity Overview Source: https://help.boltapp.com/gaming/platforms/unity/intro/overview Bolt offers native Unity support for Bolt-powered web checkout. Use the guides below to install the SDK, connect your backend, and handle checkout in your game. ## Guides [#guides] * [Installation](/gaming/platforms/unity/intro/installation): Add the Bolt Unity SDK to your project * [Unity + Node.js full guide](/gaming/guide/unity/nodejs): End-to-end sample with a Node.js backend * [Purchase & checkout](/gaming/platforms/unity/web-checkout/purchase-checkout): Open a payment link from Unity * [Transaction events](/gaming/platforms/unity/events/lifecycle): Lifecycle callbacks from the SDK * [iOS deep links](/gaming/platforms/unity/deep-links/ios): Return players to your game after checkout Markdown: https://help.boltapp.com/llms/gaming/platforms/unity/intro/overview.md --- # Installation > Installation Source: https://help.boltapp.com/gaming/platforms/unity/intro/installation ## Prerequisites [#prerequisites] To get started, you will need the following three requirements: * Unity Game or Application * Backend Web Server with an accessible API * Bolt Merchant Dashboard To read about how this all comes together with a Node.js web server, [you can check out this scenario we've put together.](/gaming/guide/unity/nodejs) ## Install the SDK [#install-the-sdk] ### Option #1 - Using the Unity manifest.json (Recommended) [#option-1---using-the-unity-manifestjson-recommended] * Open your Unity project * Navigate to the Packages folder in your project root * Open the manifest.json file in a text editor * Add the Bolt SDK dependency to the dependencies section: ```json title="manifest.json" { "dependencies": { "com.bolt.sdk": "https://github.com/BoltApp/bolt-unity-sdk.git#main" } } ``` * ***Save the file*** - Unity will automatically download and import the package ### Option 2: Using Package Manager UI [#option-2-using-package-manager-ui] * Open Package Manager in Unity (Window > Package Manager) * Click the "+" button in the top-left corner * Select "Add package from git URL" * Enter the repository URL: [https://github.com/BoltApp/bolt-unity-sdk.git](https://github.com/BoltApp/bolt-unity-sdk.git) * Click "Add" Markdown: https://help.boltapp.com/llms/gaming/platforms/unity/intro/installation.md --- # Unity | Node.js | Getting Started > Unity | Node.js | Getting Started Source: https://help.boltapp.com/gaming/guide/unity/nodejs ## Full Guide with Unity & Node.js [#full-guide-with-unity--nodejs] ⏱️ 45-minute integration Sample integration between the frontend and backend. If you use a different language the concepts here should still apply and easy to follow. ### Getting Started [#getting-started] In this scenario, we will be using the following stack: * **Unity** as our game engine * **Node.js** for our backend web server ### Prerequisites [#prerequisites] Before we get started here, let's cover some requirements that are need to get going. Don't worry, we go over each step here in detail. Feel free to skip around using the aside navigation. * Minimum Unity version: "unity": "2021.3" * Bolt [Sandbox Account](#your-bolt-sandbox-dashboard) Make sure to check your **spam folder** for the verification email * This is where you'll get your **API Key** for your web server integration! * [Bolt Unity SDK](#unity-sdk) * Your Game's Backend [Web Server Integration](#web-server-integration) * [Deep Links Enabled](#deep-links) for your iOS Build 💹 Do You Like Diagrams? We've got a high-level IAP sequence diagram that we will be updating as we iterate on our release(s). [You can check that out over here!](/gaming/guide/process-flow-overview) ### Your Bolt Sandbox Dashboard [#your-bolt-sandbox-dashboard] Head on over to [the Bolt Sandbox Dashboard](https://merchant-sandbox.boltapp.com/onboarding/get-started) and sign up for your **Sandbox Account.** Once you're in, finish up the on-boarding steps to make sure you get a *production-esque* experience. #### Getting through the Tasks (Tax Compliance) [#getting-through-the-tasks-tax-compliance] ⚠️ Setup Tax Compliance Provider We are currently working on automating or bypassing the ***Tax Setup*** in Sandbox accounts. For now, we need to manually flag this as complete. If you have any issues with on-boarding completion, **please contact us directly and we will assist you with the configuration step.** *Thank you!* #### Your API Key [#your-api-key] Go into your **Administration** section to access you API key(s) and secret(s). ⚠️ Protect Your Keys! When building out your web service integration, be sure to securely store your API key(s) in a non-public .env. Here is a helpful **Strapi** [article touching upon that](https://strapi.io/blog/how-to-store-API-keys-securely). You will also need your Publishabke Key to generate your Payment URL. #### Webhooks [#webhooks] It's also important to get *at least one* webhook registered for your game studio. #### Your First Payment Link! [#your-first-payment-link] Here is an example cURL request to dynamically generate a Payment URL: ```bash title="Request" curl --location 'https://api-sandbox.boltapp.com/v1/gaming/payment_links' \ --header 'x-api-key: {BOLT_API_KEY}' \ --header 'x-publishable-key: {BOLT_PUBLISHABLE_KEY}' \ --header 'Content-Type: application/json' \ --data '{ "game_id": "{YOUR_GAME_ID}", "user_id": "USER_ID", "redirect_url": "bubble-merge://bolt-webcheckout/success", "item": { "price": 699, "name": "Gems Pack", "currency": "USD" }, "metadata": { "beamable_id": "9876543210", "greeting": "Hello World!" } }' ``` ```json title="Response" { "id": "8PFm6ZzP7hX22vpeEwGoXs", "link": "https://knights-of-valor-bolt.c-staging.boltapp.com/c?u=Fv8ZMmDmRb86C4XRiB92x2&publishable_key=_Kq5XZXqaLiS.3TOhnz9Wmacb.9c59b297d066e94294895dd8617ad5d9d8ffc530fe1d36f8ed6d624a4f7855ae" } ``` For more information on generating Payment URLs from your web server using the Bolt API, [please dig in here](/gaming/guide/bolt-api/create-payment-link). ### Unity SDK [#unity-sdk] If you're a Unity pro, you can just skip right through this and [go source dive our GitHub repo](https://github.com/BoltApp/bolt-unity-sdk). Otherwise, take your time and follow this guide and corresponding links to get better acquainted with the Bolt Unity SDK. * Download and install the Bolt Unity SDK [\[Read More\]](/gaming/platforms/unity/intro/installation) * Set up an HTTP client (or use your existing one) within Unity for communicating with your web server [\[Read More\]](/gaming/platforms/unity/intro/overview) * Implement the method calls to initate and open the native browser with **Web Checkout via Bolt Payment URL's** [\[Read More\]](/gaming/platforms/unity/web-checkout/purchase-checkout#integrating-your-payment-url) * Enable deep links within your iOS build (Android coming soon) * Hook into the Bolt Unity SDK event loop to handle success/failures from the browser; we have some [raw source code with deep link integration examples here.](https://github.com/BoltApp/bolt-unity-sdk/blob/main/Samples~/DeepLinkIntegration/BoltDeepLinkExample.cs) * Test, deploy, and ***game on!*** ### Web Server Integration [#web-server-integration] We are going to be using Node.js as our backend server. It's imperative that we have a minimal of two endpoints for this to work: * **Payment URL Endpoint** - This is the endpoint that your game will call to get a Payment URL from Bolt [using the Bolt API.](/gaming/guide/bolt-api/create-payment-link) * **Transaction Webhook** - This is the endpoint that Bolt will call to notify your game of a transaction's status. 🔥 Example Game Web Server We have [an entire GitHub repo showcasing how to set up a game's web server.](https://github.com/BoltApp/bolt-gameserver-sample/tree/main) #### Payment URL Endpoint [#payment-url-endpoint] Here is an example using Node.js and the fetch() [API approach](https://nodejs.org/en/learn/getting-started/fetch) to get a checkout link from Bolt using the Bolt API. ⚠️ Important The fetch() API is only available in Node.js 18.0.0 or higher. ```javascript title="Node.js" require('dotenv').config() const bodyPayload = { game_id: 'game-01234567890', user_id: 'user-9876543210', return_url: 'bubble-merge://bolt-webcheckout/success', item: { title: 'Gems', price: 999, currency: 'USD' }, metadata: { beamable_id: '9876543210', greeting: 'Hello World!' } } // Because these just don't exist in your repo, right? const boltKey = process.env.BOLT_API_KEY const boltPublishKey = process.env.BOLT_PUBLISH_KEY const response = await fetch('https://api-sandbox.boltapp.com/v1/gaming/payment_links', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-API-Key': `${boltKey}`, 'X-Publishable-Key': `${boltPublishKey}` }, body: JSON.stringify(bodyPayload) }) const data = await response.json() ``` #### Example Response [#example-response] 🥚 Stub Out a Reponse? If you are wanting to stub out the checkout link to get started in your game's code base while development on your backend is in progress, you can use a test/no-expiry link from our Bolt treasure chest. See below. ```json title="Response" { "id": "RsT4cu4KoeF7rbZjxVajy6", "link": "https://bolt.c-staging.boltapp.com/o?order_token=4c3e736d0e5020df4bfddabbdef7f00c5ac54b752d7d883256588351265e2e96" } ``` ### Deep Links [#deep-links] Deep links are a way to open your game from a URL, specifically the return\_url that you provide to Bolt in your API call. This is a requirement for Bolt's Unity SDK to work on iOS. ⚠️ Tip We pass back the metadata that you provide to Bolt in your API call as a base64 encoded query parameter in the return\_url. ### Testing Purchases [#testing-purchases] Go ahead and do the above, then test with a fake credit card: 4111 1111 1111 1111 Markdown: https://help.boltapp.com/llms/gaming/guide/unity/nodejs.md --- # Purchase & Checkout in Unity > Purchase & Checkout in Unity Source: https://help.boltapp.com/gaming/platforms/unity/web-checkout/purchase-checkout ## Purchase & Checkout in Unity [#purchase--checkout-in-unity] ### Integrating Your Payment URL [#integrating-your-payment-url] Once you have [requested and received your Payment URL from your web server API](/gaming/guide/web-server/setup#payment-link-flow), you can now use the URL within the product purchasing flow within your Unit code base. #### Example [#example] Here is a pseudo-code mock up of getting your game to trigger the web browser kick-off through the Unity SDK. ```csharp title="C#" string checkoutLink = YourClient.getPaymentUrlByProductId(sku); BoltSDK.OpenCheckout(checkoutLink); ``` Markdown: https://help.boltapp.com/llms/gaming/platforms/unity/web-checkout/purchase-checkout.md --- # Transaction Events in Unity > Transaction Events in Unity Source: https://help.boltapp.com/gaming/platforms/unity/events/lifecycle This page is in progress. Event documentation will be expanded soon. You can see the available [events that you can subscribe to in our sample code here.](https://github.com/BoltApp/bolt-unity-sdk/tree/main/Samples~) * onTransactionComplete * onTransactionFailed * onWebLinkOpen Markdown: https://help.boltapp.com/llms/gaming/platforms/unity/events/lifecycle.md --- # Deep Links in Unity for iOS Builds > Deep Links in Unity for iOS Builds Source: https://help.boltapp.com/gaming/platforms/unity/deep-links/ios INFO Please refer to the Unity docs: [Deep Links in iOS](https://docs.unity3d.com/6000.1/Documentation/Manual/deep-linking-ios.html) Markdown: https://help.boltapp.com/llms/gaming/platforms/unity/deep-links/ios.md --- # Bolt JavaScript SDK > Open Bolt checkout links and reward ads from a web game built with React, Vue, or plain JavaScript. Source: https://help.boltapp.com/gaming/platforms/javascript/intro/overview `@boltpay/bolt-js` is the browser client for Bolt Gaming. Your backend creates a payment link, and the SDK opens it for the player without sending them away from your game. It also opens reward ads. This package is for games. It does not provide the browser client for [Custom Cart](/custom-cart/overview), which loads Bolt's hosted scripts instead. ## What you need [#what-you-need] * A web game or web app built with React, Vue, Angular, or plain JavaScript. * Your own backend server, in any language, to create payment links and receive webhooks. * A Bolt merchant account with gaming enabled. Create a [sandbox account](https://merchant-sandbox.boltapp.com/onboarding/get-started/gaming) to build against. ## What it does [#what-it-does] Call `BoltSDK.initialize({ gameId, publishableKey, environment })` once before anything else. `BoltSDK` is the package's only export. | Capability | Call | Use it for | | ---------- | ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | | Checkout | `BoltSDK.gaming.openCheckout(checkoutLink)` | Opening a payment link you created on your backend. Resolves with a `PaymentLinkSession`. | | Reward ads | `BoltSDK.gaming.openAd({ onClaim })` | Showing an ad and granting a reward when the player claims it. Resolves with a `status` of `success` or `error`. | The SDK does not create payment links. That is a server call your backend makes with your API key. See [Create payment link](/gaming/guide/bolt-api/create-payment-link). ## Next steps [#next-steps] * [JavaScript SDK quickstart](/gaming/platforms/javascript/intro/quickstart): install the package and open your first checkout link. * [Checkout quickstart](/gaming/guide/quickstart): create the sandbox account, API keys, payment link, and webhook your client depends on. * [bolt-frontend-sdk on GitHub](https://github.com/BoltApp/bolt-frontend-sdk): source, sample integration, and release history. Markdown: https://help.boltapp.com/llms/gaming/platforms/javascript/intro/overview.md --- # JavaScript SDK quickstart > Install @boltpay/bolt-js and open a Bolt checkout link from your web game. Source: https://help.boltapp.com/gaming/platforms/javascript/intro/quickstart Install the SDK, open a payment link your backend created, and read the transaction reference the player's purchase returns. ## Prerequisites [#prerequisites] * A web game or web app built with React, Vue, Angular, or plain JavaScript. * A backend server that can call the Bolt Gaming API with your secret API key. * A sandbox merchant account and API keys. Follow [Checkout quickstart](/gaming/guide/quickstart) first if you do not have them. ## 1. Install the SDK [#1-install-the-sdk] npm pnpm yarn bun ```bash npm install @boltpay/bolt-js ``` ```bash pnpm add @boltpay/bolt-js ``` ```bash yarn add @boltpay/bolt-js ``` ```bash bun add @boltpay/bolt-js ``` **Expected result:** `@boltpay/bolt-js` appears in your dependencies. The package ships TypeScript definitions, so no separate types package is needed. ## 2. Create a payment link on your backend [#2-create-a-payment-link-on-your-backend] The SDK opens a link; it does not create one. Your backend calls `POST /v1/gaming/payment_links` with your secret API key and returns the `link` value to your client. Never put the API key in browser code. ```bash title="Request" curl --location 'https://api-sandbox.boltapp.com/v1/gaming/payment_links' \ --header 'X-API-KEY: YOUR_API_KEY' \ --header 'X-PUBLISHABLE-KEY: YOUR_PUBLISHABLE_KEY' \ --header 'Content-Type: application/json' \ --data '{ "game_id": "YOUR_GAME_ID", "user_id": "USER_ID", "redirect_url": "yourgame://bolt-webcheckout/success", "item": { "price": 699, "name": "Gems Pack", "image_url": "https://yourcdn.example.com/gems.png", "currency": "USD" } }' ``` The response carries the URL your client opens: ```json title="Response" { "id": "CyzZV9Zh45t2rLE3ubG75x", "link": "https://your-store.c-sandbox.boltapp.com/o?order_token=...&payment_link_id=...&publishable_key=..." } ``` See [Create payment link](/gaming/guide/bolt-api/create-payment-link) for every parameter. ## 3. Open checkout from your app [#3-open-checkout-from-your-app] Initialize the SDK once when your app starts. `gameId` and `publishableKey` come from the sandbox dashboard: ```ts title="TypeScript" import { BoltSDK } from '@boltpay/bolt-js' await BoltSDK.initialize({ gameId: 'YOUR_GAME_ID', publishableKey: 'YOUR_PUBLISHABLE_KEY', environment: 'sandbox', }) ``` Then pass the `link` value to `BoltSDK.gaming.openCheckout()`. It resolves with a `PaymentLinkSession` carrying the link's `paymentLinkId` and `status`. React Vue JavaScript ```tsx import { BoltSDK } from '@boltpay/bolt-js' import { useState } from 'react' function CheckoutButton({ checkoutUrl }: { checkoutUrl: string }) { const [loading, setLoading] = useState(false) const handlePayment = async () => { setLoading(true) try { const session = await BoltSDK.gaming.openCheckout(checkoutUrl) console.log('Checkout session:', session?.paymentLinkId, session?.status) } finally { setLoading(false) } } return ( ) } ``` ```vue ``` ```js import { BoltSDK } from '@boltpay/bolt-js' async function buyItem(checkoutUrl) { const session = await BoltSDK.gaming.openCheckout(checkoutUrl) console.log('Checkout session:', session?.paymentLinkId, session?.status) // The purchase is confirmed by the webhook your backend received. // Re-read the player's entitlements from your backend rather than // granting them in the browser. await syncPlayerData() } document.getElementById('buy-button')?.addEventListener('click', async () => { const checkoutUrl = await fetchCheckoutUrlFromYourBackend() await buyItem(checkoutUrl) }) ``` **Expected result:** Bolt checkout opens over your game. On completion the promise resolves with a transaction object. ## 4. Grant the purchase from your backend [#4-grant-the-purchase-from-your-backend] `openCheckout()` resolves with a `PaymentLinkSession`: ```ts title="TypeScript" import type { PaymentLinkSession } from '@boltpay/bolt-js' interface PaymentLinkSession { paymentLinkId: string paymentLinkUrl: string status: 'pending' | 'successful' | 'expired' | 'abandoned' createdAt: Date updatedAt: Date completedAt?: Date } ``` Treat the session as a client-side record to display or log, not as proof of payment. Bolt sends a transaction webhook to your backend, so grant items there and have the client re-read the player's state. See [Validate webhook authenticity](/gaming/guide/bolt-webhooks/validate-authenticity). ## 5. Verify the flow [#5-verify-the-flow] 1. Run your app against a sandbox payment link and complete a purchase. **Expected result:** The promise resolves and your console logs the payment link id and status. 2. Open **Transactions** in the [sandbox Merchant Dashboard](https://merchant-sandbox.boltapp.com/). **Expected result:** The transaction appears with the item and amount from your payment link. 3. Confirm your backend received the transaction webhook and granted the item. ## Show a reward ad [#show-a-reward-ad] The SDK can also open an ad and tell you when the player claims the reward. ```tsx title="React" import { BoltSDK } from '@boltpay/bolt-js' function AdButton() { const handleAdDisplay = async () => { await BoltSDK.gaming.openAd({ onClaim: () => { console.log('Reward claimed') }, }) } return } ``` ## Next steps [#next-steps] * [Check payment link status](/gaming/guide/bolt-api/check-payment-link) * [Consume payment link](/gaming/guide/bolt-api/consume-payment-link) * [Check transaction status](/gaming/guide/bolt-api/check-transaction) Markdown: https://help.boltapp.com/llms/gaming/platforms/javascript/intro/quickstart.md --- # Authenticate shoppers with Bolt ID > Checkout-native identity that unites authentication, trust signals, and one-click checkout across the Bolt network. Source: https://help.boltapp.com/products/bolt-id **Bolt ID** is the identity layer for commerce: it unites identity intelligence, authentication, and checkout so merchants get stronger trust and higher conversion while shoppers get a faster path to purchase. ## Who this is for [#who-this-is-for] Merchants who want passwordless login, cross-site shopper recognition, and identity signals built into checkout instead of bolting on a separate auth stack. ## How it works [#how-it-works] 1. Shoppers sign in with one-time codes, passkeys, or linked store accounts. 2. Bolt ID verifies the shopper against the Bolt network and your store policies. 3. Saved addresses and payment methods prefill at checkout. 4. Identity and risk signals feed fraud review without extra integration work. ## Get started [#get-started] | Task | Link | | ----------------------------------- | ---------------------------------------------------- | | Passwordless SSO on your storefront | [SSO Commerce](/products/add-ons/sso-commerce) | | Biometric login on mobile | [Passkeys](/products/add-ons/passkeys) | | Link existing store accounts | [Account Linking](/products/add-ons/account-linking) | | Auth in a custom UI | [Embeddable Checkout](/products/embeddable-checkout) | ## Related [#related] * [Check-In](/products/check-in): one-click sign-in at checkout * [User Network](/products/user-network): recognized shoppers across Bolt merchants * [Bolt Checkout](/products/checkout) Markdown: https://help.boltapp.com/llms/products/bolt-id.md --- # Sign in shoppers with Check-In > Turn every sign-in into a one-click, passwordless experience at checkout and across your storefront. Source: https://help.boltapp.com/products/check-in **Check-In** replaces long login forms and forgotten passwords with a one-click sign-in flow built for checkout and repeat purchases. ## Who this is for [#who-this-is-for] Merchants who want faster account access at checkout and across the storefront without maintaining password infrastructure. ## How it works [#how-it-works] 1. Shoppers enter an email or phone number at sign-in or checkout. 2. Bolt sends a secure one-time code or prompts for a passkey. 3. Bolt ID verifies the shopper and loads saved profile data. 4. Returning shoppers complete checkout in fewer steps on your store and across the Bolt network. ## Get started [#get-started] | Task | Link | | --------------------------------------- | ---------------------------------------------- | | Enable passwordless login on your store | [SSO Commerce](/products/add-ons/sso-commerce) | | Add biometric login on mobile | [Passkeys](/products/add-ons/passkeys) | | Shopper account overview | [Shop with Bolt](/shoppers/shop-with-bolt) | | Identity layer details | [Bolt ID](/products/bolt-id) | ## Related [#related] * [Bolt ID](/products/bolt-id) * [User Network](/products/user-network) * [Bolt Checkout](/products/checkout) Markdown: https://help.boltapp.com/llms/products/check-in.md --- # Convert with the User Network > Recognize millions of Bolt shoppers instantly for one-click checkout, saved profiles, and higher conversion. Source: https://help.boltapp.com/products/user-network The **User Network** is Bolt's cross-merchant shopper graph: recognized users check out faster with saved addresses, payment methods, and preferences wherever Bolt is enabled. ## Who this is for [#who-this-is-for] Merchants who want network effects from Bolt's shopper base: fewer form fields, higher returning-shopper conversion, and identity you do not have to build yourself. ## How it works [#how-it-works] 1. A shopper creates or links a Bolt account at any network store. 2. Bolt ID recognizes them on your storefront or embedded checkout. 3. Profile, address, and payment data prefill when the shopper opts in. 4. Fraud and identity signals improve with network-wide purchase history. ## Get started [#get-started] | Task | Link | | ----------------------------- | ---------------------------------------------------- | | Full hosted checkout | [Checkout](/products/checkout) | | Custom UI with network login | [Embeddable Checkout](/products/embeddable-checkout) | | Passwordless storefront login | [SSO Commerce](/products/add-ons/sso-commerce) | | Identity product overview | [Bolt ID](/products/bolt-id) | ## Related [#related] * [Check-In](/products/check-in) * [Checkout 2.0](/products/add-ons/checkout-2) * [Shop with Bolt](/shoppers/shop-with-bolt) Markdown: https://help.boltapp.com/llms/products/user-network.md --- # Bolt Payments > Implement Bolt Payments as your payment facilitator with consolidated reporting and settlement. Source: https://help.boltapp.com/products/payments **Bolt Payments** is an all-in-one payment solution for major card brands and alternative payment methods, with simplified reporting, reconciliation, settlement, and fee structures. ## Who this is for [#who-this-is-for] US-based merchants who want Bolt to act as payment facilitator instead of maintaining a separate PSP relationship. ## Payment models [#payment-models] Bolt offers two models: * **Bolt Payments**: Bolt supports card brands and APMs, handles PSP and acquiring relationships, and provides consolidated reporting and settlement. * **Payment gateway**: You keep your PSP relationship. Bolt transmits transaction data for processing. See [payment processors](https://help.boltapp.com/integrations/payment-processors/). ## Key benefits [#key-benefits] * Support for 30+ currencies and major APMs (Affirm, Afterpay, Amazon Pay, Apple Pay, Google Pay, Klarna, PayPal, Sezzle, and more) * PCI Level 1 compliance with tokenized payment data * Consolidated reporting and fee deduction at time of sale * Optional [Fraud Protection](/products/add-ons/fraud-protection) with chargeback guarantee Bolt Payments is available for merchants with US-based accounts. ## Platform support [#platform-support] | Product | Supported | | ------------------- | --------- | | Bolt Checkout | Yes | | Embeddable Checkout | No | | Checkout Everywhere | No | ## Get started [#get-started] | Task | Link | | --------------------------- | -------------------------------------------------------------------------------- | | Setup guide | [Bolt Pay setup](https://help.boltapp.com/add-ons/bolt-payments/bolt-pay-setup/) | | Alternative payment methods | [APM integrations](https://help.boltapp.com/integrations/alternative-payments/) | | Financials | [Statements and settlements](https://help.boltapp.com/financials/) | | Processor comparison | [Payment processors](https://help.boltapp.com/integrations/payment-processors/) | ## Related [#related] * [Bolt Checkout](/products/checkout) * [Fraud Protection](/products/add-ons/fraud-protection) * [Payout schedule](https://help.boltapp.com/financials/payout-schedule/) Markdown: https://help.boltapp.com/llms/products/payments.md --- # Build custom checkout with Checkout OS > Composable APIs for identity, payments, fraud, tax, and subscriptions when you control the checkout experience. Source: https://help.boltapp.com/products/checkout-os **Checkout OS** is Bolt's modular checkout platform: pick the APIs you need for identity, payments, fraud, and subscriptions without rebuilding billing infrastructure from scratch. ## Who this is for [#who-this-is-for] SaaS companies, app developers, and headless commerce teams that own the checkout UI and need production-ready payment and identity APIs. ## How it works [#how-it-works] 1. Choose modules: identity, payments, fraud review, subscriptions, or the full stack. 2. Integrate Bolt APIs and embedded components into your checkout flow. 3. Bolt handles PCI scope, network shopper recognition, and payment routing. 4. Configure products, webhooks, and dashboard settings as you scale. ## Get started [#get-started] | Task | Link | | ---------------------------------------------- | ---------------------------------------------------------------------- | | Payment fields and shopper accounts in your UI | [Embeddable Checkout](/products/embeddable-checkout) | | Embeddable API guide | [Embeddable checkout guide](/api-reference/guides/embeddable-checkout) | | Recurring billing APIs | [Subscriptions](/products/subscriptions) | | Choose an integration path | [Choose an integration](/getting-started/choose-integration) | ## Related [#related] * [Custom Payments](/products/custom-payments) * [Bolt for SaaS](/use-cases/saas) * [Bolt for App Developers](/use-cases/app-developers) Markdown: https://help.boltapp.com/llms/products/checkout-os.md --- # Configure custom payment flows > Plug in your processor, add payment methods, and adapt checkout as your business grows. Source: https://help.boltapp.com/products/custom-payments **Custom Payments** lets you choose how money moves: keep your existing processor through Bolt's gateway, use Bolt as payment facilitator, or mix methods as you scale. ## Who this is for [#who-this-is-for] Merchants and developers who need flexible payment routing, multiple processors, or alternative payment methods without replacing the rest of the Bolt stack. ## How it works [#how-it-works] 1. Choose **Bolt Payments** (Bolt as facilitator) or **payment gateway** (keep your PSP). 2. Connect processors and APMs in the Merchant Dashboard. 3. Bolt routes transactions and surfaces unified reporting where configured. 4. Add fraud review, subscriptions, or network checkout on top of your payment model. ## Payment models [#payment-models] | Model | Best for | | ---------------------------------------------------- | ------------------------------------------------------ | | [Bolt Payments](/products/payments) | US merchants who want Bolt to handle PSP and acquiring | | Payment gateway | Merchants keeping an existing processor relationship | | [Embeddable Checkout](/products/embeddable-checkout) | Custom UI with Bolt payment components | See [Payment processors](https://help.boltapp.com/integrations/payment-processors/) for processor-specific setup. ## Get started [#get-started] 1. [Create a merchant account](https://merchant.boltapp.com/onboarding/get-started) 2. Connect your processor in **Settings** > **Payments** 3. Run a test transaction and confirm it in the [Merchant Dashboard](https://merchant.boltapp.com) ## Related [#related] * [Checkout OS](/products/checkout-os) * [Fully Managed Fraud](/products/add-ons/fraud-protection) * [Stablecoins](/products/stablecoins) Markdown: https://help.boltapp.com/llms/products/custom-payments.md --- # Accept stablecoin payments > Borderless stablecoin checkout and payouts with faster settlement and lower cross-border fees. Source: https://help.boltapp.com/products/stablecoins **Stablecoins** on Bolt give merchants and shoppers a digital payment option with faster settlement, lower fees, and simpler cross-border payouts. ## Who this is for [#who-this-is-for] Marketplaces, global merchants, and platforms that want crypto-native checkout and payout rails alongside cards and alternative payment methods. ## How it works [#how-it-works] 1. Enable stablecoin payments through your Bolt merchant or Connect configuration. 2. Shoppers pay at checkout without traditional card or bank requirements where supported. 3. Bolt routes and settles funds through Bolt's payment infrastructure. 4. Platforms using [Bolt Connect](/products/connect) can offer stablecoin payouts to sellers. ## Get started [#get-started] | Task | Link | | ------------------------------ | --------------------------------------------- | | Marketplace and seller payouts | [Connect](/products/connect) | | Payment methods and processors | [Custom Payments](/products/custom-payments) | | Contact sales for availability | [sales@boltapp.com](mailto:sales@boltapp.com) | Stablecoin availability varies by merchant type, region, and product configuration. Contact your Bolt account team to confirm eligibility. ## Related [#related] * [Bolt Connect](/products/connect) * [Custom Payments](/products/custom-payments) * [Checkout 2.0](/products/add-ons/checkout-2) Markdown: https://help.boltapp.com/llms/products/stablecoins.md --- # Protect orders with Fraud Protection > Bolt's fraud product and review process with scoring methodology and chargeback indemnification. Source: https://help.boltapp.com/products/add-ons/fraud-protection **Bolt Fraud Protection** flags risky orders for manual review and indemnifies transactions that charge back as fraudulent. ## Who this is for [#who-this-is-for] Merchants on Bolt Checkout or Embeddable Checkout who want chargeback guarantee and outsourced fraud review. ## How it works [#how-it-works] * Bolt scores every card transaction using network-wide fraud models. * Risky orders are flagged for manual review by Bolt's in-house analysts. * Approved transactions that later charge back as fraud are indemnified. * View risk scores and fraud tools in the [Merchant Dashboard](https://merchant.boltapp.com). ## Platform support [#platform-support] | Product | Supported | | ------------------- | --------- | | Bolt Checkout | Yes | | Embeddable Checkout | Yes | | SSO Commerce | No | | Checkout Everywhere | No | Contact your Customer Success Manager (CSM) to enable Fraud Protection. ## Get started [#get-started] | Task | Link | | ------------------------ | ------------------------------------------------------------------------------- | | Fraud review process | [Fraud review guide](https://help.boltapp.com/add-ons/bolt-fraud/fraud-review/) | | Order rejection settings | [Order rejection](https://help.boltapp.com/add-ons/bolt-fraud/order-rejection/) | | Dashboard fraud tools | [Fraud & risk ops](https://help.boltapp.com/fraud/) | | View risk scores | [View risk scores](/dashboard/risk-scores) | ## Related [#related] * [Risk Assessment Scoring](/products/add-ons/risk-assessment-scoring): scores without indemnification * [Bolt Payments](/products/payments): integrated risk and chargeback services * [Bolt Checkout](/products/checkout) Markdown: https://help.boltapp.com/llms/products/add-ons/fraud-protection.md --- # Assess transaction risk with Risk Assessment Scoring > Identify and assess risk for transactions that are not indemnified by Bolt Fraud Protection. Source: https://help.boltapp.com/products/add-ons/risk-assessment-scoring **Risk Assessment Scoring** provides fraud signals at the transaction level. Use it with [Fraud Protection](/products/add-ons/fraud-protection) for transparency, or alone if you do not need chargeback indemnification. ## Who this is for [#who-this-is-for] Merchants who want per-transaction risk scores and factor breakdowns in the dashboard, with or without full Fraud Protection. ## How it works [#how-it-works] Each merchant-liable card transaction includes a risk score and breakdown of influencing factors from the same models that power Fraud Protection. Bolt identifies the factors with the highest impact on each score. Bolt only provides risk scoring for card transactions. Alternative payment method transactions are not assessed. ## Benefits [#benefits] **Without Fraud Protection:** * Understand what makes a transaction risky * Gain insight into chargeback patterns * Reduce fraud losses through better manual reviews **With Fraud Protection:** * See how Bolt's team evaluates transaction risk * Understand indemnification decisions per transaction ## Platform support [#platform-support] Contact your Customer Success Manager (CSM) to enable Risk Assessment Scoring. ## Get started [#get-started] | Task | Link | | ------------------------------ | --------------------------------------------------------------------- | | View risk scores | [View risk scores](/dashboard/risk-scores) | | Dashboard risk scores (legacy) | [Risk scores guide](https://help.boltapp.com/fraud/view-risk-scores/) | | Fraud Protection | [Fraud Protection](/products/add-ons/fraud-protection) | ## Related [#related] * [Fraud Protection](/products/add-ons/fraud-protection) * [Bolt Checkout](/products/checkout) * [Investigate transactions](/dashboard/investigate-transactions) Markdown: https://help.boltapp.com/llms/products/add-ons/risk-assessment-scoring.md --- # Let shoppers track orders > Confirmation page, order tracking page, and SMS updates for post-purchase visibility. Source: https://help.boltapp.com/products/add-ons/order-tracking **Bolt Order Tracking** gives shoppers post-purchase visibility through a confirmation page, branded tracking page, and SMS status updates. ## Who this is for [#who-this-is-for] Merchants on Bolt Checkout who want shoppers to opt into tracking, view order status, and receive SMS updates. ## Suite features [#suite-features] | Feature | Description | | -------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | | [Confirmation page](https://help.boltapp.com/add-ons/bolt-order-tracking/confirmation-page/) | Post-checkout landing page where shoppers choose tracking options | | [SMS updates](https://help.boltapp.com/add-ons/bolt-order-tracking/sms-updates/) | Real-time shipping and delivery notifications with links to the tracking page | | [Tracking page](https://help.boltapp.com/add-ons/bolt-order-tracking/tracking-page/) | Mobile-friendly page with order summary, contact info, and status | Checkout customizations (logos, colors) also apply to the order tracking page. ## Couriers [#couriers] Bolt uses **AfterShip** by default. See their [courier list](https://www.aftership.com/couriers/). If your courier is unsupported, Bolt can use [EasyPost](https://www.easypost.com/carriers). ## Get started [#get-started] | Task | Link | | ----------------------- | -------------------------------------------------------------------------------------------- | | Installation | [Enable Order Tracking](https://help.boltapp.com/add-ons/bolt-order-tracking/installation/) | | Confirmation page setup | [Confirmation page](https://help.boltapp.com/add-ons/bolt-order-tracking/confirmation-page/) | | SMS setup | [SMS updates](https://help.boltapp.com/add-ons/bolt-order-tracking/sms-updates/) | Contact your CSM to enable Order Tracking. ## Related [#related] * [Bolt Checkout](/products/checkout) * [SSO Commerce](/products/add-ons/sso-commerce) * [Shopper Assistant](/products/add-ons/shopper-assistant) Markdown: https://help.boltapp.com/llms/products/add-ons/order-tracking.md --- # Unify logins with SSO Commerce > Link Bolt Accounts with your storefront for passwordless login and one-click checkout. Source: https://help.boltapp.com/products/add-ons/sso-commerce **SSO Commerce** connects your store accounts with the Bolt Shopper Network so shoppers get passwordless login, saved payment data, and one-click checkout across Bolt Network stores. ## Who this is for [#who-this-is-for] Merchants who want shoppers to log in once and check out fast, with Bolt handling account creation and PCI-scoped authentication. ## Key features [#key-features] **For merchants:** * Passkey biometric sign-in on mobile ([Passkeys](/products/add-ons/passkeys)) * Automatic store account creation when shoppers save checkout data * Linked accounts for existing store customers * Higher conversion for first-time Bolt Network shoppers **For shoppers:** * Saved address and payment information * OTP and passkey login without passwords * One-click checkout across Bolt Network retailers * Tokenized payment data in a central vault SSO Commerce also unlocks [Embedded Shopper Accounts Editing](https://help.boltapp.com/add-ons/sso-commerce/embedded-shopper-accounts-editing/) and [Global Consent Collection](https://help.boltapp.com/add-ons/sso-commerce/global-consent-collection/). ## Get started [#get-started] Bolt Accounts are supported natively by Bolt Checkout with no extra setup. For Embeddable Checkout: | Task | Link | | --------------------- | --------------------------------------------------------------------------------------- | | Installation | [SSO installation](https://help.boltapp.com/add-ons/sso-commerce/installation/) | | SSO FAQ | [SSO FAQ](https://help.boltapp.com/add-ons/sso-commerce/sso-faq/) | | Mobile SSO | [SSO for mobile app](https://help.boltapp.com/add-ons/sso-commerce/sso-for-mobile-app/) | | Shopper account setup | [Shopper account guide](https://help.boltapp.com/shoppers/account/) | ## Related [#related] * [Embeddable Checkout](/products/embeddable-checkout) * [Account Linking](/products/add-ons/account-linking): email-based account linking * [Frontend events](/developers/frontend-events#sso-sign-in-modal): sign-in events from `account.js` * [Passkeys](/products/add-ons/passkeys) * [Order Tracking](/products/add-ons/order-tracking) Markdown: https://help.boltapp.com/llms/products/add-ons/sso-commerce.md --- # Engage shoppers with Shopper Assistant > A floating widget that delivers cart, order, and recommendation experiences across your storefront. Source: https://help.boltapp.com/products/add-ons/shopper-assistant **Shopper Assistant** is a floating module on every storefront page that guides shoppers to orders, recommendations, and checkout. ## Who this is for [#who-this-is-for] BigCommerce and Adobe Commerce (Magento 2) merchants who want to capture intent earlier and convert guest shoppers to logged-in shoppers. Shopper Assistant is currently available for BigCommerce and Magento 2 / Adobe Commerce only. ## Merchant benefits [#merchant-benefits] * Increase conversion with sign-up discounts * Increase retention with a personalized experience * Capture intent earlier in the shopper journey ## Widget tabs [#widget-tabs] | Tab | Description | | --------------- | ---------------------------------------------------------- | | Your Orders | Order history, tracking status, reorder, and track actions | | Recently Viewed | Products the shopper viewed for quick repurchase | | Buy It Again | Add recently purchased items to cart (BigCommerce) | ## Platform feature parity [#platform-feature-parity] | Feature | BigCommerce | Magento 2 / Adobe Commerce | | ------------------ | ----------- | -------------------------- | | Bolt Account login | Yes | Yes | | View past orders | Yes | No | | Reorder | Yes | No | | Buy it again | Yes | No | | Customization | Yes | No | ## Get started [#get-started] | Task | Link | | ----------------- | -------------------------------------------------------------------------------------------------- | | Installation | [Shopper Assistant installation](https://help.boltapp.com/add-ons/shopper-assistant/installation/) | | Settings | [Settings guide](https://help.boltapp.com/add-ons/shopper-assistant/settings/) | | Add to storefront | [Add assistant](https://help.boltapp.com/add-ons/shopper-assistant/add/) | ## Related [#related] * [SSO Commerce](/products/add-ons/sso-commerce) * [Order Tracking](/products/add-ons/order-tracking) * [Bolt Checkout](/products/checkout) Markdown: https://help.boltapp.com/llms/products/add-ons/shopper-assistant.md --- # Link store accounts to Bolt Accounts > Connect existing store accounts to Bolt Accounts and register new shoppers with a simple checkbox. Source: https://help.boltapp.com/products/add-ons/account-linking **Account Linking** lets merchants tap into Bolt Shopper Accounts and link them with store accounts using the shopper's email address. ## Who this is for [#who-this-is-for] Developers who have implemented [Embeddable Checkout](/products/embeddable-checkout) and want shoppers to check out with Bolt without logging into the store separately. ## Prerequisites [#prerequisites] * [Embeddable Checkout](/products/embeddable-checkout) implemented on your storefront * OAuth flow configured with `openid` scope ## How it works [#how-it-works] 1. Shopper enters email on your storefront. 2. Bolt identifies whether a Bolt Account exists and prompts OTP login. 3. After authorization, Bolt returns an ID token via OAuth. 4. Your backend validates the email from the ID token against store accounts and logs the shopper in. ## Implementation overview [#implementation-overview] | Step | Task | | ---- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 1 | Exchange authorization code for ID token via [OAuth token endpoint](https://help.boltapp.com/api-bolt/#tag/OAuth/operation/OAuthToken) with `scope` including `openid` | | 2 | Fetch Bolt's RSA public key from `/.well-known/openid-configuration` | | 3 | Parse the ID token to get the Bolt Account email | | 4 | Match email to a store account and trigger store login | When you set up Bolt Accounts, require shoppers to input their email when logging into your store account. For the full OAuth and JWT validation flow, see the [Account Linking guide](https://help.boltapp.com/add-ons/account-linking/) and [access shopper data](https://help.boltapp.com/products/checkout/embeddable-checkout/api-implementation/endpoints/access-shopper-data/). ## Related [#related] * [Embeddable Checkout](/products/embeddable-checkout) * [SSO Commerce](/products/add-ons/sso-commerce): full single sign-on vs email linking * [Authentication](/api-reference/authentication) * [OpenID Connect](https://help.boltapp.com/developers/bolt-oauth/) Markdown: https://help.boltapp.com/llms/products/add-ons/account-linking.md --- # Recover abandoned carts > Enable one-click checkout from automated notifications when shoppers abandon their cart. Source: https://help.boltapp.com/products/add-ons/abandoned-carts **Abandoned Carts** sends automated notifications that bring shoppers back to complete their purchase. Manage emails and view analytics in the Merchant Dashboard. ## Who this is for [#who-this-is-for] Merchants on Bolt Checkout who want to recover revenue from shoppers who leave before completing checkout. ## How it works [#how-it-works] When a shopper with a Bolt account abandons a cart, Bolt logs the cart and device. After a waiting period (to rule out accidental closes), Bolt sends abandoned cart notifications. Bolt adheres to **CAN-SPAM** regulations. Typical flow: shopper abandons cart on desktop, email sent after 2 hours, follow-up email after 24 hours. After enabling the feature, view [Abandoned Carts Analytics](https://help.boltapp.com/dashboard/analytics/abandoned-carts/). ## Message statuses [#message-statuses] | Status | Description | | --------- | ----------------------------------------------- | | New | Notification queued to send | | Skipped | Not sent due to duplication or shopper returned | | Emailed | Notification sent | | Recovered | Notification converted to completed checkout | ## Get started [#get-started] 1. Log in to the [Merchant Dashboard](https://merchant.boltapp.com) 2. Click **Abandoned Carts** in the left navigation 3. Click **Get Started Today**. A Bolt representative enables the feature After enablement, open **Merchant Dashboard** > **Abandoned Carts** to review abandon rate, recovered rate, and per-cart details. ## Related [#related] * [Bolt Checkout](/products/checkout) * [Shopper Assistant](/products/add-ons/shopper-assistant) * [Dashboard tour](/dashboard/dashboard-tour) Markdown: https://help.boltapp.com/llms/products/add-ons/abandoned-carts.md --- # Enable passkey login for shoppers > Let shoppers log into Bolt on your store using mobile biometric passkeys with no extra setup. Source: https://help.boltapp.com/products/add-ons/passkeys **Passkeys** let shoppers sign into your store using biometric data from a face or fingerprint scan instead of a password or one-time code. ## Who this is for [#who-this-is-for] Merchants using Bolt Checkout, Embeddable Checkout, or SSO Commerce who want faster mobile login. ## Set up [#set-up] No additional implementation is required. Passkeys become available after you implement one of: * [Embeddable Checkout](/products/embeddable-checkout) * [Bolt Checkout](/products/checkout) * [SSO Commerce](/products/add-ons/sso-commerce) ## Shopper experience [#shopper-experience] Shoppers opt into Passkeys when logging into your storefront. They complete setup in the Bolt Shopper Dashboard, then sign in quickly on subsequent mobile visits. Passkeys are currently only available on mobile devices. ## Related [#related] * [SSO Commerce](/products/add-ons/sso-commerce) * [Embeddable Checkout](/products/embeddable-checkout) * [Shopper passkeys guide](https://help.boltapp.com/shoppers/account/passkeys/) * [One-time codes](https://help.boltapp.com/shoppers/security-privacy/one-time-code/) Markdown: https://help.boltapp.com/llms/products/add-ons/passkeys.md --- # Optimize checkout with Checkout 2.0 > AI-powered checkout orchestration that personalizes payment ranking and product recommendations. Source: https://help.boltapp.com/products/add-ons/checkout-2 **Bolt Checkout 2.0** is an AI-powered checkout orchestration platform that personalizes the checkout experience per shopper in real time based on behavior, device, cart contents, and customer history. ## Who this is for [#who-this-is-for] Existing or new Bolt merchants who want intelligent checkout optimization beyond standard Bolt Checkout. ## Key features [#key-features] * **Payment method ranking**: surfaces payment methods based on shopper purchase history (e.g., preferred card brand first) * **Product recommendations**: suggests complementary items at checkout (e.g., pillows with a mattress) The Bolt team is actively developing additional Checkout 2.0 features. ## Get started [#get-started] **Existing merchants:** Contact your Bolt Account Executive to request an upgrade. The Bolt team completes the upgrade. **New merchants:** Complete [onboarding](https://merchant.boltapp.com/onboarding/smart-checkout) or contact [sales@boltapp.com](mailto:sales@boltapp.com). See [Checkout 2.0 pricing](https://www.boltapp.com/pricing) for plan details. ## Related [#related] * [Bolt Checkout](/products/checkout) * [Bolt Payments](/products/payments) * [Choose an integration](/getting-started/choose-integration) Markdown: https://help.boltapp.com/llms/products/add-ons/checkout-2.md # Documentation: Commerce platforms --- # Move your integration to boltapp.com > Bolt is moving its services from bolt.com to boltapp.com. Review the breaking changes, then upgrade your cart platform plugin and update anything on your side that names a bolt.com host. Source: https://help.boltapp.com/platforms/boltapp-domain-migration ## Overview [#overview] Bolt is moving its services from `bolt.com` to `boltapp.com`. Every Bolt host your store talks to gets a `boltapp.com` twin: `api.boltapp.com`, `connect.boltapp.com`, `account.boltapp.com`, and `merchant.boltapp.com`, with the same `-sandbox` variants as before. The [Environments](/api-reference/environments) page lists the current sandbox and production URLs. The `boltapp.com` hosts serve all Bolt traffic. Anything still pointing at a `bolt.com` host stops working once that host is deprecated. Older Bolt plugins have the `bolt.com` hosts built in, so the fix for most stores is a plugin upgrade. **`boltapp.com` is Bolt's permanent domain.** Bolt's own site has already moved, and `api.bolt.com`, `connect.bolt.com`, and every other `bolt.com` sub-domain will be deprecated. Once they are, a store that still uses `bolt.com` hosts loses the Bolt button, checkout, and the plugin's calls to the Bolt API. Upgrade now to the version listed for your platform under [Choose your platform](#choose-your-platform) to move your store to `boltapp.com`. ## What is changing [#what-is-changing] | Area | Before | After | What breaks if you do nothing | | -------------------------------------------------- | ----------------------------------------------------- | ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | | Bolt API your plugin calls | `api.bolt.com`, `api-sandbox.bolt.com` | `api.boltapp.com`, `api-sandbox.boltapp.com` | Order creation, authorization, refunds, and every other server call from the plugin fail whenever `bolt.com` is unavailable. | | Checkout scripts on your storefront | `connect.bolt.com/track.js`, `connect.js`, `embed.js` | `connect.boltapp.com/...` | The Bolt button and checkout modal stop loading. | | Shopper accounts | `account.bolt.com` | `account.boltapp.com` | Shopper login, one-click checkout, and SSO Commerce stop working. | | Merchant Dashboard | `merchant.bolt.com` | [merchant.boltapp.com](https://merchant.boltapp.com) | Bookmarks and links in older plugin admin screens stop resolving. | | Emails from Bolt | `@bolt.com` senders | `@boltapp.com` senders | Mail rules that only allow `@bolt.com` drop Bolt notifications. | | User-Agent on calls Bolt makes to your store | `Bolt - https://www.bolt.com` | `Bolt - https://www.boltapp.com` | Firewall or bot rules that match the old string block Bolt's webhook and shipping calls. | | Webhook URL you registered in your payment gateway | Bolt webhook URL on a `bolt.com` host | The same URL on a `boltapp.com` host | Bolt stops receiving transaction updates from your processor. | ## What is not changing [#what-is-not-changing] * **Your API keys, signing secret, and publishable keys.** Upgrading does not change your credentials. Keep the same values in the plugin. * **Your callback URLs.** The webhook, shipping, tax, and create-order URLs under **Administration > API > Merchant Callbacks** are on your domain, so they stay as they are. * **Bolt's outbound IP addresses.** If you allowlist Bolt's IPs for callbacks or a payment processor, that list is the same. See the processor setup pages under [Payment processors](/integrations/payment-processors). * **Your SSO signing keys, audience, and shopper claims.** Only the `iss` claim varies by domain, and code that validates it needs to accept both. See [Changes that affect every store](#changes-that-affect-every-store). * **Your Trusted Domains.** The storefront domains you registered with Bolt are your own domains and need no change. ## Changes that affect every store [#changes-that-affect-every-store] Work through these regardless of platform. Do them before the plugin upgrade so the new hosts are reachable the moment the plugin switches. If your storefront sends a `Content-Security-Policy` header, you must add the `boltapp.com` hosts next to the existing `bolt.com` entries. A policy that only names `bolt.com` blocks the upgraded plugin's scripts and API calls. Keep both domains until Bolt confirms `bolt.com` is retired. | Directive | Add | | ------------- | ---------------------------------------------------------------------------------------------------------- | | `script-src` | `connect.boltapp.com`, `connect-sandbox.boltapp.com`, `account.boltapp.com`, `account-sandbox.boltapp.com` | | `frame-src` | `connect.boltapp.com`, `connect-sandbox.boltapp.com`, `account.boltapp.com`, `account-sandbox.boltapp.com` | | `connect-src` | `api.boltapp.com`, `api-sandbox.boltapp.com`, `connect.boltapp.com`, `connect-sandbox.boltapp.com` | | `img-src` | `img.boltapp.com`, `img-sandbox.boltapp.com`, `assets.boltapp.com` | A wildcard `*.boltapp.com` entry in each directive is a simpler alternative and is what Bolt's Salesforce Composable guide uses. **Expected result:** the browser console shows no CSP violations for `boltapp.com` after the plugin upgrade. If your servers restrict outbound HTTPS, allow `api.boltapp.com` and `api-sandbox.boltapp.com` alongside the current `api.bolt.com` hosts. **Expected result:** `curl -I https://api.boltapp.com` from your web server returns an HTTP response instead of a timeout. If a WAF or bot rule identifies Bolt by its User-Agent, match on `Bolt - https://www.boltapp.com` as well as the old value. Bolt's IP addresses have not changed, so IP-based rules need no edit. **Expected result:** the webhook and shipping calls Bolt makes to your store keep returning 2xx in your server logs. If your code checks the `iss` claim on Bolt SSO or OpenID Connect tokens, accept all four values. A token can name either domain as its issuer, so keep the `bolt.com` values even after your store moves to `boltapp.com`: ```text title="Accepted iss values" https://bolt.com https://api.bolt.com https://boltapp.com https://api.boltapp.com ``` Bolt's own plugins do this; the Magento 2 patch widens the check the same way. Apply the same rule to any regular expression or allowlist that matches Bolt URLs so a `boltapp.com` host is not rejected. **Expected result:** a shopper signs in with Bolt and your code accepts the token no matter which domain issued it. If you registered a Bolt webhook URL in your payment processor's dashboard, that URL is a Bolt host and it moves with the migration. Open **Settings > Payments > Manage** your processor in the [Merchant Dashboard](https://merchant.boltapp.com), copy the webhook URL shown there, and paste it into your processor's webhook configuration. The setup page for your processor under [Payment processors](/integrations/payment-processors) shows where each one keeps that setting. **Expected result:** a test event from your processor reaches Bolt, and the transaction status updates in the Merchant Dashboard. This applies only if you registered your own Google Pay merchant ID rather than using Bolt's. The Google Pay console lists the websites allowed to show the Google Pay button, and checkout served from `connect.boltapp.com` is a different origin from `connect.bolt.com`. Add `connect.boltapp.com` to your integration in the [Google Pay and Wallet Console](https://pay.google.com/business/console), and keep the existing `connect.bolt.com` entry until Bolt retires it. **Expected result:** the Google Pay button renders in Bolt checkout served from `connect.boltapp.com`. Allow `@boltapp.com` senders in any mail rule that currently allows only `@bolt.com`. Update saved links to the Merchant Dashboard to [merchant.boltapp.com](https://merchant.boltapp.com) and [merchant-sandbox.boltapp.com](https://merchant-sandbox.boltapp.com). ## Choose your platform [#choose-your-platform] Work through [Changes that affect every store](#changes-that-affect-every-store) first. Then follow the section for your platform. Each one lists the versions in use, what carries `boltapp.com`, and the steps to get there. * [Adobe Commerce and Magento 2](#adobe-commerce-and-magento-2) * [BigCommerce](#bigcommerce) * [Salesforce Commerce Cloud](#salesforce-commerce-cloud) * [WooCommerce](#woocommerce) * [Shopify](#shopify) * [SAP Commerce Cloud, Solidus, and Volusion](#sap-commerce-cloud-solidus-and-volusion) * [Direct API and custom carts](#direct-api-and-custom-carts) ### Adobe Commerce and Magento 2 [#adobe-commerce-and-magento-2] The `boltpay/bolt-magento2` plugin has the Bolt hosts compiled into `Helper/Config.php`. Every release up to and including 2.27.8 points at `bolt.com`. Version 3.0.0 and later already point at `boltapp.com` and need no change. For each earlier version in the table below, Bolt publishes a patch file in the [patches/boltapp-domains](https://github.com/BoltApp/bolt-magento2/tree/master/patches/boltapp-domains) folder of the plugin repository. The patch keeps you on the version you run today. You apply it with the `patch` command, following Adobe's [command-line method for applying patches](https://experienceleague.adobe.com/en/docs/commerce-operations/upgrade-guide/patches/apply#command-line). If Composer manages the plugin, you also register the patch with the [cweagans/composer-patches](https://github.com/cweagans/composer-patches) plugin, so that Composer applies it again whenever it reinstalls the plugin. Every edit in the patch belongs to the move to `boltapp.com`. It adds the `boltapp.com` hosts to `csp_whitelist.xml` next to the existing `bolt.com` entries and accepts SSO tokens from both domains, so the plugin keeps working on either domain during the transition. It also accepts `boltapp.com` custom URLs and updates the `bolt.com` link in the copyright header of every plugin file, which is why the patch touches several hundred files. Version 2.13.0 predates the plugin's CSP policy and its SSO support, so its patch carries neither of those changes. **Plugin versions in use and their boltapp.com patch** Find the version you run today and use the patch file on its row. Each patch is built for that version alone, and another version's patch fails its dry run. | Version in use | Patch file | | -------------- | -------------------------------------------- | | 2.27.8 | `bolt-magento2-2.27.8-boltapp-domains.patch` | | 2.27.7 | `bolt-magento2-2.27.7-boltapp-domains.patch` | | 2.27.5 | `bolt-magento2-2.27.5-boltapp-domains.patch` | | 2.27.4 | `bolt-magento2-2.27.4-boltapp-domains.patch` | | 2.27.3 | `bolt-magento2-2.27.3-boltapp-domains.patch` | | 2.27.2 | `bolt-magento2-2.27.2-boltapp-domains.patch` | | 2.27.1 | `bolt-magento2-2.27.1-boltapp-domains.patch` | | 2.26.4 | `bolt-magento2-2.26.4-boltapp-domains.patch` | | 2.26.1 | `bolt-magento2-2.26.1-boltapp-domains.patch` | | 2.25.2 | `bolt-magento2-2.25.2-boltapp-domains.patch` | | 2.24.1 | `bolt-magento2-2.24.1-boltapp-domains.patch` | | 2.24.0 | `bolt-magento2-2.24.0-boltapp-domains.patch` | | 2.13.0 | `bolt-magento2-2.13.0-boltapp-domains.patch` | If your version is not in the table, upgrade to 3.0.0. If `composer show` reports a `dev-` version, such as `dev-2.27.8.yourstore`, your store runs a build Bolt made for it, and the patch for its base version may not fit. [Contact Bolt support](/support) before you patch it. **Which steps you need** * **Composer installed the plugin**, so it lives in `vendor/boltpay/bolt-magento2`: every step. Step 2 checks that the patch fits and patches the plugin right away. Step 3 has Composer apply the patch again each time it reinstalls the plugin, which keeps the change in place and carries it to production. * **You installed the plugin by hand**, so it lives in `app/code/Bolt/Boltpay`: every step except step 3. Composer never replaces `app/code`, so committing the patched files keeps the change. From your Magento root: ```bash title="Terminal" composer show boltpay/bolt-magento2 | grep versions ``` A version in the output means Composer installed the plugin under `vendor/boltpay/bolt-magento2`. If Composer reports that the package is not found, the plugin was installed by hand under `app/code/Bolt/Boltpay`: read the version from `composer.json` in that folder. You can also read the version in **Stores > Configuration > Sales > Payment Methods > Bolt Pay**. **Expected result:** you know your version and where the plugin is installed. From your Magento root, download the patch for your version, dry run it, then apply it in maintenance mode and rebuild the store. Replace `VERSION` with your version: ```bash title="Terminal" # Download the patch for your version, if you do not have it yet curl -fLO https://raw.githubusercontent.com/BoltApp/bolt-magento2/master/patches/boltapp-domains/bolt-magento2-VERSION-boltapp-domains.patch # Check that the patch applies, without changing any file patch --dry-run -p1 -d vendor/boltpay/bolt-magento2 < bolt-magento2-VERSION-boltapp-domains.patch # Enable maintenance mode php bin/magento maintenance:enable # Apply the patch patch -p1 -d vendor/boltpay/bolt-magento2 < bolt-magento2-VERSION-boltapp-domains.patch # Run upgrade, compilation, and static deployment php bin/magento setup:upgrade php bin/magento setup:di:compile php bin/magento setup:static-content:deploy -f en_US php bin/magento cache:clean # Disable maintenance mode after all steps succeed php bin/magento maintenance:disable ``` If the plugin is under `app/code/Bolt/Boltpay`, use that path after `-d` in both `patch` commands. Keep `-p1` and `-d`: the patch file names paths inside the plugin, such as `Helper/Config.php`, so Adobe's plain `patch < FILE` from the Magento root cannot find the files and asks `File to patch:` for each one. Go past the dry run only if every line it prints starts with `checking file`. Anything else means the patch does not fit as it stands, and `patch` changes every file it can, so applying it would leave the plugin partly patched. If the dry run stops at a question, press Ctrl+C. Then: * `File to patch:` means the folder after `-d` is not your plugin folder. * A line containing `FAILED` means the patch does not match your version, or a plugin file it changes has local edits. Compare that file with the patch. * `Reversed (or previously applied) patch detected!` means the patch is already applied. Skip the apply command. `setup:static-content:deploy -f en_US` deploys the `en_US` locale only. If your storefronts or admin users work in other locales, list them all, for example `en_US fr_FR`, or leave the list out to deploy every locale in use. **Expected result:** the first line of the downloaded file names your plugin version, `grep api.boltapp.com vendor/boltpay/bolt-magento2/Helper/Config.php` prints the production API URL, the storefront renders the Bolt button, and the browser network tab shows calls to `connect.boltapp.com`. This step applies when Composer manages the plugin. Composer treats `vendor/` as its own: whenever it installs the plugin again, it unpacks the original release over your patched files. That happens whenever `vendor/` is built from scratch, as most deployment pipelines and every new server do, and each time it does, the store goes back to the `bolt.com` hosts without any error. Register the patch with the composer-patches plugin, and Composer applies it, with the same `patch` command as the previous step, every time it installs the plugin. If the plugin is under `app/code/Bolt/Boltpay`, skip this step and commit the patched files to your repository instead. From your Magento root, install composer-patches and move the patch file into your project: ```bash title="Terminal" composer require cweagans/composer-patches:^1.7 mkdir -p patches mv bolt-magento2-VERSION-boltapp-domains.patch patches/ ``` When Composer asks whether to trust `cweagans/composer-patches`, answer `y`. Then add the patch to the `extra` section of your root `composer.json`, next to the keys already there: ```json title="composer.json" "extra": { "composer-exit-on-patch-failure": true, "patches": { "boltpay/bolt-magento2": { "Move Bolt hosts to boltapp.com": "patches/bolt-magento2-VERSION-boltapp-domains.patch" } } } ``` Apply it through Composer and record the change in `composer.lock`: ```bash title="Terminal" composer install composer update --lock ``` `composer install` removes the plugin, installs it again, and applies the patch. The plugin files end up the same as after the previous step, so you do not need to rebuild the store again. `composer-exit-on-patch-failure` makes Composer stop with an error if the patch ever fails to apply, instead of leaving the plugin on `bolt.com`. Commit `composer.json`, `composer.lock`, and the `patches` folder so that every environment applies the same patch. The `^1.7` constraint keeps composer-patches on its 1.x line, which works with every PHP and Composer version these plugin releases support. If your project already uses composer-patches 2.x, keep it: skip `composer require`, add the same `patches` entry, and run `composer patches-relock`, `composer patches-repatch`, and `composer update --lock` instead of the two commands above. When you later upgrade the plugin, for example to 3.0.0, remove the `patches` entry in the same change. The patch fits only the version it was made for, so Composer stops with an error otherwise. **Expected result:** `composer install` prints `Applying patches for boltpay/bolt-magento2` followed by the patch file, and `grep api.boltapp.com vendor/boltpay/bolt-magento2/Helper/Config.php` still prints the production API URL. From 2.25.2 on, the patch points the Magento integration record at `api.boltapp.com`. Open **Stores > Configuration > Sales > Payment Methods > Bolt Pay > Advanced Options** and choose **Create and send API keys to Bolt** again so Magento re-authorizes the connection. Versions 2.24.1, 2.24.0, and 2.13.0 have no Bolt integration, so skip this step on them. **Expected result:** the integration under **System > Integrations** shows **Active**, and a sandbox test order syncs back to Magento. If a developer previously set `payment/boltpay/custom_api`, `custom_cdn`, `custom_account`, or `custom_merchant_dash` to a `bolt.com` host, clear them or point them at the `boltapp.com` equivalent: ```bash title="Terminal" php bin/magento config:show payment/boltpay/custom_api php bin/magento config:set payment/boltpay/custom_api "" ``` Most stores never set these, and the admin form hides the fields, so read them from the command line rather than looking for them in the UI. Note that the admin form's own validation still only accepts `bolt.me` URLs, so set any replacement value with `bin/magento config:set` instead of typing it into the admin. Place a sandbox order with a [test card](/getting-started/test-cards) and confirm it appears in the sandbox Merchant Dashboard. Then repeat the change in production during a low-traffic window. Release the committed files through your normal deployment; with composer-patches, its `composer install` applies the patch. Then run the Magento commands from the apply step between `maintenance:enable` and `maintenance:disable`, and place a low-value live order. If you maintain your own `csp_whitelist.xml`, add the `boltapp.com` hosts from the table above. The patched plugin's own policy already includes both domains. The [Adobe Commerce setup guide](/platforms/adobe) covers the full configuration if you need to reinstall. **Magento 1 is not covered.** The separate `bolt-magento1` plugin ended at 2.10.0 and is no longer maintained, so there is no `boltapp.com` release and no patch for it. If your store still runs Bolt on Magento 1, [contact Bolt support](/support) before the cutover to plan a move to a supported platform. ### BigCommerce [#bigcommerce] Bolt runs the BigCommerce app itself, so there is no plugin package to upgrade. Three things on your side can still reference `bolt.com`: Bolt's tracking script, Bolt scripts you added in Script Manager, and Bolt script tags in your theme. **Plugin versions in use and their boltapp.com patch** Bolt hosts the BigCommerce app, so there is no plugin version to track. The only versioned Bolt code on your side is the Bolt script tags you added, in Script Manager or in your theme. | Component | Version | Action | | ---------------------------------------------- | ------------------------------- | ----------------------------------------------- | | Bolt app (BigCommerce App Marketplace) | Always current, managed by Bolt | None. | | Bolt scripts you added in Script Manager | Whatever each script references | Point them at `connect.boltapp.com`, see below. | | Theme script tags (`connect-bigcommerce.js`) | Whatever your theme references | Point them at `connect.boltapp.com`, see below. | | Bolt Checkout for BigCommerce WordPress plugin | 1.0.0 | No update exists. Contact Bolt. | Bolt installs `track.js` into your store through the BigCommerce Script Manager as an entry named `bolt-track`. New installs already point at `connect.boltapp.com`. Stores installed before the migration keep the old script until Bolt re-installs it. Bolt runs this update for all stores; you do not need to act, but you can confirm the host under **Storefront > Script Manager**. **Expected result:** the `bolt-track` entry in Script Manager references `connect.boltapp.com`. Bolt updates only its own `bolt-track` entry. If you or your developer added a Bolt script through Script Manager, such as `connect-bigcommerce.js` or your own `track.js` tag, it keeps loading from `bolt.com` until you change it. Open **Storefront > Script Manager** and check every entry other than `bolt-track`. For each one whose **Script URL** or **Script contents** names a `bolt.com` host, select **Edit**, replace the host with the same name on `boltapp.com`, and select **Save**. For example, `connect.bolt.com` becomes `connect.boltapp.com` and `connect-sandbox.bolt.com` becomes `connect-sandbox.boltapp.com`. **Expected result:** no entry in Script Manager names a `bolt.com` host, and the browser's network tab shows those scripts loading from `boltapp.com`. If you followed the [BigCommerce installation guide](/platforms/bigcommerce-full) and pasted a `connect-bigcommerce.js` tag into your Cornerstone theme files, open **Storefront > My Themes > Advanced > Edit Theme Files** and change any `src` that reads `connect.bolt.com` or `connect-sandbox.bolt.com` to the `boltapp.com` host: ```html title="Theme template" ``` If you pasted a `track.js` tag into a template yourself, change its host the same way. Bolt updates only its own `bolt-track` entry in Script Manager. Check `cart.html`, `product.html`, the mini-cart template, and the **Footer Scripts** section. **Expected result:** the Bolt button renders on the cart page and the browser's network tab shows the scripts loading from `connect.boltapp.com`. If you run BigCommerce on WordPress with the **Bolt Checkout for BigCommerce** WordPress plugin, that plugin is version 1.0.0 from 2019 and has `bolt.com` built in with no update available. [Contact Bolt support](/support) before the cutover to plan a replacement. ### Salesforce Commerce Cloud [#salesforce-commerce-cloud] Every Bolt cartridge has the hosts compiled into cartridge code. Site preferences only select the environment, and the `bolt.http` service has no URL in its credential, so the only way to move a Salesforce Commerce Cloud (SFCC) storefront is to deploy an updated cartridge. Metadata imports and service definitions do not need to change. **Plugin versions in use and their boltapp.com patch** The cartridges do not report their version to Bolt, so these rows come from the cartridge releases rather than from traffic. Read the version from `package.json` in the cartridge or from **Administration > Site Development > Code Deployment**. | Integration | Repository | Release line in use | Version with boltapp.com | Release tag | Action | | ------------------------------- | -------------------------- | ------------------- | ------------------------ | -------------------------- | ------------------------------------------------------------- | | SFRA V2 (managed checkout) | `bolt-demandware-managed` | 25.1.x and older | 26.6.0 | `bolt-sfcc-managed-26.6.0` | Deploy 26.6.0. | | Embeddable Checkout | `bolt-demandware-embedded` | 23.5.x and older | 23.6.1 | `release-23.6.1` | Deploy 23.6.1. | | Legacy SiteGenesis or SFRA 21.x | `bolt-demandware` | 21.4.x | None planned | Move to SFRA V2 | This repository is deprecated. Contact Bolt to plan the move. | Fetch the tags from the repository for your integration, check out the release tag from the table, install dependencies, and build. Do not pull the head of the default branch; it may contain unreleased changes. For managed checkout (`bolt-demandware-managed`): ```bash title="Terminal" git clone https://github.com/BoltApp/bolt-demandware-managed.git cd bolt-demandware-managed git fetch --tags git checkout bolt-sfcc-managed-26.6.0 npm install npm run build ``` For Embeddable Checkout (`bolt-demandware-embedded`): ```bash title="Terminal" git clone https://github.com/BoltApp/bolt-demandware-embedded.git cd bolt-demandware-embedded git fetch --tags git checkout release-23.6.1 npm install npm run build ``` If you already have a clone, skip the `git clone` and `cd` lines and run the rest from the repository root. If you carry custom changes on top of the Bolt cartridge, create a branch from the release tag and reapply your changes there. **Expected result:** `git describe --tags` prints the release tag, the build completes, and the cartridge folders contain the compiled client scripts. Point `dw.json` at your sandbox, upload, then activate the new code version in **Administration > Site Development > Code Deployment**: ```bash title="Terminal" npm run upload:all ``` If you upload under a new code version name, re-import the Bolt metadata and re-add the cartridges to the cartridge path as described in the [SFCC setup guide](/platforms/salesforce). **Expected result:** **Administration > Operations > Service Status** shows `bolt.http` healthy and the Bolt button renders on the cart page. Script tags are rendered into cached pages, so a storefront can keep serving the old `connect.bolt.com` tag after activation. Invalidate the page cache under **Administration > Sites > Manage Sites > your site > Cache**. **Expected result:** view-source on the cart page shows `connect.boltapp.com` in the Bolt script tags. For a Composable Storefront, update the `helmet` directives in `app/ssr.js` so every directive that lists `*.bolt.com` also lists `*.boltapp.com`, as shown in the [Composable Storefront guide](/platforms/salesforce/sfcc-sfra-v2/sfcc-composable/). For an SFRA storefront, update any CSP configured at your CDN or in `httpHeadersConf.json`. Deploy the same code version through your normal release process, activate it, invalidate the cache, and place a low-value order. ### WooCommerce [#woocommerce] The **Bolt Checkout for WooCommerce** plugin is distributed through the WordPress plugin directory. Upgrading to 2.23.1 or newer is the whole fix, with one caveat for the companion extension plugin. **Plugin versions in use and their boltapp.com patch** Find the version you run today under **WP Admin > Plugins**, then upgrade to 2.23.1. There are no patch releases for older lines. | Version in use | Version to install | Action | | ---------------- | ------------------ | ---------------------------------------------------------------------------------------- | | 2.23.0 | 2.23.1 | Upgrade to 2.23.1. 2.23.0 already uses `boltapp.com`, but it can reject Bolt SSO logins. | | 2.22.x and older | 2.23.1 | Upgrade to 2.23.1. | In **WP Admin > Plugins**, find **Bolt Checkout for WooCommerce** and note the version. Anything below 2.23.1 needs the update, including 2.23.0. The current release is listed on the plugin's directory page at [wordpress.org](https://wordpress.org/plugins/bolt-checkout-woocommerce/). In **WP Admin > Dashboard > Updates**, select **Bolt Checkout for WooCommerce** and choose **Update Plugins**. If your host disables updates, download the current release from the plugin directory and install it under **Plugins > Add New > Upload Plugin**. **Expected result:** the plugin row shows version 2.23.1, and Bolt still appears as a payment gateway under **WooCommerce > Settings > Payments**. If your store also runs a **WooCommerce Bolt Checkout Extensions** plugin built for your store, install the matching new build. Running a new core plugin with an old extension plugin breaks checkout features. Delete the old extension plugin, install the new one, and activate it. Toggle **Bolt Sandbox Mode** on, place an order with a [test card](/getting-started/test-cards), then turn sandbox mode off and repeat the update on production. If your store uses Bolt SSO, also sign in with a Bolt account and confirm the login completes. ### Shopify [#shopify] Bolt hosts the Shopify integration, so there is no app package to upgrade. Check the snippet you pasted into your theme. **Plugin versions in use and their boltapp.com patch** Bolt hosts the Shopify integration, so there is no plugin version to track. | Component | Version | Action | | -------------------------------------- | ------------------------------- | ------------------------------------------------- | | Bolt Checkout Everywhere app | Always current, managed by Bolt | None. | | Theme snippet in `layout/theme.liquid` | Whatever your theme references | Confirm it loads from `c.boltapp.com`, see below. | Open `layout/theme.liquid` and find the Bolt `` and ` ``` If either still reads `bolt.com`, change it to `boltapp.com`. **Expected result:** the Bolt button renders and opens checkout on a `.c.boltapp.com` host. ### SAP Commerce Cloud, Solidus, and Volusion [#sap-commerce-cloud-solidus-and-volusion] These integrations are delivered by your Bolt customer success manager rather than through a public package registry. **Plugin versions in use and their boltapp.com patch** These integrations are not published to a package registry, so Bolt does not track a release line for them. | Platform | Release line in use | Patch release with boltapp.com | Action | | ------------------ | --------------------------- | ------------------------------ | ---------------------------------------------------------- | | SAP Commerce Cloud | Build delivered by Bolt | Next build from Bolt | Request the current `boltbackoffice` and `boltocc` build. | | Solidus | Bolt gems in your `Gemfile` | Next gem release | Update the gems and run `bundle update`. | | Volusion | Files in `vspfiles` | Next file set from Bolt | Request the current plugin files and edit the script tags. | * **SAP Commerce Cloud (hybris):** request the current `boltbackoffice` and `boltocc` extension build from Bolt, deploy it through your normal hybris release process, and update any `bolt.com` host in your Spartacus environment configuration. See [Set up Bolt on SAP Commerce Cloud](/platforms/sap). * **Solidus:** update the Bolt gems in your `Gemfile` to the current release and run `bundle update`, then check any initializer that sets a Bolt API host. See [Set up Bolt on Solidus](/platforms/solidus). * **Volusion:** the plugin lives in your store's `vspfiles` folder and the script tags are in your template. Request the current plugin files from Bolt, upload them over sFTP, and edit the `track.js` and `connect.js` tags in your template so they load from `connect.boltapp.com` or `connect-sandbox.boltapp.com`. See [Set up Bolt on Volusion](/platforms/volusion). ### Direct API and custom carts [#direct-api-and-custom-carts] If you call the Bolt API directly, you control every host, so no plugin upgrade applies. **Plugin versions in use and their boltapp.com patch** Not applicable. You call the Bolt API directly, so there is no Bolt plugin or release line on your side. The hosts you need to change are listed in the steps below. Set your API base URL to `https://api.boltapp.com` for production and `https://api-sandbox.boltapp.com` for sandbox. If you use the environment variable pattern from the [Environments](/api-reference/environments) page, update `BOLT_API_BASE_URL`. **Expected result:** a call your integration already makes, such as creating an order token, succeeds against the new host and returns the same data as before. Update the `track.js` and `connect.js` tags described in the [Direct API guide](/platforms/direct-api) so they load from `connect.boltapp.com` or `connect-sandbox.boltapp.com`. If you verify Bolt SSO or OpenID Connect tokens, accept the `iss` values for both domains as listed in [Changes that affect every store](#changes-that-affect-every-store). Fetch signing keys from the discovery document on your API base URL rather than a hardcoded JWKS URL so they follow the host change automatically. ## Verify the migration [#verify-the-migration] After upgrading, confirm each of these on your live store: 1. The browser's network tab shows Bolt scripts loading from `connect.boltapp.com`, with no CSP errors in the console. 2. A low-value order completes and appears under **Transactions** in the [Merchant Dashboard](https://merchant.boltapp.com). 3. Your server logs show the order webhook from Bolt arriving with a 2xx response. 4. A shopper can log in with a Bolt account, if you use SSO Commerce or one-click checkout. ## If something goes wrong [#if-something-goes-wrong] Roll back to the previous plugin version, then [contact Bolt support](/support) with your merchant ID, platform, and the plugin versions you moved between. A rollback puts your store back on `bolt.com` hosts, which Bolt is deprecating. Treat it as a stopgap and move forward to `boltapp.com` as soon as Bolt support helps you fix the problem. Markdown: https://help.boltapp.com/llms/platforms/boltapp-domain-migration.md --- # Shopify Quickstart > Accept a sandbox payment from a checkout link for a product in your Shopify store. - **Bolt handles** hosted checkout and payment collection. - **Your store owns** products, inventory, shipping settings, and order records. Source: https://help.boltapp.com/platforms/shopify ## Your integration steps [#your-integration-steps] **This flow:** a Shopify test store, Checkout Everywhere, and Bolt Payments sandbox. ## Before you begin [#before-you-begin] Have these ready before the estimated integration time begins. Account provisioning, app approval and distribution setup are separate; the estimate has not been measured in a live walkthrough. | Requirement | What you need | | ------------ | ------------------------------------------------------------------------------------------------------------------------------------ | | Test store | Shopify admin access to create and install an app, with any required app scopes/distribution approved for this store. | | Test product | One available physical product and a shipping address your test store serves. | | Bolt sandbox | Checkout Everywhere and Bolt Payments enabled for your division. Confirm Shopify test-store connectivity with Bolt before beginning. | **Platform support** This guide is for Shopify. Supporting guides for this platform: [Managed Checkout theme installation](/platforms/shopify-installation) and the [app connection reference](/platforms/shopify-checkout-everywhere). Checkout links on BigCommerce are covered by the [Checkout Everywhere quickstart](/products/checkout-everywhere). Other platforms and custom web apps have their own quickstarts. ## 1. Create app [#1-create-app] 1. In your Shopify admin, open **Apps > Develop apps > Build apps in Dev Dashboard**. 2. Select **Create app**, name it **Bolt sandbox**, and select **Create**. 3. Open **Versions > Access**. Grant read/write access to **draft orders** and **orders**, read access to **products** and **discounts**, and read access to **all orders**. Keep access limited to the scopes required by this integration. 4. Select **Release**. On the app's **Home** tab, select **Install app**, choose your test store, and confirm **Install**. 5. Open the app's **Settings** and copy its **Client ID** and **Client Secret**. Keep the secret private; it belongs in Bolt's connection form, never your theme. ### Validate app [#validate-app] Confirm the app is installed on the intended test store with the required access. If Shopify requires approval or distribution changes before installation, finish that setup before continuing. The [connection reference](/platforms/shopify-checkout-everywhere#optional-steps-for-shopify-partners) covers Partner distribution. ## 2. Connect store [#2-connect-store] 1. Open [Bolt sandbox Integrations](https://merchant-sandbox.boltapp.com/integrations) and select **Shopify**. 2. Enter your test store URL and the app's Client ID and Client Secret. 3. Select **Connect Shopify** and wait for the product catalog to import. ### Validate connection [#validate-connection] The catalog count increases and your test product appears. Confirm the store URL and product variant match your test store. If the catalog stays empty, check the app installation and access scopes before creating links. ## 3. Configure checkout [#3-configure-checkout] 1. Open [sandbox Payments](https://merchant-sandbox.boltapp.com/payments). Select **Bolt Payments** and configure **Immediate Capture** for the test product's currency. 2. Open [Business Information](https://merchant-sandbox.boltapp.com/administration/business-info). Save your test store's support email and phone number under **Support Contact Information**. 3. Open [Checkout settings](https://merchant-sandbox.boltapp.com/checkout/configuration), add your store logo, and publish the change. 4. In Shopify, confirm the test product is in stock. Record its variant, price, currency and expected shipping/tax for your test address. ### Validate settings [#validate-settings] Reopen Payments and confirm Immediate Capture is saved. Confirm the test currency is enabled and the support details belong to this store. Resolve unavailable payment settings with Bolt before taking a payment. ## 4. Create link [#4-create-link] 1. Open [Checkout Everywhere](https://merchant-sandbox.boltapp.com/checkout-everywhere), select **Create Campaign**, name it **Shopify sandbox**, and create it. 2. Inside the campaign, select **Create Links** and select the campaign and channel. 3. Choose **Direct to checkout**, find your test product, and select its variant. Leave promotion and optional tracking fields empty for this first purchase. 4. Select **Create Link** and copy the generated URL from the table row. ### Validate link [#validate-link] Open the URL in a new tab. Confirm the intended product, variant, quantity, price, currency and branding. Keep the test link private. Do not substitute a production link if the sandbox catalog or link fails. ## 5. Verify order [#5-verify-order] 1. Enter your test shopper details and supported shipping address in the checkout opened from the link. 2. Compare shipping, tax and final total with the test store's settings. 3. Use the [Bolt Payments approved test card](/getting-started/test-cards#bolt-payments): `4111 1111 1111 1111`, expiration `03/30`, CVV `737`. Submit one payment and save its confirmation details. 4. Open [sandbox Transactions](https://merchant-sandbox.boltapp.com/transactions) and find the completed payment. Record its order number, amount and currency. 5. Open **Orders** in Shopify. Find the matching order and compare shopper, product, variant, quantity and total. ### Validate purchase [#validate-purchase] You are done when the Bolt payment and matching Shopify order agree. A generated link or browser confirmation alone is not proof. If the payment exists but the store order is missing, investigate that transaction before attempting another purchase. ## Test before sharing [#test-before-sharing] Run the [Shopify test plan](/platforms/shopify-test-plan). It tests this link flow; theme-button placement and production lifecycle operations have separate launch checks. When finished, open the test link's row in Checkout Everywhere, select **Deactivate Link**, and confirm. Before publishing a real campaign, configure production separately and complete [production readiness](https://help.boltapp.com/developers/production-readiness-guides/). Test results here do not establish live fulfillment or refund readiness. Markdown: https://help.boltapp.com/llms/platforms/shopify.md --- # Shopify test plan > Verify the Shopify quickstart, reconcile payments and orders, and check the experience your store controls. Source: https://help.boltapp.com/platforms/shopify-test-plan Run this plan after the [Shopify Quickstart](/platforms/shopify). These are acceptance criteria, not recorded results. Unexecuted cases are **Not run**. The refund launch case is **Blocked** until Bolt confirms a supported Shopify procedure. ## Fixtures and evidence [#fixtures-and-evidence] Use the test store and Bolt Payments sandbox division from the guide, one available physical product, a supported shipping address, and [test cards](/getting-started/test-cards#bolt-payments). Record the app/theme version, date, case ID, Bolt transaction reference, store order ID, amounts and screenshots. Do not record keys, secrets or card details in evidence. Use a fresh cart or purchase for each payment case. Keep the same product/variant fixture; wait for catalog/configuration updates before retesting. Reconcile uncertain outcomes before retrying. Preserve order evidence; do not refund or delete records as routine cleanup. Refund testing is a separate launch case. Do not infer Shopify support from the other platforms on the general refund page. Restore changed permissions and browser network settings after failure tests. Mark **Passed**, **Failed**, or **Blocked** with evidence after execution. Baseline completion requires all applicable setup, checkout, payment and merchant-experience cases to pass. Missing required capabilities are Blocked. Additional launch cases do not imply the quickstart built those features. ## App and store connection [#app-and-store-connection] | Test | Input | Steps | Expected output | Result | | ------------------------- | ------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | | SH-01: App permissions | Test-store app from checkpoint 1 | 1. Inspect installed app/store. 2. Compare granted scopes with the guide. | App is installed on the intended test store; required scopes approved; secret is absent from theme/browser code. | Not run | | SH-02: Catalog identity | One known variant in test store | 1. Connect the store. 2. Find the imported product. 3. Compare variant/price/currency. | The imported product belongs to the intended store and matches the selected variant. | Not run | | SH-03: Unavailable access | Sandbox app; new product created after revoking product-read access | 1. Remove product-read access and release the change. 2. Create a new product. 3. Attempt catalog import. 4. Restore/release access and reconnect. | The new product cannot import without product-read access and imports after access is restored. Previously cached products do not count as evidence. | Not run | ## Link and checkout [#link-and-checkout] | Test | Input | Steps | Expected output | Result | | -------------------- | ------------------------------------------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ------- | | SH-04: Correct link | Direct-to-checkout link for one variant | 1. Copy URL. 2. Open in a fresh browser. | Correct product/variant opens; no live-store or production payment destination. | Not run | | SH-05: Inactive link | Separate disposable link, not the link used for payment cases | 1. Deactivate in Checkout Everywhere. 2. Reopen copied URL. | Inactive link cannot start a new purchase; existing transaction evidence remains available. | Not run | ## Payment and order [#payment-and-order] | Test | Input | Steps | Expected output | Result | | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | ------- | | SH-07: Approved payment | Available product; approved Bolt Payments test card | 1. Pay once. 2. Find Bolt transaction. 3. Find store order. | One completed Bolt payment and one matching store order; item, quantity, shopper, amount and currency agree. | Not run | | SH-08: Rejected payment | Pre-auth irreversibly rejected card from the Bolt Payments table, using the card set assigned to your division; fresh cart/link | 1. Submit the rejection card. 2. Inspect transaction and order state. | Payment is rejected before authorization; no order is marked paid; shopper can retry with a supported payment method. | Not run | | SH-09: Cancel checkout | Unpaid cart/link | 1. Open checkout. 2. Close it before paying. 3. Return to the cart/link. | No completed payment; chosen product remains available for another attempt. | Not run | | SH-10: Repeat submit | Fresh test purchase | 1. Submit twice quickly. 2. Reconcile resulting transactions and orders. | No duplicate completed purchase or duplicate paid order. | Not run | | SH-11: Uncertain result | Fresh purchase; controlled connection interruption | 1. Interrupt the browser connection after submit. 2. Restore it. 3. Check Bolt and store before retrying. | Operator can determine the existing outcome; no blind second charge. A completed payment has a matching order. | Not run | ## Experience you control [#experience-you-control] | Test | Input | Steps | Expected output | Result | | ------------------------- | ----------------------------------------- | -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | | SH-12: Store identity | Configured logo and support contact | 1. Open checkout. 2. Inspect identity/support. 3. Inspect confirmation. | Correct store identity and contact details; confirmation refers to the purchased product/order. | Not run | | SH-13: Phone and keyboard | Narrow phone and keyboard-only desktop | 1. Reach the checkout entry. 2. Open and close checkout. 3. Return to the entry. | Merchant-owned entry remains usable, visible and reachable; focus and return behavior do not strand the shopper. Report Bolt-owned checkout defects to Bolt. | Not run | | SH-14: Totals | Known product price and supported address | 1. Record store pricing. 2. Enter shipping address. 3. Compare final breakdown. | Product, quantity, shipping, tax, currency and total agree with store configuration. | Not run | ## Additional launch checks [#additional-launch-checks] | Test | Input | Steps | Expected output | Result | | ------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | ------- | | SH-06: Theme buttons | Separate installed theme-button extension | 1. Follow the theme guide. 2. Test product, cart and cart-drawer entries. | Each supported entry opens its actual cart and returns correctly. This is outside the link-only baseline. | Not run | | SH-15: Refund reconciliation | Owner-confirmed Shopify / Checkout Everywhere refund procedure required | 1. Ask Bolt to confirm the supported refund entry point and synchronization behavior. 2. Document it here. 3. Execute against a separate completed sandbox order. | Bolt and Shopify refund amount/status reconcile with no duplicate refund. Do not execute until the procedure is confirmed. | Blocked | | SH-16: Fulfillment | Separate paid order and configured fulfillment workflow | 1. Process the order in the store. 2. Check shopper-facing confirmation/tracking. | Only paid orders enter the intended fulfillment flow; merchant-owned messaging matches the order. | Not run | | SH-17: Production configuration | Approved production account and separate launch checklist | 1. Review hosts/keys/account. 2. Run an approved production-readiness test. | No sandbox keys, links or scripts in production; launch evidence is recorded separately from sandbox acceptance. | Not run | Markdown: https://help.boltapp.com/llms/platforms/shopify-test-plan.md --- # Shopify connection reference > Find Shopify app credentials, configure Partner distribution, and review alternate connection settings. Source: https://help.boltapp.com/platforms/shopify-checkout-everywhere Use the [Shopify Quickstart](/platforms/shopify) for the complete sandbox flow. This reference covers app credentials, Partner distribution, and alternate configuration. ## App access and credentials [#app-access-and-credentials] Follow [Create app](/platforms/shopify#1-create-app) for the required scopes and installation. Your app must be installed on the store before you connect it to Bolt. In the app's **Settings**, the Client ID and Client Secret identify this connection; keep the secret out of your theme. App Settings tab showing the Client ID and Client Secret ### Optional steps for Shopify Partners [#optional-steps-for-shopify-partners] If you are a Shopify Partner, limit the app's distribution to your store. Without this, the access scopes may not take effect. 1. On the app's **Home** tab, select **Select distribution method**. Select distribution method on the app Home tab 2. Select **Custom distribution**, then **Select**. Custom distribution option 3. Enter your store domain, such as `mystore.com`, and select **Generate link**. Store domain field and Generate link button ## Connection settings [#connection-settings] [Connect store](/platforms/shopify#2-connect-store) identifies the test store and app credentials to enter. A catalog count alone is not sufficient: compare a known product and variant with your source store. Shopify integration form in the Merchant Dashboard ## Other payment processors [#other-payment-processors] The quickstart uses Bolt Payments sandbox. For another processor, confirm compatibility with Bolt and use the [processor-specific configuration](/integrations/payment-processors). Do not substitute credentials across environments. ## Checkout settings [#checkout-settings] Use [Configure checkout](/platforms/shopify#3-configure-checkout) for the sandbox flow. Further [checkout settings](/dashboard/checkout-settings/) belong to the connected store and division. ## After connecting [#after-connecting] Return to [Create link](/platforms/shopify#4-create-link) and [Verify order](/platforms/shopify#5-verify-order). To install Managed Checkout in the storefront theme after verifying the link flow, use [theme installation](/platforms/shopify-installation). Markdown: https://help.boltapp.com/llms/platforms/shopify-checkout-everywhere.md --- # Install Managed Checkout in your Shopify theme > Add the Bolt scripts and theme asset, replace the native checkout button with Bolt, and add Buy Now to product pages. Source: https://help.boltapp.com/platforms/shopify-installation For your first verified sandbox purchase from a checkout link, follow the [Shopify Quickstart](/platforms/shopify). This page installs Managed Checkout in the storefront theme itself. Bolt Managed Checkout on Shopify uses three scripts: `connect.js`, Bolt's Shopify Managed Checkout loader, and one asset you add to the theme. The Bolt checkout button replaces the native Shopify checkout button in the cart, and a Buy Now button goes on product pages. Complete the steps in order on an unpublished copy of your theme. Use sandbox hosts and keys until every check in [Test your website](#test-your-website) passes, then move the whole configuration to production in one release. Element names below match Shopify's Dawn theme; other themes use different names. ## Prerequisites [#prerequisites] * Your store is connected to Bolt. See [Connect Shopify to Bolt](/platforms/shopify-checkout-everywhere). * **Storefront API access.** Install or open Shopify Headless and create the Storefront API configuration. Record the Storefront API token in your approved secure location, and confirm its token type before sharing it with your Bolt implementation contact. * **A Bolt custom app in Shopify Admin** with Admin API access to draft orders, fulfillments, gift cards, orders, assigned fulfillment orders, third-party fulfillment orders, marketplace fulfillment orders, merchant-managed fulfillment orders, products, and customers, plus unauthenticated Storefront API access to product listings and their subsections. Install the app and record its consumer key and consumer secret securely. * The generic Shopify Managed Checkout script for the theme asset, supplied by your Bolt implementation contact. * Shopify admin access that can edit theme code. Keep the custom app secret and every private access token out of Liquid, JavaScript, HTML, screenshots, logs, and browser storage. Only the publishable key belongs in the theme. ## 1. Prepare for installation [#1-prepare-for-installation] 1. Log in to the [sandbox Merchant Dashboard](https://merchant-sandbox.boltapp.com/), select **Administration > API**, and copy the **Publishable Key**. 2. In your Shopify admin, go to **Online Store > Themes**. Shopify theme list with the current theme 3. Duplicate the published theme and name the copy clearly, for example **Bolt Managed Checkout Staging**. Record the current theme version or Git commit so you can roll back. 4. On the copy, open the **three dots** menu and select **Edit code**. Theme context menu with Edit code 5. Back up `layout/theme.liquid`, the cart drawer or cart footer snippet, and the product page template before editing them. Do all implementation and testing on the unpublished copy. If your configuration requires Bolt `track.js`, include it once. **Expected result:** The theme code editor opens on the duplicate theme with the **Layout**, **Snippets**, and **Assets** folders in the sidebar. ## 2. Add code snippets [#2-add-code-snippets] ### Install global scripts [#install-global-scripts] 1. Open `layout/theme.liquid` and find the closing `` tag. 2. Paste the three scripts before it, in this order: `connect.js`, the Managed Checkout loader, then the theme asset. ```html title="theme.liquid" ``` 3. Replace `PUBLISHABLE_KEY` with the sandbox publishable key. 4. Replace `DYNAMIC_ELEMENT_SELECTOR` with a stable parent element that stays in the page when the cart drawer re-renders. [Configure dynamic cart behavior](#4-configure-dynamic-cart-behavior) explains how to choose it. 5. Reload the storefront. Confirm all three scripts load once and the browser console shows no Content Security Policy or JavaScript errors. theme.liquid with the Bolt scripts in the head ### Create the Bolt asset [#create-the-bolt-asset] 1. Under **Assets**, select **Add a new asset** and create `bolt-connect-shopify.js`. 2. Paste the generic Shopify Managed Checkout script from Bolt into the asset. 3. Update the configuration block near the top of the file: ```javascript title="bolt-connect-shopify.js" const BOLT_BUTTON_SELECTOR = '[data-tid*="instant-bolt-checkout-button"]'; const DYNAMIC_ELEMENTS_SELECTOR = "#shopify-section-cart-drawer"; const CART_TOKEN_COOKIE_NAME = "cart"; const analytics = { klaviyo: true, northbeam: true, google: true, }; var debug = false; ``` 4. Replace the example dynamic selector with the stable selector your theme uses. 5. Set an analytics destination to `false` when the store does not use it. 6. Leave `debug` set to `false` in production. Enable it only temporarily when you need lifecycle logging during QA. 7. If shopper prefill is required, populate only the supported hints fields, and confirm the consent and privacy requirements that apply. ## 3. Add Bolt Checkout buttons [#3-add-bolt-checkout-buttons] ### Cart drawer and cart page [#cart-drawer-and-cart-page] 1. Open the cart drawer, cart footer, or cart page snippet your theme uses. 2. Remove or comment out the native Shopify checkout control, including the element with id `CartDrawer-Checkout` when present. 3. Insert the Bolt V1 checkout button in the same visual location: ```html title="cart-drawer.liquid"
``` 4. Replace `PUBLISHABLE_KEY`, and check that the button URL uses the host for the active environment. 5. Confirm no second native checkout button remains where Bolt is meant to be the only checkout path. 6. Add and remove products, change quantities, and apply a discount. The Bolt button must survive every cart update. ### Product page Buy Now button [#product-page-buy-now-button] Add the product page button in the theme customizer. 1. Go to **Themes > Customize** on the duplicate theme. Customize button on the theme card 2. In the top navigation dropdown, select **Products > Default product**. Default product template selection 3. In the left navigation, go to **Template > Product information**. Product information section in the template tree 4. In the **Buy buttons** block, turn off **Show dynamic checkout buttons**. 5. Select **Add block**, then **Custom Liquid**, at the intended product page location. Add block menu with Custom Liquid 6. Paste the same Bolt V1 button markup as the cart, with the correct publishable key and environment host, and save. 7. Test the default variant, another variant, a quantity change, an unavailable variant, and the quick-add modal. Confirm `configureProductCheckout` receives the selected variant and quantity. ## 4. Configure dynamic cart behavior [#4-configure-dynamic-cart-behavior] 1. Change the cart while inspecting the theme, and note whether Shopify replaces the drawer's contents or the entire drawer element. 2. If only the contents change, use the cart drawer as the dynamic selector. 3. If the entire drawer is replaced, use a stable Shopify section or a wrapper around the drawer. 4. Keep the document-level `MutationObserver` fallback when a third-party app injects the Bolt button after page load. 5. Confirm every cart re-render produces one working Bolt button and does not repeat configure calls, callbacks, or checkout events. ## 5. Configure callbacks and analytics [#5-configure-callbacks-and-analytics] 1. Confirm the success callback clears the Shopify cart only after a successful checkout and invokes the Bolt completion callback exactly once. 2. Enable the Klaviyo Checkout Start and identify events only when Klaviyo is installed and approved. 3. Enable Northbeam email identification only when Northbeam is installed and approved. 4. Enable the Google `begin_checkout`, `add_payment_info`, and `purchase` events only when GA4 or GTM is installed and approved. 5. Check that the Google ecommerce payload contains currency, value, transaction ID, item ID, item name, price, and quantity. 6. Confirm no other storefront or order-status integration also sends `purchase` for the same order. 7. Keep analytics best-effort: an analytics failure must never stop checkout from opening or completing. ## Test your website [#test-your-website] Every check must pass in sandbox before you move to production. * `connect.js`, `managed-checkout.js`, and `bolt-connect-shopify.js` each load once. * The Bolt checkout button appears on the cart page and, where applicable, the cart drawer. * Cart changes do not remove or break the Bolt button. * Checkout receives the correct Shopify cart token, current line items, discounts, shipping, tax, and total. * Product Buy Now uses the selected variant and quantity, including from quick add. * Closing and reopening checkout does not create duplicate buttons, callbacks, or events. * A successful checkout creates one Shopify order and clears the storefront cart once. * Enabled Klaviyo, Northbeam, GA4, and GTM events fire once with the expected payload. * Empty cart, unavailable item, expired cart token, slow cart response, declined payment, and callback errors all recover. * Mobile Safari, mobile Chrome, desktop Safari, and desktop Chrome pass the core flow. * No private token, consumer secret, or other sensitive value appears in page source, console output, or browser requests. ## Move to production [#move-to-production] 1. Get approval from the merchant, the Bolt implementation owner, the analytics owner, and the QA owner. 2. Schedule the theme publication window and confirm who owns rollback. 3. Replace every sandbox Bolt script host with the production host: `connect-sandbox.boltapp.com` becomes `connect.boltapp.com`, and `c-sandbox.boltapp.com` becomes `c.boltapp.com`. 4. Replace the sandbox publishable key with the production publishable key in `connect.js` and in every Bolt button URL. 5. Check that the production build contains no sandbox hostname or sandbox credential. 6. Publish the validated theme. 7. Immediately run one cart checkout and one product Buy Now smoke test. 8. During the launch window, watch checkout-start rate, JavaScript failures, order creation, payment success, duplicate analytics events, and cart clearing. ## Roll back [#roll-back] 1. If Bolt cannot open or orders cannot complete, republish the previous theme or restore the backed-up files. 2. Restore the prior `theme.liquid` scripts, native checkout controls, and product page configuration as the rollback plan requires. 3. When it does not delay recovery, capture redacted browser errors, timestamps, cart tokens, order references, and request identifiers first. 4. Confirm the restored checkout path completes successfully. Markdown: https://help.boltapp.com/llms/platforms/shopify-installation.md --- # BigCommerce Quickstart > Accept a sandbox payment from the cart page of your existing BigCommerce store. - **Bolt handles** the checkout interface and payment collection. - **Your store owns** the cart, product catalog, shipping settings, and order records. Source: https://help.boltapp.com/platforms/bigcommerce ## Your integration steps [#your-integration-steps] **This flow:** a BigCommerce sandbox store using a copied Cornerstone theme, Full Bolt Checkout with Fraud Protection, and Bolt Payments sandbox. ## Before you begin [#before-you-begin] Have these ready before the estimated integration time begins. Store enablement and account provisioning are separate; this estimate has not been measured in a live walkthrough. | Requirement | What you need | | ------------ | ------------------------------------------------------------------------------------------------------------------------- | | Test store | BigCommerce sandbox admin access to apps, users, payments, and theme files. Bolt Checkout enabled by BigCommerce Support. | | Bolt sandbox | An approved merchant division with Bolt Payments sandbox configured for Immediate Capture. | | Test cart | One in-stock physical product, a supported shipping address, and a copy of your Cornerstone theme. | **Platform support** This guide is for BigCommerce. Supporting guides for this platform: [fraud protection only](/platforms/bigcommerce/bigcommerce-setup-guide/fraud-only/), [checkout links](/products/checkout-everywhere), [theme configuration](/platforms/bigcommerce-full), and [production installation](/platforms/bigcommerce-full#install-for-production). Other platforms and custom web apps have their own quickstarts. ## 1. Install app [#1-install-app] 1. On your test store, install the [Bolt sandbox app](https://apps.bigcommerce.com/details/22164). Select **Get This App > Install > Confirm**. 2. In **Settings > Users**, create the Bolt support user `dev@boltapp.com` with **Active** status and the **Sales Manager** role, as required by the plugin setup. 3. Grant **Manage Settings**, **Manage Payments**, **Store Design**, **Design Mode**, **Store Logs**, and **Use Script Manager**. Enable **Bolt Checkout** under **Single Click Apps** and enable **WebDav**. Save the user. ### Validate installation [#validate-installation] Confirm **Bolt** appears under **Store Setup > Payments > Online Payment Methods** and the support user has the listed permissions for this test store. Do not install the production app for this walkthrough. ## 2. Connect Bolt [#2-connect-bolt] 1. Open [sandbox API keys](https://merchant-sandbox.boltapp.com/administration/api-keys#apiKeys). Copy the **API Key**, **Signing Secret**, and **Publishable Key** for your test division. 2. In BigCommerce, open **Store Setup > Payments > Online Payment Methods > Bolt > Setup**. Enter the corresponding keys and save. Keep the API key and signing secret out of theme code. 3. Open **Bolt Settings**. Set **Configuration Mode** to **Full Bolt Checkout with Fraud Protection** and save. 4. Open [sandbox Webhooks](https://merchant-sandbox.boltapp.com/administration/webhooks). Under **Endpoints**, confirm the app populated `https://api.bigcommerce.com/stores/{store_hash}/v2/` for your store. The merchant dashboard API keys panel shows the publishable key to copy into BigCommerce. ### Validate connection [#validate-connection] Check that the store hash is your test store and the keys belong to the same sandbox division. If the webhook URL is absent, reinstall the sandbox app; contact Bolt if it remains missing. Do not continue with a manually guessed endpoint. ## 3. Add checkout [#3-add-checkout] In **Store Front > My Themes > Advanced > Edit Theme Files**, edit your copied theme's `templates/pages/cart.html`. Add this script above the cart's first `div`, replacing `YOUR_SANDBOX_PUBLISHABLE_KEY` with your publishable key: ```html title="cart.html" ``` Add the Bolt button beside the cart's checkout action: ```html title="cart.html"
``` Add tracking in BigCommerce's **Footer Scripts**, using the same publishable key: ```html title="Footer scripts" ``` Save and apply the copied theme to your sandbox store. Open its cart page with your test product. ### Validate checkout [#validate-checkout] Select the Bolt button. Confirm checkout opens with the correct item, quantity and currency, then close it without paying. Keep the original checkout action available until the Bolt button works. This walkthrough validates the cart page; fast cart and mini cart use the [additional template instructions](/platforms/bigcommerce-full#step-6-add-scripts--buttons) before launch. ## 4. Refresh cart [#4-refresh-cart] Add this observer to `templates/pages/cart.html`. Cornerstone uses `cart-total-value` for the total; confirm that class exists in your copied theme. ```html title="cart.html" ``` Save the theme. Change the quantity on the cart page, then reopen Bolt checkout. ### Validate refresh [#validate-refresh] The updated quantity and total must appear in checkout. Restore the quantity for your first test purchase. If totals are stale, check the theme's total selector and the connect script before continuing; do not test a payment against the wrong cart. ## 5. Verify order [#5-verify-order] 1. Record the cart's product, quantity, currency and expected shipping/tax for your test address. 2. Open Bolt checkout from the cart page, enter the test shopper details, and compare the final total. 3. Pay once with the [Bolt Payments approved test card](/getting-started/test-cards#bolt-payments): `4111 1111 1111 1111`, expiration `03/30`, CVV `737`. 4. Open [sandbox Transactions](https://merchant-sandbox.boltapp.com/transactions). Find the completed payment and record its order number, amount and currency. 5. Open **Orders** in BigCommerce. Find the matching order and compare product, quantity, shopper and total. ### Validate purchase [#validate-purchase] You are done when one matching store order and completed Bolt payment agree. If payment exists but the store order is missing, investigate the transaction and webhook delivery before retrying. Browser confirmation alone is not authoritative payment proof. ## Test before launch [#test-before-launch] Run the [BigCommerce test plan](/platforms/bigcommerce-test-plan). Cart-page acceptance is separate from testing every storefront checkout entry, capture/refund operations and fulfillment. Use [Install for production](/platforms/bigcommerce-full#install-for-production) for the live-store handoff and the [configuration reference](/platforms/bigcommerce-full) for fast/mini cart and re-platforming. Complete [production readiness](https://help.boltapp.com/developers/production-readiness-guides/). Configure production independently; do not copy sandbox keys or script hosts into a live theme. Markdown: https://help.boltapp.com/llms/platforms/bigcommerce.md --- # BigCommerce test plan > Verify the BigCommerce quickstart, reconcile payments and orders, and check the experience your store controls. Source: https://help.boltapp.com/platforms/bigcommerce-test-plan Run this plan after the [BigCommerce Quickstart](/platforms/bigcommerce). These are acceptance criteria, not recorded results. All cases start **Not run**. ## Fixtures and evidence [#fixtures-and-evidence] Use the test store and Bolt Payments sandbox division from the guide, one available physical product, a supported shipping address, and [test cards](/getting-started/test-cards#bolt-payments). Record the app/theme version, date, case ID, Bolt transaction reference, store order ID, amounts and screenshots. Do not record keys, secrets or card details in evidence. Use a fresh cart or purchase for each payment case. Keep the same product/variant fixture; wait for catalog/configuration updates before retesting. Reconcile uncertain outcomes before retrying. Preserve order evidence; do not refund or delete records as routine cleanup. Refund testing is a separate launch case using the [platform refund guidance](/dashboard/refunds). Restore changed permissions and browser network settings after failure tests. Mark **Passed**, **Failed**, or **Blocked** with evidence after execution. Baseline completion requires all applicable setup, checkout, payment and merchant-experience cases to pass. Missing required capabilities are Blocked. Additional launch cases do not imply the quickstart built those features. ## App and Bolt connection [#app-and-bolt-connection] | Test | Input | Steps | Expected output | Result | | ---------------------------- | -------------------------------- | ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------- | ------- | | BC-01: Sandbox configuration | Sandbox app and test division | 1. Inspect installed app. 2. Review payment keys and configuration mode. | Sandbox app, matching division and Full Bolt Checkout mode; no production credentials. | Not run | | BC-02: Webhook destination | Known test store hash | 1. Open sandbox Webhooks. 2. Compare endpoint with store hash. | Endpoint is populated by the app and points to the intended BigCommerce store. | Not run | | BC-03: Secret placement | Rendered copied theme and source | 1. Inspect script attributes/source. 2. Compare key types. | Only publishable key appears in theme; API key and signing secret remain in payment-method configuration. | Not run | ## Cart integration [#cart-integration] | Test | Input | Steps | Expected output | Result | | ------------------------- | -------------------------------------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------ | ------- | | BC-04: Open cart checkout | One item on copied Cornerstone cart page | 1. Select Bolt checkout. 2. Compare item and quantity. | Checkout opens for the current cart and sandbox division. | Not run | | BC-05: Quantity refresh | Cart quantity changed from 1 to 2 | 1. Change quantity. 2. Wait for total update. 3. Open checkout. | Checkout quantity and total reflect the updated cart, with no stale price. | Not run | | BC-06: Load failure | Block sandbox connect script in browser test tools | 1. Reload cart. 2. Inspect available checkout actions. 3. Restore script and reload. | Existing cart/checkout remains accessible; no false payment confirmation; Bolt action recovers after reload. | Not run | ## Payment and order [#payment-and-order] | Test | Input | Steps | Expected output | Result | | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | ------- | | BC-08: Approved payment | Available product; approved Bolt Payments test card | 1. Pay once. 2. Find Bolt transaction. 3. Find store order. | One completed Bolt payment and one matching store order; item, quantity, shopper, amount and currency agree. | Not run | | BC-09: Rejected payment | Pre-auth irreversibly rejected card from the Bolt Payments table, using the card set assigned to your division; fresh cart/link | 1. Submit the rejection card. 2. Inspect transaction and order state. | Payment is rejected before authorization; no order is marked paid; shopper can retry with a supported payment method. | Not run | | BC-10: Cancel checkout | Unpaid cart/link | 1. Open checkout. 2. Close it before paying. 3. Return to the cart/link. | No completed payment; chosen product remains available for another attempt. | Not run | | BC-11: Repeat submit | Fresh test purchase | 1. Submit twice quickly. 2. Reconcile resulting transactions and orders. | No duplicate completed purchase or duplicate paid order. | Not run | | BC-12: Uncertain result | Fresh purchase; controlled connection interruption | 1. Interrupt the browser connection after submit. 2. Restore it. 3. Check Bolt and store before retrying. | Operator can determine the existing outcome; no blind second charge. A completed payment has a matching order. | Not run | ## Experience you control [#experience-you-control] | Test | Input | Steps | Expected output | Result | | ------------------------- | ----------------------------------------- | -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | | BC-14: Phone and keyboard | Narrow phone and keyboard-only desktop | 1. Reach the checkout entry. 2. Open and close checkout. 3. Return to the entry. | Merchant-owned entry remains usable, visible and reachable; focus and return behavior do not strand the shopper. Report Bolt-owned checkout defects to Bolt. | Not run | | BC-15: Totals | Known product price and supported address | 1. Record store pricing. 2. Enter shipping address. 3. Compare final breakdown. | Product, quantity, shipping, tax, currency and total agree with store configuration. | Not run | ## Additional launch checks [#additional-launch-checks] | Test | Input | Steps | Expected output | Result | | ------------------------------- | ----------------------------------------------------------- | ----------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ------- | | BC-13: Store identity | Configured logo and support contact | 1. Open checkout. 2. Inspect identity/support. 3. Inspect confirmation. | Correct store identity and contact details; confirmation refers to the purchased product/order. | Not run | | BC-07: Other cart entries | Configured fast cart and mini cart extensions | 1. Apply template reference. 2. Open each entry. 3. Change cart contents and retry. | Every enabled storefront entry reflects its cart. This is outside the cart-page baseline. | Not run | | BC-16: Refund reconciliation | Separate completed sandbox purchase; supported refund setup | 1. Follow the platform refund procedure. 2. Compare Bolt and store records. | Refund amount/status reconcile; no duplicate refund. Record unsupported behavior as a launch blocker, not a passed baseline case. | Not run | | BC-17: Fulfillment | Separate paid order and configured fulfillment workflow | 1. Process the order in the store. 2. Check shopper-facing confirmation/tracking. | Only paid orders enter the intended fulfillment flow; merchant-owned messaging matches the order. | Not run | | BC-18: Production configuration | Approved production account and separate launch checklist | 1. Review hosts/keys/account. 2. Run an approved production-readiness test. | No sandbox keys, links or scripts in production; launch evidence is recorded separately from sandbox acceptance. | Not run | Markdown: https://help.boltapp.com/llms/platforms/bigcommerce-test-plan.md --- # BigCommerce configuration reference > Configure additional BigCommerce checkout entry points, product modes, analytics, and account migrations. Source: https://help.boltapp.com/platforms/bigcommerce-full Use the [BigCommerce Quickstart](/platforms/bigcommerce) for one verified sandbox purchase. This reference covers additional theme entry points, product modes, analytics and re-platforming. ## App installation and access [#app-installation-and-access] Follow [Install app](/platforms/bigcommerce#1-install-app) for the sandbox app and required user permissions. For a live store, use [Install for production](#install-for-production). ## Install for production [#install-for-production] Complete the [BigCommerce test plan](/platforms/bigcommerce-test-plan) and [production readiness](https://help.boltapp.com/developers/production-readiness-guides/) with Bolt before your live rollout. If changing an existing Bolt account, read [Re-platforming](#re-platforming) first. 1. On your live BigCommerce store, install the [Bolt production app](https://apps.bigcommerce.com/details/13488). Select **Get This App > Install > Confirm**. 2. Configure the [required support-user permissions](/platforms/bigcommerce#1-install-app). In **Store Setup > Payments > Online Payment Methods > Bolt > Setup**, enter the keys from your [production division](https://merchant.boltapp.com/administration/api-keys#apiKeys). 3. Apply the [production scripts and checkout entry points](#step-6-add-scripts--buttons) below using that division's publishable key. Confirm the payment settings, theme keys, and script hosts all belong to production before publishing your theme. **Expected result:** the production app is installed and configured for the live store. Use your agreed launch checks to verify payment and store-order matching; sandbox acceptance alone does not verify the production configuration. ## Keys and webhook destination [#keys-and-webhook-destination] Follow [Connect Bolt](/platforms/bigcommerce#2-connect-bolt) to configure matching keys and validate the app-created webhook destination. When changing Bolt accounts, update old publishable-key references in every theme template as well as the payment-method settings. ## Product modes [#product-modes] The quickstart uses **Full Bolt Checkout with Fraud Protection**. To retain native checkout, follow the separate [fraud protection only guide](/platforms/bigcommerce/bigcommerce-setup-guide/fraud-only/). Set the selected **Configuration Mode** under **Store Setup > Payments > Bolt > Bolt Settings** and save. For manual orders using authorization before capture, the documented **Manual Orders Transaction Type** is `Authorize Only`. Confirm the supported capture workflow before changing this setting; the quickstart uses Immediate Capture. ## Step 6: Add scripts & buttons [#step-6-add-scripts--buttons] Bolt delivers checkout and [SSO Commerce](/products/add-ons/sso-commerce) through two scripts, `connect-bigcommerce.js` and `track.js`. Add the scripts and the Bolt button to every template that shows the cart. In the Cornerstone theme these are: * Cart page: `templates/pages/cart.html` or `templates/components/cart/totals.html` * Fast cart: `templates/components/cart/preview.html` * Mini cart: `templates/components/common/cart-preview.html` To edit them, go to **Store Front > My Themes > Advanced > Edit Theme Files**. Copy your theme before making changes. ### Add scripts [#add-scripts] Add the following scripts above the first `div` tag in each template. The examples use production hosts; on a sandbox store, use `https://connect-sandbox.boltapp.com/connect-bigcommerce.js` and `https://connect-sandbox.boltapp.com/track.js` instead. Where to insert the scripts in the theme file Load the connect script. Replace `{PUBLISHABLE_KEY}` with your publishable key. Replace `{ROUTE_PUBLIC_TOKEN}` if you use [Route shipping insurance](https://route.com/), or remove that attribute. ```html title="HTML" ``` For the main cart page, use the tested [cart refresh observer](/platforms/bigcommerce#4-refresh-cart). On the fast cart and mini cart templates, refresh the Bolt cart when the template renders. ```html title="HTML" ``` Include the tracking script on every page. BigCommerce's **Footer Scripts** section is a convenient place for it. ```html title="HTML" ``` ### Add buttons [#add-buttons] 1. In the same templates, add the Bolt button where your original checkout button appears. Replace `PUBLISHABLE_KEY` with your publishable key, and use the `connect-sandbox.boltapp.com` host on a sandbox store. ```html title="HTML"
``` 2. Hide the original checkout button. Update the `querySelector` to match your theme's checkout button. ```html title="HTML" ``` **Expected result:** The Bolt button appears in place of the original checkout button on the cart, fast cart, and mini cart, and changing the cart total updates the Bolt cart. ## Optional configuration [#optional-configuration] ### Embeddable account editing [#embeddable-account-editing] Let shoppers edit their Bolt account details on your site with the [component setup guides](/add-ons/embedded-shopper-accounts-editing/). ### Set up Google Analytics [#set-up-google-analytics] 1. Link your Google Analytics account under **Settings > Data Solutions > Web Analytics** in the [BigCommerce dashboard](https://support.bigcommerce.com/articles/Public/Data-Solutions). 2. Use [Script Manager](https://support.bigcommerce.com/s/article/Using-Script-Manager?language=en_US) to add the GA4 tag. Replace `` with your GA4 measurement ID. ```html title="HTML" ``` ## Re-platforming [#re-platforming] When you move to BigCommerce from another platform or Bolt account division: * Issue [refunds](/dashboard/refunds) from the Merchant Dashboard. Do not use a platform back-office flow connected to the deprecated account division. * Disable the old [webhooks](/developers/webhooks) for the deprecated account division. ## Update plugin [#update-plugin] Update the Bolt app when BigCommerce prompts you [in the control panel](https://support.bigcommerce.com/s/article/How-do-I-access-and-install-the-new-single-click-apps-within-my-Bigcommerce-store-control-panel?language=en_US#updating) so checkout keeps working as intended. Markdown: https://help.boltapp.com/llms/platforms/bigcommerce-full.md --- # Set up Bolt on Adobe Commerce > Install and configure the Bolt Checkout plugin for Adobe Commerce (Magento 2). Source: https://help.boltapp.com/platforms/adobe Install the Bolt Checkout plugin (`boltpay/bolt-magento2`) on Adobe Commerce (Magento 2) to render the Bolt button and Checkout modal on your storefront. Requires Magento 2.3 or above and command-line access to your Magento root folder. **Platform support** This guide is for Adobe Commerce. Other platforms and custom web apps have their own quickstarts. ## Prerequisites [#prerequisites] * Bolt merchant account with API credentials (Administration > API > API Keys) * Adobe Commerce / Magento 2.3+ with access to the Magento root folder * Command-line (SSH) access to run Composer and `bin/magento` ## Install the plugin [#install-the-plugin] 1. From your Magento root, require the plugin with Composer: ```bash title="Terminal" composer require boltpay/bolt-magento2 ``` 2. Run the Magento setup sequence: ```bash title="Magento server" bin/magento setup:upgrade bin/magento setup:di:compile bin/magento setup:static-content:deploy bin/magento cache:clean bin/magento cache:flush ``` **Expected result:** **Bolt Pay** appears under **Stores > Configuration > Sales > Payment Methods**. ## Configure the plugin [#configure-the-plugin] 1. Open **Stores > Configuration > Sales > Payment Methods > Bolt Pay** and set **Enabled** to **Yes**, then add a title. 2. Enter your Bolt **API Key** and **Signing Secret** from the Merchant Dashboard. 3. Under **Advanced Options**, choose **Create and send API keys to Bolt** to establish the OAuth connection so Bolt can call the Magento API. **Expected result:** the Bolt button renders on your storefront and sandbox checkout opens. ## Verify [#verify] Place a test order in sandbox with a [test card](/getting-started/test-cards) and confirm the transaction appears under **Transactions** in your sandbox dashboard. ## Fraud-only setup [#fraud-only-setup] If you use Bolt's Fraud-Only offering, enter the **Publishable Key - Payment Only** together with your API key and signing secret, enable only **Native Checkout Page** under **Where to add Bolt?**, and make sure no global CSS hides the original checkout button. ## Production checklist [#production-checklist] * Disable **Sandbox Mode** * Switch to production API keys and dashboard * Live processor credentials configured in the Bolt dashboard ## Troubleshooting [#troubleshooting] * Bolt button missing: check **Replace Button Selectors** (use `|append` or `|prepend` to place the button adjacent instead of replacing the element). * API calls failing: confirm the M2 API keys were sent to Bolt to complete the OAuth connection. ## Related [#related] * [Full Adobe Commerce setup guide](/platforms/adobe/adobe-commerce-setup-guide/adobe-installation/) * [Set up SSO](/platforms/adobe/adobe-commerce-setup-guide/sso/adobe-sso/) * [Migrate to native API integration](/platforms/adobe/adobe-commerce-setup-guide/m2-migrate/) * [Manage orders and operations](/platforms/adobe/operations/) * [Bolt Checkout](/products/checkout) Markdown: https://help.boltapp.com/llms/platforms/adobe.md --- # Set up Bolt on Salesforce Commerce Cloud > Install Bolt cartridges on Salesforce Commerce Cloud (SFCC) storefronts. Source: https://help.boltapp.com/platforms/salesforce Integrate Bolt into a Salesforce Commerce Cloud (SFCC) storefront using Bolt-provided cartridges. Bolt uses the Open Commerce API (OCAPI) to render the Bolt checkout button on the cart, minicart, and product pages. **Platform support** This guide is for Salesforce Commerce Cloud. Other platforms and custom web apps have their own quickstarts. ## Choose an integration [#choose-an-integration] | Integration | Use it when | | ---------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | | [SFRA V2 (Checkout)](/platforms/salesforce/sfcc-sfra-v2/installation/) | You run a standard SFRA storefront and want Bolt's hosted checkout to replace the native SFRA checkout. | | [Composable Storefront](/platforms/salesforce/sfcc-sfra-v2/sfcc-composable/) | You run a headless PWA Kit storefront. Extends the SFRA V2 plugin with a React `BoltCheckout` component. | | [Embeddable Checkout](/platforms/salesforce/sfcc-embedded/setup/) | You keep your own checkout UX and want Bolt to add account SSO, one-click login, and payment methods. | ## Prerequisites [#prerequisites] * A working SFCC storefront with a base SFRA cartridge running * Bolt API credentials (API Key, Signing Secret, Publishable Key) from the Merchant Dashboard * OCAPI access, with Account Manager access granted to `dev@boltapp.com` * System Object Extension enabled to store payment and basket data * Embeddable Checkout also requires SFRA v6.0+ and your Bolt Merchant Division ID ## Install (SFRA V2) [#install-sfra-v2] 1. **Upload the cartridge:** clone `bolt-demandware-managed`, add a `dw.json`, and upload to your SFCC digital server. 2. **Activate the cartridge:** add `int_bolt_sfra:int_bolt_core` in front of the base cartridge path for each site. 3. **Authorize Bolt:** grant Account Manager access and share your API client credentials with Bolt. 4. **Configure OCAPI settings:** paste the provided `OCAPIshop.json` and `OCAPIdata.json`, replacing `client_id`, then disable OCAPI 404 response caching of dynamic content. 5. **Import metadata:** rename `RefArch` to your site ID, edit `library.xml`, then import `Bolt-meta-import`. 6. **Set site preferences:** configure the **Bolt Payment Setting** attributes (API key, signing secret, publishable key, environment). 7. **Add the checkout button:** wire the Bolt button into `cart.isml` via `boltModalConfigure.js`. **Expected result:** the Bolt checkout button renders on the cart and sandbox checkout opens. ## Production checklist [#production-checklist] * Switch to production API keys, credentials, and environment site preference * OCAPI methods allowed through your CDN * Live processor credentials configured in the Bolt dashboard ## Troubleshooting [#troubleshooting] * Shoppers cannot check out: monitor **Administration > Operations > Service Status > `bolt.http`**. * OCAPI requests break: confirm **OCAPI 404 Response Caching of Dynamic Content** is disabled. * Email bypasses Bolt: a shopper already logged into SFCC may auto-fill their email. Use `Bolt-accountExists` to check for a Bolt account. ## Related [#related] * [SFRA V2 installation](/platforms/salesforce/sfcc-sfra-v2/installation/) * [Embeddable Checkout setup](/platforms/salesforce/sfcc-embedded/setup/) * [Manage orders and operations](/platforms/salesforce/operations/) * [Bolt Checkout](/products/checkout) * [Embeddable Checkout](/products/embeddable-checkout) Markdown: https://help.boltapp.com/llms/platforms/salesforce.md --- # Set up Bolt on WooCommerce > Install and configure the WooCommerce Bolt Checkout plugin for WordPress. Source: https://help.boltapp.com/platforms/woocommerce Install the **WooCommerce Bolt Checkout** plugin to add Bolt Checkout to your WordPress store. Requires WooCommerce 3.7 or above and access to your WordPress admin dashboard. **Platform support** This guide is for WooCommerce. Other platforms and custom web apps have their own quickstarts. ## Prerequisites [#prerequisites] * Bolt merchant account (Production and Sandbox dashboard credentials) * WordPress admin access and WooCommerce 3.7+ * Bolt-provided IPs approved for endpoint access * For Fraud-Only: API Key, Publishable Key (Payment Only), and Signing Secret ## Install the plugin [#install-the-plugin] 1. In **WP Admin > Plugins > Add New**, search for **bolt checkout**, then install and activate the plugin. 2. Open the plugin **Settings**, enable the **Bolt Gateway**, and toggle **Bolt Sandbox Mode** on for testing. **Expected result:** Bolt appears as a payment gateway in your WooCommerce settings. ## Configure webhooks [#configure-webhooks] In your Merchant Dashboard, go to **Administration > API > Merchant Callbacks** and set the endpoints, then approve the Bolt IPs: * **Webhook URL:** `https://[store_url]/wp-json/bolt/response` * **Shipping URL:** `https://[store_url]/wp-json/bolt/shipping` * **Tax URL:** `https://[store_url]/wp-json/bolt/tax` * **Create Order URL:** `https://[store_url]/wp-json/bolt/create-order` **Expected result:** Bolt can reach your store endpoints for order, shipping, and tax callbacks. ## Enable Single Sign-On (optional) [#enable-single-sign-on-optional] 1. Ask your Bolt account manager to enable [SSO Commerce](/products/add-ons/sso-commerce) for your store. 2. Add the `[bc_login]` shortcode to your **My Account** page. ## Production checklist [#production-checklist] * Turn off **Bolt Sandbox Mode** * Switch to production API keys and dashboard * Bolt IPs approved on production * If you use the Bolt Extension plugin, update it alongside the core plugin ## Troubleshooting [#troubleshooting] * Endpoints unreachable: confirm the Bolt IPs are approved. * Fraud-Only: enable only **Native Checkout Page** and **Pay for Order**, and clear any **Additional CSS** that could hide the original checkout button. * Voids in the Bolt dashboard do not auto-update the WooCommerce order status; update the order manually. ## Related [#related] * [Full WooCommerce setup guide](/platforms/woocommerce/woocommerce-setup-guide/) * [Manage orders and operations](/platforms/woocommerce/operations/) * [Bolt Checkout](/products/checkout) Markdown: https://help.boltapp.com/llms/platforms/woocommerce.md --- # Set up Bolt on Volusion > Install the Bolt plugin on a Volusion v1 storefront over sFTP. Source: https://help.boltapp.com/platforms/volusion Add Bolt Checkout to a Volusion v1 storefront by deploying the Bolt plugin to your store's `vspfiles` folder over sFTP. Only Volusion v1 is supported. Your account must have the Generic API enabled and be a full (non-trial) account on a plan that supports sFTP. **Platform support** This guide is for Volusion. Other platforms and custom web apps have their own quickstarts. ## Prerequisites [#prerequisites] * Bolt merchant account with API keys * Volusion v1 with the Generic API enabled (full, non-trial account) * The Bolt plugin from your Customer Success Manager * An sFTP client using your Volusion Admin credentials on port 2222 ## Upload the plugin [#upload-the-plugin] 1. Get the plugin from your CSM and edit `vspfiles/bolt/config.asp` with your Bolt API keys and Volusion API credentials. 2. Connect over sFTP (port 2222) and upload the `vspfiles` folder into your store root, overwriting existing content. **Expected result:** the Bolt files are present under `vspfiles/bolt/`. ## Update meta tags [#update-meta-tags] 1. In **Volusion Admin > Marketing > SEO**, add the Bolt connect/track script and CSS snippet to **Globally Appended Meta Tags**, then insert your Bolt publishable key. 2. For sandbox, switch the connect and track script URLs to their sandbox variants. 3. Save, then open `[store_url]/v/vspfiles/bolt/install.asp` to confirm the success message. **Expected result:** `install.asp` reports a successful install. ## Configure webhooks [#configure-webhooks] In your Merchant Dashboard, go to **Administration > API > Merchant Callbacks** and set: * **Webhook URL:** `[store_url]/v/vspfiles/bolt/order-update.asp` * **Shipping and Tax URL:** `[store_url]/v/vspfiles/bolt/shipping-and-tax.asp` ## Production checklist [#production-checklist] * Switch the connect and track script URLs back to production * Use production API keys and dashboard * Confirm `install.asp` success on the production store ## Troubleshooting [#troubleshooting] * Install not confirmed: reopen `[store_url]/v/vspfiles/bolt/install.asp` and check for the success message. * sFTP connection fails: use port 2222 with your Volusion Admin credentials. * Not supported on Volusion v2: confirm you are on v1 with the Generic API enabled. ## Related [#related] * [Full Volusion setup guide](/platforms/volusion/volusion-setup-guide/) * [Manage orders and operations](/platforms/volusion/operations/) * [Bolt Checkout](/products/checkout) Markdown: https://help.boltapp.com/llms/platforms/volusion.md --- # Set up Bolt on SAP Commerce Cloud > Install the Bolt backoffice and Spartacus frontend modules on SAP Commerce Cloud (hybris). Source: https://help.boltapp.com/platforms/sap Install Bolt on SAP Commerce Cloud (also known as hybris) by adding the Bolt backoffice extensions and wiring the Bolt Checkout module into your Spartacus storefront. The SAP integration is delivered by your Bolt CSM as a package. Contact your CSM to get the Bolt file before you start. **Platform support** This guide is for SAP Commerce Cloud. Other platforms and custom web apps have their own quickstarts. ## Prerequisites [#prerequisites] * Bolt merchant account * SAP Commerce Cloud (hybris) installation with access to `/hybris/config` * A Spartacus storefront * The Bolt integration file provided by your CSM ## Backoffice setup [#backoffice-setup] 1. Unzip the Bolt file from your CSM to a temporary folder. 2. Copy the `boltbackoffice` and `boltocc` directories into your Commerce Cloud installation. 3. In `/hybris/config/localextensions.xml`, register the extensions: ```xml title="XML" ``` 4. Restart the server (Commerce Cloud: **Deploy with Recreate**; local: `ant clean all`, start the server, then run a system update). **Expected result:** the **Bolt** tab appears in the backoffice menu. If it does not, refresh the backoffice: press **F4**, open the menu icon, and select **Reset Everything**. ## Frontend setup (Spartacus) [#frontend-setup-spartacus] 1. Copy the **Bolt-Checkout** folder into `project folder/js_storefront/spartacusstore/src/app/Spartacus`. 2. Import `BoltCheckoutModule` in `checkout-feature.module.ts` (`spartacus/features/checkout`) and add it to the module `imports`: ```js title="JavaScript" import { BoltCheckoutModule } from '../../bolt-checkout-base/bolt-checkout.module'; @NGModule({ declarations: [], imports: [CheckoutRootModule, BoltCheckoutModule], providers: [ provideConfig(){ featureModules: { [CHECKOUT FEATURE]: { module: () => import('../../bolt-checkout-base/bolt-checkout.module').then( (m) => m.BoltCheckoutModule ), }, }, }, ], }) ``` 3. Copy the files under `spartacus/features/shared`. 4. In `apps.module.ts`, add `BoltMessageComponentModule` to `imports` and `...boltErrorHandlers` to `providers`. **Expected result:** the Bolt Checkout module builds with your Spartacus storefront. ## Production checklist [#production-checklist] * Switch to production API keys and dashboard * Approved production domain * Processor live credentials configured in the Bolt dashboard ## Troubleshooting [#troubleshooting] * **Bolt** tab missing in backoffice: reset the backoffice (**F4** > menu icon > **Reset Everything**). * Extensions not loading: confirm both `boltocc` and `boltbackoffice` are registered in `localextensions.xml` and that a system update ran. Markdown: https://help.boltapp.com/llms/platforms/sap.md --- # Set up Bolt on Solidus > Install the Bolt SSO Commerce plugin on a Solidus (Ruby on Rails) store. Source: https://help.boltapp.com/platforms/solidus Add Bolt to a Solidus store to connect existing storefront accounts with Bolt for a one-click, passwordless login flow. This integration uses Bolt [SSO Commerce](/products/add-ons/sso-commerce). Solidus SSO Commerce is in limited release and available to a select number of Bolt merchants. Confirm availability with Bolt before you start. **Platform support** This guide is for Solidus. Other platforms and custom web apps have their own quickstarts. ## Prerequisites [#prerequisites] * Bolt merchant account * A Solidus store * Command line and Ruby familiarity ## Install the plugin [#install-the-plugin] 1. Add the required gems to your `Gemfile`: ```ruby title="Ruby" gem 'multi_json', '~> 1.11', '>= 1.11.2' gem 'solidus_auth_devise' ``` 2. Set your environment in your local `ENV` file (`sandbox` for testing, `production` for a live store), then run `bundle install`: ```bash title="Terminal" export BOLT_ENVIRONMENT=sandbox ``` 3. Add and install the Bolt gem: ```bash title="Terminal" bundle add solidus_bolt bin/rails generate solidus_bolt:install ``` **Expected result:** a **Bolt** section appears in your Solidus store dashboard. ## Create your Bolt account [#create-your-bolt-account] 1. In the Solidus dashboard, open the **Bolt** section. 2. Create a **Production** or **Sandbox** merchant account and fill in your store information. 3. Enter the one-time code Bolt emails you to verify the account. ## Connect a payment method and webhooks [#connect-a-payment-method-and-webhooks] 1. In the **Bolt** section, link your payment processor and copy each credential into **Solidus Dashboard > Bolt > Edit**. 2. Under **Settings > Payments**, add a **New Payment Method** of type **Bolt**. Name it **Bolt Payments**, leave **Auto Capture** on **Use App Default (No)**, and check **Available to Users**, **Available to Admin** and **Active**. 3. Once the method exists, set its **Preference Source** to `bolt_config_credentials` and click **Update**. 4. In the **Bolt** section, select **Configure Webhooks** and enter `https://[store_url]/webhooks/bolt`, the endpoint the gem provides. **Expected result:** Bolt is available as a payment method and receives webhook updates. ## Production checklist [#production-checklist] * Set `BOLT_ENVIRONMENT=production` * Switch to production API keys and dashboard * Live processor credentials linked in the Bolt section ## Troubleshooting [#troubleshooting] * **Bolt** section missing: confirm `bin/rails generate solidus_bolt:install` completed without errors. * Payments not processing: verify **Preference Source** is set to `bolt_config_credentials`. ## Related [#related] * [Bolt-Solidus plugin on GitHub](https://github.com/solidusio-contrib/solidus_bolt/) * [Solidus storefront customization guide](https://guides.solidus.io/customization/customizing-your-storefront) Markdown: https://help.boltapp.com/llms/platforms/solidus.md # Documentation: SDKs --- # SDKs > Find Bolt libraries by product, platform, and runtime. Source: https://help.boltapp.com/getting-started/sdks Choose a library after [selecting a Bolt integration](/getting-started/choose-integration). Each entry identifies the product and runtime it supports. ## Frontend SDKs [#frontend-sdks] Pick the library for the product you chose and the client you ship. Frontend SDKs render or open Bolt checkout; they never hold your API key. Open Bolt checkout from a web storefront. Open Bolt checkout from an iOS app. Open Bolt checkout from an Android app. Bolt card fields inside your own checkout page. Card fields, Apple Pay, and Google Pay in a mobile app. ## Gaming [#gaming] Open checkout links from a Unity game. Open checkout links from a web game. The gaming JavaScript package is not the browser client for Managed Checkout; web storefronts use the JS Embed above. Payment links and webhooks are covered by the [Gaming quickstart](/gaming/guide/quickstart). ## Marketplaces [#marketplaces] Seller onboarding component and status helpers. ## Backend SDKs [#backend-sdks] Generated clients for calling the Bolt API from your server with your API key. Check each repository's operations against the endpoint you need before adopting one. Bolt TypeScript SDK Bolt Python SDK bolt-go Bolt C# SDK bolt-php The TypeScript, Python, Go, and C# repositories label their generated SDKs as beta and recommend pinning a package version. Review the repository README before adopting one. You can also call the REST API directly. For Custom Cart: * The Python SDK wraps `POST /v1/merchant/orders`, standard Merchant Callback URL registration, and webhook subscription management. * The TypeScript, Go, and C# Orders clients target `/v3/orders`. They do not create Custom Cart order tokens. * The older PHP library includes `ApiClient::createOrder()` and an HMAC signature helper, but its examples use older API hostnames. Follow the current Custom Cart guides for URLs and payloads. [Review API authentication](/api-reference/authentication) ## Related [#related] Copy credentials for the correct environment and division. Match each credential and request header to its purpose. Markdown: https://help.boltapp.com/llms/getting-started/sdks.md --- # React Native SDK > Integrate Bolt Embeddable Checkout into your cross-platform mobile applications using the Bolt React Native SDK. Supports credit card tokenization, 3D Secure, Apple Pay, and Google Pay. Source: https://help.boltapp.com/developers/sdks/react-native **This SDK needs server work, but not the custom cart server integration.** The React Native SDK tokenizes the payment instrument; your backend takes that token and creates the payment through the Bolt API. Bolt does not call your server for shipping options, tax, or discounts here, because there is no Bolt-hosted checkout in this flow to call them from. Build these first, then return here for the client installation: 1. [Create the payment from your backend](https://help.boltapp.com/api-embedded/) 2. [Transaction webhooks](/custom-cart/transaction-webhooks), so the transaction lands in your own records Looking for the flow where Bolt Checkout drives shipping, tax, and discounts through your callbacks? That is the [custom cart integration](/custom-cart/overview), and its clients are [web](/platforms/direct-api), [iOS](/developers/sdks/ios-swift), and [Android](/developers/sdks/android-kotlin). ## Overview [#overview] The React Native SDK is currently designed for use with **[Bolt Embeddable Checkout](/products/embeddable-checkout)**. See the [Embeddable Checkout API Reference](https://help.boltapp.com/api-embedded/) for full backend API documentation. The Bolt React Native SDK (`@boltpay/react-native`) provides everything you need to accept payments in your React Native app: credit card tokenization, 3D Secure verification, Apple Pay, and Google Pay: all integrated with Bolt Embeddable Checkout. | Feature | How it works | | --------------------- | ----------------------------------------------------------------------------------------------------------------- | | **Credit Card & 3DS** | WebView-based, loading secure pages from `connect.boltapp.com`. Card data never touches your app (PCI compliant). | | **Apple Pay** | Native `PKPaymentButton` on iOS via a Fabric view component, with a TurboModule for the payment sheet. | | **Google Pay** | Native `PayButton` on Android via a Fabric view component, with a TurboModule for the payment sheet. | ## Installation [#installation] Install the SDK and its peer dependencies: npm yarn ```bash npm install @boltpay/react-native react-native-webview react-native-get-random-values ``` ```bash yarn add @boltpay/react-native react-native-webview react-native-get-random-values ``` `react-native-get-random-values` is a crypto polyfill that `@boltpay/tokenizer` needs to tokenize Apple Pay and Google Pay payloads. Omitting it installs cleanly and then fails at wallet payment. For iOS, install the native pods: ```bash title="Terminal" cd ios && pod install ``` ### Requirements [#requirements] | Dependency | Minimum Version | | ------------------------------ | --------------- | | React Native | >= 0.73.0 | | React | >= 18.0.0 | | react-native-webview | >= 13.0.0 | | react-native-get-random-values | >= 2.0.0 | ## Quick Start [#quick-start] ### 1. Initialize Bolt [#1-initialize-bolt] Create a `Bolt` client instance and wrap your app with the `BoltProvider` context: ```typescript title="TypeScript" import { Bolt, BoltProvider } from '@boltpay/react-native'; const bolt = new Bolt({ publishableKey: 'YOUR_PUBLISHABLE_KEY', environment: 'sandbox', // or 'production' }); function App() { return ( ); } ``` ### 2. Accept a Payment [#2-accept-a-payment] From here, choose the payment method that fits your use case: * **[Credit Card Payments](/developers/sdks/react-native/credit-card/)**: Tokenize card details and handle 3D Secure challenges. * **[Apple Pay](/developers/sdks/react-native/apple-pay/)**: Accept Apple Pay on iOS devices. * **[Google Pay](/developers/sdks/react-native/google-pay/)**: Accept Google Pay on Android devices. * **[Styling](/developers/sdks/react-native/styling/)**: Customize the look and feel of Bolt components. * **[API Reference](/developers/sdks/react-native/api-reference/)**: Full reference for all exports, props, types, and error codes. ## Example App [#example-app] The SDK repository includes a full checkout demo in the `example/` directory. The repository is a Yarn workspace and its `example` script runs `yarn workspace`, so this part needs Yarn regardless of which package manager you used to install the SDK above. iOS Android ```bash yarn example ios ``` ```bash yarn example android ``` ## Resources [#resources] * [npm: @boltpay/react-native](https://www.npmjs.com/package/@boltpay/react-native) * [Embeddable Checkout API Reference](https://help.boltapp.com/api-embedded/) Markdown: https://help.boltapp.com/llms/developers/sdks/react-native.md --- # Credit Card and 3D Secure > Tokenize credit card details and handle 3D Secure verification using the Bolt React Native SDK for Embeddable Checkout. Source: https://help.boltapp.com/developers/sdks/react-native/credit-card ## Credit Card Payment [#credit-card-payment] This guide covers credit card tokenization and 3D Secure for [Bolt Embeddable Checkout](/products/embeddable-checkout). For the full backend API, see the [Embeddable Checkout API Reference](https://help.boltapp.com/api-embedded/). The Bolt React Native SDK tokenizes card data securely in a WebView loaded from `connect.boltapp.com`. Card details never touch your app, keeping your integration PCI compliant. ### Basic Flow [#basic-flow] ```typescript title="TypeScript" import { CreditCard, useThreeDSecure } from '@boltpay/react-native/payments'; function CheckoutScreen() { const cc = CreditCard.useController(); const threeDSecure = useThreeDSecure(); // Listen for field events cc.on('valid', () => setCanSubmit(true)); cc.on('error', (msg) => setFieldError(msg)); const handlePayment = async () => { // 1. Tokenize: returns TokenResult | Error (never throws) const result = await cc.tokenize(); if (result instanceof Error) { console.error(result.message); return; } // result: { token?, last4?, bin?, network?, expiration?, postal_code? } // 2. Fetch 3DS reference ID: throws ThreeDSError on failure const referenceID = await threeDSecure.fetchReferenceID({ token: result.token, bin: result.bin, last4: result.last4, }); // 3. Send token to your backend to create the payment const paymentResponse = await yourApi.createPayment(result); // 4. Handle 3DS challenge if required: returns ThreeDSResult (never throws) if (paymentResponse['.tag'] === 'three_ds_required') { const challengeResult = await threeDSecure.challengeWithConfig( paymentResponse.id, { referenceID, jwtPayload: paymentResponse.jwt_payload, stepUpUrl: paymentResponse.step_up_url, } ); if (!challengeResult.success) { console.error(challengeResult.error?.message); } } }; return ( <>