The first person to tell you your payments are broken is usually a customer who was trying to pay you. One owner of a newly live AI-built app put it this way:

… Now that it’s live, one of my users reached out to tell me that the IAP was hanging up.

That is a money incident, and the ordinary shape of the first week after payments go on. The app works. The card path is the one piece that never carried a real card, and the customer finds out first.

Payments do not make an app less safe. They change what a failure costs. Seven conditions decide whether to switch payments on at all, and the order below ranks them by how much money can move before anyone notices. The first three carry the most.

This is for the owner of a working app that is free today, or free to a small group, who cannot read the code. The phrasings that land here are “how to turn on payments in my app” and “take money in an app I built with AI”. The page decides one thing: whether to open the money path now, and if not, which condition to close first.

What must be true before you take the first card

Seven conditions, in the order money moves through them.

  1. The server decides the price and the plan, and the browser only picks from a list the server already holds.
  2. A stranger cannot spend your money by using the app exactly as it was built.
  3. Access ends when payment ends: cancel, refund, failed renewal, downgrade.
  4. The payment provider’s events are verified, and the same event arriving twice pays once.
  5. You can fund a refund, and you can still prove who paid what.
  6. One customer cannot reach another customer’s money data.
  7. You find out about the six above before a customer tells you.

None of those is about the card form, which is the part your builder already finished.

What taking money adds to an app that already works

A free app that breaks costs you a user. A paid app that breaks costs the money, the processing fee charged on it, the customer, and the hours spent working out which of the three moved first.

The order below is not a security ranking. It comes from reading every finding in AxonBuild’s 420-finding audit ledger that touched money or paid access, sorting those by how much could move before anyone would notice, and then checking each condition against Stripe’s own documentation on 25 August 2026. Nothing here was run against a live account.

One measure orders all seven: how much money can move before anyone notices. A condition that fails on every transaction and leaves a clean, successful payment in the dashboard outranks one that fails once a quarter and emails you about it. Loud failures are cheap, because you fix them the same afternoon.

People describe this moment before they have a name for it. One owner, weighing a move to a different stack, gave the trigger as:

now that I will start charging customers, I need a serious platform

The instinct is sound and the target is usually wrong, because moving platform closes none of the seven conditions. All seven live in the app you already have, and they move with it.

Payments is one of the faults an app can carry while it runs fine, and it is the one with a price attached; working out what else could be wrong under the same app is the wider version of this question. Whether the app as a whole is ready for anyone at all is a wider question with its own seven checks, in whether the app is ready to launch. This page covers only what taking money adds.

1. The server is the only place a price is decided

What must be true: the amount, the currency and the plan come from a catalogue your server holds. The browser sends a choice and the server looks up what it costs. Nothing in the browser’s request is treated as a number to charge.

What it costs: the difference between the real price and the sent price, on every transaction, for as long as it is open. This is the only condition here that fires on all traffic rather than an edge case, and it is silent in the worst way. The payment succeeds and the dashboard shows a clean charge for the amount asked for, because Stripe charged exactly what your server told it to charge.

In 10 of the 21 third-party apps in a fixed June and July 2026 study of 26 audited AI-built apps, a server accepted a value the browser asserted that it should have derived or checked itself. The category covered prices and roles alike and reached well past payments, so ten is not ten broken checkouts. Every finding in that ledger was adversarially verified against the code.

The failure also happens with no payment provider anywhere near it. One app in that ledger, a retail point-of-sale build, priced the same item twice: the client read a price from the product record while the server applied a price from a batch override. Together they mischarged at the till, quietly, in whichever direction the two records disagreed. That app took no card payments at all, which is the point: a price is data before it is a payment.

How to tell from outside: open your browser’s network tab, start a purchase, and read the request your app sends when you click buy. If it carries an amount or a currency you can see and edit, your customer can edit it too. A plan or price identifier in the request is fine on its own; the test is to change it to a plan you are not allowed, or to a lower amount, and confirm the server rejects it or charges the catalogue price regardless. A July 2026 community thread titled “How do desktop applications implement monthly/yearly subscriptions securely?” was answered with the same rule: the server is the source of truth.

How each of those failures is wired, leak by leak, is six ways a vibe-coded checkout leaks money. That page takes the charge path apart; this one decides whether to open it.

2. A stranger cannot spend your money

