225 | You are currently in your 48-hour trial period. Your trial will end on {' '}
226 | {trialEndTime ? new Date(trialEndTime).toLocaleDateString() : 'soon'}.
227 |
228 |
Subscribe now to continue using the app after the trial ends.
229 | >
230 | ) : trialEndTime ? (
231 | <>
232 |
233 |
234 | Your trial period ended on {new Date(trialEndTime).toLocaleDateString()}.
235 |
236 |
Subscribe now to regain access to the cooking experience.
237 |
238 | >
239 | ) : (
240 |
Subscribe to unlock the amazing cooking experience.
241 | )}
242 |
243 |
247 |
248 | )}
249 |
250 |
251 | {/* Show pricing section if user doesn't have an active subscription */}
252 | {/* {(!subscription || subscription.status === 'canceled') && (
253 |
254 | )} */}
255 |
256 | {/* Cancel Confirmation Modal */}
257 | {isCancelModalOpen && (
258 |
259 |
260 |
Cancel Subscription?
261 |
262 | You'll continue to have access until the end of your billing period on {new Date(subscription?.current_period_end || '').toLocaleDateString()}. No refunds are provided for cancellations.
263 |
264 |
265 |
272 |
286 |
287 |
288 |
289 | )}
290 |
291 |
292 | );
293 | };
294 |
295 |
296 | export default function ProfilePage() {
297 | return (
298 | }>
299 |
300 |
301 | );
302 | }
303 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Next.js + Stripe + Supabase Production-Ready Template
2 |
3 | A production-ready Next.js template featuring authentication, dark mode support, Stripe integration, and a clean, modern UI. Built with TypeScript and Tailwind CSS.
4 |
5 | 
6 | 
7 | 
8 | 
9 |
10 | 📹 Full YouTube Guide: [Youtube link](https://www.youtube.com/watch?v=ad1BxZufer8&list=PLE9hy4A7ZTmpGq7GHf5tgGFWh2277AeDR&index=8)
11 |
12 | 🚀 X Post: [X link](https://x.com/ShenSeanChen/status/1895163913161109792)
13 |
14 | 💡 Try the App: [App link](https://mvp.seanchen.io)
15 |
16 | ☕️ Buy me a coffee: [Cafe Latte](https://buy.stripe.com/5kA176bA895ggog4gh)
17 |
18 | 🤖️ Discord: [Invite link](https://discord.com/invite/TKKPzZheua)
19 |
20 | ## ✨ Features
21 |
22 | - 🔐 Authentication with Supabase
23 | - 💳 Stripe payment integration
24 | - 🌓 Dark mode support
25 | - 📱 Responsive design
26 | - 🎨 Tailwind CSS styling
27 | - 🔄 Framer Motion animations
28 | - 🛡️ TypeScript support
29 | - 📊 Error boundary implementation
30 | - 🔍 SEO optimized
31 | - 🤖 MCP integration for AI-powered development
32 |
33 | ## 🚀 Getting Started
34 |
35 | ### Prerequisites
36 |
37 | - Node.js 18+
38 | - npm or yarn
39 | - A Supabase account
40 | - A Stripe account
41 | - A Google Cloud Platform account
42 |
43 | ### Installation and Setup
44 |
45 | 1. Clone the template:
46 |
47 | ```bash
48 | git clone https://github.com/ShenSeanChen/launch-stripe-nextjs-supabase my-full-stack-app
49 | cd my-full-stack-app
50 | rm -rf .git
51 | git init
52 | git add .
53 | git commit -m "Initial commit"
54 | # git remote add origin https://github.com/ShenSeanChen/my-full-stack-app.git
55 | git remote add origin https://github.com/USE_YOUR_OWN_GITHUB_NAME/my-full-stack-app.git
56 | git push -u origin main
57 | ```
58 |
59 | 2. Install dependencies:
60 | ```bash
61 | npm install
62 | ```
63 | or
64 | ```bash
65 | yarn install
66 | ```
67 |
68 | 3. Create .env.local with all variables from .env.example
69 | ```
70 | NEXT_PUBLIC_APP_URL=http://localhost:8000
71 | NEXT_PUBLIC_API_URL=http://localhost:8080
72 | NEXT_PUBLIC_WS_URL=ws://localhost:8080
73 |
74 | # Supabase Configuration
75 | NEXT_PUBLIC_SUPABASE_URL=
76 | NEXT_PUBLIC_SUPABASE_ANON_KEY=
77 | SUPABASE_SERVICE_ROLE_KEY=
78 |
79 | # OpenAI Configuration (you'll need to add your key)
80 | OPENAI_API_KEY=
81 |
82 | # Stripe Configuration
83 | # NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_
84 | NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_
85 | NEXT_PUBLIC_STRIPE_BUTTON_ID=buy_btn_
86 | # STRIPE_SECRET_KEY=sk_test_
87 | STRIPE_SECRET_KEY=sk_live_
88 | # STRIPE_WEBHOOK_SECRET=whsec_
89 | STRIPE_WEBHOOK_SECRET=whsec_
90 |
91 | # ANALYTICS
92 | NEXT_PUBLIC_POSTHOG_KEY=
93 | NEXT_PUBLIC_POSTHOG_HOST=https://app.posthog.com
94 | ```
95 |
96 | 4. Set up Google Cloud Platform (GCP):
97 | - Create new OAuth 2.0 credentials in GCP Console
98 | - Configure authorized JavaScript origins
99 | - Configure redirect URIs
100 | - Save the Client ID and Client Secret
101 |
102 | 5. Configure Supabase:
103 |
104 | a. Get API Keys (Project Settings > API):
105 | - Project URL → NEXT_PUBLIC_SUPABASE_URL
106 | - Anon Public Key → NEXT_PUBLIC_SUPABASE_ANON_KEY
107 | - Service Role Secret → SUPABASE_SERVICE_ROLE_KEY
108 |
109 | b. Set up Authentication:
110 | - Go to Authentication > Providers > Google
111 | - Add your GCP Client ID and Client Secret
112 | - Update Site URL and Redirect URLs
113 |
114 | c. Database Setup:
115 | - Enable Row Level Security (RLS) for all tables
116 | - Create policies for authenticated users and service roles
117 | - Create the following trigger function:
118 |
119 | ```sql
120 | CREATE OR REPLACE FUNCTION public.handle_new_user()
121 | RETURNS trigger AS $$
122 | BEGIN
123 | INSERT INTO public.users (id, email, created_at, updated_at, is_deleted)
124 | VALUES (NEW.id, NEW.email, NOW(), NOW(), FALSE);
125 |
126 | INSERT INTO public.user_preferences (user_id, has_completed_onboarding)
127 | VALUES (NEW.id, FALSE);
128 |
129 | INSERT INTO public.user_trials (user_id, trial_start_time, trial_end_time)
130 | VALUES (NEW.id, NOW(), NOW() + INTERVAL '48 hours');
131 |
132 | RETURN NEW;
133 | END;
134 | $$ LANGUAGE plpgsql SECURITY DEFINER;
135 |
136 | CREATE TRIGGER on_auth_user_created
137 | AFTER INSERT ON auth.users
138 | FOR EACH ROW EXECUTE FUNCTION public.handle_new_user();
139 | ```
140 |
141 | 6. Set up Stripe:
142 |
143 | a. Create a live account and configure:
144 | - Create product in Product Catalog
145 | - Create promotional coupon codes
146 | - Set up Payment Link with trial period
147 |
148 | b. Get required keys:
149 | - Publishable Key → NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY
150 | - Secret Key → STRIPE_SECRET_KEY
151 | - Buy Button ID → NEXT_PUBLIC_STRIPE_BUTTON_ID
152 |
153 | c. Configure webhooks:
154 | - Add endpoint: your_url/api/stripe/webhook
155 | - Subscribe to events: customer.subscription.*, checkout.session.*, invoice.*, payment_intent.*
156 | - Copy Signing Secret → STRIPE_WEBHOOK_SECRET
157 |
158 | 8. Start the development server:
159 | ```bash
160 | npm run dev
161 | ```
162 | or
163 | ```bash
164 | yarn dev
165 | ```
166 |
167 | 8. Open [http://localhost:3000](http://localhost:3000) in your browser.
168 |
169 | ## 🛠️ MCP Integration Setup
170 |
171 | ### What is MCP?
172 |
173 | MCP (Model Control Protocol) enables enhanced AI assistant capabilities for this project, allowing the AI to interact directly with your Stripe and Supabase accounts to help with debugging, configuring, and managing your application.
174 |
175 | For a comprehensive demonstration of MCP capabilities, check out our dedicated demo repository:
176 | - 🔗 [launch-mcp-demo](https://github.com/ShenSeanChen/launch-mcp-demo) - Collection of powerful MCP tools
177 | - 📹 [Full YouTube Guide](https://www.youtube.com/watch?v=sfCBCyNyw7U&list=PLE9hy4A7ZTmpGq7GHf5tgGFWh2277AeDR&index=10)
178 | - 🚀 [X Post](https://x.com/ShenSeanChen/status/1910057838032097688)
179 |
180 | ### Setting up MCP
181 |
182 | 1. Create an `mcp.json` file:
183 |
184 | Copy the example file to create your own configuration:
185 |
186 | ```bash
187 | cp .cursor/mcp.json.example .cursor/mcp.json
188 | ```
189 |
190 | 2. Configure your credentials:
191 |
192 | a. Stripe Integration:
193 | - Get your Stripe API key from the Stripe Dashboard
194 | - Replace `your_stripe_test_key_here` with your actual test key
195 |
196 | b. Supabase Integration:
197 | - Generate a Supabase access token from your Supabase dashboard (Project Settings > API)
198 | - Replace `your_supabase_access_token_here` with your actual token
199 |
200 | c. GitHub Integration (optional):
201 | - Create a GitHub Personal Access Token with appropriate permissions
202 | - Replace `your_github_personal_access_token_here` with your actual token
203 |
204 | 3. Example of a completed `mcp.json` file:
205 |
206 | ```json
207 | {
208 | "mcpServers": {
209 | "stripe": {
210 | "command": "npx",
211 | "args": [
212 | "-y",
213 | "@stripe/mcp"
214 | ],
215 | "env": {
216 | "STRIPE_SECRET_KEY": "sk_test_51ABC123..."
217 | }
218 | },
219 | "supabase": {
220 | "command": "npx",
221 | "args": [
222 | "-y",
223 | "@supabase/mcp-server-supabase@latest",
224 | "--access-token",
225 | "sbp_1234abcd5678efgh..."
226 | ]
227 | }
228 | }
229 | }
230 | ```
231 |
232 | 4. Using MCP with AI assistants:
233 |
234 | After configuring `mcp.json`, the AI assistant within the Cursor editor will be able to:
235 | - Query and manage your Stripe subscriptions
236 | - Interact with your Supabase database
237 | - Help troubleshoot integration issues
238 | - Provide contextual help based on your actual configuration
239 |
240 | 5. Security Considerations:
241 |
242 | - Never commit your `mcp.json` file to version control
243 | - Use test credentials during development
244 | - Limit access tokens to only the permissions needed
245 |
246 | ### Extending MCP with Additional Tools
247 |
248 | The MCP framework can be extended with various tools beyond Stripe and Supabase. Our [launch-mcp-demo](https://github.com/ShenSeanChen/launch-mcp-demo) repository demonstrates how to integrate basic MCP examples.
249 |
250 | To integrate these additional tools with your project:
251 |
252 | 1. Clone the demo repository:
253 | ```bash
254 | git clone https://github.com/ShenSeanChen/launch-mcp-demo.git
255 | ```
256 |
257 | 2. Follow the installation instructions in the repository's README
258 |
259 | 3. Update your `.cursor/mcp.json` to include the additional tools:
260 | ```json
261 | {
262 | "mcpServers": {
263 | "stripe": {
264 | // Your existing Stripe configuration
265 | },
266 | "supabase": {
267 | // Your existing Supabase configuration
268 | },
269 | "weather": {
270 | "command": "/path/to/your/python/environment",
271 | "args": [
272 | "--directory",
273 | "/path/to/launch-mcp-demo/weather",
274 | "run",
275 | "weather.py"
276 | ]
277 | },
278 | "files": {
279 | "command": "/path/to/your/python/environment",
280 | "args": [
281 | "--directory",
282 | "/path/to/launch-mcp-demo/files",
283 | "run",
284 | "files.py"
285 | ]
286 | }
287 | }
288 | }
289 | ```
290 |
291 | 4. Restart your Cursor editor to apply the changes
292 |
293 | These additional tools can help enhance your development workflow and provide more capabilities to the AI assistant when working with your project.
294 |
295 | ## 📖 Project Structure
296 |
297 | ```
298 | ├── app/ # Next.js 14 app directory
299 | │ ├── api/ # API routes
300 | │ │ ├── stripe/ # Stripe payment endpoints
301 | │ │ └── user/ # User API endpoints
302 | │ ├── auth/ # Auth-related pages
303 | │ │ ├── callback/ # handle auth callback
304 | │ ├── dashboard/ # Dashboard pages
305 | │ ├── pay/ # Payment pages
306 | │ ├── profile/ # User profile pages
307 | │ ├── reset-password/ # Reset password pages
308 | │ ├── update-password/ # Update password pages
309 | │ ├── verify-email/ # Verify email pages
310 | │ ├── layout.tsx # Root layout
311 | │ └── page.tsx # Home page
312 | ├── components/ # Reusable components
313 | ├── contexts/ # React contexts
314 | ├── hooks/ # Custom React hooks
315 | ├── utils/ # Utility functions
316 | ├── types/ # TypeScript type definitions
317 | ├── public/ # Static assets
318 | ├── styles/ # Global styles
319 | └── .cursor/ # Cursor editor and MCP configurations
320 | ├── mcp.json.example # Example MCP configuration
321 | └── mcp.json # Your custom MCP configuration (gitignored)
322 | ```
323 |
324 | ## 🛠️ Built With
325 |
326 | - [Next.js](https://nextjs.org/) - React framework
327 | - [TypeScript](https://www.typescriptlang.org/) - Type safety
328 | - [Tailwind CSS](https://tailwindcss.com/) - Styling
329 | - [Supabase](https://supabase.com/) - Authentication & Database
330 | - [Stripe](https://stripe.com/) - Payments
331 | - [Framer Motion](https://www.framer.com/motion/) - Animations
332 |
333 | ## 🔧 Configuration
334 |
335 | ### Tailwind Configuration
336 |
337 | The template includes a custom Tailwind configuration with:
338 | - Custom colors
339 | - Dark mode support
340 | - Extended theme options
341 | - Custom animations
342 |
343 | ### Authentication
344 |
345 | Authentication is handled through Supabase with support for:
346 | - Email/Password
347 | - Google OAuth
348 | - Magic Links
349 | - Password Reset
350 |
351 | ### Payment Integration
352 |
353 | Stripe integration includes:
354 | - Subscription management
355 | - Trial periods
356 | - Webhook handling
357 | - Payment status tracking
358 |
359 | ## 🤝 Contributing
360 |
361 | 1. Fork the repository
362 | 2. Create your feature branch (`git checkout -b feature/AmazingFeature`)
363 | 3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)
364 | 4. Push to the branch (`git push origin feature/AmazingFeature`)
365 | 5. Open a Pull Request
366 |
367 | ## 📝 License
368 |
369 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
370 |
371 | ## 🙏 Acknowledgments
372 |
373 | - Next.js team for the amazing framework
374 | - Vercel for the deployment platform
375 | - Tailwind CSS team for the utility-first CSS framework
376 | - Supabase team for the backend platform
377 | - Stripe team for the payment infrastructure
378 | - Cursor team for the AI-powered editor and MCP capabilities
379 | - Anthropic for Claude AI and Claude Desktop integration
380 | - MCP framework developers for enabling extended AI capabilities
381 |
382 | ## 📫 Contact
383 |
384 | X - [@ShenSeanChen](https://x.com/ShenSeanChen)
385 |
386 | YouTube - [@SeanTechStories](https://www.youtube.com/@SeanTechStories)
387 |
388 | Discord - [@Sean's Stories](https://discord.gg/TKKPzZheua)
389 |
390 | Instagram - [@SeanTechStories](https://www.instagram.com/sean_tech_stories )
391 |
392 | Project Link: [https://github.com/ShenSeanChen/launch-stripe-nextjs-supabase](https://github.com/ShenSeanChen/launch-stripe-nextjs-supabase)
393 |
394 | ## 🚀 Deploy
395 |
396 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js).
397 |
398 | [](https://vercel.com/new/clone?repository-url=https://github.com/yourusername/your-repo-name)
399 |
400 | ---
401 |
402 | Made with 🔥 by [ShenSeanChen](https://github.com/ShenSeanChen)
403 |
--------------------------------------------------------------------------------
/app/page.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useAuth } from '@/contexts/AuthContext';
4 | import { PricingSection } from '@/components/PricingSection';
5 | import { useTrialStatus } from '@/hooks/useTrialStatus';
6 | // import { DemoWidget } from '@/components/DemoWidget';
7 | // import { MetricCard } from '@/components/MetricCard';
8 | import { TypewriterEffect } from '@/components/TypewriterEffect';
9 | import { FaReddit } from 'react-icons/fa';
10 | import {
11 | FaGithub,
12 | FaDiscord,
13 | FaProductHunt,
14 | FaXTwitter,
15 | FaHackerNews,
16 | FaInstagram,
17 | FaTiktok,
18 | FaYoutube
19 | } from 'react-icons/fa6';
20 | import {
21 | Lock, CreditCard, Moon
22 | } from 'lucide-react';
23 | import { motion, useScroll, useTransform } from 'framer-motion';
24 | import { useInView } from 'react-intersection-observer';
25 | import { useState } from 'react';
26 | import { useRouter } from 'next/navigation';
27 | import { Link as ScrollLink } from 'react-scroll';
28 | import { VideoModal } from '@/components/VideoModal';
29 |
30 | /* eslint-disable @typescript-eslint/no-unused-vars */
31 |
32 | // Update workflowSteps to be more generic
33 | const workflowSteps = [
34 | {
35 | title: "Step One",
36 | description: "First step of your workflow",
37 | preview:
38 | },
39 | {
40 | title: "Step Two",
41 | description: "Second step of your workflow",
42 | preview:
43 | },
44 | {
45 | title: "Step Three",
46 | description: "Third step of your workflow",
47 | preview:
48 | },
49 | {
50 | title: "Step Four",
51 | description: "Fourth step of your workflow",
52 | preview:
53 | }
54 | ];
55 |
56 | // Update platforms to be generic
57 | const platforms = [
58 | { name: 'Platform 1', icon: FaGithub },
59 | { name: 'Platform 2', icon: FaDiscord },
60 | { name: 'Platform 3', icon: FaReddit },
61 | { name: 'Platform 4', icon: FaProductHunt },
62 | { name: 'Platform 5', icon: FaXTwitter },
63 | { name: 'Platform 6', icon: FaHackerNews },
64 | { name: 'Platform 7', icon: FaInstagram },
65 | { name: 'Platform 8', icon: FaTiktok },
66 | { name: 'Platform 9', icon: FaYoutube }
67 | ];
68 |
69 | // Update workflowSections to be generic
70 | const workflowSections = [
71 | {
72 | id: "overview",
73 | title: "Overview",
74 | description: "Everything you need to build modern SaaS applications",
75 | bgColor: "bg-white dark:bg-[#0B1120]"
76 | },
77 | {
78 | id: "authentication",
79 | title: "Authentication",
80 | description: "Secure user authentication with multiple providers",
81 | bgColor: "bg-slate-50 dark:bg-[#0B1120]",
82 | metrics: [
83 | { label: "Auth Providers", value: "5+" },
84 | { label: "Setup Time", value: "2min" },
85 | { label: "Security", value: "A+" }
86 | ]
87 | },
88 | {
89 | id: "payments",
90 | title: "Payments",
91 | description: "Seamless payment integration with Stripe",
92 | bgColor: "bg-white dark:bg-[#0B1120]",
93 | metrics: [
94 | { label: "Integration", value: "1-Click" },
95 | { label: "Providers", value: "Stripe" },
96 | { label: "Setup Time", value: "5min" }
97 | ]
98 | },
99 | {
100 | id: "database",
101 | title: "Database",
102 | description: "Powerful database with Supabase integration",
103 | bgColor: "bg-slate-50 dark:bg-[#0B1120]",
104 | metrics: [
105 | { label: "Database", value: "PostgreSQL" },
106 | { label: "Real-time", value: "Yes" },
107 | { label: "Security", value: "RLS" }
108 | ]
109 | },
110 | {
111 | id: "features",
112 | title: "Features",
113 | description: "Additional features to enhance your application",
114 | bgColor: "bg-white dark:bg-[#0B1120]",
115 | metrics: [
116 | { label: "Dark Mode", value: "Built-in" },
117 | { label: "Components", value: "50+" },
118 | { label: "TypeScript", value: "100%" }
119 | ]
120 | },
121 | {
122 | id: "pricing",
123 | title: "Pricing",
124 | description: "Simple, transparent pricing for your needs",
125 | bgColor: "bg-slate-50 dark:bg-[#0B1120]"
126 | }
127 | ];
128 |
129 | // Custom Hook to create section progress values
130 | function useSectionProgressValues(numSections: number) {
131 | const { scrollYProgress } = useScroll();
132 |
133 | // Create all transforms at once, at the top level
134 | const section1Progress = useTransform(
135 | scrollYProgress,
136 | [0 / numSections, 1 / numSections],
137 | [0, 1]
138 | );
139 | const section2Progress = useTransform(
140 | scrollYProgress,
141 | [1 / numSections, 2 / numSections],
142 | [0, 1]
143 | );
144 | const section3Progress = useTransform(
145 | scrollYProgress,
146 | [2 / numSections, 3 / numSections],
147 | [0, 1]
148 | );
149 | const section4Progress = useTransform(
150 | scrollYProgress,
151 | [3 / numSections, 4 / numSections],
152 | [0, 1]
153 | );
154 |
155 | return [section1Progress, section2Progress, section3Progress, section4Progress];
156 | }
157 |
158 | // Feature cards data
159 | const featureCards = [
160 | {
161 | title: "Authentication",
162 | description: "Supabase auth with social providers",
163 | icon: ,
164 | bgGradient: "from-blue-500/10 to-purple-500/10"
165 | },
166 | {
167 | title: "Payments",
168 | description: "Stripe subscription management",
169 | icon: ,
170 | bgGradient: "from-green-500/10 to-emerald-500/10"
171 | },
172 | {
173 | title: "Dark Mode",
174 | description: "Built-in theme management",
175 | icon: ,
176 | bgGradient: "from-orange-500/10 to-red-500/10"
177 | }
178 | ];
179 |
180 | export default function LandingPage() {
181 | const { user } = useAuth();
182 | const { isInTrial } = useTrialStatus();
183 | const [activeSection, setActiveSection] = useState("overview");
184 | const sectionProgressValues = useSectionProgressValues(workflowSections.length);
185 |
186 | const router = useRouter();
187 |
188 | const [dashboardRef, inView] = useInView({
189 | triggerOnce: true,
190 | threshold: 0.1
191 | });
192 |
193 | const { scrollYProgress } = useScroll();
194 |
195 | const [isVideoModalOpen, setIsVideoModalOpen] = useState(false);
196 |
197 | return (
198 |