Documentation Index
Fetch the complete documentation index at: https://grantmaster.dev/llms.txt
Use this file to discover all available pages before exploring further.
Engineering reference: For service contracts, EventBus events, and data-layer details see src/features/billing/billing.md.
Billing
Overview
The Billing feature manages the complete financial lifecycle of organizations on GrantMaster. It provides subscription tier selection (Potential/Professional/Ultimate), seat-based licensing, Stripe payment processing, event-based usage metering (API calls, AI generations, storage, agent operations), agent credit reservations with atomic consumption, module marketplace add-on billing, and threshold-based usage alerts. Revenue analytics track MRR, ARR, and platform-wide financial metrics. All sensitive Stripe operations are handled server-side via Cloud Functions.
Data Model
Firestore Collections
| Collection | Document Type | Description |
|---|
organizations | Organization | Core tenant with subscription tier, billing cycle, feature entitlements, seat allocation, usage metrics, and agent credit balance |
subscriptionPricing | SubscriptionTierDefinition | Pricing definitions per tier (monthly/yearly base price, per-seat price, included seats, feature set) |
billingTransactions | BillingTransaction | Payment history, refunds, chargebacks with Stripe references and amount tracking |
refundRequests | RefundRequest | Refund request workflows (pending → approved → processed) with review notes |
billingAdjustments | BillingAdjustment | Admin-applied credits, debits, discounts with audit trail |
organizations/{orgId}/creditReservations | CreditReservation | Holds on agent credits before runs (active → consumed/released/expired after 1 hour TTL) |
usage_events | UsageEvent | Event-based metering: API calls, AI generations, storage, agent steps, document processing |
usage_alerts | UsageAlert | Threshold crossing records (80%/90%/95%/100%) to prevent duplicate notifications |
Key TypeScript Types
| Type | Purpose |
|---|
SubscriptionTier | Enum: POTENTIAL | PROFESSIONAL | ULTIMATE |
SubscriptionFeatures | Feature flags per tier: includedSeats, maxProjects, aiGenerationsPerMonth, agentCreditsPerMonth, customReports, etc. |
SubscriptionSeats | { included, purchased, total, used, available } — seat allocation and usage |
SubscriptionPricing | { basePriceMonthly, basePriceYearly, seatPriceMonthly, seatPriceYearly, currency, nextBillingAmount, nextBillingDate } |
SubscriptionTierConfig | Full tier definition: name, description, pricing, feature set, Stripe price IDs, display metadata |
SubscriptionTierDefinition | Tier pricing persisted in Firestore with isActive, effectiveFrom, quotas |
CreditReservation | { id, organizationId, agentRunId, amount, consumedAmount, status: 'active'|'consumed'|'released'|'expired', createdAt, expiresAt } |
CreditBalance | { total, used, reserved, available, purchasedExtra } — snapshot of org credit status |
CreditConsumptionResult | Result of consuming credits: { success, creditsConsumed, remainingInReservation, totalUsedThisMonth } |
BillingTransaction | Payment record: organizationId, amount, currency, transactionDate, stripeChargeId, status, type |
RefundRequest | Refund workflow: organizationId, transactionId, amount, status (PENDING|APPROVED|REJECTED|PROCESSED), reviewer details |
UsageEvent | Event record: organizationId, eventType, metadata, timestamp, billingPeriodKey |
StripePaymentMethod | Card/SEPA method: { id, type, card?, sepaDebit?, billingDetails, isDefault, createdAt } |
StripeSubscriptionDetails | Stripe subscription snapshot: status, tier, currentPeriodStart/End, cancelAtPeriodEnd, nextInvoiceDate |
Key Behaviors
Subscription Lifecycle
Tier selection, upgrades/downgrades, seat enforcement, and period lifecycle.
Upgrade/Downgrade: User initiates tier change, service calculates prorated cost via calculateTotalCost(), Stripe charges via Cloud Functions, subscription.tier and features updated on success.
Cancellation: cancelAtPeriodEnd flag set; services continue through billing period; at period end, status → ‘canceled’, features revert to POTENTIAL tier.
Seat Management: canAddUser() checks seats.available; user invitation blocked if exhausted; additional seats trigger per-seat Stripe charge (seatPriceMonthly × quantity).
Credit Reservation & Consumption
Agent credit lifecycle: reserve → consume per step → release on completion.
Reserve: Before agent run, reserveCredits(orgId, amount, agentRunId) atomically checks available = (monthlyLimit + purchased) - used - reserved. If sufficient, creates CreditReservation (active, expires in 1 hour TTL). Emits CREDITS_RESERVED event.
Consume: After each step, consumeCredits(orgId, reservationId, actualAmount) moves credits from reserved to used (decrements both counters). Throws if reservation inactive or amount exceeds remaining. Idempotent within Firestore transaction.
Release: On run completion/cancellation, releaseCredits() marks reservation as ‘released’ and refunds unused amount back to available pool.
Query Balance: getCreditBalance() returns snapshot with total, used, reserved, available, purchasedExtra.
Usage Tracking
Event-based metering records feature usage in usage_events with billingPeriodKey (YYYY-MM) for aggregation.
Events tracked: api_call, ai_generation, rag_query, rag_document_process, rag_embedding, agent_run, agent_step, storage_upload, storage_delete, credit_consumed, credit_purchased, module_install. Each includes organizationId, timestamp, eventType, and type-specific metadata.
Query via trackUsageEvent() or convenience trackers (trackApiCall(), trackAgentRun(), etc.). Aggregate by period with getUsageEvents(orgId, billingPeriodKey) and getRAGUsageStats().
Usage Alerts
Threshold-based monitoring: checkUsageAlerts(orgId) compares current usage vs tier feature limits. For thresholds 80%/90%/95%/100% crossed on metrics (API calls, AI generations, storage), emits alert if not already sent this period. Prevents duplicate notifications. Emails admins with “Approaching limit” (80-95%) or “Limit reached” (100%) messages.
Alerts recorded in usage_alerts to track history and avoid re-notification within same period.
Refund Workflow
Request: User/admin submits refund with reason; status: PENDING.
Approval: SuperAdmin reviews, approves or rejects, records notes and decision; status: APPROVED.
Processing: processRefund() calls Cloud Functions to issue Stripe refund, marks related BillingTransaction as REFUNDED, updates status: PROCESSED.
Module Marketplace Billing
Add-on modules as optional Stripe SubscriptionItems.
Free in Tier: installModule(moduleId) checks if module included in current subscription features. If yes, activates directly without Stripe charge. Response: { activatedDirectly: true }.
Paid Module: Module not included → redirect to createCheckoutSession() for Stripe Checkout. On success, adds SubscriptionItem with module price to existing subscription. Monthly charge applies on next billing date.
Operations: installModule() (activate or redirect), uninstallModule() (remove SubscriptionItem, trigger prorated refund), getModuleStatus() (check activation, pricing, billing state).
Service Contract
| Service | Owns | Key Methods |
|---|
| CreditService | Agent credit reservation, consumption, balance tracking | reserveCredits(orgId, amount, agentRunId), consumeCredits(orgId, reservationId, amount), releaseCredits(orgId, reservationId), getCreditBalance(orgId), addPurchasedCredits(orgId, packSize, stripePaymentIntentId?) |
| BillingService | Subscription pricing, billing transactions, revenue metrics, refunds | getAllSubscriptionPricing(), getSubscriptionPricing(tier), getOrganizationBillingHistory(orgId), createBillingTransaction(), calculateRevenueMetrics(), getAllBillingTransactions(), getPendingRefunds(), processRefund(), createBillingAdjustment() |
| SubscriptionService | Subscription tier config, seat allocation, cost calculation | getSubscriptionTierConfig(tier), calculateSeatsNeeded(userCount), calculateTotalCost(tier, totalSeats, billingCycle), canAddUser(orgId), calculateSeatUsage(orgId) |
| StripePaymentService | Stripe API integration: checkout, payment methods, customer portal | createCheckoutSession(), createCustomer(), fetchPaymentMethods(orgId), getCustomerPortalUrl(orgId), getUpcomingInvoice(orgId), attachPaymentMethod(), detachPaymentMethod() |
| StripeModuleBilling | Module marketplace Stripe billing | installModule(orgId, moduleId), uninstallModule(orgId, moduleId), previewModuleCheckout(), getModuleStatus() |
| UsageTrackingService | Usage metering: event recording and querying | trackUsageEvent(), trackApiCall(), trackAiGeneration(), trackRAGQuery(), trackAgentRun(), trackAgentStep(), trackStorageUpload(), trackStorageDelete(), getUsageEvents(orgId, billingPeriodKey), getRAGUsageStats() |
| UsageAlertsService | Threshold monitoring and notifications | checkUsageAlerts(orgId), getOrganizationAlerts(orgId) |
Events
Emitted
| Event | Trigger | Severity | Persisted |
|---|
CREDITS_PURCHASED | addPurchasedCredits() succeeds | INFO | Yes |
SUBSCRIPTION_TIER_UPDATED | Tier change confirmed | INFO | Yes |
SUBSCRIPTION_PRICING_UPDATED | SuperAdmin updates pricing | INFO | Yes |
SEAT_LIMIT_REACHED | canAddUser() returns false | ERROR | Yes |
USAGE_ALERT_SENT | Threshold 80%/90%/95%/100% crossed | WARNING | Yes |
Consumed
Currently minimal; future integrations with agents and other features will consume credit and subscription events.
| Event | Expected Handler |
|---|
| (Agent execution events) | Credit reservation/consumption hooks |
Dependencies
- Depends on: Firebase (Firestore, Cloud Functions, Auth), Stripe API, EventBus, BaseService, logger, timeProvider
- Depended on by: Agents (credit reservation), Users (seat checking), Documents (storage tracking), Reports (AI metering), Integrations (API call tracking), Dashboard (usage display)
File Structure
src/features/billing/
├── services/
│ ├── creditService.ts # CreditService: reserve/consume/release, balance tracking
│ ├── billingService.ts # BillingService: pricing, transactions, revenue, refunds
│ ├── subscriptionService.ts # SubscriptionService: tier config, seats, cost calc
│ ├── stripePaymentService.ts # StripePaymentService: checkout, portal, payment methods
│ ├── stripeModuleBilling.ts # StripeModuleBilling: module add-on subscriptions
│ ├── usageTracking.ts # UsageTrackingService public barrel
│ ├── usageTrackingCore.ts # trackUsageEvent() core implementation
│ ├── usageTrackingTypes.ts # UsageEvent type definitions
│ ├── usageTrackingQueries.ts # getUsageEvents(), getRAGUsageStats()
│ ├── usageTrackingTrackers.ts # trackApiCall(), trackAgentRun(), etc.
│ ├── usageAggregation.ts # Aggregate usage by billing period (YYYY-MM)
│ ├── usageAlerts.ts # UsageAlertsService: checkUsageAlerts()
│ ├── billingPeriod.ts # getBillingPeriodKey(), getCurrentBillingPeriod()
│ ├── billingPeriod.test.ts # Tests for billing period utilities
│ └── subscriptionService.test.ts # Tests for subscription calculations
├── components/
│ ├── Billing.tsx # Main page container
│ ├── billing-page/
│ │ ├── SubscriptionTab.tsx # Tier selection, upgrade/downgrade UI
│ │ ├── SubscriptionStatusBanner.tsx
│ │ ├── PaymentMethodsTab.tsx # Payment methods, customer portal
│ │ ├── InvoicesTab.tsx # Billing history, invoices
│ │ ├── UsageTab.tsx # Usage metrics, limits, alerts
│ │ ├── ModuleExtensionsTab.tsx # Module marketplace UI
│ │ ├── BillingInfoTab.tsx # Billing details
│ │ ├── CancelDialog.tsx # Cancellation workflow
│ │ └── types.ts
│ └── pages/
│ └── FinancialsManagement.tsx # Admin: revenue metrics, refunds, adjustments
├── hooks/
│ ├── useBilling.ts # tRPC billing queries
│ ├── useBillingState.ts # Local state (activeTab, billingCycle, dialogs)
│ ├── useBillingActions.ts # Mutation wrappers
│ └── useBillingState.test.ts
├── index.ts # Public API barrel (services, components)
└── README.md # This file
Architecture Notes
Firestore Transactions: CreditService uses runTransaction() to atomically check balance and reserve credits, preventing overdraft from concurrent agent runs. Reservation and consumption both read current org state, validate, and update in a single transaction.
Cloud Functions: Sensitive Stripe operations (checkout, refunds, subscriptions) are server-side via Cloud Functions. Frontend calls via httpsCallable() with safe publishable keys only; secret keys never exposed.
Usage Metering: Events recorded in usage_events with billingPeriodKey (YYYY-MM) for efficient querying by period. Aggregation queries sum events within period; alerts compare aggregates against tier limits.
Module Billing: Modules as optional Stripe SubscriptionItems on org subscription. Install check: if included in tier, activate directly; else redirect to Stripe Checkout. Uninstall removes SubscriptionItem and triggers prorated refund.
Seat Enforcement: Checked at user invitation time via canAddUser(). Blocks invitation if seats.available == 0. Extra seats purchased via Stripe add per-seat charges to subscription.