⚡ You're viewing a live demo of ChimerAI. Data resets daily at midnight UTC.Get the CLI →

Billing & Payments Guide

This guide explains the Stripe billing integration that ships with ChimerAI and how to install it via the CLI.


Installation

chimerai add billing

This command installs the following files and configures your environment:

FilePurpose
app/billing/page.tsxPricing page with plan overview and upgrade flow
lib/stripe.tsStripe helper functions (no SDK needed)
app/api/billing/checkout/route.tsCreate Stripe Checkout Session
app/api/billing/portal/route.tsCreate Stripe Customer Portal Session
app/api/billing/subscription/route.tsRead current subscription and credit balance
app/api/webhooks/stripe/route.tsHandle incoming Stripe webhook events

Dependency installed automatically:

stripe ^14.0.0

Environment variables added to .env:

STRIPE_SECRET_KEY=sk_test_        # From https://dashboard.stripe.com/apikeys
STRIPE_WEBHOOK_SECRET=whsec_      # From Stripe Dashboard -> Webhooks

lib/stripe.ts

The Stripe library uses the raw Stripe REST API via fetch - no Stripe SDK needed at runtime. It exports the following functions:

getOrCreateStripeCustomer(userId, email)

Looks up an existing Stripe Customer ID on the user record. If none exists, creates a new Stripe customer and returns the ID.

const customerId = await getOrCreateStripeCustomer(session.user.id, session.user.email);

createCheckoutSession(customerId, priceId, successUrl, cancelUrl)

Creates a Stripe Checkout Session in subscription mode. Returns { id, url }. Redirect the user to url to complete payment.

const { url } = await createCheckoutSession(
  customerId,
  'price_xxx',
  `${origin}/billing?success=1`,
  `${origin}/billing?canceled=1`
);
redirect(url);

createPortalSession(customerId, returnUrl)

Creates a Stripe Customer Portal session where the user can manage or cancel their subscription. Returns { url }.

verifyStripeSignature(payload, signature)

Verifies a Stripe webhook signature using the Web Crypto API (no external dependency). Checks the HMAC-SHA256 signature and a 5-minute timestamp tolerance.

const valid = await verifyStripeSignature(rawBody, request.headers.get('stripe-signature'));
if (!valid) return new Response('Invalid signature', { status: 400 });

API Routes

POST /api/billing/checkout

Creates a Stripe Checkout Session and returns the redirect URL.

Request body:

{ "priceId": "price_xxx" }

Response:

{ "url": "https://checkout.stripe.com/..." }

POST /api/billing/portal

Creates a Stripe Customer Portal session.

Response:

{ "url": "https://billing.stripe.com/..." }

GET /api/billing/subscription

Returns the current user's subscription status and credit balance.

Response:

{
  "plan": "Pro",
  "status": "active",
  "currentPeriodEnd": "2026-05-29T00:00:00.000Z",
  "stripeSubscriptionId": "sub_xxx",
  "credits": {
    "current": 4800,
    "limit": 5000,
    "lifetimeUsed": 12400
  }
}

POST /api/webhooks/stripe

Handles incoming Stripe events. Must be registered in the Stripe Dashboard as a webhook endpoint.

Handled events:

EventAction
checkout.session.completedActivates the subscription in the DB
customer.subscription.updatedUpdates status and period end date
customer.subscription.deletedSets subscription status to canceled
invoice.paidTops up the user's credit balance
invoice.payment_failedLogs a warning

Billing Page

The page at /billing shows pricing plans and the current subscription status. It:

  • Loads the current plan from /api/billing/subscription
  • Redirects to Stripe Checkout when the user clicks an upgrade button
  • Shows a "Manage subscription" button that redirects to the Stripe Customer Portal
  • Handles ?success=1 and ?canceled=1 query params after checkout

Setup Steps

  1. Create products and prices in the Stripe Dashboard
  2. Copy the price IDs (e.g. price_xxx) into the billing page plan configuration
  3. Register your webhook endpoint in Stripe Dashboard -> Webhooks:
    • URL: https://yourdomain.com/api/webhooks/stripe
    • Events: checkout.session.completed, customer.subscription.updated, customer.subscription.deleted, invoice.paid, invoice.payment_failed
  4. Copy the signing secret (whsec_...) into STRIPE_WEBHOOK_SECRET
  5. For local development, use the Stripe CLI:
    stripe listen --forward-to localhost:3000/api/webhooks/stripe
    

Notes

  • The Stripe library uses fetch directly against the Stripe REST API. There is no stripe npm package import in the generated code, which reduces bundle size.
  • The webhook handler requires the raw request body for signature verification. Next.js provides this by default in Route Handlers.
  • Credit balances are only meaningful if your Prisma schema includes a CreditBalance model. The subscription route handles missing models gracefully and returns default values.
  • Test with Stripe's test mode keys (sk_test_, pk_test_) before switching to live keys.
ChimerAI Docs · Back to Demo