Hire Lovable Xperts
Production & Scale

Building a Marketplace App on Lovable

A two-sided marketplace — buyers, sellers, and a platform taking a cut — is one of the most structurally complex apps you can build. Lovable scaffolds the listing and checkout UI fast, but the hard parts are multi-tenant data isolation across two distinct roles, Stripe Connect for split payments, a trust-and-safety model for reviews and disputes, and the policy layer that stops one party from reading another's private transaction data.

By Founder Name · Last verified: 2026-06-25

What does a typical Lovable marketplace scaffold look like?

A Lovable marketplace usually starts with a listings table (seller-owned), a transactions table (buyer to seller), a reviews table, and a Stripe Checkout session. Lovable generates the listing UI, a search and filter page, and the buyer checkout flow well. The gap is structural: it emits direct Stripe Charges rather than Stripe Connect, so the platform cannot take a fee or hold funds — which a real marketplace must do.

The second early gap is the trust model. Lovable produces a reviews table and a star-rating widget, but the rules for who may review (only a verified buyer of a completed transaction), whether reviews can be edited, and how disputes resolve are not encoded by default. Those rules belong in database constraints and RLS policies, not in UI checks a determined user can bypass by calling the API directly.

The third gap is the one that causes incidents: a marketplace has two distinct tenant roles — buyer and seller — over the same rows. Most Lovable scaffolds reuse the single-table pattern auth.uid() = user_id, which has no concept of who is party to a given transaction. That assumption holds in a one-user demo and breaks the moment two real accounts query the same table.

Related: Lovable production-readiness checklist · The 5 production gaps in Lovable apps

Why is multi-tenant data isolation the riskiest part of a marketplace?

Because a marketplace has two tenant roles — buyer and seller — sharing the same tables, and each row has two legitimate owners and many illegitimate viewers. A naive policy that grants broad SELECT on transactions for a dashboard query will leak a competitor's order history, payout amounts, and customer contacts. This is the failure mode that turns a launch into a breach disclosure, and it does not surface until you have multiple real accounts.

Concretely: a seller dashboard that runs select * from transactions and filters in the frontend looks correct in the editor preview, where you are the only user. With a permissive RLS policy behind it, every seller can read every other seller's revenue. The frontend filter is cosmetic; the database is the only enforcement boundary that matters, and RLS is how you set it.

The fix is transaction-scoped policies that resolve the caller's role per row, plus testing each policy with two separate authenticated accounts. Lovable enables RLS on tables it creates but rarely writes the role-aware policies a marketplace needs without explicit prompting — and even prompted policies should be audited by someone who understands Postgres policy evaluation order.

Test every policy with two separate authenticated accounts, not just the policy text. Sign in as Seller A, query the full table, and confirm you see zero of Seller B's rows. A policy that reads correctly can still leak under the exact query your frontend sends.
Marketplace RLS isolation: failure mode by table and role
TableNaive Lovable policyWhat it leaksCorrect scoping
listingsauth.uid() = seller_id for all opsDrafts and unpublished prices to buyersPublic SELECT on status='active'; write limited to owning seller_id
transactionsBroad SELECT for dashboardsOther sellers' orders, payouts, buyer contactsSELECT only where auth.uid() IN (buyer_id, seller_id)
reviewsauth.uid() = reviewer_idAbility to review without a completed purchaseINSERT gated by a completed transaction FK; SELECT public
disputesauth.uid() = opened_byThe counterparty cannot see a dispute against themSELECT where auth.uid() IN (buyer_id, seller_id) via transaction join
payoutsNot scoped, service-role readSeller A sees Seller B balances if exposed client-sideNever client-readable; compute server-side in an edge function

Related: Fix Lovable Supabase RLS and permission errors · When users can see each other's data

How do you write transaction-scoped RLS policies for two roles?

