Skip to main content

Billing

The Billing admin view is an at-a-glance dashboard of plan distribution and active subscriptions. It does not manage individual Stripe subscriptions, refunds, or manual overrides - those actions are still performed inside the Stripe Dashboard. This page is a monitoring surface only, with deeper subscription and coupon tooling living on dedicated admin pages.

Where to find it​

The page lives at /billing. It has no sidebar entry - navigate to it directly, or reach billing work through Coupons, Subscriptions, and Plans, which are all in the sidebar's Commerce section.

Quick start​

  1. Open /billing.
  2. Scan the KPI cards at the top for total / free / pro / enterprise account counts.
  3. Read the Subscriptions card for a count of active Stripe subscriptions and a link reminder that Stripe Dashboard is the source of truth for details.
  4. For coupon work, jump to Coupons.
  5. For plan-level configuration, jump to Plans.

Detailed walkthrough​

KPI grid​

Four KPI cards:

  • Total Accounts - every account in the system.
  • Free Accounts - accounts on the free plan slug.
  • Pro Accounts - accounts on the pro plan slug.
  • Enterprise Accounts - a leftover tile from the dropped enterprise tier (removed by migration 20260415000009_drop_enterprise_plan). It has no accounts behind it.

The page is a server component (page.tsx is async) and renders counts via a getPlanStats() helper. That helper returns hard-coded zeros - it is not wired to the admin API - so every card reads 0. The translations and layout are final.

Subscriptions card​

A single card titled Subscriptions shows:

  • "{count} active subscriptions" - a read-only count.
  • A static footnote: "Detailed subscription management is available in the Stripe Dashboard."

There are no buttons, no refund flow, and no manual subscription editing here.

Coming-soon banner​

apps/admin/messages/\{en,de\}.json carries billing.comingSoon and billing.comingSoonDescription copy for a richer billing view. Neither string is rendered by the shipped page.

Common scenarios​

  • "How many paying customers do we have right now?" - the Pro KPI reads 0 regardless; count Pro badges on the Subscriptions or Accounts list instead.
  • Refund / cancel a subscription - not possible here. Go to Stripe Dashboard.
  • Find or cancel a developer credit note - use Credit Notes (/credit-notes).
  • Apply a manual coupon to an account - use Coupons to create the coupon, then apply it from the Stripe customer record.
  • Override a plan without Stripe - use Accounts → Plan Management and Limits. That change is independent of Stripe.

Permissions​

ActionPermission
Open the billing pagebilling:read

Entry also requires admin:access. System admins have this implicitly.

billing:edit exists in the admin permission registry but no endpoint or UI element enforces it today.

API​

There is no dedicated billing analytics endpoint. When getPlanStats() is wired up it will aggregate the admin accounts listing.

UI actionGraphQLREST
Count accounts by planadminAccountsGET /v1/admin/accounts

For end-user billing actions (checkout, portal, cancel, invoices), see the non-admin billing REST endpoints in apps/api/src/routes/billing.rs:

  • POST /v1/billing/checkout
  • POST /v1/billing/portal
  • GET /v1/billing/status
  • POST /v1/billing/cancel
  • GET /v1/billing/invoices
  • GET /v1/billing/validate-coupon
  • POST /v1/webhooks/stripe

These are not admin-scoped - they act on the authenticated user's own subscription.

Tips & gotchas​

  • KPIs read zero - getPlanStats() returns constants. Don't treat the numbers on this page as data.
  • Stripe is the source of truth. Manual DB edits here will drift out of sync with Stripe webhooks.
  • There is no manual-override or refund UI today. If you need one, file an issue - there are currently no hidden admin flows for it.
  • Coupons - discount codes issued to Stripe
  • Subscriptions - per-account subscription overview
  • Plans - plan definition and feature grid
  • Accounts - per-account plan override (independent of Stripe)