Build a SaaS with Lovable: foundation, proof, landing page, then Stripe
A SaaS on Lovable is built in six steps, and the order is the whole method: a foundation with accounts, workspaces and roles; a schema whose row-level security is proven by queries run as the wrong user; a landing page that counts sign-ups; the mechanics that make it chargeable; billing last. My foundation run cost 10.4 credits and the starter kit's schema carries 12 policies on 4 tables. For payments Lovable offers two paths, built-in payments on a paid plan or your own Stripe account on any plan, and both end at the same rule: the webhook decides who paid.
By Marco Kohns · Payments docs and Stripe pricing read 23 October 2026
10.4credits
Foundation with sign-up, workspaces, roles and invites; 4 questions first.
12policies
In the SaaS starter kit's schema, with 6 checks run as an ordinary member.
20.4credits
One whole day on one project: foundation, two reviews, phone install, landing page.
The order
In what order do you build a SaaS with Lovable?
Each step builds on a checked one. Billing comes last because it is the step that touches money.
- 0110.4 credits
Foundation
Accounts, workspaces, three roles, invites. The SaaS MVP prompt. - 0212 policies
Schema
Tables with row-level security, deny by default. The SaaS starter kit. - 036 checks
Prove it
Run the checks as an ordinary member: another workspace's rows return nothing, a self-promotion fails. - 044.5 credits
Landing page
Count sign-ups against visitors before charging. The SaaS landing page prompt. - 0515 prompts
Mechanics
Plan limits enforced on the server, trials that end, billing pages, an audit log. - 06Webhook first
Billing
Built-in payments or your own Stripe account; the webhook is the only writer of entitlement.

