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
- Authenticated user clicks "Subscribe" in the frontend
- Frontend calls the backend to create a Stripe Checkout Session
- Backend creates the session with the price ID, success/cancel URLs, and user metadata
- User is redirected to Stripe's hosted checkout page
- After payment, Stripe sends a
checkout.session.completedwebhook to the backend - 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 thepaidCancelledflag 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 customerstripePriceID— The active subscription pricepaidCancelled— Whether the subscription has been cancelledmonthlyCreditsUsed— Usage tracking, reset on renewalcurrentBillingPeriodStart— Tracks the current billing cycleappliedDiscountCode/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
- Create a Stripe account
- Create a subscription product and monthly price in the Stripe dashboard
- Add to your backend
.env:STRIPE_PRIVATE_KEY— Your Stripe secret keySTRIPE_SIGNING_SECRET— Webhook endpoint signing secretSTRIPE_MONTHLY_PRICE_ID— The price ID for your monthly plan
- Set up a webhook endpoint in Stripe pointing to
https://backend.yourdomain.com/webhook/stripe - 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