What must be true: every path that costs you money when someone uses it has a server-side limit, for signed-out visitors and free accounts as well as paying customers.

The day you add a paid tier is the day you also have a free tier, and the free tier is where the bill comes from. AI features are the sharpest version, because the cost lands on your card in real time. Within that same fixed June and July 2026 study of 26 audited AI-built apps, 12 of the 14 third-party apps that carried an AI feature had a confirmed path by which a stranger or a free account could burn the owner’s paid AI or compute bill without limit, and in 13 of the 21 third-party apps in that ledger the most expensive endpoint had no rate limiting at all.

What it costs: whatever a script spends between the first request and the moment you open the billing page. The meter runs at machine speed and the invoice arrives at month end.

One app in the ledger, a multi-tenant business SaaS starter, shows how this survives a correct-looking payment setup. The function granting credits was meant to be called only by the verified payment webhook. It was left callable by the anonymous and signed-in database roles with no ownership check, while the policy letting a customer read their own wallet handed them the identifier needed to call it. A customer reads their wallet id, calls the function, and grants themselves credits. The product is free from then on, and every model call those credits buy is billed to the owner.

How to tell from outside: signed out, or on a throwaway free account, use the most expensive thing your app does fifty times. If nothing stops you and nothing tells you, nothing stops the next person either.

If the plan you are switching on opens with a free trial, the eligibility clock has to sit on your server before the card does, and free trial abuse separates the two ways it gets reset.

3. Access ends when payment ends

What must be true: cancellation, refund, failed renewal and downgrade each change what the account can do, and your server writes each change after hearing about it from the payment provider.

This condition compounds. A customer who keeps paid access after they stop paying generates no error, no ticket and no alert, because every request succeeds. The only symptom is revenue growing more slowly than customer count, which takes months to become legible.

Cancellation is not something you get to schedule. Stripe’s customer portal documentation lists, among the things a customer can do for themselves, “Cancel subscriptions immediately or at the end of the current billing period”, and says that customers who cancel anyway can share why, because “You can collect those reasons through webhooks” or Stripe Sigma. Both land on the same wire, read there on 25 August 2026. If your app is not listening to that wire, the cancel happens in Stripe and nothing happens in your app.

Keeping the entitlement record honest after the first card is a standing job rather than a one-off check, and paid access that survives after payment stops is the five checks for it. When the cancel does reach Stripe and the app still shows the paid plan, a canceled subscription that still shows as active traces the gap from the delivery log through the stored plan value to the cached screen. A renewal that fails is this same condition arriving from the other direction, and what your app should do when a renewal fails is the retry, notice and access policy for it.

How to tell from outside: cancel a test subscription, then reload the app as that account. If you cancelled immediately and the paid screen still loads, the condition is open. If you cancelled at the end of the period, the paid screen should keep loading until that date and stop after it, so check on both sides of the date.

4. The webhook is verified, and one event pays once

What must be true: your endpoint checks that each event actually came from your payment provider before acting on it, and acting on the same event twice produces the same result as acting on it once.

Verification is the whole of the first half. Stripe’s guidance is direct: “When processing webhook events, we recommend securing your endpoint by verifying that the event is coming from Stripe.” The check takes three inputs, which Stripe names requestBody, signature and endpointSecret. Without it, your endpoint is a public URL handing out product to anyone who finds it and posts a plausible completed-payment event.

Idempotency is the second half, and Stripe treats it as ordinary rather than exceptional. Its go-live checklist asks you to confirm that your production endpoint “Handles delayed webhook notifications”, “Handles duplicate webhook notifications”, and “Doesn’t require event notifications to occur in a specific order”, and, under edge cases, to test with duplicate data, “for example, retry the same request to see what happens”. Both read on 25 August 2026. A duplicate delivery granting a second month is a documented expectation, not an edge case.

What it costs: on the verification half, the entire product, at whatever rate someone asks for it. On the duplicate half, one extra grant or charge per delivery, plus the refund that follows.

The reason a genuine event fails the check, framework by framework, is why a real Stripe event fails the signature check. There is also the case where the signature passes, the endpoint answers, and the database still does not change, which is a different trace with its own four causes.

How to tell from outside: not from the browser. Your provider’s event log records what was sent, what your endpoint answered, and how many times it tried.

5. You can fund a refund, and you can still prove who paid

