Back to BlogQuality & Testing

Testing Payments in an AI-Built App Before You Take Real Money

Rupak Amin

Founder & Lead Engineer, RAITHub

12 min read

Before an AI-built app takes real money, find where the AI put the payment logic, then run seven tests in Stripe's test mode: the price is set on the server, access unlocks from a verified webhook rather than the success page, duplicate events change nothing, declines and 3-D Secure behave, cancellations remove access, secret keys stay server-side, and live mode is really configured.

If you would rather have your payment flow tested for you, see how RAITHub would test this below.

AI tools are good at producing a checkout button that works once, in the demo, with the card 4242 4242 4242 4242. They do not try the other cards, close the tab halfway, replay a webhook or edit the price in the browser. Those are the cases where money goes missing. This post is specific to apps built with Lovable, Bolt, Cursor, Claude Code, Replit and similar tools. For a full, general Stripe test plan, including test clocks and unit-testing a webhook handler, read testing payments and webhooks end to end.

Where does an AI-built app usually put the payment logic?

Open the code, or ask your AI tool to list every file that touches payments, and find out which of these patterns you have. The answer decides what to test.

PatternWhat it looks likeThe risk
Success-page fulfilmentThe app marks the user as paid when the browser lands on /successPaid users with no access when the page never loads; anyone who visits the URL gets access
Webhook fulfilmentA server function or edge function receives Stripe events and updates the databaseMissing signature check, duplicate handling or event ordering
Client-side priceThe browser sends an amount or a price to the checkout functionA user edits the request and pays one cent
Payment Links onlyA static Stripe link, with access granted by hand or by email matchWorks for low volume; breaks when emails differ or someone shares the link

Stripe is explicit that the landing page alone is not enough: "You can't rely on triggering fulfillment only from your checkout landing page," because a customer can pay and lose their connection before it loads (Stripe fulfilment guide). Check your builder's defaults too. Lovable's Stripe documentation says its integration does not use webhooks by default, and that a webhook must be requested and set up as an event destination in Stripe (Lovable Stripe docs). That may be fine for a simple one-off purchase, but it is something to know, not to discover.

What are the seven payment tests for an AI-built app?

1. Is the price set on the server?

Open the browser's developer tools, start a checkout and look at the request your app sends to its own checkout function. If the body contains an amount, or a price ID the server uses without checking, edit it and resend. The server should look up the price from its own product list and ignore what the browser says. A test you can keep:

import { test, expect } from '@playwright/test'

test('checkout ignores a price sent by the browser', async ({ request }) => {
  const res = await request.post('/api/checkout', {
    headers: { Authorization: 'Bearer ' + process.env.MEMBER_TOKEN },
    data: { plan: 'pro', amount: 1 },
  })
  expect(res.ok()).toBeTruthy()
  const { sessionId } = await res.json()
  // Read the session back with the secret key, on the server side of the test
  const stripeRes = await request.get(
    'https://api.stripe.com/v1/checkout/sessions/' + sessionId,
    { headers: { Authorization: 'Bearer ' + process.env.STRIPE_TEST_SECRET_KEY } },
  )
  const session = await stripeRes.json()
  expect(session.amount_total).toBe(2900) // your real Pro price, in cents
})

Adapt the route and field names to your app. Run it only with a test-mode key.

2. Does access come from a verified webhook?

  • Visit the success URL directly, without paying. Nothing should unlock.
  • Pay with a test card and close the tab the moment the card is accepted. Access should still appear within a minute, because the webhook grants it.
  • Send a fake event to your webhook URL with no valid signature. It must be rejected with a 400 and change nothing. Stripe's webhook docs say an unverified endpoint lets an attacker "send fake webhook events to your endpoint to trigger actions like fulfilling orders, granting account access, or modifying records" (Stripe webhooks docs).

A common AI-generated bug here: the handler parses the JSON body before checking the signature. Stripe needs the raw body, and "any manipulation to the raw body of the request causes the verification to fail" (same source). The tool then "fixes" the failing check by removing it. Read the handler and confirm the check is still there.

3. Do duplicate and out-of-order events change nothing?

Stripe can deliver the same event more than once and does not guarantee order (Stripe webhooks docs). Resend one event from the Stripe dashboard or with stripe events resend, and check the user got one month, one credit pack or one order, not two. The simplest guard is a table of processed event IDs with a unique constraint:

create table processed_stripe_events (
  event_id text primary key,
  processed_at timestamptz not null default now()
);

-- In the handler, inside the same transaction as the fulfilment:
insert into processed_stripe_events (event_id) values ($1)
on conflict (event_id) do nothing
returning event_id;
-- No row returned means this event was already handled: return 200 and stop.

More on the idea in what idempotency means in API design.

4. Do declines and 3-D Secure end in a clear state?

Run checkout with Stripe's test cards (Stripe testing docs):

Test cardSimulatesExpected result in your app
4242 4242 4242 4242SuccessAccess granted, receipt sent
4000 0000 0000 0002Generic declineClear message, no access, can retry
4000 0000 0000 9995Insufficient fundsSame as above, with no half-created order
4000 0027 6000 31843-D Secure always requiredChallenge appears on desktop and phone; failing it leaves the user unpaid
4000 0000 0000 0259Payment succeeds, then is disputedYour team is alerted; you decide whether access is paused

Run the 3-D Secure card on a real phone as well. Authentication pop-ups and redirects are where mobile checkouts most often stall, as covered in why AI-built apps break on phones.

5. Do subscriptions end when they should?

If you sell subscriptions, cancel one in the Stripe customer portal and confirm the app removes access at the end of the period. Then simulate a failed renewal. Stripe's test clocks move a subscription forward in time so you do not wait a month. AI-built apps often store "is_pro = true" once and never listen for customer.subscription.deleted or invoice.payment_failed, so access lasts forever.

