Building a Production RAG System: Part 7 - Production Features, Subscriptions, and Deployment

Series: Building a Production-Ready Textbook Q&A System with RAG
Part: 7 of 7 Read Time: 30 minutes Level: Intermediate to Advanced

What We'll Build in This Part

Estimated time: 3-4 hours

Subscription Tiers

Free

$0
  • 50 questions/day
  • 3 documents
  • Text search only

Unlimited

$29.99/mo
  • Unlimited questions
  • Unlimited documents
  • Image support
  • API access

Step 1: Set Up Stripe

Install Stripe SDK

npm install stripe @stripe/stripe-js

Add Environment Variables

# .env.local
STRIPE_SECRET_KEY=sk_test_...
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...

# Stripe Price IDs
STRIPE_PRICE_PRO=price_...
STRIPE_PRICE_UNLIMITED=price_...

Step 2: Create Stripe Client

Create lib/stripe/client.ts:

import Stripe from 'stripe'

export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
  apiVersion: '2024-11-20.acacia',
  typescript: true,
})

Step 3: Create Checkout API

Create app/api/stripe/create-checkout/route.ts:

import { stripe } from '@/lib/stripe/client'

export async function POST(request: NextRequest) {
  const { priceId } = await request.json()
  const supabase = createClient()
  const { data: { user } } = await supabase.auth.getUser()

  if (!user) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
  }

  // Create checkout session
  const session = await stripe.checkout.sessions.create({
    payment_method_types: ['card'],
    line_items: [{ price: priceId, quantity: 1 }],
    mode: 'subscription',
    success_url: `${request.nextUrl.origin}/dashboard`,
    cancel_url: `${request.nextUrl.origin}/pricing`,
  })

  return NextResponse.json({ sessionId: session.id })
}

Step 4: Handle Stripe Webhooks

Create app/api/stripe/webhook/route.ts:

export async function POST(request: NextRequest) {
  const body = await request.text()
  const signature = headers().get('stripe-signature')!

  const event = stripe.webhooks.constructEvent(
    body,
    signature,
    process.env.STRIPE_WEBHOOK_SECRET!
  )

  switch (event.type) {
    case 'checkout.session.completed':
      await handleCheckoutCompleted(event.data.object)
      break

    case 'customer.subscription.updated':
      await handleSubscriptionUpdated(event.data.object)
      break

    case 'customer.subscription.deleted':
      await handleSubscriptionDeleted(event.data.object)
      break
  }

  return NextResponse.json({ received: true })
}

Step 5: Implement Rate Limiting

Create lib/rate-limit.ts:

const LIMITS = {
  free: 50,
  pro: 500,
  unlimited: 999999,
}

export async function checkRateLimit(userId: string) {
  const supabase = createClient()

  // Get subscription tier
  const { data: profile } = await supabase
    .from('profiles')
    .select('subscription_tier')
    .eq('id', userId)
    .single()

  const tier = profile?.subscription_tier || 'free'
  const limit = LIMITS[tier]

  // Get today's usage
  const today = new Date()
  today.setHours(0, 0, 0, 0)

  const { count } = await supabase
    .from('usage_logs')
    .select('*', { count: 'exact', head: true })
    .eq('user_id', userId)
    .gte('created_at', today.toISOString())

  const remaining = Math.max(0, limit - (count || 0))
  const allowed = (count || 0) < limit

  return { allowed, remaining, limit }
}
Usage in Chat API:
const rateLimit = await checkRateLimit(user.id)

if (!rateLimit.allowed) {
  return NextResponse.json(
    { error: 'Daily limit reached' },
    { status: 429 }
  )
}

Step 6: Create Analytics Dashboard

Create app/dashboard/analytics/page.tsx showing:

Cost Breakdown & Optimization

Monthly Costs (100 active users)

Infrastructure:

AI APIs (50 queries/user/day):

Revenue:

Initial margin: $1,099.30 - $2,265 = -$1,165.70

Cost Optimization Strategy

  1. Reduce context window: Use 3 chunks instead of 5 (40% savings)
  2. Use cheaper models: Claude Haiku for simple queries (10x cheaper)
  3. Implement caching: Cache common questions
Optimized cost: $680/mo (70% reduction)
New margin: $1,099.30 - $680 = +$419.30

Step 7: Deploy to Vercel

Prepare for Deployment

  1. Push code to GitHub
  2. Create Vercel account
  3. Import project from GitHub
  4. Add all environment variables
  5. Click Deploy

Set Up Stripe Webhooks

  1. In Stripe Dashboard → Webhooks
  2. Add endpoint: https://your-domain.vercel.app/api/stripe/webhook
  3. Select events: checkout.session.completed, customer.subscription.*
  4. Copy webhook secret to Vercel env vars

Production Checklist

  • Security - Environment variables, RLS policies, API keys
  • Performance - Database indexes, caching, CDN
  • Monitoring - Error tracking, analytics, uptime
  • User Experience - Loading states, mobile responsive
  • Business - Stripe production mode, terms, privacy policy
  • What We Accomplished

    Series Recap

    Over this 7-part series, we built a complete production RAG system:

    Part Topic
    1 Understanding RAG architecture
    2 Next.js + Supabase + Auth
    3 Vector embeddings + pgvector
    4 PDF ingestion pipeline
    5 RAG query pipeline
    6 Vision support for diagrams
    7 Production features + deployment

    Final stats:

    🎉 Congratulations! 🎉

    You've built a complete, production-ready RAG system from scratch!

    This is real, working code that can:

    What's Next?

    1. Deploy your app
    2. Share it with beta users
    3. Gather feedback
    4. Iterate and improve
    5. Launch publicly!

    Next Enhancements to Consider

    1. Team/organization support
    2. Document sharing
    3. Conversation history
    4. Export to Notion/Obsidian
    5. Mobile app (React Native)
    6. Chrome extension
    7. API for third-party integrations
    8. White-label solution
    Tags:
    #Production #Stripe #Deployment #Subscriptions #Monetization #Scale