Billing System

Billing System Documentation #

This document describes the billing and subscription management system in BonsAI, which integrates Stripe for payment processing and Clerk for free trial management.

Architecture Overview #

The billing system consists of several components working together:

  1. Stripe Integration: Handles payment processing, subscription management, and billing events
  2. Clerk Integration: Manages organizations and user authentication, also free trial limitations
  3. Billing API: Provides endpoints for retrieving billing information
  4. Cache Layer: Redis-based caching for performance optimization
  5. Webhook Handlers: Process real-time updates from Stripe and Clerk

Billing Flow Diagram #

graph TB
    subgraph "User Actions"
        A[User Creates Organization]
        B[User Manages Subscription]
    end

    subgraph "Clerk"
        C[Organization Created Event]
        D[Organization Metadata]
    end

    subgraph "BonsAPI"
        E[Clerk Webhook Handler]
        F[Stripe Webhook Handler]
        G[Billing Service]
        H[Organization Repository]
        I[Billing API Endpoints]
    end

    subgraph "Stripe"
        J[Customer Created]
        K[Subscription Events]
        L[Customer Portal]
    end

    subgraph "Cache Layer"
        M[Redis Cache]
    end

    subgraph "Frontend"
        N[Billing Page]
        O[Billing Alerts]
    end

    A --> C
    C --> E
    E --> J
    E --> D
    J --> K
    K --> F
    F --> M
    B --> L
    I --> G
    G --> H
    G --> M
    G --> J
    N --> I
    O --> I
    M --> I

    style A fill:#e1f5fe
    style B fill:#e1f5fe
    style N fill:#e1f5fe
    style O fill:#e1f5fe
    style M fill:#fff3e0
    style J fill:#e8f5e9
    style K fill:#e8f5e9
    style L fill:#e8f5e9

Core Components #

1. Stripe Client (libs/rust/bonsai-integration/src/billing/stripe/) #

The Stripe client provides methods for:

  • Creating customers
  • Managing subscriptions
  • Handling webhooks
  • Retrieving billing information

2. Billing Service (apps/bonsapi/src/service/billing/) #

The billing service layer handles:

  • Reading billing information
  • Managing trial periods
  • Checking subscription status
  • Calculating usage limits

3. Organization Management #

Organizations are the billable entities in BonsAI:

  • Each organization has a stripe_customer_id field
  • Organizations are created via Clerk webhooks
  • Stripe customers are automatically created for new organizations

Database Schema #

Organization Table Extension #

ALTER TABLE organization
ADD COLUMN stripe_customer_id VARCHAR(255);

CREATE INDEX organization_stripe_customer_id_idx
ON organization(stripe_customer_id);

Webhook Handling #

Stripe Webhooks #

Endpoint: POST /webhook/stripe

Handled Events:

  • customer.subscription.created - Handles cache invalidation and immediate charging
  • customer.subscription.deleted - Invalidates billing cache
  • customer.subscription.resumed - Invalidates billing cache
  • customer.subscription.updated - Invalidates billing cache

Security:

  • Webhook signatures are verified using STRIPE_WEBHOOK_SIGNING_SECRET
  • Uses stripe::Webhook::construct_event() for cryptographic verification
  • Falls back to manual parsing for API version mismatches (with signature verification)
  • Rejects any unsigned or tampered requests

Processing Flow:

  1. Verify webhook signature
  2. Extract customer ID from subscription event
  3. Look up organization by stripe_customer_id
  4. Invalidate billing cache for the organization

Clerk Webhooks #

Endpoint: POST /webhook/clerk

Handled Events:

  • organization.created: Creates Stripe customer and sets trial metadata
  • organization.updated: Upserts Stripe customer and invalidates cache

Trial System #

New organizations receive a 14-day free trial with:

  • Trial Duration: 14 days from organization creation
  • Trial Limits:
    • Max entities: 2
    • Max invoices per month: 100

Trial metadata is stored in Clerk organization metadata:

{
  "trial_expiry_date": "2024-01-15",
  "trial_max_entities": 2,
  "trial_max_invoices_per_month": 100
}

Immediate Charging for Mid-Month Subscriptions #

When a subscription is created after the 1st of the month, BonsAI handles immediate charging to ensure customers pay for the current month’s usage:

