HyperSaaS
FrontendBilling

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 portal

The 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

RouteMethodDescription
/api/subscriptions/pricing/GETList available plans/prices
/api/subscriptions/checkout/POSTCreate Stripe Checkout session
/api/subscriptions/create-portal-link/POSTGet Stripe Customer Portal URL
/api/subscriptions/webhook/POSTStripe webhook receiver

Configuration

VariableDescription
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEYStripe publishable key for client-side

The Stripe secret key and webhook secret are configured on the backend only.

On this page