Steps 2 and 3
How do you prove a SaaS schema keeps tenants apart?
Run the kit's checks as an ordinary signed-in member. The service role bypasses every policy, so testing with it proves nothing.
| # | Expected result |
|---|---|
| 1 | Should return only workspaces you are a member of, never every workspace. |
| 2 | Should return zero rows for a workspace you do not belong to. Substitute a workspace id you are NOT a member of. |
| 3 | Should FAIL for a member (non-admin). If it succeeds, the write policy is too loose. |
| 4 | Should FAIL. Nobody may read another workspace's invites. |
| 5 | Should FAIL with 'invite is invalid, expired, or already used'. |
| 6 | Confirm RLS is on. Every row must show rowsecurity = true. |
The highlighted check catches the trap this kit exists for: a member promoting themselves to owner. Why the scans alone do not prove this is in the Lovable security guide.
Step 6: payments
Should you use Lovable's built-in payments or your own Stripe account?
Built-in payments for the least setup on a paid plan; your own Stripe account on the Free plan or when you want the Stripe dashboard. A project uses one or the other.
| Built-in payments | Your own Stripe account | |
|---|---|---|
| Plan | Pro or higher | Any plan, including Free |
| Providers | Paddle (merchant of record) or Stripe | Stripe, your account |
| Keys | None to handle; Lovable provisions the account | A restricted (rk_) or secret (sk_) key in the Connect Stripe form, never in the chat |
| Webhooks | Lovable registers them per environment; do not edit them | None by default; ask Lovable to add one |
| Testing | Test mode in the preview, test cards only | A test key keeps everything in Stripe's test mode; the customer portal does not open in the preview |
| Limits | Paddle: digital products and software; Stripe: also services and physical products; One payment provider per project; products, prices and subscriptions do not migrate when you switch; Projects with payments cannot be remixed; Do not create or delete webhooks manually | A project can't use your own Stripe account and built-in payments at the same time; The Payments view is read-only for this integration. You create products, issue refunds, and configure webhooks in your Stripe dashboard |
Sources: Lovable docs: Payments and Stripe integration, read 23 October 2026.
What a sale costs
What does one sale cost through Stripe or Paddle?
Fees per sale, computed from the published rates. Using a provider through Lovable costs the same as connecting it directly.
| Sale | Fee | Rate |
|---|---|---|
| Stripe, US card, $29 | $1.14 | 2.9% + 30¢ |
| Stripe, international card, $29 | $1.58 | 4.4% + 30¢ |
| Stripe, international + FX, $29 | $1.87 | 5.4% + 30¢ |
| Paddle via Lovable, $29 | $1.95 | 5.0% + 50¢ |
| Stripe, US card, $9 | $0.56 | 2.9% + 30¢ |
| Paddle via Lovable, $9 | $0.90 | flat 10% under $10 |
Stripe, US pricing read in a browser 23 October 2026: 2.9% + 30¢ per successful transaction for domestic cards, + 1.5% for international cards, + 1% if currency conversion is required, + 0.5% for manually entered cards; Billing 0.7% of Billing volume (pay as you go); disputes $15.00 for each dispute you receive. Paddle through Lovable: 5.0% + 50¢, a flat 10% under $10, Paddle acting as merchant of record for tax.
The rule
Why is the success page not proof that someone paid?
Because the buyer may never reach it, and because a URL can be bookmarked. Stripe's own docs say so.
Stripe's fulfilment guide: “You can't rely on triggering fulfillment only from your checkout landing page, because it's not guaranteed customers visit that page. For example, a customer can pay successfully and then lose their internet connection before your landing page loads.” And: “Perform fulfillment only once per payment. Because of how this integration and the internet work, your fulfill_checkout function might be called multiple times, possibly concurrently, for the same Checkout Session.” So entitlement is written by the webhook, once per event, and the success page only reads it.
This site sells its bundle that way. The handler claims each event id before acting, so a retry or a concurrent delivery is a no-op, and releases the claim if the handler fails so Stripe's retry is not swallowed.
This site's webhook, the idempotency layer
// Layer 1. Insert-and-check: if we did not insert, someone already handled this event.
const { data: claimed, error: claimError } = await supabase
.from("stripe_webhook_events")
.insert({ id: event.id, type: event.type })
.select("id")
.maybeSingle();
if (claimError?.code === "23505") {
return NextResponse.json({ received: true, duplicate: true });
}
try {
switch (event.type) {
case "checkout.session.completed":
case "checkout.session.async_payment_succeeded":
await handleCheckoutSession(event.data.object);
break;
case "charge.refunded":
await markPurchaseRefunded(charge.payment_intent);
break;
case "charge.dispute.created":
await markPurchaseDisputed(dispute.payment_intent);
break;
}
} catch (error) {
// 500 so Stripe retries. Release the claim first, or the retry short-circuits as a duplicate.
await supabase.from("stripe_webhook_events").delete().eq("id", event.id);
return NextResponse.json({ error: "Handler failed" }, { status: 500 });
}| What broke | Effect | Fix |
|---|---|---|
| The guest access cookie was written inside a server component render, which Next.js forbids; the throw was swallowed by a try/catch that had already set access to true. | A guest paid, saw "It is yours", and could not download anything. | A Route Handler at /api/bundle/claim settles the session and writes the cookie, then redirects to /thank-you, which only reads state. |
| The receipt's sender address was not verified with the email provider and the 403 was swallowed. | No receipt could send. | Verified sender, reply-to unchanged, List-Unsubscribe header. |
| The post-purchase sign-up button carried pre-purchase intent. | A buyer who created an account afterwards was sent back to the $199 page. | A separate purchase intent that returns to the member area. |
| A missing webhook secret was reported as a bad signature with a 400. | Stripe marked deliveries permanently failed and the log named the wrong cause. | A 503 before signature verification when the secret is absent. |
The SaaS prompts, in build order
Disclosure: if you create a Lovable account through links on this page, we may earn a commission. It costs you nothing and does not change what we recommend. Create a free Lovable account
Frequently asked
How do you build a SaaS with Lovable?
Foundation first (accounts, workspaces, roles, invites), then the schema with row-level security, then the verification checks as an ordinary member, a landing page that counts sign-ups, the mechanics that make it chargeable, and billing last. My foundation run cost 10.4 credits.
Does Lovable work with Stripe?
Yes, two ways. Built-in payments on a Pro plan or higher, where Lovable provisions Stripe or Paddle and manages keys and webhooks; or your own Stripe account on any plan, connected with a restricted or secret key in the Connect Stripe form. A project uses one or the other.
Does Lovable's Stripe integration use webhooks?
Not by default on your own Stripe account: the app checks payment status with Stripe directly. Ask Lovable to add a webhook, finish the endpoint in the Stripe dashboard, and make the webhook the only writer of entitlement. Built-in payments register their webhooks for you.
Can I test Lovable payments in the preview?
Yes. Built-in payments run in test mode in the preview and accept test cards only. On your own Stripe account a test key keeps everything in test mode and checkout opens in a new tab; the customer portal does not open inside the preview.
What does a sale cost on Lovable with Stripe?
Lovable adds nothing: provider rates apply. On 23 October 2026 Stripe's US rate was 2.9% + 30¢ for domestic cards, +1.5% international, +1% with currency conversion. Paddle through Lovable charges 5.0% + 50¢, or a flat 10% under $10.
Can I remix a Lovable project that takes payments?
No. Lovable's payments docs say projects with payments enabled cannot be remixed, and switching providers does not migrate products, prices or subscriptions.
What we did not publish
- A Lovable run of the billing step. This site's checkout is in its own Next.js code; the webhook and defects are from there.
- Paddle in production; the rate is Lovable's published one.
- Tax obligations for your country; Paddle and Stripe Managed Payments act as merchant of record, Stripe's standard path does not.
Sources
- Lovable docs: Payments, read 23 October 2026
- Lovable docs: Stripe integration, read 23 October 2026
- Stripe pricing (US), read in a browser 23 October 2026
- Stripe docs: Fulfill orders
- My SaaS foundation run, 2026-09-26; this site's git history for the checkout
Written by

Marco Kohns
Founder of ProtoBites - Venture Growth Studio
Growth PM at a Silicon Valley scale-up (a16z and General Catalyst backed), ex-Techstars where he consulted 13 early-stage startups, Reforge-trained. Every prompt on this site comes out of shipping ProtoBites' own portfolio products.
Related reading
- the SaaS starter kit · The schema and checks this page counts.
- Lovable SaaS prompts · The foundation and the mechanics prompts.
- Lovable and Supabase · The three RLS failure shapes.
- Lovable security · What the scans prove, and what only a query does.
- SaaS landing page prompt · Step four, run for 4.5 credits.
- how to build an MVP · The measured day behind the ledger.
- SaaS ideas · Fifteen products composed from the same mechanics.
- three prompts and what they built · The foundation run's screenshots.
- Lovable pricing and credits · Plans read today and what one prompt costs in credits.
- CRM app prompts · A worked product category on top of the foundation.
- admin dashboard prompts · The operator surface on top of the foundation.
- the SaaS MVP prompt page · The foundation step, run and measured, with the next four prompts.