Scope every policy to the caller's relationship to the row, not to a single owner column. A transaction has a buyer and a seller; a dispute is visible to both parties; a payout is visible to neither from the client. The pattern is auth.uid() IN (buyer_id, seller_id) on transactions, and a join through transactions for dependent tables like reviews and disputes so the same two-party rule propagates.

  1. On transactions, write a SELECT policy: using (auth.uid() = buyer_id OR auth.uid() = seller_id). Add separate INSERT and UPDATE policies so a buyer cannot flip a transaction to 'completed' to unlock a review.
  2. On reviews, gate INSERT on a completed transaction the reviewer was party to: using (EXISTS (SELECT 1 FROM transactions t WHERE t.id = reviews.transaction_id AND t.status = 'completed' AND t.buyer_id = auth.uid())).
  3. On disputes, derive visibility by joining to the parent transaction so both parties see it: using (EXISTS (SELECT 1 FROM transactions t WHERE t.id = disputes.transaction_id AND auth.uid() IN (t.buyer_id, t.seller_id))).
  4. Keep payout balances out of any client-readable table. Compute seller earnings in a Supabase edge function that runs with the service role and returns only the calling seller's figures.
  5. For every policy, run the query your frontend actually sends as Seller A, then as Seller B, and confirm zero cross-account rows before shipping.
Beware recursive policies. A policy on transactions that subqueries a table whose own policy subqueries transactions can trigger infinite recursion in Postgres. Keep the dependency direction one-way: dependent tables join up to transactions, never the reverse.

Related: Lovable RLS and auth best practices · Fix infinite recursion in an RLS policy

How do you set up Stripe Connect for a Lovable marketplace?

Stripe Connect is the correct architecture: the platform holds a Connect account, sellers onboard as connected accounts via account links, and payments flow through the platform with an application fee. Lovable does not scaffold Connect — it emits standard Checkout sessions that pay a single recipient. Migrating adds seller onboarding and changes the payment-intent shape, typically a half-day to one-day engineering task once the data model is ready.

  1. Create a Stripe Connect platform account, then choose Express accounts so sellers see only their earnings, not your full Stripe dashboard.
  2. Add a seller onboarding flow with stripe.accountLinks.create() against each seller's connected-account ID, and store the resulting account ID on a sellers table.
  3. Replace stripe.checkout.sessions.create() calls with sessions carrying payment_intent_data: { application_fee_amount, transfer_data: { destination: sellerStripeAccountId } }.
  4. Handle the account.updated webhook to track onboarding completion, and transfer.created to confirm payouts; refuse checkout for any seller whose onboarding is incomplete.
  5. Verify the stripe-signature header and dedupe by event ID before processing, so a redelivered event cannot double-credit a payout.
Connect has Standard and Express modes. Express is usually right for marketplaces: you control the onboarding and payout UX while Stripe handles identity verification and compliance for the seller.

Related: Fix a Stripe webhook that is not firing · Avoid Stripe payment-provider lock-in

What data model does a production marketplace need?

A production marketplace needs explicit relationships with cascade and soft-delete behavior defined. Core tables: listings(seller_id, status, price_cents), transactions(buyer_id, seller_id, listing_id, stripe_payment_intent_id, status), reviews(reviewer_id, reviewee_id, transaction_id, rating), and disputes(transaction_id, opened_by, status, resolution). Every foreign key needs an index, every ON DELETE needs a defined behavior, and every table that holds user data needs a role-aware RLS policy rather than the default single-owner check.

Soft-delete matters more in a marketplace than elsewhere: you cannot hard-delete a seller who still has open transactions, outstanding payouts, or unresolved disputes without orphaning financial records. Use deleted_at TIMESTAMPTZ on sellers, listings, and reviews, and exclude soft-deleted rows in your RLS SELECT policies so they vanish from the UI without breaking referential integrity.

Index every column that appears in a WHERE clause over user data — seller_id, buyer_id, status, listing_id — or seller dashboards degrade as transaction volume grows. Lovable's generated schema commonly omits these indexes and leaves ON DELETE at the default NO ACTION, which produces obscure errors the first time a seller tries to close their account.

Related: Fix Lovable Supabase errors · Lovable app crashes under load

How do you handle trust and safety in a Lovable marketplace?