6. Are the secret keys only on the server?

Search the built front end for sk_live_, sk_test_ and rk_live_. Only the publishable key (pk_) belongs in the browser. Stripe's go-live checklist also recommends rotating keys "in case they've been saved somewhere outside of your codebase during development" (Stripe go-live checklist), which, for an AI-built app, includes every chat where a key was pasted. Exposed API keys in a Lovable app covers the search and the fix.

7. Is live mode really set up?

Test-mode success proves little about live mode. Stripe notes that objects created in a sandbox, such as products and prices, "aren't usable in live mode", and that live webhook endpoints must be registered separately (Stripe go-live checklist). Check that live products and prices exist, that the app uses their live IDs, that the live webhook points at the production domain with its own signing secret, and then make one real low-value purchase and refund it. If the webhook is not arriving at all, Stripe webhook not firing walks through the causes.

How long does this take to do yourself?

For one product with a one-off price or a single subscription plan, about one day for a developer who can read the payment code and use the Stripe CLI: an hour to map where payment logic lives, half a day for tests 1–4, a couple of hours for subscriptions and keys, and an hour for the live switch. The main risk of doing it yourself is asking the same AI tool to fix what you find: a signature check that keeps failing is often "fixed" by removing it. Re-run test 2 after every payment change.

What should you test first on a small budget?

Tests 2, 1 and 6, in that order. A missing webhook check or a browser-set price lets people take your product without paying; an exposed secret key lets them do much worse. Then run the decline cards and one real purchase. Subscriptions can be tested in the first billing cycle, before the first renewal.

Buy, build or hire?

OptionChoose this whenTrade-off
Hosted checkout with Payment Links and manual accessYou sell a handful of one-off products while you validate demandLittle code to test; does not scale and relies on matching emails
A tool or SaaS testing platform plus the Stripe CLIA developer can own the seven tests and automate themCheap; needs someone who can read the webhook handler
Freelancers or crowdtestingYou want checkout tried on many phones and banksGood for 3-D Secure and device coverage; rarely reviews the server code
An in-house QA hirePayments change every sprintStrong over time; too slow for a first launch
A managed QAaaS team or a one-off launch auditYou want the payment flow tested end to end before real moneyAn outside dependency; keep the tests in your repository

Why RAITHub for this

  • Payments are routine work. PropDesk, a property-management SaaS RAITHub built, collects rent through Stripe and runs 1,024 tests. TheSkinProof, the founder's own marketplace venture, handles bKash, Nagad, SSLCommerz and cash on delivery under 750+ tests. PadhAI, an AI tutoring platform RAITHub built, has 9 payment gateways.
  • Reads the code and tests the flow. A tester runs the cards and the phones; an engineer reads the webhook handler for the signature, duplicate and ordering bugs that clicking cannot find.
  • Honest about proof. There is no AI-built-app case study yet, and RAITHub does not certify PCI compliance.

When you don't need us

  • You use Payment Links or a hosted store with no custom fulfilment code. Test one purchase and one refund.
  • A developer who did not write the payment code can spend a day on the seven tests.
  • You need PCI DSS validation or a certified penetration test. That needs a qualified assessor or a certified testing firm.

How RAITHub would test this

  • Map: find every file and function that touches payments, and which of the four patterns your app uses.
  • Test: run all seven tests in test mode, on desktop and on real iPhone and Android phones, including tampered prices, forged and duplicate webhooks, and subscription lifecycle with test clocks.
  • Report: each finding ranked by money at risk, with reproduction steps and a suggested fix; a go or no-go for switching to live mode.
  • Keep it working: optional monthly QA that re-runs the payment tests after each release, with the key ones automated in your CI.
  • What you receive: the report, the tests in your repository and a handover note. IP is yours; an NDA is standard.

The audit is fixed-price, quoted in writing after a free 15-minute call. See AI-built app testing and QA as a service, the AI-built SaaS launch checklist for everything else, or ask for a payment and launch audit.

Frequently asked questions

How do I test Stripe in a vibe-coded app?

Use test-mode keys and Stripe's test cards, then go beyond the success card: try declines and 3-D Secure, visit the success page without paying, replay a webhook, send one without a valid signature, and edit the price in the browser request. Then check live-mode products, prices and webhooks before the first real sale.

Is it safe to unlock access on the payment success page?

Not on its own. Stripe says fulfilment cannot rely only on the landing page, because customers do not always reach it. Grant access from a verified webhook, and use the success page only to show the result.

Does Lovable set up Stripe webhooks automatically?

According to Lovable's Stripe documentation, its integration does not use webhooks by default; you ask for one and create an event destination in Stripe. Check what your own project does before relying on it.

Which Stripe test card simulates a declined payment?

4000 0000 0000 0002 gives a generic decline, and 4000 0000 0000 9995 an insufficient-funds decline, according to Stripe's testing documentation. Use any future expiry date and any three-digit CVC.

Why does my webhook signature check fail after the AI edited it?

Usually because the code parses or changes the request body before verifying it. Stripe needs the raw body. Restore raw-body handling instead of removing the check, and make sure the signing secret matches the endpoint and mode.

Can RAITHub certify my app for PCI DSS?

No. RAITHub tests payment flows and application security against OWASP guidance, and can fix the bugs it finds, but does not provide PCI DSS validation or a compliance attestation. Ask a qualified assessor how your checkout setup affects your PCI scope.

test stripe in vibe coded applovable stripe testingai app payments testingstripe webhook testingpayment testing checklistqa for ai-built apps

Ready to discuss your project?

Book a free 15-minute technical audit with our engineering team.