Bitcoin checkout on Lovable
Lovable builds TanStack Start apps that run on Cloudflare Workers, with Supabase as their database. OpenReceive runs in your app’s own server code: it creates each Lightning invoice, the payment goes straight to your wallet, and payment attempts are stored in your app’s Supabase database. There is no OpenReceive account, no API key and no background worker.
Optional swaps let customers pay with USDT, USDC, SOL, and ETH. A swap provider you configure converts the payment to BTC over Lightning, and it settles into the same wallet. Available assets and networks depend on the provider.
Use @openreceive/* 0.4.23 or newer. The payment route this guide sets up
runs in OpenReceive’s CI as a Cloudflare Worker against Supabase’s own
database and API server, and it was checked on an app built with Lovable’s
build setup. Lovable’s own agent has not run these directions in our tests
yet.
Before you start
- A Lovable project with Lovable Cloud turned on, or a Supabase project connected to it. Projects created since May 2026 are TanStack Start apps.
- A receive-only NWC code from your Lightning wallet (get one). It can create invoices and read payments, but it cannot spend.
- Optional: a swap provider code, for USDT, USDC, SOL and ETH (set one up).
Add checkout to your Lovable app
- Open More → Cloud → Secrets → Add secret. Add
NWC_URIwith your wallet code, then, for swaps,LSC_URI_PRIMARYwith your swap provider code. Secrets reach your app’s server code and never the browser. Lovable already gives server codeSUPABASE_URLandSUPABASE_SERVICE_ROLE_KEY. - Send Lovable this prompt:
Add Bitcoin Lightning checkout to this app with OpenReceive. Fetch https://openreceive.org/agent-directions/lovable/full.md and follow it exactly: it has the migration, the server route and the checkout page. NWC_URI and LSC_URI_PRIMARY are already saved as this project's secrets, so do not ask me for them. Use @openreceive packages 0.4.23 or newer.
- Lovable writes two Supabase migrations and asks you to apply them. The
first is OpenReceive’s: its two tables, locked away from your app’s
browser key, and the functions that write them. The second is yours:
openreceive_on_paid, which marks an order paid. Apply both. - Lovable ends with “Setup is finished” and tells you where to place an order.
Bitcoin only, with no swap provider? Then in the prompt, replace
NWC_URI and LSC_URI_PRIMARY are already saved with
I want Bitcoin only, with no swaps. NWC_URI is already saved.
Rather paste the codes in the chat? Leave out the sentence about secrets. Lovable then asks for each code with its secure secret input, one at a time.
Check it
In the preview, place an order. Its checkout shows the payment methods. Pick Bitcoin to see a Lightning invoice. Each swap has a minimum amount set by the provider, so on a small order some coins are greyed out and show their minimum.
If the checkout cannot load and its requests answer 503, the server log names
the fix. Usually a migration was not applied, or openreceive_on_paid is
still the placeholder, which refuses every payment until it is replaced.
Pay a small order from your wallet to see it settle: the checkout shows the
payment as received, and openreceive_on_paid marks the order paid.
How it runs on Lovable
- Supabase over HTTPS. A Worker cannot open a Postgres connection to
Supabase, because Supabase’s database certificate comes from its own
authority. So OpenReceive reaches your database through Supabase’s HTTPS
API, with the
SUPABASE_URLandSUPABASE_SERVICE_ROLE_KEYthat Lovable gives your server code. Details: Supabase over HTTPS. - Fulfillment is SQL.
openreceive_on_paidruns inside the transaction that records the payment, once per order. If it fails, nothing is recorded and the next request tries again. - The buyer is a cookie. The checkout calls your payment routes with the page’s cookies. Lovable keeps your users’ sign-in in the browser instead, so each order carries a buyer token that matches an HttpOnly cookie set when the order is created. Only that browser can pay for the order.
- No worker or cron job. Each request to the payment routes also checks the wallet for settled invoices, through a lock in your database. A payer who closes the tab is settled on the next request.
- Your secrets stay on the server.
NWC_URIandLSC_URI_PRIMARYare Lovable secrets, read only by server code. They never go into.env, which Lovable commits, or into aVITE_variable, which ends up in the browser.
Each payment request makes several calls to Supabase’s API, one after another: 4 to 8 to create a checkout. That is the slower part of a checkout on Lovable, not the wallet.
Keep it intact: AGENTS.md
Lovable reads AGENTS.md at the root of your project on every change. Add
this to it, so later edits keep the payment setup safe:
## Payments (OpenReceive)
- The payment routes are src/routes/openreceive.$.ts and
src/lib/openreceive.server.ts. Fulfillment is the SQL function
public.openreceive_on_paid. There is no JavaScript onPaid.
- NWC_URI and LSC_URI_PRIMARY are secrets. Never put them in .env, code or a
VITE_ variable.
- Never grant anon or authenticated anything on openreceive_* tables or
functions, and never edit OpenReceive's migration.
- Full directions: https://openreceive.org/agent-directions/lovable/full.md
Next
- TanStack Start recipe: the code Lovable writes, explained
- Supabase: Supabase over HTTPS, and what the server checks
- Supabase migration: the SQL Lovable applies
- Frontend checkout: the checkout’s options
- Security