Trust and safety means verified-purchase reviews, dispute resolution with a payout hold, and a moderation flag on listings and profiles. None of this is scaffolded by Lovable. It requires database triggers, edge functions, and policy decisions you should make before the first real transaction — not after the first dispute, when money has already moved and you have no hold to reverse.

A minimum viable trust layer for launch: a reviewed_transaction_id constraint so a review must reference a completed transaction the reviewer was party to; a payout_hold_days setting (typically 3-7 days after delivery confirmation) before funds transfer to the seller; and a reports table with a pending / reviewed / actioned workflow so you can moderate content without building a full admin console on day one.

The payout hold is the single most valuable control. It gives you a window to freeze or reverse a transfer when a buyer disputes, before the money leaves your platform balance. Without it, your only recourse after a fraudulent sale is chasing a payout that has already settled to the seller's bank.

Decide your hold period and dispute workflow before launch. Retrofitting a payout hold after sellers have been paid instantly trains them to expect instant payouts, and tightening that later reads as a broken promise rather than a policy.

Related: The 5 production gaps in Lovable apps · Is your Lovable app secure? A checklist

What does Lovable handle well in a marketplace vs what needs hardening?

Lovable is strong at the listing UI, search and filter pages, and the buyer checkout flow. It is weaker at the payment split (Connect), the policy layer (who can see which transaction rows), and the operational tooling (dispute admin, seller analytics). The browse and listing experience can often ship with light review; the payment and data-isolation layers almost always need a senior engineer before real money moves.

Lovable marketplace: handled well vs needs hardening
AreaLovable handlesNeeds hardening before launch
Listing UICreate, edit, delete listing pagesImage size limits, status workflow, per-listing SEO metadata
Search/browseFilter and sort UIFull-text index (Postgres tsvector), pagination performance at scale
PaymentsStripe Checkout for the buyerStripe Connect split, application fee, seller onboarding, webhook dedupe
ReviewsStar-rating UI and displayVerified-purchase constraint, edit/delete policy, aggregate score
DisputesNot scaffoldedDispute table, payout hold, resolution workflow, admin interface
Data accessRLS enabled per tableTwo-role transaction-scoped policies tested with buyer and seller accounts
ScaleWorks for a handful of demo usersIndex coverage, connection pooling, search performance, soft-delete integrity

Related: Productionize your Lovable app · Lovable production-readiness checklist

How does a marketplace scale once real volume arrives?

Marketplace load is asymmetric: browse traffic is read-heavy and unauthenticated, while transactions are write-heavy and contended. The first bottlenecks are unindexed seller and status filters, naive LIKE search instead of a tsvector full-text index, and connection exhaustion from per-request Supabase clients. None of these appear in the editor preview; they appear the week a listing gets traction and concurrent buyers hit the same seller.

Add a generated tsvector column with a GIN index for search rather than ILIKE scans, which table-scan every listing. Paginate with keyset (WHERE created_at < cursor) instead of OFFSET, which slows linearly as users page deeper. Move payout and earnings aggregation into edge functions so a heavy dashboard query cannot lock rows the checkout path needs.

Watch for hot rows: a popular seller's row in a counts or ratings table becomes a contention point under concurrent reviews and orders. Aggregate ratings with a periodic job or a trigger that updates a denormalized score, rather than recomputing AVG(rating) on every listing page load.

If load problems appear before launch hardening is done, treat data isolation first and performance second. A slow page is recoverable; a cross-tenant data leak under load is a disclosure event you cannot take back.

Related: Lovable app crashes under load · Scale and productionize: the production hub

When is a Lovable marketplace ready for real transactions?

It is ready for real money when Stripe Connect is configured with working seller onboarding; the payout flow is tested end-to-end in Stripe test mode including a failed payment and a refund; RLS policies are verified with separate buyer and seller accounts; a payout hold and dispute path exist; and you can freeze or cancel a transaction without touching the database directly. A senior review usually catches two or three more gaps.

