How to Add Payments to a Vibe-Coded App: Stripe, Webhooks and Refunds Checked Before You Charge Real Customers

How to Add Payments to a Vibe-Coded App: Stripe, Webhooks and Refunds Checked Before You Charge Real Customers

Adding Stripe to a Lovable or Bolt app? Set prices on the server, verify webhooks, handle refunds and test 8 cases before you charge real customers.

Table of Contents

To add payments to a vibe-coded app safely, keep three things on the server: your Stripe secret key, the price of each product, and the code that unlocks paid access. Then test the whole flow in Stripe test mode before you switch to live keys. That means failed cards, webhooks and refunds too. Your AI builder can make a checkout button in minutes, and the checkout usually works. The trouble comes later: who gets access, when that access ends, and what happens when money goes back. This guide covers each step, the bugs we see most in AI-built payment code, and a test list to run before your first real customer pays. Building payments into a Lovable or Bolt app? Book a call. Book a free 30-minute call and tell us what you built.

Ready to build?

Fixed-price web and mobile MVPs from $3,460. Book a call or WhatsApp us.

How payments work in a vibe-coded app

A Stripe payment has four parts. AI tools often build the first two well and skip the last two.

  • Products and prices. What it does: defines what you sell and for how much. Where it must run: created in your Stripe dashboard, referenced by ID.
  • Checkout. What it does: takes the card and charges it. Where it must run: Stripe Checkout, opened from your backend.
  • Webhooks. What it does: tells your app what happened to the payment. Where it must run: a backend endpoint that checks Stripe's signature.
  • Access and refunds. What it does: gives or removes paid features. Where it must run: your database, only updated by the backend.

What a webhook is, and how Lovable handles payments

A webhook is a message Stripe sends to your server when something happens, like a successful payment, a failed renewal or a refund. Without one, your app only knows what the browser tells it, and a browser can be faked. On Lovable, the built-in Stripe integration stores your key as a backend secret and calls Stripe from edge functions. Lovable's Stripe docs say it works without webhooks by default: the app asks Stripe for the payment status. Lovable can add webhooks when you need fulfillment. Refunds and product changes are done in your Stripe dashboard, not in Lovable.

Step 1: set prices on the server, never in the browser

The most common bug in AI-generated payment code is a price sent from the frontend. The button sends "amount: 49" to the backend, and the backend passes it to Stripe unchanged. Anyone who opens the browser tools can change it to "amount: 1". How to fix it:

  • Create every product and price in the Stripe dashboard. Each price gets an ID that starts with "price_".
  • Send only the plan name or price ID from the browser. The backend checks it against an allowed list, then creates the Checkout session.
  • Never accept a quantity, discount or currency from the client unless the server checks it.

Step 1: a prompt you can give your AI tool

Here's a prompt you can give your AI tool: "Create the Stripe Checkout session in an edge function. Accept only a plan key from the client. Map it to a Stripe price ID on the server. Reject anything else."

Step 2: keep the secret key secret

Stripe gives you two keys. The publishable key ("pk_") is safe in the browser. The secret key ("sk_") can charge cards, issue refunds and read your customer data, so it must only live in backend secrets. Search your project for "sk_test_" and "sk_live_". If either one shows up in a frontend file, in a .env file pushed to GitHub or in a chat prompt you pasted, rotate the key in Stripe and store the new one as a secret. One leaked secret key gives someone full control of your Stripe account.

Step 3: unlock access from a verified webhook

Many vibe-coded apps unlock the paid plan on the success page. The user lands on "/success" and the app sets "is_pro = true". The problem is that anyone can type "/success" into the address bar. Access should only change when your backend receives a verified event from Stripe. A correct webhook endpoint does four things:

  • 1. Checks the signature. Stripe signs each event with your endpoint's signing secret ("whsec_"). The code must verify it using the raw request body and reject the request if verification fails.
  • 2. Ignores duplicates. Stripe can send the same event more than once. Save each event ID and skip the ones you've already processed.
  • 3. Answers fast. Return a success response quickly, then do the slow work. Stripe retries events that fail or time out.
  • 4. Writes to the database with backend rights only. Users must never be able to update their own plan column. If they can, a user can call your API and make themselves a paying customer.

The webhook events most MVPs need

Here is what your app should do for each event:

  • checkout.session.completed: grant access or create the order.
  • invoice.paid: extend a subscription for another period.
  • invoice.payment_failed: warn the user, start a grace period.
  • customer.subscription.deleted: remove paid access.
  • charge.refunded: remove access or mark the order refunded.
  • charge.dispute.created: flag the account for you to review.

Get your payment flow built right

Your payment flow has more moving parts than your AI tool showed you. Our MVP Development service is Fixed at $3,460, and the code lives on your GitHub. Book a call for a free 30-minute call and tell us what you're building.

Step 4: decide your refund rules before launch

A refund is a business decision first and code second. Before you take real money, write down:

  • Who can refund. Usually only you, from the Stripe dashboard. Don't let your AI tool add a refund button on the user side.
  • What happens to access. Pick one: a full refund either removes access right away or at the end of the period.
  • Partial refunds. If you allow them, decide whether access stays.
  • Subscriptions. Refunding a payment doesn't cancel the subscription. Cancel it too, or it renews next month.

