Anyone Can Upgrade to Pro for Free. Here's How.
Your app has a free tier and a paid tier. Users sign up, enter their card, pay through Stripe, and unlock premium features. The flow works perfectly in testing. It works perfectly in production.
But behind the paywall, the access decision is made by the wrong thing — a writable billing field in the database, a client-side plan check, or a checkout signal the server never verified. The app decides who gets premium access based on something the user can control.
The most common pattern: a database column like is_subscribed, plan, or credits that any authenticated user can update directly. Open browser DevTools, write to the column, unlock every paid feature — without ever entering a credit card. This is not a hack. It's a database update. And in AI-generated apps, it's one of the most common billing vulnerabilities.
A SaaS founder built entirely with Cursor shipped to production with zero hand-written code. Within 72 hours, users discovered they could bypass the paywall by changing a single value in the browser. The founder publicly announced the shutdown.
Another app at $10K MRR had an RLS policy that let free users update their own has_access and access_level fields. The fix was a single WITH CHECK constraint. The revenue leak had been live for months.
Who This Is For
- Founders with a SaaS that has paid tiers, subscriptions, or credits — especially if built with AI tools
- Developers who store subscription status or plan information in a database that the frontend can access
- Teams who haven't verified whether their billing state can be modified by users
- Anyone whose premium feature gating is implemented in the UI but not enforced on the server
If your app decides what a user can access based on a database field that the user can write to, your paywall is a suggestion — not a gate.
What Founders Experience
- Revenue looks normal. Users sign up, some convert to paid. The dashboard shows growth. Nothing triggers an alarm — because the users who bypassed the paywall look exactly like paying users in the product.
- The fraud is invisible. There are no failed payments, no chargebacks, no error logs. The user simply has access they didn't pay for. The only way to detect it is to reconcile database subscription states against actual Stripe payment records.
- Discovery comes late. The founder notices when growth metrics don't match revenue, when a user reports something odd, or when a security researcher demonstrates the bypass. By then, the revenue leak may have been active for weeks or months.
- The trust damage compounds. When paying users discover that others have the same access for free, they don't file a support ticket — they churn. A billing credibility problem is harder to recover from than a billing bug.
What's Actually Happening
AI tools generate billing flows that work for the happy path: user pays, app activates access. But they consistently skip the adversarial path: what happens when the user doesn't pay but tries to get access anyway.
Three patterns create this vulnerability:
1. Subscription Status in a User-Writable Database Column
The most common pattern. AI generates a profiles or subscriptions table with fields like is_subscribed, plan, has_access, or credits. The frontend reads these fields to decide what to show. But the table either has no RLS, or has RLS policies that let authenticated users update their own rows — including the billing fields.
The user opens browser DevTools, calls the Supabase API directly, and sets is_subscribed = true. Done.
This is what happened in the Post-Bridge case ($10K+ MRR): the RLS UPDATE policy was overly permissive, and free users could modify has_access and access_level on their own rows. Any authenticated free user could promote themselves to premium without payment.
2. Client-Side Feature Gating Without Server Enforcement
The AI generates code like:
if (user.plan === 'pro') {
return <PremiumDashboard />;
}
return <FreeDashboard />;
The premium features are hidden in the UI, but the API endpoints that serve premium data have no plan check. A user who calls the API directly — or modifies the client-side state — gets full premium access.
One documented case (Vibeflow) showed that free users could generate unlimited AI content by calling the API directly. The UI said "no," but the backend said "sure, go ahead."
3. Checkout Completed on the Client, Fulfillment Not Verified on the Server
AI tools sometimes generate checkout flows where the frontend confirms payment success based on Stripe's redirect URL or client-side callback — then immediately writes the subscription status to the database. The server never independently verifies with Stripe that the payment actually happened.
An attacker can navigate directly to the success URL, or intercept and replay the client-side callback, and get access without paying. In one documented proof-of-concept, a researcher unlocked a $79/month pro plan without payment — the server assumed the payment was done because the client said so.
What This Puts at Risk
Direct revenue loss. Every user who gains premium access without paying is revenue that never arrives. Unlike a pricing mistake or a failed payment (which generates a Stripe event), this creates no signal at all. The revenue simply doesn't exist.
Metric corruption. Product analytics show active premium users. Revenue shows a different number. The founder makes growth decisions based on inflated engagement data. Investor reporting becomes unreliable.
Paying customer churn. When legitimate paying users discover that the paywall is bypassable — through word of mouth, a forum post, or their own curiosity — they don't complain. They cancel. A billing integrity problem erodes trust in a way that a feature bug doesn't.
Marketplace abuse. In platforms with seller payouts or credit systems, writable billing fields can be catastrophic. One audited marketplace (aicourses.com) found that any seller could redirect other sellers' earnings to their own bank account by changing a single database column.
How Trust Score Detects It
Trust Score checks three patterns that catch billing bypass before it reaches production:
BIL-09: No client-side billing state. Verifies that subscription status, plan tier, and billing decisions are not determined by client-accessible state. The server — not the browser — must be the source of truth for what a user has paid for.
BIL-14: Server-initiated checkout. Checks that Stripe Checkout Sessions are created on the server, not assembled on the client. When checkout creation is driven by client-controlled pricing input, users can tamper with plan selection or price identifiers before the server validates them.
AUTH-03: RLS policies have WITH CHECK. Catches one of the most common patterns behind this failure: writable billing-related fields due to incomplete UPDATE/INSERT protection. Even if a table has RLS on SELECT, a missing or overly permissive WITH CHECK clause can let users modify their own rows — including columns that control access.
Together, these checks answer a simple question: can a user give themselves access they didn't pay for?
Real Incidents
CursorAI SaaS shutdown (2025). A founder built a SaaS with 100% AI-generated code using Cursor. All payment and subscription logic was in frontend JavaScript. Users bypassed payment restrictions by changing a single value in the browser console within 72 hours. The founder publicly announced shutdown: "I shouldn't have deployed unsecured code to production."
Post-Bridge self-upgrade ($10K+ MRR, October 2025). An app with meaningful revenue had RLS enabled but an overly permissive UPDATE policy. Free users could modify has_access and access_level fields on their own rows. Any authenticated free user could promote themselves to premium without payment. The fix was a single WITH CHECK constraint.
Vibeflow — unlimited AI content for free users (2026). Free users could generate unlimited AI content by calling the API directly, bypassing the frontend paywall entirely. The UI enforced limits; the backend did not. "The UI says 'no,' but the backend says 'sure, go ahead.'"
aicourses.com — seller earnings redirection. A marketplace audit found that any seller could redirect all other sellers' earnings to their own bank account by changing a single database column (stripe_account_id). The column had no write protection.
$79/month Pro plan unlocked without payment (LinkedIn PoC). A security researcher demonstrated a full subscription bypass: "Pro plan ($79/month) unlocked without payment. Unlimited fake upgrades. The server assumed the payment was done. No Stripe validation. No webhook confirmation check. No session verification."
Lovable-related app audits — writable billing fields (2025). In research surrounding exposed Lovable-generated apps, investigators found cases where users could modify is_paid, is_subscribed values or give themselves 99,999 credits by writing directly to unprotected database columns.
Detection: How to Check Your Own App
Check 1: Can users write to billing-related columns?
If you use Supabase, check your RLS policies for tables containing subscription or billing data:
-- In Supabase SQL Editor: check policies on your profiles/subscriptions table
SELECT tablename, policyname, cmd, qual, with_check
FROM pg_policies
WHERE tablename IN ('profiles', 'subscriptions', 'users')
ORDER BY tablename, cmd;
Interpretation: If UPDATE policies exist without a WITH CHECK that restricts modifications, that's a strong warning sign — users may be able to write to billing-related fields on their own rows. If no UPDATE policy exists at all (and RLS is off), the table is fully writable.
Check 2: Do premium API endpoints check subscription status on the server?
Test whether premium features are enforced server-side:
# Call a premium API endpoint with a free user's token
curl 'https://your-app.com/api/premium-feature' \
-H "Authorization: Bearer FREE_USER_TOKEN"
Interpretation: If the endpoint returns data or performs the action, the premium check is UI-only. The server doesn't verify the user's subscription status.
Check 3: Where does the checkout session originate?
Search your codebase for Stripe Checkout Session creation:
grep -r "checkout.sessions.create" --include="*.ts" --include="*.tsx" --include="*.js"
Interpretation: If this appears in a file that runs on the client (e.g., inside a React component or a file without server-only imports), the checkout is client-initiated and the price can be manipulated.
Related Launch Risks
- Your Stripe Webhook Trusts Strangers. — Even if your paywall is server-enforced, unverified webhooks can grant access through a different path.
- Your Database Is Public. You Just Don't Know It Yet. — Missing RLS is often the root cause of writable billing fields.
- They Cancelled. They Still Have Access. You're Losing Money. — The opposite billing problem: users who should lose access keep it.
- Most Stripe Accounts Are Leaking Revenue. — Revenue leakage at the Stripe level compounds client-side billing trust failures.
FAQ
Our Stripe integration works correctly. Doesn't that mean billing is secure?
Stripe handles the payment side correctly. But Stripe doesn't control what your app does after payment. If your app decides access based on a database field that users can modify — or based on a client-side callback that isn't verified server-side — the billing integration is secure but the access control is not.
How would a user even know how to change a database value?
This usually doesn't require advanced exploitation — often just DevTools, visible API patterns, and a writable backend path. Users can observe Supabase API calls in network requests, copy the pattern, and make their own request.
We use Supabase RLS. Aren't we protected?
RLS on SELECT protects who can read data. But if your RLS doesn't restrict UPDATE operations — specifically which columns can be modified — users can write to billing fields on their own rows. A WITH CHECK constraint on UPDATE is what prevents this. Many AI-generated RLS policies allow updates without restricting the columns.
Can this happen with Stripe-managed billing?
Yes, if your app maintains its own copy of subscription status. Even with Stripe handling payments correctly, if your app reads is_subscribed from a local database instead of verifying with Stripe's API, and that database column is writable, the bypass is possible. The source of truth for billing state must be server-verified, not client-trusted.
What if we use client-side feature flags for premium features?
Feature flags that control UI visibility are fine for user experience. But every premium API endpoint must independently verify the user's subscription status on the server. If the API trusts the client's claim about the user's plan, any HTTP client can bypass the paywall.