Payments Overview

How Nebutra's billing system works — Stripe subscriptions, plan sync, and quota enforcement.

Architecture

Nebutra's billing pipeline is built on three layers that work together to gate access by plan and enforce usage quotas in real time.

User
 └─▶ Stripe Checkout
       └─▶ Stripe sends webhook
             └─▶ /api/v1/webhooks/stripe
                   └─▶ DB plan updated (tenant record)
                         └─▶ @nebutra/metering enforces quotas

All Stripe integration code lives in packages/commerce/billing/. Application code calls helpers from @nebutra/billing — it never touches the Stripe SDK directly.

Plans

FREE

$0 / month

  • 1,000 API calls / month
  • 1 organization member
  • Community support

PRO

$49 / month

  • 100,000 API calls / month
  • 10 organization members
  • Email support
  • All features unlocked

ENTERPRISE

Custom pricing

  • Unlimited API calls
  • Unlimited members
  • SSO / SAML
  • SLA guarantee
  • Custom contract

How plan sync works

The user is redirected to a Stripe-hosted checkout page. On success, Stripe fires a checkout.session.completed event.

The API gateway endpoint POST /api/v1/webhooks/stripe receives the event, verifies the signature using STRIPE_WEBHOOK_SECRET, and routes it to the correct handler in @nebutra/billing.

The handler updates the tenant's plan and planExpiresAt columns in the database, synchronizing the Stripe subscription state.

On every subsequent API request, @nebutra/metering reads the tenant's current plan limits from the database and compares them against accumulated ClickHouse usage. Requests over quota receive a 429 Too Many Requests response.

Where Stripe config lives

LocationPurpose
packages/commerce/billing/src/stripe.tsStripe client initialization
packages/commerce/billing/src/plans.tsPlan definitions and limit constants
packages/commerce/billing/src/handlers/Webhook event handlers
packages/commerce/billing/src/checkout.tsCheckout session creation helpers
packages/commerce/billing/src/portal.tsStripe Customer Portal session helpers

Environment variables

Set the following in your .env file (or your deployment platform's secret manager):

# Stripe secret key — use sk_test_ in development, sk_live_ in production
STRIPE_SECRET_KEY=sk_live_xxxxxxxxxxxxxxxxxxxx

# Webhook signing secret — obtained from the Stripe Dashboard or Stripe CLI
STRIPE_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxxxxxx

# Publishable key for the frontend (safe to expose)
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_xxxxxxxxxxxxxxxxxxxx

# Price IDs for each paid plan (from Stripe Dashboard → Products → Prices)
STRIPE_PRO_PRICE_ID=price_xxxxxxxxxxxxxxxxxxxx
STRIPE_ENTERPRISE_PRICE_ID=price_xxxxxxxxxxxxxxxxxxxx

Never commit real Stripe keys to source control. Use .env.local locally and inject secrets through your CI/CD environment for production deployments.

Stripe Dashboard setup

Sign up at stripe.com and activate your account.

Go to Stripe Dashboard → Products → Add product. Create one product for PRO ($49/month recurring) and one for ENTERPRISE (custom). Copy the Price IDs into your environment variables.

Go to Developers → Webhooks → Add endpoint. Set the URL to https://api.yourdomain.com/api/v1/webhooks/stripe and select the events listed in the Webhooks page.

After creating the endpoint, reveal and copy the Signing secret (whsec_...) into STRIPE_WEBHOOK_SECRET.

During local development, use the Stripe CLI to forward live events to your local server instead of setting up a public endpoint.

How is this guide?

Edit on GitHub

Last updated on

On this page