A pre-launch productionization pass on a marketplace runs two to four days of senior engineer time and outputs a written list of every change made and every remaining gap, so you launch knowing exactly what was hardened.
Marketplace pre-launch gate: ship only when every row passes
CheckPass conditionHow to verify
Data isolationNo cross-account rows under real queriesQuery each table as Seller A then Seller B; expect zero leakage
Payment splitApplication fee and seller transfer both landRun a test-mode sale; confirm platform fee and connected-account payout
Payout holdFunds held N days before transferTrigger a sale, confirm the transfer is delayed and reversible
Dispute pathA transaction can be frozen and refundedOpen a dispute, hold payout, issue a partial refund via Stripe
Failure handlingFailed payment leaves no half-stateForce payment_intent.payment_failed; confirm no orphaned transaction

Related: Get a Lovable security audit · Book a productionization call

Frequently asked questions

Does Lovable generate Stripe Connect for marketplaces?
No. Lovable generates standard Stripe Checkout sessions that route payment to a single recipient. To take a platform fee and split payments between buyer and seller, you migrate to Stripe Connect with connected accounts, account-link onboarding, and an application fee. It is a well-defined engineering task but changes the payment flow and the seller data model.
Why is multi-tenant data isolation harder in a marketplace than a normal app?
Because a marketplace has two tenant roles — buyer and seller — over the same rows, and each transaction has two legitimate owners and many illegitimate viewers. A single-owner policy like auth.uid() = user_id cannot express that. You need transaction-scoped policies where SELECT is allowed only when auth.uid() is the buyer or the seller, tested with two real accounts.
How do I prevent sellers from seeing other sellers' transaction data?
Write an RLS SELECT policy on transactions allowing rows only where auth.uid() = buyer_id OR auth.uid() = seller_id, and keep payout balances out of any client-readable table. Then sign in as Seller A, run your dashboard's exact query, and confirm you see zero of Seller B's rows. Lovable-generated dashboards often grant broad access that this test catches.
What is the minimum trust-and-safety setup before accepting real money?
Three controls: verified-purchase reviews (a foreign key to a completed transaction the reviewer was party to), a payout hold of 3-7 days after delivery confirmation before transferring to the seller, and a manual dispute override that lets you refund or freeze a transaction. These handle the majority of early marketplace incidents without a full admin console.
How do I avoid infinite recursion in marketplace RLS policies?
Keep policy dependencies one-directional. Dependent tables like reviews and disputes may join up to transactions, but transactions must not join back down to them. A policy on transactions that subqueries a table whose own policy subqueries transactions can trigger Postgres infinite recursion. If you hit it, flatten the dependency or move the check into a security-definer function.
Can I use Supabase Storage for marketplace listing images?
Yes. Configure a Storage bucket with a policy that lets authenticated users upload only to a path scoped to their user_id, such as listings/{user_id}/{listing_id}/*. Set a max file size in the bucket policy and mirror it with client-side validation. Without a size limit, one user can upload files large enough to exhaust your Storage quota.
How does a Lovable marketplace handle scale once a listing gets traction?
Add a tsvector GIN index for search instead of ILIKE scans, index every seller_id, buyer_id, and status filter, paginate by keyset rather than OFFSET, and aggregate ratings via a trigger or job instead of recomputing AVG on every page load. Move payout aggregation into edge functions so a heavy dashboard query cannot contend with the checkout path.
Should I migrate a Lovable marketplace off Lovable before launch?
Not necessarily. Most marketplaces launch on Lovable's Supabase backend after a hardening pass on RLS, Connect, and trust controls. Migration becomes worthwhile when you need control the platform does not give you — custom infrastructure, a separate Postgres, or compliance requirements. Decide based on those needs, not on a default assumption that you must leave.
Can a non-developer launch a Lovable marketplace with real payments?
You can reach a working demo, but you should have a senior engineer audit RLS isolation, Stripe Connect, and the dispute and payout-hold flow before real money moves. Data isolation across two tenant roles and split payments are the two areas where Lovable-generated marketplace code most reliably has gaps that cause leaks or payout errors.

Talk to a senior engineer — not a salesperson.

Book a free 30-minute audit call. We'll diagnose what's wrong and tell you exactly what it costs to fix.

Book a free audit call