Step 4: make the code match your rules

Then make the code match. Your "charge.refunded" handler should apply the rule you wrote down, not whatever the AI guessed. Lovable's docs also point out that disconnecting Stripe doesn't cancel active subscriptions. They keep renewing until you cancel them in Stripe.

Want us to build it?

Fixed-price web and mobile MVPs from $3,460. Book a call or WhatsApp us.

Step 5: the 8 tests to run before you charge real customers

Run these in Stripe test mode. Stripe's test card 4242 4242 4242 4242 simulates a successful payment, and the Stripe testing docs list cards for declines and 3D Secure. On Lovable, test on the deployed app as well as in the preview.

  • 1. Successful payment. Access unlocks only after the webhook arrives, not on page load.
  • 2. Declined card. The user sees a clear message and gets no access.
  • 3. 3D Secure card. The extra check opens and the payment completes.
  • 4. Fake success page. Open "/success" without paying. Nothing unlocks.
  • 5. Edited price. Change the price or plan in the browser request. The server rejects it.
  • 6. Duplicate webhook. Resend the same event from the Stripe dashboard. Nothing is granted twice.
  • 7. Failed renewal. Use the Stripe CLI to trigger "invoice.payment_failed". The grace period starts.
  • 8. Refund. Refund a test payment in the dashboard. Access changes the way your rule says.

Switching to live keys

When all eight pass, switch to live keys and create a separate live webhook endpoint with its own signing secret. Test mode and live mode use different secrets. Forgetting this is a common reason payments go through but nobody gets access.

Mobile apps: one extra rule

If your vibe-coded app is going to the App Store or Google Play, you may not be allowed to use Stripe for everything. The stores split purchases into two kinds:

  • Digital features or content used in the app (subscriptions, premium content, unlocking a full version). Apple App Store: in-app purchase required (guideline 3.1.1). Google Play: Google Play's billing system required (Payments policy).
  • Physical goods or services used outside the app. Apple App Store: must use methods other than in-app purchase, such as Apple Pay or card entry (guideline 3.1.3(e)). Google Play: Google Play's billing system must not be used (Payments policy).

Mobile apps: what this means in practice

In practice, a food delivery or cleaning booking app can take card payments through Stripe. A paid tier that unlocks premium features generally goes through the store's own billing. Both stores have exceptions by region and app type. Read the current rules before you build the paywall, or your app could be rejected at review. Our mobile app building guides cover store submission.

When to fix it yourself and when to get help

You can fix it yourself if you sell one product, your tests pass and your secret key never left the backend. Get help if you see any of these:

  • Prices or amounts sent from the frontend
  • Access unlocked on the success page
  • No webhook signature check
  • Users able to edit their own plan or role
  • Marketplace payouts between users, which need Stripe Connect and more work

How we help

Our AI prototype to production service handles these cases. It starts with an audit of the AI-generated code: you get a punch list that covers payments and a recommendation to harden or rebuild. You keep the GitHub. You'll find more on AI builders in our Lovable and AI builders guides. About to take your first real payment? Book a call. Book a free 30-minute call and tell us what you built.

Can Lovable add Stripe payments on its own?

Yes. Lovable's Stripe integration stores your secret key as a backend secret and handles payments through edge functions. Webhooks aren't on by default, so ask for them when access depends on payment events like renewals or refunds.

Do I need webhooks for a simple one-time payment?

Yes, if the payment unlocks anything. Without a verified webhook, your app has to trust the browser or keep asking Stripe for the status. A webhook is the only clean way to learn about refunds, disputes and failed renewals after checkout.

How do I refund a customer in a vibe-coded app?

Issue the refund from your Stripe dashboard. Then make sure your app listens for "charge.refunded" and changes access according to your refund rule. For subscriptions, cancel the subscription too, or it keeps renewing.

How do I test Stripe before going live?

Use test mode keys, the test card 4242 4242 4242 4242, the decline and 3D Secure test cards, and the Stripe CLI to trigger webhook events. Run the 8 tests above, then create a separate live webhook endpoint before you switch keys.

Can I use Stripe in an iOS or Android app?

Yes, for physical goods and services used outside the app. Apple doesn't allow in-app purchase for them, and Google Play doesn't allow its billing system for them, so you use another payment method such as Stripe. For digital features or content unlocked inside the app, both stores generally require their own billing. Check Apple's guideline 3.1 and the Google Play Payments policy for your case.

Ready to build yours?

Fixed-price web and mobile MVPs from $3,460. Book a call or WhatsApp us.

Related Articles

Ready to ship your MVP?

Fixed-price builds from $3,460 · Post-launch support from $500/mo

Frequently Asked Questions

Who Is Behind BuildMVPFast?

BuildMVPFast is led by a passionate team of Creators, Designers, and Developers. Together, we deliver innovative software solutions tailored to each client's unique needs. Our vision is to help clients unlock their full potential by providing a rapidly built, high-quality MVP that serves as the perfect launchpad for their business idea.

Who Owns The Code, Design And Intellectual Property?

How Long Does It Take To Build An MVP?

What Is Your Development And Delivery Process?

Do You Offer Post-Launch Support And Maintenance?