Payments
Stripe integration, plan structure, and the webhook flow.
Plan Structure
Three plans ship out of the box in config/subscriptions.ts. Replace them with your own SaaS pricing before launch. Payments are processed via Stripe — you'll need a Stripe account and API keys.
| Plan | Monthly | Yearly | Daily Requests | Monthly Requests | Max Tokens/Request |
|---|---|---|---|---|---|
| Free | $0 | $0 | 5 | 150 | 12,000 |
| Pro | $19 | $199 | 50 | 1,000 | 25,000 |
| Business | $35 | $336 | 150 | 3,000 | 40,000 |
Each plan entry in pricingData has a usageLimits object that drives rate limiting:
usageLimits: {
dailyRequests: 50,
monthlyRequests: 1000,
maxTokensPerRequest: 25000,
maxImageSizeMB: 2,
maxResolution: 2000,
}The Stripe Client
Import the Stripe client from lib/stripe.ts. See the Stripe API reference for all available methods.
import { stripe } from "@/lib/stripe";
// Use in any server-side code
const session = await stripe.checkout.sessions.create({ ... });Webhook Flow
POST /api/webhooks/stripe handles two events:
checkout.session.completed — fires when a user completes checkout for the first time:
- Verifies the webhook signature with
STRIPE_WEBHOOK_SECRET - Retrieves the subscription from Stripe and extracts the
stripePriceId - Finds the matching plan in
pricingData - Updates the
teamsrow:stripeSubscriptionId,stripeCustomerId,stripePriceId,stripeCurrentPeriodEnd,planName
invoice.payment_succeeded — fires on every renewal and plan change (skipped for the initial subscription_create invoice, which is already handled above):
- Looks up the team by
stripeCustomerId - Retrieves the current subscription to get the latest
stripePriceId - Updates the
teamsrow:stripePriceId,stripeCurrentPeriodEnd,planName
Rate Limiting
lib/utils/usage.ts exports checkRateLimits() which is called at the top of the main chat route:
const { allowed, reason } = await checkRateLimits(team);
if (!allowed) {
return new Response(JSON.stringify({ error: reason }), { status: 429 });
}Usage is tracked per-day in dailyUsages and per-month in monthlyUsages. The updateUsage() function fires after each successful agent call.
Setup Guide
Create products in Stripe dashboard
Create a product for each paid plan (Pro, Business). Add both a monthly and a yearly price to each.
Copy Price IDs to env vars
NEXT_PUBLIC_STRIPE_PRO_MONTHLY_PLAN_ID=price_...
NEXT_PUBLIC_STRIPE_PRO_YEARLY_PLAN_ID=price_...
NEXT_PUBLIC_STRIPE_BUSINESS_MONTHLY_PLAN_ID=price_...
NEXT_PUBLIC_STRIPE_BUSINESS_YEARLY_PLAN_ID=price_...Update config/subscriptions.ts
Set stripeIds.monthly and stripeIds.yearly on each plan entry in pricingData to match your Stripe Price IDs.
Configure webhook endpoint
In the Stripe dashboard, add a webhook pointing to https://yourdomain.com/api/webhooks/stripe.
Subscribe to both events:
checkout.session.completedinvoice.payment_succeeded
Copy the signing secret to STRIPE_WEBHOOK_SECRET.
Test locally
stripe listen --forward-to localhost:3000/api/webhooks/stripe