Billing Summary
block / payments / stripe
Current plan, subscription status, usage meters and a Stripe billing-portal link.
- type
- block
- files
- 3
- registry deps
- badge, button, card, progress, skeleton, @automindz/stripe-checkout
- npm deps
- stripe, lucide-react
- env vars
- STRIPE_SECRET_KEY, NEXT_PUBLIC_APP_URL
installs
- lib/automindz/billing.ts
- components/automindz/billing-summary.tsx
- app/api/billing-portal/route.ts
- Status drives the copy and the primary action
- Usage meters warn at 80% and turn destructive at the limit
- Portal route ships fail-closed until you wire the session
The portal route ships unwired on purpose: implement getCustomerIdForRequest to resolve the Stripe customer from your session. It must never come from the request body — that would let any caller open another customer's billing portal. Until you implement it the route answers 501.
Variations
one state per section
Active subscription
Renewal date, then one meter per allowance: seats at 85% take the warning tone, projects are unmetered, and API calls are at the limit — destructive bar plus the reason.
Payment failed
past_due promotes the portal button to primary and relabels it — the only useful action is fixing the card.
Cancelled, still in period
“Renews” becomes “Access ends”, and an upgrade Price ID adds a second action next to the portal.
Portal unavailable
The real route answers 501 until you implement the session lookup. The failure lands in the card instead of a console nobody reads.
Loading
Skeleton keeps the card's height, so the page does not reflow when billing data lands.
