An open-source creator-membership platform β creators, subscriptions, monetized content, and payouts β with a web app, admin dashboard, and mobile app.
Patreon Clone is a full-stack, multi-tenant membership platform in the style of Patreon: creators publish monetized content (text, image, video, audio, links) organized into tiers, fans subscribe via Stripe, and creators get paid out through Stripe Connect. It ships as four apps that work together:
| App | Tech | Role |
|---|---|---|
app-backend |
Node.js, NestJS 11, MongoDB | REST + WebSocket API: auth, memberships, payments, chat, push, admin |
app-web |
Next.js 16, Chakra UI, framer-motion | Public site: discover creators, subscribe, consume gated content |
app-dashboard |
Next.js 16, shadcn/ui, TanStack Query | Platform operations: moderation, payouts, analytics, reporting |
app-mobile |
Flutter (GetX), Firebase | Native mobile client for creators and members |
Try the deployed demo instances (backed by a shared demo database β don't create real subscriptions or payouts against it; use the credentials below to log in):
| App | URL | Login |
|---|---|---|
| Web app | https://patreon-clone-web.up.railway.app/ | test@bluetouch.dev / 12345678 at /auth/login |
| Admin dashboard | https://patreon-clone-dashboard.up.railway.app/ | info@bluetouch.dev / 12345678 at /login |
This repo is fully self-contained β no SaaS lock-in. Every external service (Stripe, Firebase, S3, OAuth) is configured through environment variables, and the entire product can be rebranded by editing a handful of config files (see Rebranding).
βββββββββββββββββββββββββββββββ
β Stripe β
web / mobile ββββΊβ β’ Checkout (subscriptions) β
/ dashboard β β’ Connect (creator payouts)β
ββββββββββββββββ¬βββββββββββββββ
β webhooks (signature-verified)
βΌ
ββββββββββββ JWT + OAuth ββββββββββββββββββββββββββββββββ
β MongoDB ββββββββββββββββββΊβ app-backend β
β (Mongoose) β NestJS REST + Socket.IO API β
ββββββββββββ β β’ auth (JWT/Google/Apple/β¦)β
ββββββββββββ β β’ memberships / payments β
β S3 or ββββββββββββββββββΊβ β’ posts / collections / chatβ
β R2/MinIO β β β’ admin / analytics / push β
ββββββββββββ ββββββββββββββββββββββββββββββββ
ββββββββββββ
β Firebase ββββ FCM push (service account via env)
ββββββββββββ
Request flow (subscriber)
app-web/app-mobileβPOST /auth/*or OAuth (Google) against the backend.- Backend verifies identity, mints a JWT (bcrypt local login or social profile lookup), and returns it.
- Client stores the token and sends it in the
Authorizationheader on every API call. - To subscribe: the web app calls
POST /memberships/checkout/:id, the backend creates a Stripe Checkout Session and returns acheckoutUrl; the browser redirects to Stripe and back (see Stripe setup). - Stripe webhook β backend updates the subscription, grants tier access, credits the creator's Connect balance. Payouts are initiated by the dashboard or scheduled jobs and verified through Stripe Connect.
Creator payout flow β creators complete Stripe Connect onboarding
(/onboarding/stripe + /onboarding/refresh in the web app). Their earnings accrue to a
connected Stripe account; the backend tracks available balance and creates
Stripe transfers when a payout is approved (dashboard β admin/payouts/:id/approve).
Real-time β group chats and DMs run over Socket.IO (app-backend/src/chat),
persisted to MongoDB; unread counts and notification state are mirrored to the
notifications module, and the mobile app receives push via Firebase Cloud Messaging
with a per-user device-token registry (database/schemas/device).
| Tool | Version | Notes |
|---|---|---|
| Node.js | 20+ (LTS) | backend + both Next.js apps |
| MongoDB | 5+/Atlas | MONGODB_URI |
| Flutter | 3.32.2 (see app-mobile/.fvmrc) |
mobile app |
| Stripe account | test or live | platform account + Connect enabled |
| S3-compatible storage | AWS S3 / Cloudflare R2 / MinIO | for media uploads |
| Google Cloud project | (optional) | for Google OAuth sign-in |
| Firebase project | (optional) | mobile push notifications |
Clone the repo once; each app has its own install/build commands below.
cd app-backend
yarn install # or: npm install
cp .env.example .env # then fill in real values (see table below)
yarn start:dev # NestJS dev server on :8080Seed demo users/content (optional β writes sample creators, posts, and picture files):
yarn seed
yarn run-migrate # run any pending schema migrationsRequired env vars (all documented in .env.example):
| Var | Purpose |
|---|---|
MONGODB_URI |
MongoDB connection string |
JWT_SECRET |
JWT signing secret |
STRIPE_SECRET_KEY |
Stripe API key (sk_β¦) |
STRIPE_WEBHOOK_SECRET |
Signing secret for platform webhooks (whsec_β¦) |
STRIPE_WEBHOOK_CONNECT_SECRET |
Signing secret for Stripe Connect webhooks |
AWS_* / S3_* |
S3-compatible credentials, endpoint, and bucket names |
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET |
Google OAuth app credentials |
BACKEND_URL, FRONTEND_URL |
Public URLs (OAuth callbacks, Stripe redirects, CORS) |
FIREBASE_SERVICE_ACCOUNT_KEY_JSON |
Firebase service-account JSON (compact one-liner) for FCM push |
ALLOWED_ORIGINS |
Comma-separated CORS origins |
β οΈ Webhooks: Stripe can't reachlocalhostβ forward webhook events locally with the Stripe CLI (stripe listen --forward-to localhost:8080/webhooks/stripe). Setup details: docs/stripe_setup.md and docs/google_oauth_cred.md.
cd app-web
npm install
cp .env.example .env # point NEXT_PUBLIC_API_URL at your backend
npm run dev # http://localhost:3000cd app-dashboard
npm install
cp .env.example .env
npm run dev # http://localhost:3000 (use a different port in dev)| Var | Purpose |
|---|---|
NEXT_PUBLIC_API_URL |
Backend base URL, e.g. http://localhost:8080 |
NEXT_PUBLIC_API_IMAGE_BUCKET |
Public media bucket base URL |
NEXT_PUBLIC_WEB3FORMS_ACCESS_KEY |
(optional) contact-form service key |
The dashboard's login button delegates to the web app's Google OAuth flow and verifies
the returned JWT β a regular user simply gets no access to admin routes; only accounts
with isAdmin: true (a flag on the user document, typically set on the first seeded
admin or via docs/admin-user bootstrap) can use it.
- Create a Stripe platform account and enable Connect (the app uses the Express/destination charges model for creator payouts).
- Put your secret key in
STRIPE_SECRET_KEY(usesk_test_β¦keys while developing). - In Dashboard β Developers β Webhooks, add two endpoints both pointing at
https://<your-backend>/webhooks/stripe:- a standard endpoint for
customer.subscription.*,invoice.*events β its signing secret isSTRIPE_WEBHOOK_SECRET; - a "listen to events on connected accounts" endpoint for
account.updatedβ its secret isSTRIPE_WEBHOOK_CONNECT_SECRET.
- a standard endpoint for
- In the Dashboard β Settings β Developer tools "Known callback URL domains", add
your
FRONTEND_URLso Stripe's hosted Checkout redirects back to the site.
Step-by-step (including Stripe CLI local forwarding): docs/stripe_setup.md.
Push notifications only need this when you build the mobile app:
- Create a Firebase project; register your app for Android
(
com.example.patreoncloneafter rebranding, see below) and iOS. - Download
google-services.jsonintoapp-mobile/android/app/andGoogleService-Info.plistintoapp-mobile/ios/Runner/(the repo ships placeholder versions of both β replace with yours). flutterfire configure(or editapp-mobile/lib/firebase_options.dart/app-mobile/lib/config/app_config.dartβFirebaseConfig) with your project's API keys.- Backend: download a service-account key JSON from
Firebase β Project settings β Service accounts, minify it to one line, and set it as
FIREBASE_SERVICE_ACCOUNT_KEY_JSONinapp-backend/.env. The backend stores the mobile app's device tokens and sends FCM messages through it.
cd app-mobile
flutter pub get
# 1. edit lib/config/app_config.dart β apiServerUrl (your backend) + Firebase values
# 2. for Android signing: cp android/key.properties.example android/key.properties
# and generate your own .jks keystore (instructions in that file's header)
flutter run # pick a target; .fvmrc pins Flutter 3.32.2Android builds use a release signing config read from
android/key.properties(gitignored β you must create it; the.exampletemplate is committed). If you don't have a keystore, debug builds still work but the config falls back safely to an unsigned build.
Create a Web application OAuth client in the Google Cloud Console, then set
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET in the backend .env and add the
authorized redirect https://<your-backend>/auth/google/callback plus your
frontend URL as authorized JavaScript origins. Walkthrough:
docs/google_oauth_cred.md.
Everything sensitive lives in .env files (all gitignored; each app ships a
.env.example). The full matrix is in docs/open-source-readiness-report.md
Β§"Required external services" if you want the exhaustive version.
| Service | Backend var(s) | Frontend var(s) |
|---|---|---|
| MongoDB | MONGODB_URI |
β |
| JWT | JWT_SECRET |
β |
| Stripe | STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, STRIPE_WEBHOOK_CONNECT_SECRET, STRIPE_CONNECT_WEBHOOK_SECRET (optional) |
β |
| S3 / R2 / MinIO | AWS_*, S3_BUCKET_NAME, S3_COLLECTIONS_BUCKET, S3_TEST_BUCKET, S3_ASSETS_BASE_URL |
NEXT_PUBLIC_API_IMAGE_BUCKET |
| Google OAuth | GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, BACKEND_URL |
β |
| Firebase (FCM) | FIREBASE_SERVICE_ACCOUNT_KEY_JSON |
β |
| CORS | ALLOWED_ORIGINS |
β |
| URLs | BACKEND_URL, FRONTEND_URL |
NEXT_PUBLIC_API_URL, NEXT_PUBLIC_APP_URL |
- Backend modules (
app-backend/src/<domain>/):auth(JWT + Passport strategies for Google/Apple/Facebook),users,members/memberships(tiers, entitlements),payments+webhooks(Stripe Checkout & Connect, signature-verified),posts/collections(media via presigned S3 URLs, video thumbnails viasharp),chat(Socket.IO + persistence),notifications+push-notifications(FCM),promotions(gifts/discounts),reports(moderation queue),admin(operations API for the dashboard),analytics+creator-history(earnings aggregation via cron),s3,search,home,platform-settings,server-status(env health checks the dashboard surfaces). - Web app (
app-web/src/app): public marketing/home/explore, creator profiles, gated posts by type,/checkout(Stripe redirect), creator/onboarding(URL β Stripe Connect β profile β first membership), community, notifications, settings, ToS/Privacy. - Dashboard (
app-dashboard/src/app/(app)): admin-users, creators (approve/reject/ suspend), members, content (withhold/restore/delete), subscriptions + stats, payouts (approve β Stripe transfer / reject β refund to balance), analytics, audit-log, reports, export. - Mobile (
app-mobile/lib): mirrors the web features in two personas β a member app (home feed, creator profiles, checkout, chat) and a creator app (post creation incl. audio/image/video upload, collections, earnings dashboard). All state is managed with GetX; HTTP + Socket.IO clients live underlib/backend/, central branding/URL config inlib/config/app_config.dart.
The project ships with generic placeholder brand values (patreon-clone,
patreon-clone.example.com) that are safe to run as-is. To rebrand:
- Backend:
app-backend/src/config/branding.config.ts(name, emails, social links, logo URLs β overridable viaBRAND_*env vars). - Web:
app-dashboard/src/config/branding.tsand any copy inapp-web/src. - Mobile: everything in one place β
app-mobile/lib/config/app_config.dart(app name, tagline, company, API URL, frontend domain, contact emails, colors, Firebase config). Alsoandroid/app/google-services.json,ios/Runner/GoogleService-Info.plist,ios/Runner/Info.plist(display name),android/app/build.gradle(namespace/applicationIdβ currentlycom.example.patreonclone, a valid placeholder ID you should change to your own), and the launcher icons (assets/images/).
βββ app-backend/ # NestJS API (MongoDB, Stripe, S3, Firebase FCM, Socket.IO)
βββ app-web/ # Next.js public site
βββ app-dashboard/ # Next.js admin dashboard
βββ app-mobile/ # Flutter mobile app (Android + iOS)
βββ docs/ # setup guides (Stripe, Google OAuth) + this project's notes
βββ README.md
- Backend:
cd app-backend && yarn test(Jest; spec files live next to their modules, e2e suite intest/). - Web/Dashboard: not yet covered by automated tests β a
next buildsmoke test is the practical gate for now. - Mobile:
cd app-mobile && flutter test(boilerplate widget test; extend per feature).
- Never commit
.env,key.properties,*.jks,google-services.json,GoogleService-Info.plist, orFIREBASE_SERVICE_ACCOUNT_KEY_JSONcontents. The root.gitignorecovers these; the committed placeholder files exist so the repo builds out of the box, with clearly markedyour-β¦values. - The old project's signing/Firebase credentials were removed from the full git history of this repository (rewritten, not just the latest commit). Treat any keys that may have leaked from previous private copies as compromised and rotate them (Stripe keys, AWS keys, Firebase project, Android keystore, JWT secret).
MIT β see LICENSE.