How It Works #

  1. Billing Cycle Anchor: All subscriptions are anchored to the 1st of each month
  2. Immediate Charge: When subscribing after the 1st, an immediate invoice is created for the current month
  3. Metadata Requirements: Subscriptions must include:
    • charge_current_month: "true" - Enables immediate charging
    • price_id: "price_xxx" - The price to charge for the current month

Processing Flow #

graph LR
    A[Subscription Created] --> B{Is it the 1st?}
    B -->|Yes| C[No Immediate Charge]
    B -->|No| D{charge_current_month = true?}
    D -->|No| E[No Immediate Charge]
    D -->|Yes| F[Create Immediate Invoice]
    F --> G[Charge Customer]
    G --> H[Regular Billing Continues on 1st]

Implementation Details #

  • Handled in customer.subscription.created webhook
  • Creates a one-time invoice for the current month
  • Charges immediately using the default payment method
  • Regular subscription billing continues on the 1st of each month

Error Handling #

  • If immediate charge fails, the invoice remains open for collection
  • Customer receives standard Stripe payment retry emails
  • Subscription remains active during retry period

Caching Strategy #

The billing system uses Redis caching to minimize API calls to Stripe:

Cache Keys #

  • Pattern: billing:plan:{organization_id}
  • TTL: 5 minutes
  • Structure:
{
  "subscription_status": "active",
  "current_plan": "starter",
  "billing_cycle_end": "2024-02-01T00:00:00Z",
  "max_invoices_per_month": 100,
  "max_entities": 10
}

Cache Invalidation #

Cache is invalidated when:

  • Stripe subscription events are received
  • Organization is updated via Clerk
  • After 5 minutes

Frontend Integration #

The webapp displays billing information and alerts:

Billing Alert Component #

Shows warnings when:

  • Free trial is expiring (< 3 days)
  • Subscription is inactive
  • Usage limits are approached

Billing Page #

Located at /settings/organization/billing:

  • Shows current plan details
  • Provides link to Stripe customer portal
  • Displays usage statistics
  • Shows trial information

State Management #

Billing state is managed via:

  • useBilling hook for fetching billing data
  • useOrganization hook for organization details
  • Automatic refetch on focus and authentication changes

Environment Variables #

Required environment variables:

# Stripe Configuration
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SIGNING_SECRET=whsec_...

# Clerk Configuration
CLERK_SECRET_KEY=sk_test_...
CLERK_WEBHOOK_SIGNING_SECRET=whsec_...

# API Configuration
BONSAPI_EXTERNAL_HOST=https://api.bonsai.com

Error Handling #

Common Error Scenarios #

  1. No Stripe Customer: Organization exists but no Stripe customer

    • Automatically creates customer on first billing request
    • Logs warning for monitoring
  2. Cache Miss: Billing information not in cache

    • Falls back to Stripe API
    • Repopulates cache with fresh data
  3. Webhook Failures: Failed to process webhook

    • Logs error with details
    • Returns 200 OK to prevent retries (for non-critical events)
    • Critical events may return error for retry

Monitoring and Debugging #

Key Log Points #

  • Webhook receipt and processing
  • Cache hits/misses
  • Stripe API calls
  • Customer creation events
  • Subscription changes

Metrics to Monitor #

  • Webhook processing time
  • Cache hit rate
  • Stripe API latency
  • Failed webhook attempts
  • Trial conversion rate

Development and Testing #

Local Development #

  1. Spin up ngrok
  2. Set up Stripe test account
  3. Configure webhook endpoint using ngrok’s host
  4. Use update BONSAPI_EXTERNAL_HOST and STRIPE_WEBHOOK_SIGNING_SECRET in /.env

Security Considerations #

  1. Webhook Verification: All webhooks are cryptographically verified
  2. Authorization: Billing endpoints require valid JWT tokens
  3. Rate Limiting: Consider implementing rate limits for billing endpoints
  4. Audit Logging: All billing changes are logged for compliance
  5. PCI Compliance: No payment information is stored locally

Future Enhancements #

  1. Usage Tracking: Real-time usage monitoring
  2. Billing Alerts: Email notifications for billing events
  3. Multiple Plans: Support for different subscription tiers
  4. Metered Billing: Usage-based pricing options
  5. Invoice Management: Direct invoice access and management