Back to Learn

    Lovable and Stripe: Built-In Payments, Your Own Account, and the Webhook Behind Our Own Checkout

    Lovable offers two ways to take money and its docs describe both precisely, including the one line most guides skip: on your own Stripe key the integration does not use webhooks by default. This page quotes both paths, reads Stripe's fees on the day of writing and prices one sale under each, quotes Stripe's own rule on why the success page is not proof of payment, and then shows the webhook and claim route that sell the bundle on this site, with the four defects that shipped with them and how each was fixed.

    Updated

    Lovable and Stripe hero: the guide title beside a checklist card, built-in needs a paid plan with Paddle or Stripe, own account works on Free with a restricted key, no webhook by default on your own key, the success redirect is not proof of payment, claim the event id before any side effect, guest cookie only in a Route Handler

    Which two paths does Lovable offer, and who qualifies for each?

    Built-in payments and your own Stripe account. Both are in Lovable's docs, read on 26 September 2026, and they differ on plan, on who holds the keys, and on webhooks. The payments page says “built-in payments require a paid Lovable plan”, and offers Paddle and Stripe with “no API keys to handle”. The own-account page says the integration “does not require a paid Lovable plan”, and that “The form accepts restricted keys (rk_...) or secret keys (sk_...), test or live mode”.

    The two Lovable payment paths as the docs describe them, read 26 September 2026
     Built-in paymentsYour own Stripe account
    PlanPaid Lovable planAny plan, including Free
    ProviderPaddle and StripeStripe only
    KeysNone to handle; Lovable provisions the accountA restricted or secret key pasted into the Connect Stripe form, stored as a backend secret
    Test and liveTest mode in the preview, live after publishing; products sync on publishYour test or live key; the full checkout runs from the preview in a new tab
    WebhooksTwo endpoints per environment, registered by Lovable; do not edit them by handNone by default; the app checks status directly with Stripe
    Payments viewProducts, prices, refunds managed in LovableRead-only; products, refunds and webhooks live in the Stripe dashboard
    CannotRemix a project with payments; run two providers; sell physical goods by defaultCombine with built-in payments in the same project

    The security line is the one to copy into your habits: “Never paste your Stripe key into chat messages.” The form is the only place for it, and “The key is stored as a backend secret, and Lovable does not read stored secret values back.” Lovable then “Lovable builds your payment flow with edge functions: small pieces of backend code that call Stripe using your stored key, so the key is never exposed in your app.”

    What does a sale cost on each path?

    Stripe's fees, from its US pricing page on 26 September 2026: “2.9% + 30¢ per successful transaction for domestic cards”, “+ 1.5% for international cards”, “+ 1% if currency conversion is required”. Checkout is “Included with Payments”; Billing is 0.7% of Billing volume (pay as you go); Tax Basic is 0.5% per transaction, where you're registered to collect taxes (no-code integration); a dispute is $15.00 for each dispute you receive. Paddle through Lovable's built-in path is “5.0% + 50¢ per transaction, no monthly fee”, with a “flat 10% under $10”. Lovable's own framing: “Using Stripe or Paddle through Lovable costs the same as setting up the provider directly.”

    Fee on one sale, computed from the published rates, in cents
    Stripe, US card, $29114 (2.9% + 30¢)Stripe, international card, $29158 (4.4% + 30¢)Stripe, international + FX, $29187 (5.4% + 30¢)Paddle via Lovable, $29195 (5.0% + 50¢)Stripe, US card, $956 (2.9% + 30¢)Paddle via Lovable, $990 (flat 10% under $10)
    Fee on one sale, computed from the published rates, in cents
    Categorycents
    Stripe, US card, $29114 (2.9% + 30¢)
    Stripe, international card, $29158 (4.4% + 30¢)
    Stripe, international + FX, $29187 (5.4% + 30¢)
    Paddle via Lovable, $29195 (5.0% + 50¢)
    Stripe, US card, $956 (2.9% + 30¢)
    Paddle via Lovable, $990 (flat 10% under $10)

    Stripe rates from stripe.com/us/pricing and Paddle rates from docs.lovable.dev/features/payments, both read 26 September 2026. Rounded to the cent; Stripe Tax and Billing not included.

    Paddle is a merchant of record, so its higher rate buys tax handling that Stripe sells separately; whether that is worth 2.1 points plus 20 cents on a $29 sale depends on where your buyers are. What the chart cannot show is the international share of your sales, which on this site is the majority, so the middle bars are our bars.

    Why is the success page not proof of payment?

    Because Stripe says so, in its "Fulfill orders" 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.” The events to handle are checkout.session.completed and, for delayed payment methods, checkout.session.async_payment_succeeded. The detail that surprises people: “Checkout waits up to 10 seconds for your server to respond to the webhook event delivery before redirecting your customer.”

    Put next to Lovable's line that on your own key “By default, the integration does not use webhooks: your app checks payment and subscription status directly with Stripe”, this is the decision the page exists for. A one-time purchase that grants access at the moment of return works until a buyer's connection drops between paying and returning. Subscriptions and delayed methods do not work that way at all. Ask Lovable for the webhook, verify its signature, and make it the only writer.

    The checkout path on this site, in order
    1Create sessionserver-side2Stripe Checkouthosted page3Webhookwrites once4Claim routesettles, sets cookie5Member areareads only
    1. Create session: server-side
    2. Stripe Checkout: hosted page
    3. Webhook: writes once
    4. Claim route: settles, sets cookie
    5. Member area: reads only

    src/app/api/create-bundle-checkout, src/app/api/stripe/webhook and src/app/api/bundle/claim, read 26 September 2026.

    What each step does, from the three route files
     What it doesWhat it must not do
    Create sessionRuns server-side; guests allowed; metadata marks the product; automatic tax on; success_url points at a Route HandlerCharge an account that already owns the bundle
    Stripe CheckoutHosted page; card never touches our serverBe trusted as proof of payment on return
    WebhookVerifies the signature, claims the event id, writes the purchase row once, sends the receipt only when this call inserted the rowReturn 500 on a bad signature; grant anything from an unpaid session
    Claim routeRetrieves the session, checks payment_status is paid, settles the same row idempotently, mints a 24-hour guest cookie, redirectsSwallow a Stripe error and show access anyway
    Member areaReads entitlement from Postgres by confirmed email or user idWrite any state

    The webhook and the claim route both call the same idempotent write, keyed on the Checkout Session id, because the browser redirect regularly arrives before the webhook and a buyer looking at a page that says they own nothing is worse than a duplicate no-op. The webhook adds a second layer on the Stripe event id so a retry cannot send a second receipt:

    // 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 });
    }

    Two rules in there are cheap to state and expensive to skip. A bad signature returns 400, never 500, because a 500 makes Stripe retry a forgery. A handler failure releases the claim before returning 500, because otherwise the retry short-circuits as a duplicate and the purchase is lost.

    What broke on our own checkout, and how was each fixed?

    The path above is the fixed version. The git log of this repository, read 26 September 2026, shows how it got here.

    This site's payment path, by commit, from git log on 26 September 2026
     DateWhat changed
    297cbb725 Jul 2026Plan resolved from Stripe instead of an is_pro_user stub: a walk of up to 100 API calls per check.
    921358c25 Jul 2026Paywall made server-authoritative.
    7af859d11 Sep 2026Subscription and Builder Pack retired; one one-time product.
    30803ce12 Sep 2026Bundle purchasable; entitlement becomes one row in purchases, written by the webhook.
    371e8b712 Sep 2026Gated downloads and the member area.
    49e24a512 Sep 2026The defect that broke every guest purchase, fixed and gated.

    The last commit fixed four defects that had shipped that morning. The first one was the default path, not an edge case: checkout is guest-first by design, so every guest who paid hit it.

    The four defects fixed in commit 49e24a5 on 12 September 2026
     Effect on a buyerFix
    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 common thread is the same as on the security page: a swallowed error looks like success. The fix that outlasts the others is that the member page now writes nothing and a gate script runs a guest purchase on every deploy. The vibe coding tips page carries the rest of that list.

    Which prompts on this site encode these rules?

    Four, across three categories. Subscription Checkout Flow and Payment History & Billing Pages sit in the SaaS prompts; Payment Provider Webhook Integration in the API prompts; Pricing Page with Tier Comparison in the landing page prompts. The lines they share, as they appear in the prompt bodies:

    ## Build Order
    Server-side session creation first, then the webhook with signature verification, then the return pages, then the status UI. The webhook is the source of truth; build it before the celebration screen.
    
    ## Safe-Guard Instructions
    - Verify webhook signatures; reject unsigned payloads
    - Make webhook handlers idempotent: Stripe retries, and double-processing a payment event is a money-losing bug
    - On Lovable, built-in payments (Paddle or Stripe) need a paid plan and run in test mode in the preview until the app is published; on the free plan connect your own Stripe account. Either way the webhook stays the source of truth
    - Never grant entitlements from the success redirect alone; a user can bookmark that URL
    - All Stripe calls server-side; the browser never sees the secret key
    - Amounts and currency stored as provided, in minor units; no floating-point money anywhere

    Every one of those lines is a defect we have either shipped or read about in the CVE record. The Pro variants add the distribution step each payment feature should carry, which is what the bundle sells at $199 USD, one time, through the exact checkout described above.

    What do the top results for this term leave out?

    • Lovable docs, Connect your own Stripe account. The primary source. Setup, how payments work, limitations, FAQ.
    • No Code MBA, Lovable Stripe Tutorial 2026. About 2,100 words, updated 19 May 2026. No webhooks, no live-mode step, no fees, no restricted keys, no screenshots.
    • RapidDev, Lovable Stripe Connect. About 4,500 words on Stripe Connect (marketplace payouts), not checkout. States the integration only works on a deployed URL; Lovable's FAQ says the full checkout runs from the preview.

    What we did not publish

    • A run of the Connect Stripe form on a Lovable test project. It would mean pasting a key from a live Stripe account into a throwaway project, and we decided the screenshot was not worth that.
    • Our sales count. The webhook table and the purchases table were both read today; their sizes stay ours.
    • A Paddle comparison from experience. We have only ever run Stripe.

    Sources

    All read on 26 September 2026. Quotes and rates in src/data/lovableStripe.ts.

    Frequently asked

    Does Lovable integrate with Stripe?↓
    Two ways, per Lovable's docs on 26 September 2026. Built-in payments provision a Paddle or Stripe account for you, need a paid Lovable plan, and register their own webhooks. Connecting your own Stripe account works on the Free plan: you paste a restricted key into a Connect Stripe form, never into the chat, and Lovable writes edge functions that call Stripe with the stored key. A project can use one path at a time.
    Do I need a paid Lovable plan to accept payments?↓
    For built-in payments, yes: the docs say built-in payments require a paid Lovable plan. For your own Stripe account, no: the integration page says it does not require a paid Lovable plan. Stripe bills its own processing fees on your account either way.
    What does Stripe charge on a Lovable app?↓
    Stripe's US pricing page, read 26 September 2026: 2.9% plus 30 cents per successful domestic card transaction, plus 1.5% for international cards and plus 1% if currency conversion is required. Checkout is included with Payments. Paddle through Lovable's built-in path is 5.0% plus 50 cents, or a flat 10% under $10. Lovable's docs say using either provider through Lovable costs the same as setting it up directly.
    Do payments work in the Lovable preview?↓
    Yes for checkout: the docs' FAQ says you can run the full checkout flow from the preview, and it opens in a new tab; use test keys and the 4242 test card. The customer portal is the exception: it must be activated once in your Stripe dashboard and does not open inside the preview panel. Built-in payments run in test mode in the preview and live mode after publishing, with products syncing across.
    Does the own-account integration use webhooks?↓
    Not by default. The docs say the integration does not use webhooks and that your app checks payment and subscription status directly with Stripe. Stripe's own fulfilment guide says you cannot rely on the success page because customers are not guaranteed to reach it, so if you sell subscriptions or grant access automatically, ask Lovable for a signature-verified webhook endpoint and make it the only writer of entitlement.
    What went wrong with your own Stripe checkout?↓
    Four defects shipped on 12 September 2026 and were fixed the same day in one commit. The guest access cookie was written in a phase where Next.js forbids it, so a guest who paid could not download; the receipt's sender was unverified; the post-purchase sign-up button carried pre-purchase intent; and a missing webhook secret was logged as a bad signature. Every one is on this page with its fix, because a payments guide that hides its author's bugs is marketing.

    Related reading

    Written by

    Marco Kohns

    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.