Docs
Payments

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.

PlanMonthlyYearlyDaily RequestsMonthly RequestsMax Tokens/Request
Free$0$0515012,000
Pro$19$199501,00025,000
Business$35$3361503,00040,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:

  1. Verifies the webhook signature with STRIPE_WEBHOOK_SECRET
  2. Retrieves the subscription from Stripe and extracts the stripePriceId
  3. Finds the matching plan in pricingData
  4. Updates the teams row: 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):

  1. Looks up the team by stripeCustomerId
  2. Retrieves the current subscription to get the latest stripePriceId
  3. Updates the teams row: 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.completed
  • invoice.payment_succeeded

Copy the signing secret to STRIPE_WEBHOOK_SECRET.

Test locally

stripe listen --forward-to localhost:3000/api/webhooks/stripe