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 quotasAll 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
| Location | Purpose |
|---|---|
packages/commerce/billing/src/stripe.ts | Stripe client initialization |
packages/commerce/billing/src/plans.ts | Plan definitions and limit constants |
packages/commerce/billing/src/handlers/ | Webhook event handlers |
packages/commerce/billing/src/checkout.ts | Checkout session creation helpers |
packages/commerce/billing/src/portal.ts | Stripe 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_xxxxxxxxxxxxxxxxxxxxNever 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.
Related
How is this guide?
Last updated on