๐ณ SaaS Mode (Stripe)
MiroTalk WebRTC can run in two modes, controlled by a single environment variable:
SAAS |
Behavior |
|---|---|
false (default) |
Self-hosted mode. No subscription checks, no Stripe. The platform behaves exactly as it always has. |
true |
Paid SaaS mode. Registered users must have an active subscription to use the platform. |
When SAAS=true, demo and admin accounts are always exempt from payment checks, so you can keep administering the platform normally.
๐งฉ How it works
- A logged-in user without an active subscription is redirected to
/pricing. - They choose a plan and are sent to Stripe Checkout.
- After payment they return to the app, the subscription is activated, and they land directly in the dashboard (
/client). - Renewals and cancellations are kept in sync through Stripe webhooks.
Plans
| Plan | Price | Stripe type | Access |
|---|---|---|---|
| Monthly | $9 / month |
Subscription | While subscriptionStatus = active and not expired |
| Lifetime | $199 once |
One-time payment | Permanent (subscriptionExpiresAt = null) |
Protected when SAAS=true
- The dashboard page
GET /clientโ redirects to/pricingif no active subscription. - Protected APIs return HTTP 403: room create/update/delete, room invitations, recurring invitations, SFU token generation, and SMS.
- Login, account, billing, and the pricing/checkout flow remain accessible.
Activation is webhook-independent
After checkout, the success page calls GET /api/v1/stripe/verify?session_id=..., which activates the subscription immediately from the Checkout Session. The webhook is still used to keep renewals/cancellations in sync, but activation does not depend on the webhook arriving first (helpful in local dev).
โ๏ธ Environment variables
Add these to your .env (see .env.template):
SAAS=true # master switch: true | false
STRIPE_SECRET_KEY=sk_... # Stripe secret key (sk_live_... / sk_test_...)
STRIPE_PUBLISHABLE_KEY=pk_... # Stripe publishable key (pk_live_... / pk_test_...)
STRIPE_WEBHOOK_SECRET=whsec_... # Signing secret of your webhook endpoint
STRIPE_MONTHLY_PRICE_ID=price_... # Price ID of the $9/month recurring price
STRIPE_LIFETIME_PRICE_ID=price_... # Price ID of the $199 one-time price
โ ๏ธ Only
STRIPE_PUBLISHABLE_KEYand the price IDs are exposed to the frontend (viaGET /config). The secret key and webhook secret are read server-side only and are never sent to the browser.
| Variable | Where to find it in Stripe |
|---|---|
STRIPE_SECRET_KEY |
Developers โ API keys โ Secret key |
STRIPE_PUBLISHABLE_KEY |
Developers โ API keys โ Publishable key |
STRIPE_WEBHOOK_SECRET |
Developers โ Webhooks โ your endpoint โ Signing secret |
STRIPE_MONTHLY_PRICE_ID |
Product catalog โ your monthly product โ Price ID |
STRIPE_LIFETIME_PRICE_ID |
Product catalog โ your lifetime product โ Price ID |
๐ ๏ธ One-time Stripe setup
- Create two Products in the Stripe Dashboard:
- Monthly โ recurring price
$9 / monthโ copy its Price ID intoSTRIPE_MONTHLY_PRICE_ID. - Lifetime โ one-time price
$199โ copy its Price ID intoSTRIPE_LIFETIME_PRICE_ID.
- Monthly โ recurring price
- Copy your API keys into
STRIPE_SECRET_KEYandSTRIPE_PUBLISHABLE_KEY. - Configure the webhook (see below) and copy its signing secret into
STRIPE_WEBHOOK_SECRET. - (Recommended) Run the database migration to backfill subscription fields:
- Set
SAAS=trueand restart the app.
Webhook endpoint
Subscribe to at least these events:
checkout.session.completedcustomer.subscription.createdcustomer.subscription.updatedcustomer.subscription.deleted
๐งช Development / testing
Use Stripe test mode (keys starting with sk_test_ / pk_test_).
- Set the test keys and price IDs in
.env, withSAAS=true. - Forward webhooks to your local server with the Stripe CLI:
The CLI prints a
whsec_...signing secret โ put it inSTRIPE_WEBHOOK_SECRETand restart the app. - Start the app:
- Log in as a normal (non-admin, non-demo) user, go to
/pricing, and pay with a test card:- Card:
4242 4242 4242 4242 - Expiry: any future date ยท CVC: any 3 digits ยท ZIP: any
- Card:
- Verify the flow:
- Lifetime โ
subscriptionType = lifetime, permanent access. - Monthly โ
subscriptionType = monthly, renewal date shown in Account โ Billing. - Cancel via Manage Subscription (Stripe Billing Portal) โ access is revoked and
/clientredirects back to/pricing.
- Lifetime โ
๐ก To test admin/demo bypass, log in with the
ADMIN_*orUSER_DEMO_*credentials โ they always have full access regardless of subscription.
๐ Production
- Switch to live keys (
sk_live_/pk_live_) and live Price IDs. - Create a live webhook endpoint pointing at
{SERVER_URL}/api/stripe/webhookand use its live signing secret inSTRIPE_WEBHOOK_SECRET. - Run the migration (
npx migrate-mongo up) before enablingSAAS=true. - Set
SAAS=trueand deploy/restart.
Notes
- The migration is recommended but not strictly required: missing subscription fields are treated as "no active subscription", so existing users are safely gated until they subscribe.
- Switching back to
SAAS=falseinstantly disables all subscription checks โ useful for self-hosted installs.