Docs

Stripe Billing

Stacknaut includes a complete Stripe integration for subscription billing — checkout, webhooks, billing portal, and cancellation handling. Everything is wired up between the frontend, backend, and database so you can start charging customers immediately.

How It Works

Checkout Flow

  1. Authenticated user clicks "Subscribe" in the frontend
  2. Frontend calls the backend to create a Stripe Checkout Session
  3. Backend creates the session with the price ID, success/cancel URLs, and user metadata
  4. User is redirected to Stripe's hosted checkout page
  5. After payment, Stripe sends a checkout.session.completed webhook to the backend
  6. Backend records the Stripe customer ID, price ID, and activates the subscription in the database

Returning customers are linked to their existing Stripe customer ID, so Stripe remembers their payment method.

Webhook Events

The backend handles these Stripe webhook events:

  • checkout.session.completed — Records the Stripe customer ID, price ID, and discount details. Marks the user as a paid subscriber.
  • invoice.paid / invoice.payment_succeeded — Resets monthly usage credits on subscription renewal.
  • customer.subscription.updated — Detects when a subscription is set to cancel at the end of the billing period and logs the details (amount, interval, cancellation reason).
  • customer.subscription.deleted — Sets the paidCancelled flag in the database, marking the subscription as cancelled.

Webhook signature verification ensures only legitimate Stripe events are processed.

Billing Portal

Users can manage their subscription through Stripe's hosted billing portal — update payment methods, view invoices, and cancel. The backend creates a portal session and returns the URL to the frontend.

Subscription State

The user's subscription status is tracked in the users table:

  • stripeCustomerID — Links the user to their Stripe customer
  • stripePriceID — The active subscription price
  • paidCancelled — Whether the subscription has been cancelled
  • monthlyCreditsUsed — Usage tracking, reset on renewal
  • currentBillingPeriodStart — Tracks the current billing cycle
  • appliedDiscountCode / discountAmountCents — Promotion code tracking

A user has an active paid subscription when stripePriceID matches the expected price and paidCancelled is false.

What You Get

  • Stripe Checkout integration with hosted payment page
  • Webhook handler for all key subscription lifecycle events
  • Billing portal for self-service subscription management
  • Promotion code / discount support
  • Monthly credit reset on subscription renewal
  • Stripe customer ID linking (existing customers keep their payment method)
  • Zod validation on checkout and billing portal API inputs
  • Webhook signature verification
  • Telegram notifications for new subscriptions

Key Files

backend/src/controllers/stripeController.ts    — checkout, webhooks, billing portal
backend/src/routes/stripeRoutes.ts             — route definitions with validation
frontend/src/stores/appStore.ts                — createCheckoutSession, getBillingPortalURL
shared/src/schemas/users.ts                    — subscription fields in user schema

Setup

  1. Create a Stripe account
  2. Create a subscription product and monthly price in the Stripe dashboard
  3. Add to your backend .env:
    • STRIPE_PRIVATE_KEY — Your Stripe secret key
    • STRIPE_SIGNING_SECRET — Webhook endpoint signing secret
    • STRIPE_MONTHLY_PRICE_ID — The price ID for your monthly plan
  4. Set up a webhook endpoint in Stripe pointing to https://backend.yourdomain.com/webhook/stripe
  5. Select these events: checkout.session.completed, customer.subscription.updated, customer.subscription.deleted, invoice.paid, invoice.payment_succeeded

For local development, use the Stripe CLI to forward webhooks:

stripe listen --forward-to https://stacknaut.com/webhook/stripe

7a9a8821

© 2026 Stacknaut