Guide Lovable
Can users get your paid plan without paying? Stripe and plan settings in Lovable apps
Short answer
They can if your app keeps the plan, credits or "paid" flag in a place users are allowed to edit, or if it trusts a payment message it didn't check. Keep those fields where only your server can write them, and make sure the payment status comes from Stripe, not from the browser. Then test it: sign in as a normal user and try to change your own plan.
Plan fields that users can edit
A common setup keeps the plan next to the name and photo in a profiles table. If the access rule (an RLS update policy) lets people update their own row, that can include every column in it, plan and credits too.
Ways to close it:
- Start here: move plan, credits and payment status into a separate table that users can read but not change, and let only your server code write to it.
- Limiting which columns a role can update (column level privileges) also exists, but Supabase calls it "an advanced feature" and says: "We do not recommend using column-level privileges for most users." It recommends RLS policies plus a dedicated table instead.
- Treat the browser as untrusted. Hiding a button in the app doesn't stop someone from sending the request themselves.
How your app learns that someone paid
Lovable offers two setups, and they work differently:
- Built in payments (Lovable's recommended option): Lovable sets up the Stripe or Paddle account and manages the keys, webhooks, and test and live environments for you.
- Your own Stripe account: by default this integration uses no webhooks. Your app checks payment and subscription status directly with Stripe. If you need webhooks (messages Stripe sends your server when something happens, such as a payment succeeding), Lovable writes the webhook function. You then create the event destination in Stripe and store the signing secret when Lovable asks for it.
If your app does use a webhook you set up, check that each message really came from Stripe. Stripe signs every webhook with a Stripe-Signature header, and your webhook code verifies it with your endpoint's signing secret. Without that check, someone could send your webhook a fake "payment succeeded" message and get the paid plan without paying.
With your own Stripe account, Lovable matches a subscription to a user by the email they sign in with; it has to match the email on their Stripe customer record. If someone pays with a different email at checkout, the signed-in account may not get the paid plan.
Keep the keys on the server
- With your own Stripe account, Lovable takes a restricted key or a secret key through its Connect Stripe form, and stores it as a backend secret, not in your app's code. Don't paste the key into the project chat.
- Stripe says to "Use restricted API keys (RAKs) for most use cases", with only the permissions your app needs.
- If you think a key leaked, rotate it in Stripe, which revokes the old key and gives you a new one. If a webhook signing secret leaked, roll (change) it in Stripe. Then update the new value in Lovable.
Test it yourself
Only use test accounts you created yourself, not a real customer's account.
- Sign in with a test account on the free plan.
- Try to change your own plan or credits, the same way you would change your name.
- If it works, the access rule for that table needs fixing before you launch.
- Repeat with a second test account to check that you can't change the first account's plan either.
If your profiles table has no plan, credits or paid field, this test doesn't cover your setup. Your app may check Stripe directly or keep the plan somewhere else, so the webhook and key checks above matter more.
This test is our suggestion; no official doc describes it.
Want a second pair of eyes?
AI agents run the checks and a human engineer reviews and approves every report and change. It is not a penetration test, a security guarantee or a compliance certification.