Most developers wanting to build an AI SaaS fall into the same trap: they buy a bloated \$199-\$299 boilerplate that is essentially a heavy wrapper around legacy OpenAI APIs.
The result?
- High latency: 800ms+ Time-To-First-Token (TTFT).
- Runaway token costs: You pay from day 1 before validating product-market fit.
- Vendor lock-in: Hardcoded API endpoints that break as LLM models evolve.
Today, we're building a production-grade, zero-operating-cost AI SaaS architecture using Next.js 14 App Router, Google's official @google/genai SDK, Gemini 2.5 Flash, and Stripe Checkout.
⚡ Why Gemini 2.5 Flash for Micro-SaaS?
- Sub-200ms TTFT: Streaming responses feel instantaneous in the UI.
- 1 Million Token Context Window: Handle massive customer documents, chat history, or codebases without expensive chunking databases.
- 100% Free-Tier Architecture: Google AI Studio provides high-rate-limit free tiers, meaning you can test, launch, and validate with \$0/month operating cost.
- Native Multimodal Vision: Extract invoices, receipts, and images natively without fragile OCR/Tesseract libraries.
🏗️ Architecture Overview
Our stack is completely decoupled and modular:
- Frontend & API: Next.js 14 (App Router, Server Actions, Tailwind CSS).
- AI Engine: Google Gen AI SDK (
@google/genai) with dynamic model selection viaGEMINI_MODELenvironment variable. - Billing: Stripe Checkout Session flow with cryptographic webhook signature verification (
constructEvent). - Hosting: Vercel / GitHub Pages ($0/mo tier).
🛠️ Step 1: Initialize the Modern Gemini SDK
Install the official Google Gen AI SDK:
npm install @google/genai Configure your decoupled Gemini client in lib/gemini.ts. Notice the dynamic model fallback to ensure your app automatically scales to Gemini 3.x without code rewrites:
// lib/gemini.ts import { GoogleGenAI } from "@google/genai"; const apiKey = process.env.GEMINI_API_KEY || ""; export const ai = new GoogleGenAI({ apiKey }); export async function generateAgentResponse( prompt: string, systemInstruction?: string, modelName: string = process.env.GEMINI_MODEL || "gemini-2.5-flash" ) { if (!apiKey) { throw new Error("GEMINI_API_KEY is not set."); } const response = await ai.models.generateContent({ model: modelName, contents: prompt, config: { systemInstruction: systemInstruction || "You are an intelligent business assistant.", temperature: 0.7, }, }); return response.text; } export async function streamAgentResponse( prompt: string, systemInstruction?: string, modelName: string = process.env.GEMINI_MODEL || "gemini-2.5-flash" ) { if (!apiKey) { throw new Error("GEMINI_API_KEY is not set."); } return await ai.models.generateContentStream({ model: modelName, contents: prompt, config: { systemInstruction: systemInstruction || "You are an intelligent business assistant.", temperature: 0.7, }, }); } 🚀 Step 2: Next.js 14 Edge-Ready API Route
Create an API route in app/api/agent/route.ts that handles client requests with error handling:
// app/api/agent/route.ts import { NextResponse } from "next/server"; import { generateAgentResponse } from "@/lib/gemini"; export async function POST(req: Request) { try { const { prompt, systemPrompt } = await req.json(); if (!prompt) { return NextResponse.json({ error: "Missing prompt" }, { status: 400 }); } const reply = await generateAgentResponse( prompt, systemPrompt || "You are an automated SaaS copilot. Answer concisely." ); return NextResponse.json({ result: reply }); } catch (error: any) { console.error("Gemini Agent API Error:", error); return NextResponse.json( { error: error.message || "Failed to generate AI response" }, { status: 500 } ); } } 💳 Step 3: Production Stripe Checkout & Webhook Security
Never trust client-side confirmations for billing! Here is how to create a Checkout Session and cryptographically verify Stripe webhooks using the raw request body:
Checkout API (app/api/checkout/route.ts)
import { NextResponse } from "next/server"; import Stripe from "stripe"; const stripe = new Stripe(process.env.STRIPE_SECRET_KEY || "", { apiVersion: "2023-10-16" as any, }); export async function POST(req: Request) { try { const session = await stripe.checkout.sessions.create({ payment_method_types: ["card"], line_items: [ { price_data: { currency: "usd", product_data: { name: "Pro AI Tier Subscription", description: "Unlimited AI Generations + B2B Document Extraction", }, unit_amount: 2900, // $29.00 }, quantity: 1, }, ], mode: "payment", success_url: `${process.env.NEXT_PUBLIC_BASE_URL || "http://localhost:3000"}/dashboard?session_id={CHECKOUT_SESSION_ID}`, cancel_url: `${process.env.NEXT_PUBLIC_BASE_URL || "http://localhost:3000"}/dashboard`, }); return NextResponse.json({ url: session.url }); } catch (err: any) { return NextResponse.json({ error: err.message }, { status: 500 }); } } Cryptographic Webhook Handler (app/api/webhook/route.ts)
import { NextResponse } from "next/server"; import Stripe from "stripe"; const stripe = new Stripe(process.env.STRIPE_SECRET_KEY || "", { apiVersion: "2023-10-16" as any, }); export async function POST(req: Request) { const body = await req.text(); const signature = req.headers.get("stripe-signature") || ""; let event: Stripe.Event; try { event = stripe.webhooks.constructEvent( body, signature, process.env.STRIPE_WEBHOOK_SECRET || "" ); } catch (err: any) { console.error("Webhook signature verification failed:", err.message); return new NextResponse(`Webhook Error: ${err.message}`, { status: 400 }); } if (event.type === "checkout.session.completed") { const session = event.data.object as Stripe.Checkout.Session; console.log("Payment successful for customer:", session.customer_details?.email); // Provision license or update database } return NextResponse.json({ received: true }); } 📄 Bonus: B2B Multimodal Document Parsing in 10 Lines
Need to extract vendor invoices, tax IDs, and totals from PDF/image uploads? With Gemini 2.5 Vision, you don't need Tesseract or heavy OCR servers.
Here is the Python FastAPI service using strict Pydantic V2 schemas:
from google import genai from pydantic import BaseModel, Field class InvoiceExtraction(BaseModel): vendor_name: str = Field(description="Name of the issuer or company") vat_number: str = Field(description="Tax ID / VAT number / Partita IVA") total_amount: float = Field(description="Final invoice balance") currency: str = Field(description="Currency symbol or code (USD, EUR)") client = genai.Client() def extract_invoice(image_bytes: bytes, mime_type: str = "image/jpeg") -> InvoiceExtraction: response = client.models.generate_content( model="gemini-2.5-flash", contents=[ genai.types.Part.from_bytes(data=image_bytes, mime_type=mime_type), "Extract structured invoice metadata strictly adhering to the schema." ], config=genai.types.GenerateContentConfig( response_mime_type="application/json", response_schema=InvoiceExtraction, ) ) return InvoiceExtraction.model_validate_json(response.text) 🎮 Try the Interactive Playground & Source Code
We built and open-sourced an entire suite covering these 3 decoupled modules:
- Next.js 14 SaaS Starter with dark-mode UI and SSE streaming.
- B2B Invoice & Document Parser with zero OCR dependencies.
- Customer Support Triage Bot with sentiment analysis and auto-drafting.
- 🌐 Live Interactive In-Browser Demo: https://skappax.github.io/gemini-ai-suite/
- 💻 Open-Source GitHub Repository: https://github.com/skappax/gemini-ai-suite
Feel free to star the repo or clone the starter template for your next AI venture!