# Recipe: Bolt

For an app built in Bolt (bolt.new). Bolt apps are usually a Vite front end; server code runs in Supabase Edge Functions when the project is connected to Supabase, or in functions on the host Bolt deploys to. This file covers what's different in Bolt and the prompt to paste; the code is in the recipe it points to. [The playbook](https://checkouts.md/playbook.md) has the reasons and every error code.

| | |
|---|---|
| Server code runs in | Supabase Edge Functions (preferred), or the functions of the host the project deploys to |
| Secrets | Edge Function secrets for Supabase; the host's environment variables otherwise. Never `VITE_` variables |
| Code | [recipes/supabase.md](https://checkouts.md/recipes/supabase.md), or [recipes/html.md](https://checkouts.md/recipes/html.md) option B's handlers as Netlify functions |
| Connect | The person creates the keys in Railbed and enters them as secrets (below) |

## 1. Decide where server code runs

- **The project is connected to Supabase** (it has a `supabase/` folder, or `@supabase/supabase-js` with a Supabase URL in its environment): use the [Supabase recipe](https://checkouts.md/recipes/supabase.md) as it is.
- **It isn't, and it deploys to Netlify** (or another host with functions): add the checkout and webhook handlers as Netlify Functions (paths in [html recipe B](https://checkouts.md/recipes/html.md#hosting-somewhere-else)) with a database the project can reach, or connect Supabase first, which Bolt supports, and use the Supabase recipe.
- **Never** call Railbed from the Vite front end. A `VITE_…` variable is compiled into the page for every visitor to read.

## 2. Connect

Bolt runs the project in the browser (StackBlitz WebContainers). Its terminal can run `npx`, but requests from it may not reach outside services the way a normal computer's do, and anything it writes stays in the browser project, not in Supabase's or Netlify's secret stores. Pick the first that applies:

1. **If `npx checkouts.md login --test-only --env-path supabase/functions/.env` runs and connects in Bolt's terminal**, use it, then set the secret in Supabase from that file without printing it (`supabase secrets set --env-file supabase/functions/.env`, where the Supabase CLI is available and linked), or ask the person to. Don't open or read the file into the chat.
2. **Otherwise**, the approval happens in Railbed's dashboard. Ask the person to sign in or sign up at https://app.railbed.io/signup, switch on **Test mode**, open **Developers**, **Create key** (purpose Checkout API, Full access, named "Bolt"), and paste it as the secret `RAILBED_SECRET_KEY`: in Supabase's dashboard (Edge Functions → Secrets) or in the deploy host's environment variables. Never into the chat.

> To connect payments I need a Railbed key, which only you can create. Sign in at app.railbed.io (or create an account), turn on Test mode in the menu at the top left, open Developers and choose Create key. Then add it as a secret named RAILBED_SECRET_KEY in your Supabase project's Edge Function secrets, not in this chat. Nothing is charged in Test mode.

## 3. The prompt to paste

```text
Read checkouts.md/recipes/bolt.md and add a Railbed checkout to this app, following it exactly.
Sell: Pro plan for 49.00 USD.
Server code goes in Supabase Edge Functions, never in the Vite front end or in VITE_ variables.
Keep RAILBED_SECRET_KEY and RAILBED_WEBHOOK_SECRET only in secrets, never in code or in this chat.
Mark an order paid only from a verified Railbed webhook after re-reading the payment.
Test it in Railbed's Test mode before anything goes live.
```

If Bolt can't open the link, paste the full prompt from the [Lovable recipe](https://checkouts.md/recipes/lovable.md#2-the-prompt-to-paste): it describes the same Supabase build.

## 4. The webhook endpoint

The deployed function's address, `https://<project-ref>.supabase.co/functions/v1/railbed-webhook`, is public, so no tunnel is needed. Create the endpoint with `npx checkouts.md webhook <address> --env-path supabase/functions/.env` if the CLI worked in step 2 (it saves `RAILBED_WEBHOOK_SECRET` in that file; set it in Supabase the same way); otherwise ask the person to add it in Railbed (Developers → **Add endpoint**), **Reveal** its signing secret, and add it as the secret `RAILBED_WEBHOOK_SECRET`. Make sure `supabase/config.toml` has `verify_jwt = false` for `railbed-webhook` and that the function is deployed with that setting.

## 5. Test it

1. In the preview, click the Pay button; Railbed's checkout opens.
2. Enter any email, choose Pay, and choose **Simulate successful payment** on the test provider page.
3. Check: the success page says the order is confirmed; the order is `paid` in Supabase's table editor; the function's logs show `fulfilled`; Railbed's dashboard (Test mode) shows **Webhook delivered** on the payment.
4. Try **Simulate an underpayment** with a new order: the order shows `held` and nothing is granted.
5. Record the payment id under Evidence in `CHECKOUTS.md`.

## 6. Go live

1. The business adds a payout wallet in Railbed's Settings.
2. Get the Live values the way the Test ones came. With the CLI: `npx checkouts.md login --env-path supabase/functions/.env --live-to env:supabase/functions/.env.production` (the person approves Test and Live), then `npx checkouts.md webhook <address> --live --live-to env:supabase/functions/.env.production`, then `supabase secrets set --env-file supabase/functions/.env.production`. Otherwise the person, in Railbed's Live mode, creates a Live key and an endpoint for the same function address. Either way, both secrets (`RAILBED_SECRET_KEY`, `RAILBED_WEBHOOK_SECRET`) now hold the Live values, never through the chat. Set `APP_URL` to the published `https://` address.
3. Deploy the site and the functions.
4. Set `mode: live` in `CHECKOUTS.md`. The first real payment is the Live evidence.

## 7. CHECKOUTS.md

As in the [Supabase recipe](https://checkouts.md/recipes/supabase.md#7-checkoutsmd), naming where the secrets were entered.

## Platform notes

These describe Bolt as it works at the time of writing; if Bolt differs, follow its documentation and keep the rules.

- A `.env` file in the Bolt project holds values for the browser build. Nothing in it is secret from visitors once a `VITE_` variable uses it, so Railbed's keys never go there.
- If the project moves to another host, the Edge Functions keep working; only `APP_URL` changes.
