Skip to main content

Billing Integration

info

Entitlements are for the Openlane SaaS product. Running the server without the entitlements option enabled (the default for open-source deployments) means no feature gating at all: every feature of the platform is accessible.

We integrate with Stripe to provide module-based billing and subscription management. The billing system manages organization subscriptions, feature entitlements, and usage-based pricing through a combination of Stripe APIs, OpenFGA authorization, and Redis caching.

Architecture Overview​

The billing system consists of several interconnected components:

ComponentRole
Stripe IntegrationCustomer, subscription, and webhook management
Module CatalogDefines available modules and add-ons with pricing
Entitlements ServiceManages feature authorization and usage limits
OpenFGA TuplesAuthorization checks for module-based features
Redis CachePerformance optimization for feature checks

Module-Based Billing​

Modules vs Add-ons​

Openlane uses a two-tier billing structure. Modules deliver core platform functionality, such as compliance_module and trust_center_module, and every subscription requires at least one. Add-ons are optional enhancements that attach to an existing subscription, such as domain_scanning_addon and extra_evidence_storage_addon.

The full set of modules, add-ons, and their prices is defined in pkg/catalog/catalog.yaml, which is synchronized to Stripe on startup. Treat that file as the source of truth; prices copied into documentation drift.

Stripe Integration​

Core Components​

StripeClient​

The pkg/entitlements package provides a wrapper around the official Stripe Go client (the catalog README and catalog CLI cover the module catalog and seeding):

type StripeClient struct {
Client *stripe.Client
Config Config
backends *stripe.Backends
}

Configuration​

Stripe integration is configured through the main application config:

type Config struct {
Enabled bool `default:"false"`
PublicStripeKey string
PrivateStripeKey string `sensitive:"true"`
StripeWebhookSecret string `sensitive:"true"`
TrialSubscriptionPriceID string
PersonalOrgSubscriptionPriceID string
StripeWebhookURL string
StripeBillingPortalSuccessURL string
StripeCancellationReturnURL string
StripeWebhookEvents []string
}

Key Integration Points​

Customer Management​

Organizations are represented as Stripe customers:

type OrganizationCustomer struct {
OrganizationID string
Email string
Name string
// Additional customer fields
}

Subscription Management​

Module subscriptions are managed through Stripe subscriptions with specific price IDs corresponding to catalog entries.

Webhook Handling​

Supported Stripe webhook events:

  • customer.subscription.updated
  • customer.subscription.deleted
  • customer.subscription.paused
  • customer.subscription.trial_will_end
  • payment_method.attached

Module Catalog System​

Catalog Structure​

The module catalog is defined in pkg/catalog/catalog.yaml and includes:

version: v0.0.4
modules:
compliance_module:
display_name: Core Compliance Module
lookup_key: compliance_module
description: Core Compliance Automation and Standards Library
billing:
prices:
- interval: month
unit_amount: 45000 # $450.00
nickname: price_compliance_monthly
lookup_key: price_compliance_monthly
price_id: price_1RizUuBvxky1R7SvelsIjdkf
- interval: year
unit_amount: 500000 # $5000.00
nickname: price_compliance_annually
lookup_key: price_compliance_annually
price_id: price_1RizUuBvxky1R7SvCQ1cygv9
audience: public
usage:
evidence_storage_gb: 25000
product_id: prod_SeI5Umu5pAUVMP
include_with_trial: true
addons:
extra_evidence_storage_addon:
display_name: Additional Evidence Storage (100GB)
lookup_key: extra_evidence_storage_addon
description: Additional Evidence Storage (100GB)
billing:
prices:
- interval: month
unit_amount: 1000 # $10.00
nickname: extra_evidence_storage_100gb
lookup_key: extra_evidence_storage_100gb
metadata:
increment_gb: "100"
price_id: price_1RizV0Bvxky1R7SvQ7xVtID7
audience: public
product_id: prod_SeI5CFhlkNjcwx

Catalog Management​

Automatic Stripe Synchronization​

On server startup, the system automatically:

  1. Loads the catalog configuration
  2. Ensures all products exist in Stripe
  3. Creates missing prices with proper metadata
  4. Updates the catalog with Stripe IDs

CLI Tools​

The cmd/catalog utility provides catalog management:

# Compare catalog with Stripe and create missing products/prices
go run cmd/catalog/main.go --write=true --stripe-key="sk_test_..."

# Migrate subscriptions between price IDs
go run cmd/catalog/pricemigrate.go --old-price="price_old" --new-price="price_new"

Feature Authorization​

Entitlements Flow​

TupleChecker​

The TupleChecker utility manages feature authorization:

type TupleChecker struct {
fgaClient fgax.Client
redisClient *redis.Client
}

// Check if a feature is enabled for an organization
allowed, err := tupleChecker.CheckFeatureTuple(ctx, FeatureTuple{
UserID: "user_123",
Feature: "compliance-module",
Context: map[string]any{"org": "org_456"},
})

Feature Annotations​

Ent schemas declare required modules using annotations:

// Annotations in schema definition
func (Control) Annotations() []schema.Annotation {
return []schema.Annotation{
entx.Features("compliance-module"),
}
}

Webhook Processing​

Event Handling​

Webhook events are processed to maintain feature state synchronization:

func (h *Handler) WebhookReceiverHandler(ctx echo.Context) error {
// Verify webhook signature
event, err := webhook.ConstructEvent(payload, signature, secret)

// Process supported event types
switch event.Type {
case stripe.EventTypeCustomerSubscriptionUpdated:
return h.processSubscriptionUpdate(ctx, event)
case stripe.EventTypeCustomerSubscriptionDeleted:
return h.processSubscriptionCancellation(ctx, event)
// ... other event types
}
}

Subscription State Management​

When subscription events are received:

  1. Extract subscription data from Stripe webhook
  2. Update organization subscription in the database
  3. Synchronize OpenFGA tuples for enabled features
  4. Refresh Redis cache to reflect changes
  5. Update usage limits based on subscription tier

Usage Limits and Metering​

Storage Limits​

Modules can define usage limits that are enforced at runtime:

usage:
evidence_storage_gb: 25000 # 25GB included with compliance module

Add-on Increments​

Add-ons can increase usage limits:

metadata:
increment_gb: "100" # Additional 100GB storage

Enforcement​

Usage limits are enforced through:

  • Database constraints for hard limits
  • Application logic for soft limits and warnings
  • Billing portal integration for usage monitoring

Development and Testing​

Local Development Setup​

  1. Configure Stripe keys in your development environment:

    export STRIPE_PRIVATE_KEY="sk_test_..."
    export STRIPE_WEBHOOK_SECRET="whsec_..."
  2. Set up webhook forwarding using Stripe CLI:

    stripe login
    stripe listen --forward-to localhost:17608/v1/stripe/webhook
  3. Test webhook events:

    stripe trigger customer.subscription.updated
    stripe trigger payment_intent.succeeded

Testing Environment​

For testing, create a separate Stripe account and:

  1. Generate test API keys
  2. Create a clean catalog configuration
  3. Use the catalog CLI to seed products and prices
  4. Test webhook integration with Stripe CLI

Sandbox Seeding​

Example of seeding a new Stripe sandbox:

# Remove existing price/product IDs from catalog
# Run catalog sync to create fresh products and prices
go run cmd/catalog/main.go --write=true --stripe-key="sk_test_sandbox_key"