What must be true: your provider balance can cover a refund on the day you need to issue one, and your records still show who bought what, when and for how much, after any deletion your app or your team can perform.

This condition is acute rather than chronic, which is why it sits fifth. It costs nothing until the day someone asks for their money back, and it escalates from there.

Refunds are funded from money you are holding. Stripe’s refund documentation puts it plainly: “Refunds use your available Stripe balance (not including pending amounts). If your available balance doesn’t cover the amount of the refund, Stripe holds the refund as pending for card transactions (refunds for other payment method types will fail) until your Stripe balance becomes sufficient.” Payouts to your bank run on their own schedule, so a business that sweeps its balance can be unable to refund a customer who is asking today.

Two more facts on that page decide how bad it gets. The fee on the original sale does not come back: “Stripe’s processing fees from the original transaction aren’t returned.” And a refund can fail outright, because “A refund can fail if the customer’s bank or card issuer can’t process it.” Stripe then notifies you with the refund.failed event, and, in its words, “you need to arrange an alternative way to provide your customer with a refund”. It lists refund.created, refund.updated, refund.failed and charge.refunded among its refund events, recommends listening for refund.created at a minimum, and gives one failure reason for a customer who disputed the charge while the refund was still pending. That is how a refund you cannot fund today becomes a dispute next week. Read on 25 August 2026.

Then the version no balance can fix. That same multi-tenant SaaS starter chained its token-transaction ledger and billing-history tables with cascading deletes, up through organisations to the authentication users table. Its table grants blocked ordinary application roles from deleting a ledger row (the service role bypasses row-level security, so policies alone never protected it), and foreign-key cascades run as the table owner and ignore row-level security entirely. One hard delete of an organisation, from a dashboard, a cleanup script or a data-erasure request, wipes every wallet, ledger row and billing record with no undo. The refund stops being the problem, and nobody can say what was owed.

How to tell from outside: compare your provider’s balance page with a normal week of sales. Then ask what happens in your database when a customer account is deleted, and whether the payment records go with it.

6. One customer cannot reach another customer’s money data

What must be true: a signed-in customer can read and write their own rows and nobody else’s, enforced somewhere other than the screen they are looking at.

Taking money changes what this is worth to whoever finds it. A free app holds preferences and drafts. A paying customer’s account holds an address, an order history and an invoice trail: records worth reading.

The sharpest mechanism found in the audits is a database function that runs with the owner’s rights and trusts an identifier the caller hands it. Because the function runs as the owner it walks past row-level security policies that are otherwise correct, and because the policies themselves look fine, static checkers see nothing wrong. That mechanism, and simpler missing-policy versions of it, showed up as row-level security gaps in 9 of the 21 third-party apps in that ledger.

What it costs: not a direct loss on the day, which is why it sits sixth. It ends the business rather than denting the month, because the thing you do next is tell every customer.

Whether row-level security alone closes that gap, and where it does not, is whether row-level security is enough on its own.

How to tell from outside: make two accounts, buy something on the first, then sign in as the second and try to open the first account’s order by changing the identifier in the address bar or the request. The correct outcome is a refusal, not an empty screen.

7. You find out before your customer tells you

What must be true: when a payment path fails, a message reaches someone who can act on it, the same day, rather than a line in a file nobody opens.

This condition multiplies the other six. Each costs the amount that moves times the hours before anyone notices, and this is the condition that sets the second number. Close it and every other failure here gets cheaper, including the ones you have not found.

Your provider’s records are not a substitute for your own. Stripe’s go-live checklist recommends logging important data on your end as well: “Your own logs serve as a backup if your server has a problem contacting Stripe or you have an issue with your API keys”. In both of those cases Stripe cannot log the request either, so the records go missing in exactly the situation that produces the question.

Wiring the part where a failure reaches a person is making a failure reach a person, and this condition is only the reason it belongs on the money list.

How to tell from outside: in a test environment, point the app at a payment key that will not authenticate, and time how long until a person hears about it.

Where Stripe’s own go-live list stops

Stripe publishes a go-live checklist, the list on this subject from the company that moves the money. Eight items, in this order: set the API version, handle edge cases, review your API error handling, review your logging, do not rely on test objects, register your production webhooks, subscribe to the API announcements list, change and secure your API keys.

