├── .env.local.example ├── .gitignore ├── LICENSE ├── README.md ├── components ├── Layout.tsx ├── Pricing.tsx ├── icons │ ├── GitHub.tsx │ └── Logo.tsx └── ui │ ├── Button │ ├── Button.module.css │ ├── Button.tsx │ └── index.ts │ ├── Footer │ ├── Footer.tsx │ └── index.ts │ ├── Input │ ├── Input.module.css │ ├── Input.tsx │ └── index.ts │ ├── LoadingDots │ ├── LoadingDots.module.css │ ├── LoadingDots.tsx │ └── index.ts │ └── Navbar │ ├── Navbar.module.css │ ├── Navbar.tsx │ └── index.ts ├── fixtures └── stripe-fixtures.json ├── next-env.d.ts ├── next.config.js ├── package.json ├── pages ├── _app.tsx ├── _document.tsx ├── account.tsx ├── api │ ├── create-checkout-session.ts │ ├── create-portal-link.ts │ └── webhooks.ts ├── index.tsx └── signin.tsx ├── pnpm-lock.yaml ├── postcss.config.js ├── public ├── architecture_diagram.svg ├── demo.png ├── favicon.ico ├── github.svg ├── nextjs.svg ├── og.png ├── stripe.svg ├── supabase.svg ├── vercel-deploy.png └── vercel.svg ├── schema.sql ├── styles ├── chrome-bug.css └── main.css ├── supabase ├── .gitignore ├── config.toml └── seed.sql ├── tailwind.config.js ├── tsconfig.json ├── types.ts ├── types_db.ts └── utils ├── helpers.ts ├── stripe-client.ts ├── stripe.ts ├── supabase-admin.ts ├── supabase-client.ts └── useUser.tsx /.env.local.example: -------------------------------------------------------------------------------- 1 | # Update these with your Supabase details from your project settings > API 2 | NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co 3 | NEXT_PUBLIC_SUPABASE_ANON_KEY= 4 | SUPABASE_SERVICE_ROLE_KEY= 5 | 6 | # Update these with your Stripe credentials from https://dashboard.stripe.com/apikeys 7 | NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_1234 8 | STRIPE_SECRET_KEY=sk_test_1234 9 | STRIPE_WEBHOOK_SECRET=whsec_1234 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | 36 | # editors 37 | .vscode 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021 Vercel, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Next.js Subscription Payments Starter 2 | 3 | The all-in-one starter kit for high-performance SaaS applications. 4 | 5 | ## Features 6 | 7 | - Secure user management and authentication with [Supabase](https://supabase.io/docs/guides/auth) 8 | - Powerful data access & management tooling on top of PostgreSQL with [Supabase](https://supabase.io/docs/guides/database) 9 | - Integration with [Stripe Checkout](https://stripe.com/docs/payments/checkout) and the [Stripe customer portal](https://stripe.com/docs/billing/subscriptions/customer-portal) 10 | - Automatic syncing of pricing plans and subscription statuses via [Stripe webhooks](https://stripe.com/docs/webhooks) 11 | 12 | ## Demo 13 | 14 | - https://subscription-payments.vercel.app/ 15 | 16 | [![Screenshot of demo](./public/demo.png)](https://subscription-payments.vercel.app/) 17 | 18 | ## Architecture 19 | 20 | ![Architecture diagram](./public/architecture_diagram.svg) 21 | 22 | ## Deploy with Vercel 23 | 24 | The Vercel deployment will guide you through creating a Supabase account and project. After installing the Supabase integration, you'll need to configure Stripe with a few simple steps. 25 | 26 | **Note:** We're working on our Stripe integration. We've documented the required steps below under "Configure Stripe" until the integration is ready. 27 | 28 | To get started, click the "Deploy with Vercel" button below. 29 | 30 | [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fnextjs-subscription-payments&project-name=nextjs-subscription-payments&repo-name=nextjs-subscription-payments&demo-title=Next.js%20Subscription%20Payments%20Starter&demo-description=Demo%20project%20on%20Vercel&demo-url=https%3A%2F%2Fsubscription-payments.vercel.app&demo-image=https%3A%2F%2Fsubscription-payments.vercel.app%2Fdemo.png&integration-ids=oac_jUduyjQgOyzev1fjrW83NYOv&external-id=nextjs-subscription-payments) 31 | 32 | [![Screenshot of Vercel deployment](./public/vercel-deploy.png)](https://vercel.com/new/git/external?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fnextjs-subscription-payments&project-name=nextjs-subscription-payments&repo-name=nextjs-subscription-payments&demo-title=Next.js%20Subscription%20Payments%20Starter&demo-description=Demo%20project%20on%20Vercel&demo-url=https%3A%2F%2Fsubscription-payments.vercel.app&demo-image=https%3A%2F%2Fsubscription-payments.vercel.app%2Fdemo.png&integration-ids=oac_jUduyjQgOyzev1fjrW83NYOv&external-id=nextjs-subscription-payments) 33 | 34 | Once the project has deployed, continue with the configuration steps below. 35 | 36 | The initial build will fail due to missing Stripe environment variables. After configuring Stripe, redeploy the application. 37 | 38 | ## Configure Supabase Auth 39 | 40 | #### Setup redirect wildcards for deploy previews 41 | 42 | For auth redirects (magic links, OAuth providers) to work correctly in deploy previews, navigate to the auth settings (i.e. `https://app.supabase.com/project/:project-id/auth/url-configuration`) and add the following wildcard URL to "Redirect URLs": `https://**vercel.app/*/*`. 43 | 44 | You can read more about redirect wildcard patterns in the [docs](https://supabase.com/docs/guides/auth#redirect-urls-and-wildcards). 45 | 46 | #### [Optional] - Set up OAuth providers 47 | 48 | You can use third-party login providers like GitHub or Google. Refer to the [docs](https://supabase.io/docs/guides/auth#third-party-logins) to learn how to configure these. Once configured you can add them to the `provider` array of the `Auth` component on the [`signin.tsx`](./pages/signin.tsx) page. 49 | 50 | ## Configure Stripe 51 | 52 | To start developing your SaaS application, we'll need to configure Stripe to handle test payments. For the following steps, make sure you have the ["Test Mode" toggle](https://stripe.com/docs/testing) switched on. 53 | 54 | ### Configure webhook 55 | 56 | We need to configure the webhook pictured in the architecture diagram above. This webhook is the piece that connects Stripe to your Vercel Serverless Functions. 57 | 58 | 1. Click the "Add Endpoint" button on the [test Endpoints page](https://dashboard.stripe.com/test/webhooks). 59 | 1. Set the endpoint URL to `https://your-deployment-url.vercel.app/api/webhooks`. 60 | 1. Click `Select events` under the `Select events to listen to` heading. 61 | 1. Click `Select all events` in the `Select events to send` section. 62 | 1. Copy `Signing secret` as we'll need that in the next step. 63 | 64 | ### Set environment variables 65 | 66 | To securely interact with Stripe, we need to add a few [Environment Variables](https://vercel.com/docs/concepts/projects/environment-variables) in the Vercel dashboard. 67 | 68 | - `NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY` 69 | - `STRIPE_SECRET_KEY` 70 | - `STRIPE_WEBHOOK_SECRET_LIVE` 71 | 72 | You can find the first two keys on the [API keys tab](https://dashboard.stripe.com/test/apikeys) in Stripe. The `STRIPE_WEBHOOK_SECRET_LIVE` is the `Signing secret` copied in the previous webhook configuration step. 73 | 74 | ### Redeploy 75 | 76 | We need to redeploy the application so that the latest environment variables are present. 77 | 78 | Redeploy your application by going to the deployments tab, finding your deployment, and clicking "redeploy." 79 | 80 | ### Create product and pricing information 81 | 82 | For Stripe to automatically bill your users for recurring payments, you need to create your product and pricing information in the [Stripe Dashboard](https://dashboard.stripe.com/test/products). When you create or update your product and price information, the changes automatically sync with your Supabase database. 83 | 84 | Stripe Checkout currently supports pricing that bills a predefined amount at a specific interval. More complex plans (e.g., different pricing tiers or seats) are not yet supported. 85 | 86 | For example, you can create business models with different pricing tiers, e.g.: 87 | 88 | - Product 1: Hobby 89 | - Price 1: 10 USD per month 90 | - Price 2: 100 USD per year 91 | - Product 2: Freelancer 92 | - Price 1: 20 USD per month 93 | - Price 2: 200 USD per year 94 | 95 | #### Generate test data with the Stripe CLI 96 | 97 | The [Stripe CLI](https://stripe.com/docs/stripe-cli#install) `fixtures` command executes a series of API requests defined in a JSON file. To speed up the setup, we have added a [fixtures file](fixtures/stripe-fixtures.json) to bootstrap test product and pricing data in your Stripe account. Simply run `stripe fixtures fixtures/stripe-fixtures.json`. 98 | 99 | **Important:** Be sure to start the webhook forwarding (see below) so that the products created by the fixtures command above are imported into your database. 100 | 101 | ### Configure the Stripe customer portal 102 | 103 | 1. Set your custom branding in the [settings](https://dashboard.stripe.com/settings/branding) 104 | 1. Configure the Customer Portal [settings](https://dashboard.stripe.com/test/settings/billing/portal) 105 | 1. Toggle on "Allow customers to update their payment methods" 106 | 1. Toggle on "Allow customers to update subscriptions" 107 | 1. Toggle on "Allow customers to cancel subscriptions" 108 | 1. Add the products and prices that you want 109 | 1. Set up the required business information and links 110 | 111 | ### Generate types from your Supabase database 112 | 113 | You can use the [Supabase CLI](https://supabase.com/docs/reference/cli/usage#supabase-gen-types-typescript) to generate types from your Database by running 114 | 115 | 1. To install supabase cli 116 | 117 | ```bash 118 | npm install supabase --save-dev 119 | yarn add supabase --dev 120 | ``` 121 | 122 | 2. Connect to supabase 123 | 124 | ```bash 125 | npx supabase login 126 | ``` 127 | 128 | 3. Enter your access token. You can generate an access token from https://app.supabase.com/account/tokens 129 | 4. Generate types 130 | 131 | ```bash 132 | npx supabase gen types typescript --project-id [YOUR-PROJECT-REF] --schema public > types_db.ts 133 | ``` 134 | 135 | ### That's it 136 | 137 | That's it. Now you're ready to earn recurring revenue from your customers 🥳 138 | 139 | ## Going live 140 | 141 | ### Archive testing products 142 | 143 | Archive all test mode Stripe products before going live. Before creating your live mode products, make sure to follow the steps below to set up your live mode env vars and webhooks. 144 | 145 | ### Configure production environment variables 146 | 147 | To run the project in live mode and process payments with Stripe, modify the environment variables from Stripe "test mode" to "production mode." After switching the variables, be sure to redeploy the application. 148 | 149 | To verify you are running in production mode, test checking out with the [Stripe test card](https://stripe.com/docs/testing). The test card should not work. 150 | 151 | ### Redeploy 152 | 153 | Afterward, you will need to rebuild your production deployment for the changes to take effect. Within your project Dashboard, navigate to the "Deployments" tab, select the most recent deployment, click the overflow menu button (next to the "Visit" button) and select "Redeploy." 154 | 155 | ## Develop locally 156 | 157 | Deploying with Vercel will create a repository for you, which you can clone to your local machine. 158 | 159 | Next, use the [Vercel CLI](https://vercel.com/download) to link your project: 160 | 161 | ```bash 162 | vercel login 163 | vercel link 164 | ``` 165 | 166 | ### Setting up the env vars locally 167 | 168 | Use the Vercel CLI to download the development env vars: 169 | 170 | ```bash 171 | vercel env pull .env.local 172 | ``` 173 | 174 | Running this command will create a new `.env.local` file in your project folder. For security purposes, you will need to set the `SUPABASE_SERVICE_ROLE_KEY` manually from your [Supabase dashboard](https://app.supabase.io/) (Settings > API). Lastly, the webhook secret differs for local testing vs. when deployed to Vercel. Follow the instructions below to get the corresponding webhook secret. 175 | 176 | ### Use the Stripe CLI to test webhooks 177 | 178 | First [install the CLI](https://stripe.com/docs/stripe-cli) and [link your Stripe account](https://stripe.com/docs/stripe-cli#login-account). 179 | 180 | Next, start the webhook forwarding: 181 | 182 | ```bash 183 | stripe listen --forward-to=localhost:3000/api/webhooks 184 | ``` 185 | 186 | Running this Stripe command will print a webhook secret (such as, `whsec_***`) to the console. Set `STRIPE_WEBHOOK_SECRET` to this value in your `.env.local` file. 187 | 188 | ### Install dependencies and run the Next.js client 189 | 190 | ```bash 191 | npm install 192 | npm run dev 193 | # or 194 | yarn 195 | yarn dev 196 | ``` 197 | -------------------------------------------------------------------------------- /components/Layout.tsx: -------------------------------------------------------------------------------- 1 | import { PropsWithChildren } from 'react'; 2 | import Head from 'next/head'; 3 | import { useRouter } from 'next/router'; 4 | 5 | import Navbar from '@/components/ui/Navbar'; 6 | import Footer from '@/components/ui/Footer'; 7 | 8 | import { PageMeta } from '../types'; 9 | 10 | interface Props extends PropsWithChildren { 11 | meta?: PageMeta; 12 | } 13 | 14 | export default function Layout({ children, meta: pageMeta }: Props) { 15 | const router = useRouter(); 16 | const meta = { 17 | title: 'Next.js Subscription Starter', 18 | description: 'Brought to you by Vercel, Stripe, and Supabase.', 19 | cardImage: '/og.png', 20 | ...pageMeta 21 | }; 22 | 23 | return ( 24 | <> 25 | 26 | {meta.title} 27 | 28 | 29 | 30 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 |
{children}
47 |