Billing
Stripe checkout integration and subscription management.
The billing module handles Stripe Checkout for subscriptions, customer portal access for self-service management, and subscription status display.
Checkout Flow
1. Pricing Page
The pricing page fetches available plans from the backend:
GET /api/subscriptions/pricing/Returns active prices with product details and features. For authenticated users, already-subscribed products are excluded.
2. Checkout Redirect
When a user selects a plan, the frontend creates a Stripe Checkout session:
const res = await fetch("/api/subscriptions/checkout/", {
method: "POST",
body: JSON.stringify({
price_id: selectedPrice.price_id,
workspace_id: currentWorkspaceId,
}),
});
const { session_id } = await res.json();
// Redirect to Stripe hosted checkout
const stripe = await loadStripe(STRIPE_PUBLISHABLE_KEY);
await stripe.redirectToCheckout({ sessionId: session_id });3. Post-Checkout
After payment, Stripe redirects to:
- Success:
/success?session={CHECKOUT_SESSION_ID} - Cancel:
/cancel
The success page confirms the subscription was created. The actual subscription activation happens via webhooks on the backend.
Customer Portal
Users manage their subscription (upgrade, downgrade, cancel, update payment) through Stripe's Customer Portal:
const res = await fetch("/api/subscriptions/create-portal-link/", {
method: "POST",
});
const { url } = await res.json();
window.location.href = url; // Redirect to Stripe portalThe portal is configured in the Stripe Dashboard and handles all subscription management UI.
Subscription Display
Workspace Settings
The workspace settings page shows the current subscription:
export default async function SettingsPage({ params }) {
const workspace = await fetchWorkspace(workspaceId);
return (
<div>
{workspace.subscription ? (
<SubscriptionDetails subscription={workspace.subscription} />
) : (
<NoSubscription />
)}
</div>
);
}Manage Subscription Button
function ManageSubscriptionButton() {
return (
<Button onClick={openCustomerPortal}>
Manage Subscription
</Button>
);
}Subscription Status
The user's subscription status is available in the session:
interface User {
subscription_status?: string; // "active", "trialing", etc.
has_active_subscription?: boolean; // Convenience flag
}This can be used for feature gating in the UI:
const { data: session } = useSession();
{session?.user?.has_active_subscription ? (
<PremiumFeature />
) : (
<UpgradePrompt />
)}API Routes
| Route | Method | Description |
|---|---|---|
/api/subscriptions/pricing/ | GET | List available plans/prices |
/api/subscriptions/checkout/ | POST | Create Stripe Checkout session |
/api/subscriptions/create-portal-link/ | POST | Get Stripe Customer Portal URL |
/api/subscriptions/webhook/ | POST | Stripe webhook receiver |
Configuration
| Variable | Description |
|---|---|
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY | Stripe publishable key for client-side |
The Stripe secret key and webhook secret are configured on the backend only.