It is addressed to somebody else. The list opens: “If you’re a developer (or had a developer perform an integration for you) also consider the following items before going live.” Its order follows integration areas, the right shape for the person who wrote the integration and now checks their own work.

Three of the seven conditions on this page have no item on that list that actually covers them:

ConditionNearest item on Stripe’s listWhat is missing
Access ends when payment endsRegister your production webhooksThe item covers registering the endpoint. What your app does with a cancellation, a refund or a failed renewal is your business, and this list does not describe it
You can fund a refundNoneRefund funding and refund failure live on Stripe’s refunds page, neither one a go-live item
The server decides the priceDo not rely on test objectsAn environment question, not a question about who chose the number

That gap is not an oversight. Stripe is answering whether an integration is production ready, and every item on its list is real. This page answers whether the app should open a money path at all, and what each open condition costs the owner. The same facts sort differently, because Stripe’s list is ordered by integration area and this one by money.

The keys item repays a close read if you are near the switch. Stripe documents that test and live modes are separate worlds: “Each mode has its own set of API keys, and objects in one mode aren’t accessible to the other. For example, a sandbox product object can’t be part of a live mode payment.” Sandbox keys start pk_test_, rk_test_, sk_test_; live keys start pk_live_, rk_live_, sk_live_. Stripe calls a secret key one “that has unrestricted permissions on all Stripe APIs” and says “we don’t recommend using secret keys for new use cases”, pointing new integrations at restricted keys. Read on 25 August 2026.

Swapping the keys and rebuilding the live product catalogue is the switch itself, and moving Stripe from test mode to live is that sequence in order. Everything on this page is what should be true before you run it.

What the audits can and cannot tell you about payments

The honest limit first. Of the 21 third-party apps in that ledger, only 3 had a payments surface at all, so the revenue and billing area was scored on those 3 and marked not applicable on the rest. Three apps is not a sample, so it cannot tell you how often an AI-built checkout is broken, and nothing here claims otherwise.

What the ledger does speak to is the machinery around the money in apps about to grow some, which is where the reader of this page is standing. Whether the server re-derives values the browser sent, whether expensive paths carry limits, whether one customer can reach another’s rows, and whether a failure reaches a person: those were checked on every app in the cohort, payments or not. That is why the ordering above draws on findings from apps that never took a card.

The cohort is fixed and dated, and it was not a random sample, so it cannot produce a probability that your app in particular is exposed.

Is my app secure enough to charge customers?

Security has no finishing line for an app that takes money. What decides the question is a short list of conditions with prices attached, and the seven above are the ones priced in money. If the top three are closed, taking a card is a reasonable next step.

The word people reach for is “secure”, because it is the only word available for the worry. One owner put it this way, asking others who had already done it:

… all security checkup from the web is good, BUT we dont know the case with hackers …

The worry underneath that is about not knowing. The checks that came back clean were the checks available to run, and the open question is whether anything that matters sat outside them. That has a better answer than yes or no: name the things that cost money when they are wrong, decide which you have closed, and switch on when the expensive ones are shut.

Attackers are a real category and a separate one. Most of what goes wrong on the money path is reachable by an ordinary customer using ordinary tools, with no skill and no intent, which is why the conditions above are written as things a customer could do. If the worry underneath the question really is attackers rather than money, the security route is the other door out of this page.

If you cannot clear a condition yet

Three honest routes, and they combine.

Switch on for a small group and watch the first real payment end to end. Take money from five people you can telephone, and follow one payment all the way through. Five customers is small enough that an open condition costs an apology rather than a quarter.

Close the condition first, if it is one of the top three. A price decided in the browser, a spend path with no limit, and access that never ends are worth stopping for, and they get more expensive with every day of traffic, so pausing costs less now than next month.

Get a person to read the code, if the condition is one you cannot check from outside. Conditions 4, 5 and 6 have parts that are invisible from a browser: whether a handler is idempotent, whether a delete cascades, whether a database function trusts an identifier it was handed. Someone who reads code answers those in an afternoon, and the answer is a specific yes or no about your app.

None of the three is a decision to sort it out later without writing down which condition is open. An open condition you have named is a business decision. An open condition you have not named is the one a customer finds. And if the reason these conditions are still open is that nobody has wired the payments up yet, what that job contains and what to watch before you pay for it is its own question.

