Category: Full Stack Software

  • Building a Full-Stack AI SaaS Platform

    Building a Full-Stack AI SaaS Platform

    This document provides a comprehensive tutorial on building a full-stack software-as-a-service (SaaS) platform powered by artificial intelligence. It guides users through integrating various AI models like conversation, code, image, music, and video generation using APIs such as OpenAI and Replicate AI. The tutorial also covers essential SaaS features, including implementing a free tier with usage limits, setting up subscription models with Stripe integration, handling authentication with Clerk, and developing a responsive user interface with Next.js, Tailwind CSS, and ChatCN UI components. Finally, it addresses deployment strategies to platforms like Vercel and optimizing API routes for performance.

    Genius: An AI SaaS Platform with Tiered Access

    The AI SaaS Platform described in the source is a full-stack, production-ready application built to offer various AI tools as a service. It is designed to include both a free tier and a monthly subscription tier, managed through Stripe.

    Key Features and AI Tools The platform, named “Genius,” provides five different AI tools:

    • Image Generation: Users can input a prompt (e.g., “a horse in Swiss Alps”) and specify the number of photos and resolution to generate images.
    • Video Generation: Users can provide a prompt (e.g., “clown fish swimming around a coral reef”) to generate videos. This feature uses Replicate AI, specifically the Zeroscope model, chosen for its faster generation time.
    • Conversation Model: This tool allows users to have conversations with an AI, such as asking questions like “What is the radius of the sun?”. This model utilizes OpenAI’s GPT-3.5-turbo.
    • Music Generation: Users can enter a prompt (e.g., “piano solo”) to generate audio files, which can then be played, have their volume or playback speed changed, or be downloaded. This tool also uses Replicate AI, specifically the Refusion model for audio generation.
    • Code Generation: Users can request code snippets using descriptive text (e.g., “a model using react Hooks and Tailwind” or “simple toggle button using react hooks”). The AI provides the code in markdown format with explanations and comments. Like the conversation model, this uses OpenAI but with a default instruction message to behave as a code generator.

    Monetization Model The platform implements a tiered access system:

    • Free Tier: Users on the free tier are limited to five generations across all AI models. Once this limit is reached, users are blocked from further AI usage.
    • Subscription Tier (Pro Plan): To continue using the AI tools after exhausting the free generations, users must upgrade to a monthly subscription, managed via Stripe. The Pro Plan provides unlimited AI generations. Subscribers can also manage or cancel their subscription from a dedicated billing page. The system differentiates between free and pro users to allow unlimited access for subscribed users and block free users once their limit is met. The subscription status is checked using Prisma and Stripe data, ensuring that only active subscriptions grant unlimited access.

    Technical Stack and Setup The platform is built with a modern web development stack:

    • Frameworks/Libraries: Next.js 13, React, and Tailwind CSS.
    • Styling: ChatCN UI is used as a styling library, providing pre-built components that are directly placed in the project’s components folder for full customization.
    • Backend & Database: Prisma is used as an ORM (Object-Relational Mapper) to interact with a MySQL database hosted on PlanetScale. A utility (Prisma db.ts) manages Prisma client instances to prevent issues during hot reloading in development.
    • Authentication: Clerk is integrated for user authentication (sign-in, sign-up). It supports various providers like Google and email. Clerk also provides UI components like the UserButton for logged-in users. Public routes are configured in middleware.ts to allow access for both logged-in and logged-out users to specific pages like the landing page and webhook endpoints.
    • Monetization Integration: Stripe is used for handling subscriptions, including checkout sessions and billing portals. Webhooks are configured to listen for Stripe events (e.g., checkout.session.completed, invoice.payment_succeeded) to manage user subscriptions in the database.
    • AI Integrations:
    • OpenAI: Used for conversation and code generation, accessed via API keys configured in the environment.
    • Replicate AI: Used for music and video generation, also requiring API keys.
    • Form Management: React Hook Form is utilized for form handling, with Zod for schema validation.
    • HTTP Requests: Axios is used for making HTTP requests to API routes.
    • Error Handling: React Hot Toast provides toast notifications for errors, offering better user feedback than just console logs.
    • Dynamic Text: Typewriter Effect library creates animated text for the landing page hero section.
    • Utilities: A cn utility (combining Tailwind Merge and CLSX) helps manage dynamic Tailwind CSS classes. An absoluteURL utility ensures Stripe redirects to the correct full URL.

    Customer Support The platform incorporates Crisp chat for customer support, allowing real-time communication between users and administrators.

    Deployment The application can be deployed using Vercel. During deployment, environment variables need to be correctly configured, including the NEXT_PUBLIC_APP_URL and Stripe webhook secrets. For hobby (free) Vercel plans, some AI models might experience timeouts due to longer cold starts or processing times, suggesting a need to upgrade to a larger plan or use Vercel AI SDK with Edge Network for better performance.

    Genius AI: Free Tier and Pro Upgrade System

    The AI SaaS platform, “Genius,” implements a free tier to allow users to experience its AI tools before requiring a paid subscription.

    Free Tier Limits:

    • Users on the free tier are limited to five generations across all AI models.
    • Once this limit of five free generations is reached, users are blocked from further AI usage. For example, if a user tries to generate another image after exhausting their five free generations, the system will block them.
    • The platform displays the user’s current usage, showing how many free generations they have used out of the total five (e.g., “two out of five of my three generations”). This counter is visible in the lower-left corner of the interface.
    • The number of free generations is configurable, set by a constant called Max_free_counts, which is initially set to five. This can be changed (e.g., to 100) if desired.

    Enforcement and Tracking:

    • The platform uses Prisma to track the user’s API limit. A UserAPIlimit model is created in the database, which includes an id, userId, and a count (defaulting to 0).
    • When a user makes an API call, the increaseAPIlimit function is triggered. This function checks if a UserAPIlimit entry exists for the user. If it does, the count is incremented. If not, a new entry is created with a count of one.
    • The checkAPIlimit function determines if a user has exceeded their free limit. It fetches the user’s UserAPIlimit from the database. If no record exists, or if the current count is less than Max_free_counts, it returns true (meaning they can still use the app). If the user has surpassed the free count, it returns false, indicating that the user should be blocked.
    • This check is implemented in the API routes for all AI models (conversation, code, image, music, video generation). Before processing a request, the system checks if the freeTrial has expired (i.e., checkAPIlimit returns false) and if the user is not a Pro subscriber. If both conditions are met, a 403 Forbidden status is returned, signaling that the free trial has expired. If the request proceeds successfully and the user is not a Pro subscriber, the increaseAPIlimit function is called.
    • The front-end detects the 403 error and triggers a “Pro Model” dialog, prompting the user to upgrade.

    User Interface for Limits:

    • A “Free Counter” component is displayed in the sidebar, showing the user’s current API limit count out of the Max_free_counts (e.g., “0 out of 5 free generations”).
    • This counter also includes a progress bar that visually represents the usage.
    • The counter dynamically updates by rehydrating server components when router.refresh() is called after an AI generation.
    • If a user is subscribed to a Pro Plan, this free counter is hidden, as they have unlimited generations.

    Upgrading to Pro Plan:

    • Users are prompted to upgrade to a monthly subscription (Pro Plan) via Stripe if they hit their free generation limit.
    • Clicking an “upgrade” button (found on the blocking message or directly in the sidebar) redirects them to the Stripe checkout page.
    • The Pro Plan grants unlimited AI generations.

    Genius AI Platform: Essential API Integrations

    The AI SaaS platform, “Genius,” integrates with several external APIs and services to provide its functionality, handle user authentication, manage subscriptions, and store data. These integrations are crucial for the platform’s full-stack and production-ready capabilities.

    Here’s a discussion of the key API integrations:

    1. OpenAI API

    • Purpose: OpenAI’s API is used for two of the platform’s core AI tools: the Conversation Model and Code Generation.
    • Integration Details:
    • Authentication: Users need to create an OpenAI account and obtain an API key. This API key is stored as an environment variable (OPENAI_API_KEY) in the project.
    • Usage Tracking: Users can monitor their OpenAI usage in their personal account dashboard, where a free tier (typically $5 for the first three months) is often available for new users. It’s recommended to set hard and soft spending limits to prevent overspending.
    • Conversation Model: This tool utilizes OpenAI’s GPT-3.5-turbo model. It is designed to allow free-form conversational queries.
    • Code Generation: This tool also uses OpenAI, but with a specific “instruction message” role set to “system”. This instructs the AI to behave as a code generator, responding only in markdown code snippets with comments for explanations.
    • API Route: A dedicated API route (/api/conversation and /api/code) handles requests to OpenAI, checking for user authentication, API key configuration, and the presence of messages/prompts before making the call. The response from OpenAI is then returned to the frontend.

    2. Replicate AI

    • Purpose: Replicate AI is integrated for the Video Generation and Music Generation tools.
    • Integration Details:
    • Authentication: Users need to create a Replicate account and obtain an API token, which is stored as REPLICATE_API_TOKEN in the environment variables. Like OpenAI, Replicate offers a free tier, but users should be mindful of usage to avoid exceeding limits. Spending limits can also be set for paid usage.
    • Music Generation: This tool uses the Refusion model from Replicate for audio generation. It accepts a text prompt (e.g., “piano solo”) and returns an audio file.
    • Video Generation: This tool uses the Zeroscope model, chosen for its faster generation time compared to other video models on Replicate. It converts a text prompt (e.g., “clown fish swimming around a coral reef”) into a video.
    • Cold Start/Performance: Replicate models can experience “cold starts,” meaning the first generation might take a long time (e.g., up to a minute or more for music generation, sometimes longer), but subsequent generations are typically faster.
    • API Route: Dedicated API routes (/api/music and /api/video) manage the interaction with Replicate, including passing prompts and handling responses.

    3. Clerk (Authentication)

    • Purpose: Clerk is used for handling user authentication, including sign-up and sign-in.
    • Integration Details:
    • Setup: Requires setting up NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY and CLERK_SECRET_KEY environment variables.
    • Provider: The ClerkProvider wraps the entire application to enable authentication context.
    • Middleware: A middleware.ts file is configured to protect routes, automatically redirecting unauthenticated users to the sign-in page. Public routes (like the landing page and webhook endpoints) can be explicitly defined to be accessible by all users.
    • UI Components: Clerk provides ready-to-use UI components like UserButton for logged-in user actions (e.g., sign-out). It also offers pre-built sign-in and sign-up pages that can be integrated with minimal code.
    • User Data: User information, such as userId and emailAddresses, is accessible through Clerk’s auth() and currentUser() functions, which are used for API limit tracking and Stripe customer creation.

    4. Stripe (Subscription Management)

    • Purpose: Stripe is integrated to manage the platform’s monthly subscription tier, including processing payments and handling billing.
    • Integration Details:
    • API Key: Requires a STRIPE_API_KEY environment variable.
    • Prisma Model: A UserSubscription Prisma model tracks subscription details (Stripe customer ID, subscription ID, price ID, current period end) for each user.
    • Checkout Sessions: When a free user upgrades, Stripe’s checkout.sessions.create is used to generate a checkout page. This session includes product details (Genius Pro, unlimited AI generations, $20/month), return URLs, and crucial metadata to link the subscription back to the userId in the database after successful payment.
    • Billing Portal: For existing subscribers, Stripe’s billingPortal.sessions.create redirects them to a dedicated page where they can manage or cancel their subscription.
    • Webhooks: A critical part of the Stripe integration involves webhooks. The platform listens for specific Stripe events (e.g., checkout.session.completed and invoice.payment_succeeded). These events trigger updates to the UserSubscription model in the database, ensuring the user’s subscription status is accurately reflected.
    • Local Development: For local testing, the Stripe CLI is used to forward webhook events to the local API endpoint (/api/webhook), requiring a STRIPE_WEBHOOK_SECRET environment variable. This webhook route must be a public route in the application’s middleware.
    • Deployment: After deployment, the webhook endpoint must be updated in the Stripe dashboard to the live URL.

    5. Crisp (Customer Support)

    • Purpose: Crisp chat is integrated to provide real-time customer support.
    • Integration Details:
    • SDK: The crisp-sdk-web package is installed.
    • Configuration: The Crisp.configure function is used within a client-side useEffect hook to initialize Crisp with a specific CRISP_WEBSITE_ID.
    • Provider: A CrispProvider component wraps the Crisp chat initialization and is added to the application’s main layout. This ensures the chat widget is available on the platform’s interface. Messages sent from the front-end appear in the Crisp dashboard for administrators to respond.

    6. Prisma (ORM) & PlanetScale (Database)

    • Purpose: Prisma serves as an ORM to interact with a MySQL database hosted on PlanetScale, handling data storage for user API limits and subscriptions.
    • Integration Details:
    • Setup: Prisma is initialized in the project, creating a schema.prisma file. The database provider is set to MySQL, and the DATABASE_URL from PlanetScale is configured in the .env file.
    • Models: Custom Prisma models like UserAPIlimit and UserSubscription are defined in schema.prisma to structure the database tables.
    • Database Synchronization: npx prisma db push is used to synchronize the schema with the PlanetScale database, and npx prisma generate generates the Prisma client and types for use in the application. npx prisma studio can be run to view and manage data in the database locally.
    • API Limit Tracking: Prisma is used to track the number of API generations for free-tier users. Functions like increaseAPIlimit and checkAPIlimit interact with the UserAPIlimit model to update and retrieve usage counts.
    • Subscription Status: The checkSubscription utility uses Prisma to query the UserSubscription model and determine if a user has an active Pro plan.
    • Prisma db.ts Utility: A specific utility file (lib/Prisma db.ts) is created to manage Prisma client instances, preventing multiple initializations during hot reloading in development.

    These integrations collectively enable the AI SaaS platform to offer diverse AI tools, manage user access and payments, and provide customer support, all while maintaining a coherent and scalable architecture.

    Genius AI: Full-Stack UI Development with Next.js & React

    The AI SaaS platform, “Genius,” emphasizes a full-stack, production-ready approach to UI development, incorporating modern frameworks, responsive design, and component-based architecture.

    Here’s a comprehensive discussion of its UI development:

    1. Core Technologies and Design Principles:

    • Next.js 13 & React: The platform is built using Next.js 13 with React, leveraging its app router for efficient routing and server components for data fetching and performance.
    • Tailwind CSS: Tailwind CSS is used for styling, providing utility-first classes for rapid UI development and responsiveness.
    • Component-Based Architecture: The UI is structured into reusable components, promoting modularity and maintainability. Crucially, the chosen UI library, chat cnui, allows developers to “own” the components by keeping their code directly in the project’s components folder, enabling deep customization beyond typical component libraries.
    • Responsiveness: Tailwind’s responsive prefixes (e.g., md:, lg:, sm:, xl:) are extensively used to adapt layouts, text sizes, and component visibility across various screen sizes, ensuring a consistent user experience on both mobile and desktop devices.

    2. Key UI Components and Their Implementation:

    • Buttons: The Button component from chat cnui is used throughout the application. It supports various variants (e.g., default, destructive, outline, secondary, ghost, link) and sizes (e.g., large, icon). A custom premium variant is added for a distinct gradient appearance for upgrade actions.
    • Forms & Inputs: react-hook-form is integrated with chat cnui’s Form component for robust form management and validation using Zod schemas. This includes FormField, FormItem, FormControl, and Input components.
    • Cards: The Card component from chat cnui is used for displaying AI tools on the dashboard and for testimonials on the landing page, as well as for generated images. They include CardHeader, CardTitle, CardContent, and CardFooter elements.
    • Dialogs (Modals): The Dialog component from chat cnui is used for the “Pro Model” upgrade prompt. It includes DialogContent, DialogHeader, DialogTitle, DialogDescription, and DialogFooter.
    • Avatars: UserAvatar (showing user’s Clerk profile image or initials) and BotAvatar (showing the Genius logo) components are used in conversation and code generation UIs to distinguish speakers. They leverage chat cnui’s Avatar component.
    • Progress Bars: The Progress component from chat cnui visually represents the user’s free generation limit in the sidebar.
    • Badges: A Badge component from chat cnui is used in the Pro Model dialog, with a custom premium variant mimicking the gradient style.
    • Selects: For image generation, chat cnui’s Select component is used to choose the number of photos and resolution, including SelectTrigger, SelectValue, SelectContent, and SelectItem.
    • Headings: A reusable Heading component is created for all AI tools, accepting title, description, icon, iconColor, and bgColor props for consistent styling.
    • Empty and Loading States: Dedicated Empty and Loader components are displayed conditionally when there are no results or when an AI generation is in progress, improving user feedback. These include dynamic text and a spinning logo for loading.

    3. Styling and Theming:

    • Tailwind Merge & CLSX (CN Utility): The cn utility (from chat cnui’s lib/utils) is used to dynamically merge Tailwind CSS classes, preventing conflicts and allowing for conditional styling based on component props or state.
    • Global Styles: Basic HTML and body height are set in globals.css.
    • Theming: globals.css is also used to modify Tailwind’s –primary CSS variable, changing the accent color (e.g., from default to light purple) for elements like buttons and progress bars across the application.
    • Custom Fonts: The Montserrat font from next/font/google is used for specific UI elements like the app title, applied dynamically via the cn utility.
    • Icons: Lucid React is the primary icon library, integrated seamlessly with chat cnui and used for navigation, tool representation, and action buttons.

    4. Layout and Structure:

    • Next.js App Router & Route Groups: The application’s routes are organized using Next.js 13’s app router and route groups (folders enclosed in parentheses like (dashboard), (auth), (landing)) which help structure files without affecting the URL path. This allows for dedicated layouts for different sections of the app (e.g., (dashboard)/layout.tsx for protected routes, (auth)/layout.tsx for authentication pages, (landing)/layout.tsx for the public landing page).
    • Sidebar Navigation: A responsive sidebar is implemented with navigation links generated from an array of routes, displaying icons, labels, and dynamically highlighting the active page using usePathname.
    • Mobile Sidebar (Drawer): For smaller screens, the sidebar transforms into a “sheet” (drawer) component that slides out from the left, triggered by a menu icon in the navbar.
    • Navbar: A separate Navbar component is positioned at the top, containing the mobile sidebar toggle and the user profile button.
    • Global Layouts: ClerkProvider, ModelProvider, and ToasterProvider are wrapped around the main application layout (app/layout.tsx) to provide global authentication context, modal functionality, and toast notifications respectively.

    5. Dynamic UI and State Management:

    • Conditional Rendering: UI elements are dynamically rendered based on various conditions, such as user authentication status, subscription status (Pro vs. Free), presence of generated content, or loading states.
    • zustand for Global State: zustand is used for lightweight global state management, specifically for controlling the visibility (open/close) of the “Pro Model” dialog across different components.
    • Server Component Rehydration: The router.refresh() function (from next/navigation) is strategically called after successful API calls (e.g., AI generations) to rehydrate server components and update UI elements like the free generation counter, ensuring data consistency without full page reloads.

    6. Third-Party UI Integrations:

    • Clerk (Authentication UI): Clerk provides pre-built, customizable UI components and pages for sign-up, sign-in, and user management (UserButton). It handles redirects and integrates seamlessly with Next.js middleware for route protection.
    • Crisp (Customer Support Chat): The crisp-sdk-web is integrated to add a live chat widget for customer support, allowing real-time communication that appears in the Crisp dashboard.
    • react-hot-toast (Notifications): Used for displaying user-friendly toast notifications for errors or success messages throughout the application.
    • typewriter-effect (Landing Page Animation): Provides an animated typewriter effect for text on the landing page hero section, highlighting the different AI capabilities.

    7. Development Workflow and Customization:

    • The setup process involves npx create next app with Tailwind and TypeScript, followed by npx chat cn-ui init for chat cnui configuration.
    • The ability to directly modify chat cnui components (e.g., changing variant names in button.tsx) offers unparalleled customization and control over the UI.

    Genius AI SaaS: Stripe and Prisma Payments

    The “Genius” AI SaaS platform incorporates a comprehensive payment system using Stripe to manage free and monthly subscription tiers, along with API limits enforced via Prisma.

    Here’s a detailed discussion of its payment system:

    1. Subscription Tiers and Overview

    • The platform offers a free tier and a monthly subscription tier.
    • Users on the free tier are limited to five AI generations. After exceeding this limit, they are blocked from using the AI models and prompted to upgrade.
    • The subscription model enables unlimited AI generations.
    • The system uses Stripe for payment processing and Crisp for customer support.

    2. Core Technologies and Setup

    • Stripe: The platform integrates with Stripe for handling subscriptions and payments.
    • Stripe API keys (publishable and secret) are configured in the .env file (STRIPE_API_KEY).
    • The stripe npm package is installed and a stripe.ts utility file is created to initialize the Stripe client with the API key, 2022-11-15 API version, and TypeScript enabled.
    • Prisma: Used for database interaction, specifically for managing user subscriptions and API limits.
    • PlanetScale: A MySQL database service (PlanetScale) is used, with its connection string configured in .env (DATABASE_URL).
    • The Prisma schema (schema.prisma) is updated to define UserAPILimit and UserSubscription models.
    • npx Prisma generate and npx Prisma DB push commands are run to sync the schema with the database and generate client types.
    • Prisma Studio (npx Prisma Studio) can be used to view and manage database records.

    3. Database Models for Payment Logic

    • UserAPILimit Model: Tracks the number of AI generations for each user.
    • Fields include id, userId (unique), count (integer, default 0), createdAt, and updatedAt.
    • UserSubscription Model: Stores details about a user’s Stripe subscription.
    • Fields include id, userId (unique), stripeCustomerId (optional, unique), stripeSubscriptionId (optional, unique), stripePriceId (optional), and stripeCurrentPeriodEnd (optional datetime).
    • @map is used for stripe_customer_id, stripe_subscription_id, stripe_price_id, and stripe_current_period_end to match Stripe’s snake_case naming convention.

    4. API Limit Enforcement (Backend)

    • increaseAPILimit Utility (lib/api-limit.ts):Increments the count for a user in the UserAPILimit model.
    • If a user’s record doesn’t exist, it creates one with a count of 1.
    • This is called after a successful AI generation if the user is not a Pro subscriber.
    • checkAPILimit Utility (lib/api-limit.ts):Checks if a user has exceeded the MAX_FREE_COUNTS (set to 5).
    • Returns false if the limit is exceeded, otherwise true.
    • API Route Integration (/api/conversation, /api/image, etc.):Before processing an AI generation request, API routes check checkAPILimit and checkSubscription.
    • If the user is not a Pro subscriber (!isPro) AND checkAPILimit returns false (limit reached), the API returns a NextResponse with “Free trial has expired” message and a 403 Forbidden status. This 403 status is crucial for the frontend to trigger the “Pro Model” dialog.
    • increaseAPILimit is only called if the user is not a Pro subscriber.

    5. Stripe API Route (/api/stripe)

    • This is a GET endpoint that handles user redirection to Stripe’s billing portal or checkout page.
    • Functionality:Check existing subscription: It queries the UserSubscription model for the current user’s active subscription.
    • Billing Portal Redirection: If userSubscription and stripeCustomerId exist, it creates a Stripe Billing Portal session. The return_url is set to the application’s /settings page.
    • Checkout Session Creation: If no active subscription exists, it creates a new Stripe Checkout Session.
    • success_url and cancel_url are both set to the /settings page.
    • payment_method_types is set to card.
    • mode is subscription.
    • customer_email is taken from Clerk’s user.email_addresses.
    • line_items define the product: “Genius Pro”, “Unlimited AI Generations”, a unit amount of 2000 (for $20.00), and recurring set to monthly.
    • Crucially, metadata.userId is passed. This links the Stripe session to the user’s ID, which is vital for the webhook to identify the subscriber after successful payment.
    • Returns a JSON response containing the Stripe session url for client-side redirection.
    • absoluteURL Utility: A helper function absoluteURL constructs full URLs (e.g., http://localhost:3000/settings) because Stripe requires absolute URLs for redirects. This URL is configured via NEXT_PUBLIC_APP_URL in .env.

    6. Stripe Webhook (/api/webhook)

    • This is a POST endpoint designed to receive events from Stripe.
    • Security: Verifies the webhook signature using stripe.webhooks.constructEvent and a STRIPE_WEBHOOK_SECRET.
    • Local Testing: The Stripe CLI (stripe listen –forward-to localhost:3000/api/webhook) is used to forward webhook events during local development. The secret generated by stripe listen is then saved as STRIPE_WEBHOOK_SECRET in .env.
    • Public Route: The /api/webhook route must be marked as public in middleware.ts so Stripe can access it without authentication.
    • Event Handling: It listens for two main Stripe events:
    • checkout.session.completed: Triggered when a user successfully completes a checkout session (first-time subscription).
    • It retrieves the subscription details from Stripe.
    • It extracts the userId from the session’s metadata (emphasizing its importance).
    • A new UserSubscription record is created in Prisma with all relevant Stripe IDs and the subscription period end date.
    • invoice.payment_succeeded: Triggered when a recurring invoice payment is successful (subscription renewal/upgrade).
    • It retrieves the updated subscription details from Stripe.
    • It updates the existing UserSubscription record in Prisma, specifically updating stripePriceId and stripeCurrentPeriodEnd.
    • It returns a 200 OK status to Stripe after processing an event.

    7. Frontend Interaction

    • ProModel Component (components/Pro-model.tsx):The “Upgrade” button’s onClick handler makes an axios.get call to /api/stripe.
    • Upon receiving the Stripe session URL, it redirects the user using window.location.href.
    • Includes a loading state to prevent multiple clicks.
    • Opened when a 403 error is returned from AI API calls (due to free trial expiry).
    • SubscriptionButton Component (components/subscription-button.tsx):Used on the /settings page.
    • It checks the isPro status (using checkSubscription utility) to conditionally display “Manage Subscription” (if pro) or “Upgrade” (if not pro).
    • The button’s variant changes to premium (gradient style) if not pro.
    • Its onClick also calls /api/stripe, which intelligently redirects to either the billing portal or checkout based on the user’s subscription status.
    • Addresses a common Stripe test mode error by requiring activation of “customer portal settings” in the Stripe dashboard.

    8. Subscription Status Check (checkSubscription Utility)

    • lib/subscription.ts: An asynchronous utility function that determines if a user is currently a Pro subscriber.
    • Logic:Retrieves userId from Clerk.
    • Fetches the UserSubscription record from Prisma based on userId.
    • Checks if the stripePriceId exists and if stripeCurrentPeriodEnd (plus a day’s grace period) is greater than the current date (Date.now()).
    • Returns true if subscribed and valid, false otherwise.
    • This utility is used in server components (like the dashboard layout and settings page) to render UI elements conditionally.

    9. UI Indicators and Styling

    • Free Counter: A FreeCounter component displays “X out of 5 free generations” in the sidebar. This counter is dynamically updated by router.refresh() which rehydrates server components after an API call. It is hidden if the user is a Pro subscriber.
    • Progress Bar: A Progress component visually represents the free generation limit.
    • “Premium” Button Variant: A custom premium variant is added to the Button and Badge components in chat cnui to create a distinct gradient style for upgrade actions.
    • Theming: The primary accent color (e.g., light purple) for buttons and progress bars can be globally changed by modifying a CSS variable in globals.css.

    10. AI Model Costs and Limits

    • OpenAI and Replicate AI offer free tiers for their models.
    • During intensive testing, the total cost for using OpenAI and Replicate AI was around $14.
    • Stripe allows setting “hard limits” and “soft limits” on spending to prevent overspending.

    11. Deployment Considerations

    • When deploying, the Stripe webhook URL must be updated to the production URL (/api/webhook) and configured in the Stripe dashboard with relevant events (checkout.session.completed, invoice.payment.succeeded).
    • The STRIPE_WEBHOOK_SECRET environment variable needs to be updated with the production secret from Stripe.
    • The NEXT_PUBLIC_APP_URL must be updated to the deployed application’s URL.
    • The package.json includes a postinstall script (Prisma generate) for Vercel deployment.
    • On Vercel’s free hobby plan, some AI generations (especially video and music using Replicate AI) might experience timeouts due to cold starts or long processing times. Upgrading to a higher plan or using Vercel AI SDK (which uses edge networks for longer timeouts and streaming) is suggested for these cases.
    Build a SaaS AI Platform with Next.js 13, React, Tailwind, Prisma, Stripe Full Tutorial 2023

    By Amjad Izhar
    Contact: amjad.izhar@gmail.com
    https://amjadizhar.blog