[KongchangAI]
Tutorials· 2 min read· 1,238 words

Open-Source SaaS Subscription Template: A Quick Setup Guide with Next.js + Stripe + Supabase

Open-Source SaaS Subscription Template: A Quick Setup Guide with Next.js + Stripe + Supabase

Complete walkthrough of building an open-source SaaS template with Next.js, Supabase, and Stripe

This article introduces an open-source, production-grade SaaS template built with Next.js, Supabase, and Stripe, designed to help indie developers skip 2-4 weeks of infrastructure setup. It provides a detailed walkthrough of Stripe payment configuration (product creation, Webhook event listeners), Google OAuth authentication integration, Supabase database design (including triggers for data synchronization), and the complete Vercel deployment process with architectural design rationale.

Why You Need a SaaS Subscription Template

When indie developers and small teams build SaaS applications, foundational infrastructure like user authentication, payment subscriptions, and database synchronization often consumes weeks of development time — while the core features that actually determine product success keep getting postponed.

This phenomenon is known as the "infrastructure tax." According to multiple developer surveys, building authentication + payments + database sync from scratch for a typical SaaS application takes an average of 2-4 weeks, yet these features are virtually "invisible" to users — what users perceive is only the core product value. The combination of Next.js, Supabase, and Stripe has become the de facto standard in the indie developer community precisely because each solves a critical problem: Next.js provides a full-stack framework that reduces technology selection overhead, Supabase offers BaaS capabilities built on open-source PostgreSQL, and Stripe provides the lowest-barrier path to global payment compliance.

Developer Sean (channel name ShanTech Stories) open-sourced a production-grade SaaS template based on Next.js + Supabase + Stripe, released under the MIT license. It covers authentication, dark mode, Stripe integration, and modern UI components, all written in TypeScript and Tailwind CSS. Below is a walkthrough of the complete setup process and core architectural design of this template.

Stripe Payment System Configuration

Creating Products and Coupons

The first step in building the payment system is creating products in the Stripe Dashboard. Navigate to Product Catalog, create a new Recurring-type product, and set a monthly subscription price — the template demo uses $0.99/month.

Stripe Product Catalog Configuration

After creating the product, you can configure coupons in the Coupon tab — for example, setting a 50% discount code limited to the first 50 users, providing flexible marketing tools for early promotion.

When creating a payment link in Stripe's Payment Links, there are several key configuration options:

  • Free trial period: For example, a 7-day free trial where users don't need to pay immediately
  • Promo codes: Check the option to allow users to enter discount codes
  • Post-payment redirect: Redirect users back to your app's Dashboard page instead of Stripe's default confirmation page

This is essentially a low-code tool — you can generate a complete payment page just by toggling configuration options, dramatically lowering the integration barrier.

Webhook: Making Your App Respond to Stripe Events in Real Time

This is the most critical and error-prone part of the entire Stripe integration. The purpose of a Webhook is to let your application listen to Stripe events in real time — when a user completes a payment, cancels a subscription, or a trial expires, Stripe proactively sends a notification to your server.

Understanding the nature of Webhooks helps avoid common misconceptions: a Webhook is a "reverse API" — with traditional APIs, you actively query for data, whereas with Webhooks, the service provider pushes data to your specified URL when an event occurs. Stripe's event-driven architecture means payment status changes (successful payments, refunds, failed subscription renewals, etc.) are communicated to your server within milliseconds, without requiring you to poll the Stripe API. This is especially critical in subscription-based SaaS: a user's credit card might be declined during renewal, and Stripe notifies you via the invoice.payment_failed event so your system can promptly downgrade user permissions or send reminder emails. Webhook security is ensured through a Signing Secret — Stripe signs each request with a secret key, and your server only processes events after verifying the signature, preventing spoofed request attacks.

Webhook Route Code Structure

The template's Webhook API route is located at app/api/stripe/webhook/route.ts and listens for the following event types:

  • customer.subscription.*: Subscription status changes (created, updated, deleted)
  • checkout.session.*: Checkout session completed
  • invoice.*: Invoice-related events
  • payment_intent.*: Payment intent events

Note: Don't subscribe to all event types — only select the events your application actually needs to handle. Otherwise, you'll introduce unnecessary latency and resource consumption.

Authentication System: Google Cloud + Supabase

Google OAuth Configuration

The template uses Google Sign-In for authentication, built on the OAuth 2.0 protocol — the industry cornerstone of modern identity authentication. The entire flow involves three parties: the user's browser, your application server (Supabase in this case), and Google's authorization server. When a user clicks "Sign in with Google," the browser redirects to Google; after Google verifies the user's identity, it sends an authorization code to the pre-registered Redirect URI (Supabase's callback address). Upon receiving the authorization code, Supabase exchanges it for an Access Token using the Client Secret, then fetches user information from Google to complete the login.

You need to create OAuth 2.0 credentials in the Google Cloud Console. The key steps are:

  1. Create an OAuth Client ID in API Services > Credentials
  2. Set Authorized JavaScript Origins (localhost:3000 for local development, your deployment domain for production)
  3. Configure Authorized Redirect URLs pointing to /auth/callback

Authentication Callback URL Configuration

Supabase Authentication Integration

As a BaaS (Backend as a Service), Supabase greatly simplifies the authentication flow. In Supabase's Authentication settings, enable the Google Provider and enter the Client ID and Client Secret obtained from Google Cloud.

Bidirectional configuration is key: Google Cloud needs to know the legitimate callback addresses in advance (to prevent authorization codes from being hijacked to malicious sites), and Supabase needs the Client Secret to complete the token exchange. The two systems establish a trust relationship by exchanging keys and callback URLs — if either side's configuration is missing, the entire OAuth flow will fail at different stages. This is also the most common source of errors during initial setup.

Bidirectional Configuration Between Supabase and Google Cloud

In Supabase's URL Configuration, you need to set three addresses:

  • Site main URL
  • /auth/callback (authentication callback)
  • /login (login page)

Database Design and Triggers

The template comes with four core data tables, created with a single click via the SQL Editor:

TablePurpose
usersBasic user information
user_preferencesUser preference settings (e.g., whether onboarding is completed)
user_trialsTrial period tracking (start time, end time, whether canceled)
subscriptionsSubscription information (synced with Stripe)

The most elegant design here is the database trigger. PostgreSQL triggers are a mechanism that automatically executes predefined functions when specific database events occur. They solve the problem of "decoupled synchronization between the authentication system and the business database" — Supabase's Auth system maintains its own auth.users table, while the user data needed for business logic (preferences, trial status, etc.) is stored in separate business tables.

If you handle this synchronization at the application layer (i.e., calling APIs to write to multiple tables after user registration), you face the risk of data inconsistency due to network failures or code exceptions. Triggers guarantee atomicity at the database level: when Supabase Authentication detects a new user registration, it automatically calls the handleNewUser function, synchronously inserting user information into the users, user_preferences, and user_trials tables — either all succeed or all roll back, fundamentally eliminating data silo issues. The subscription table syncs with Stripe in real time via Webhooks, forming a complete data loop. This is a classic application of the "database-first" architectural philosophy.

Deploying to Vercel and Final Integration Testing

Vercel Deployment Process

Deploying the application to Vercel is straightforward: import the GitHub repository, paste in all environment variables (Supabase URL, API Keys, Stripe Keys, etc.), click Deploy, and you're done.

#SaaS template#Next.js Stripe Supabase#open-source SaaS boilerplate#Stripe Webhook setup#Google OAuth Supabase#SaaS subscription template#Vercel deployment
Share:

Related articles