Public reference implementation · No client data
Membership SaaS architecture with enforceable boundaries
An inspectable design reference for a paid listing product built with Next.js App Router, Supabase and a subscription provider. It is not presented as a shipped client project.
Download the one-page technical capability brief (PDF)
System boundary
The browser may request checkout and render authorized data, but it cannot author payment state. A server-only handler verifies signed billing events and writes a normalized membership state. Supabase RLS treats that state as the source of truth.
1 · Browser
Requests checkout; never writes entitlement
2 · Provider
Signs subscription lifecycle events
3 · Route handler
Verifies, deduplicates and normalizes
4 · Postgres + RLS
Enforces the access decision
Security invariants
- A browser cannot grant or extend its own paid access.
- A signed-in user can read only their own membership record.
- Full listing access requires a database-proven active entitlement.
- Replayed or out-of-order billing events cannot apply a transition twice.
- Administrative imports and service-role operations never execute in the browser.
Minimal data model
Keep provider payload evidence separate from the compact projection used for access decisions.
profiles(id, role)
memberships(user_id, provider_customer_id, status, access_until, updated_at)
billing_events(provider_event_id UNIQUE, event_type, received_at, payload_hash)
listings(id, visibility, title, teaser, private_body, seller_contact)
listing_imports(id, actor_id, source_hash, status, created_at)RLS entitlement decision
Never copy a client-controlled “isPaid” flag into a policy. Verify server-owned state and its entitlement window.
exists (
select 1 from memberships m
where m.user_id = auth.uid()
and m.status in ('active', 'trialing')
and m.access_until > now()
)Public previews should use a restricted view or column set. Hiding full-row fields with CSS is not access control.
Webhook transition rules
- Read the raw body and verify the provider signature.
- Insert the provider event ID under a unique constraint.
- Acknowledge duplicates without mutating state.
- Validate the subscription identity before applying a transition.
- Update the membership projection in the same transaction.
- Record a safe audit result without secrets or full payment payloads.
Authorization test matrix
| Actor | Allowed | Must be denied |
|---|---|---|
| Anonymous | Preview fields | Full listing and seller data |
| Signed in, unpaid | Account and previews | Paid listing content |
| Active member | Paid content | Other users' account rows |
| Expired member | Account and previews | Paid listing content |
| Administrator | Explicit admin operations | Raw payment secrets |
Definition of done
- Cross-user and cross-tenant reads fail at the database boundary.
- Signature, replay, delay and out-of-order webhook cases have automated tests.
- Cancellation and failed-payment behavior matches a written grace-period rule.
- CSV imports are validated, attributable and reversible before publication.
- Production logs exclude tokens, service-role keys and payment payloads.
- Server-rendered listings meet the agreed SEO and performance budget.
Evidence and limitations
The public Supabase RLS leak fixture demonstrates cross-user isolation tests and remediation. This page extends that reasoning to membership billing; it does not claim a client deployment or production transactions.
See the RLS testing guide, staging and CI blueprint, security audit and engineering evidence portfolio.
Review all 20 frontend starters in one bundle
20 editable frontend starters with live demos are $79 — one payment, no subscription.
View the Bundle — $79↗