What each condition costs, and how you find out

ConditionWhat it costs when it is wrongHow you find out
1. Server decides the priceThe price difference, every transaction, with a clean payment record behind itThe buy request in the network tab
2. A stranger cannot spend your moneyWhatever a script spends before you open the billing pageSigned out, run the priciest feature fifty times
3. Access ends when payment endsEvery month of product given to accounts that stopped payingCancel a test subscription, reload as that account
4. Events verified, one event pays onceThe whole product on one half, a duplicate grant or charge on the otherThe event log: sent, answered, attempts
5. Refunds fundable and provableOne refund you cannot issue, the fee, and a dispute if it stallsThe balance against a week of sales, then what deleting a customer removes
6. Customer data stays separatedNot money on the day, the customer notification afterwardsTwo accounts, one purchase, open the first one’s order as the second
7. You find out firstEvery other row here, times the hours before anyone noticesBreak a payment path in a test environment and time it

Common questions about turning on payments

Eight questions from the same decision, answered for an owner who cannot read the code and is looking at the switch rather than the integration.

Can I turn on payments before the app has any tests?

Yes, and plenty of businesses do. Tests are not among the seven conditions, because a test proves a path behaved once while the conditions are about what the app allows. What tests buy is condition 7, cheaply: a check on the payment path tells you the day it breaks instead of the day a customer writes in. One test following a purchase to the entitlement row beats a suite here.

What happens if a customer asks for a refund and my Stripe balance is empty?

The refund does not go out that day. Stripe documents that refunds draw on your available Stripe balance, and that if the balance does not cover the refund, card refunds are held pending until it does, while refunds on other payment method types fail. If you sweep your balance to your bank on a schedule, you can be holding nothing on the day a customer wants theirs back. Keep a buffer sized to a plausible bad week.

How much can a price set by the browser actually cost me?

The full difference between the real price and the sent price, once per transaction, for as long as it stays open. Each affected payment looks successful to everyone including you. Two things make it expensive rather than merely wrong: it reaches every customer rather than an unlucky few, and the evidence sits in your database as a set of perfectly ordinary orders.

My app is free today. Does taking money change what a stranger can do to it?

It changes what they gain by doing it. The technical paths are the same the day before and the day after. Your app now holds order histories, invoices and paid tiers, so access that used to yield a preferences table yields something worth reselling. Taking money usually also means adding a free tier the same day, which is a population of accounts you have never had.

Should I use my builder’s built-in payments instead of Stripe?

It depends on where the conditions above end up living. Base44 documents a first-party option: “Accept and manage payments in your app with Base44 Payments powered by Wix, including cards, wallets, buy now pay later, payouts, refunds, and reports”, on its Builder plan and above in a listed set of countries. Lovable documents accepting “subscriptions and one-time payments in any Lovable app using built-in Paddle or Stripe integration”, on a Pro plan with its built-in Cloud backend. Both read on 25 August 2026. A built-in option can close parts of conditions 1 and 4, because the price catalogue and the event verification move inside the platform. Conditions 3, 5, 6 and 7 stay yours either way.

Should I switch payments on for a small group first?

Yes, and pick the group so you can telephone every one of them. A small group caps the cost of an open condition, and gives you the only test that counts: one real payment followed all the way through your app, including the refund on the way back. Watch the first five, then widen.

Can I turn payments on now and fix these conditions afterwards?

For conditions 5 and 7, often yes, as long as you write down which one is open and check it within the first weeks. Conditions 4 and 6 are not that kind: an unverified webhook or a duplicate grant hands out access, and one customer reaching another’s money data is a disclosure, so both have to be verified before real money moves, and a small first group does not cap them. For conditions 1, 2 and 3 the cost accrues per transaction, so every day of traffic makes the repair more expensive and adds customers who were charged the wrong amount, spent your budget, or kept access they stopped paying for. The order here exists so that “later” is a decision about named rows rather than a general intention.

How do I know the first real payment actually worked?

Follow one payment through every system it touches, confirming each step by looking rather than assuming: the charge in your provider’s dashboard, the event it says it delivered and the response your endpoint gave, the row in your database that changed, the screen the customer sees, and a refund of that payment to confirm access is removed on the way back. If any of the five is a guess, that is the step that surprises you later.

First payment checked through charge, event, database, customer screen, refund, and access removal