Billing Integration
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:
| Component | Role |
|---|---|
| Stripe Integration | Customer, subscription, and webhook management |
| Module Catalog | Defines available modules and add-ons with pricing |
| Entitlements Service | Manages feature authorization and usage limits |
| OpenFGA Tuples | Authorization checks for module-based features |
| Redis Cache | Performance 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.updatedcustomer.subscription.deletedcustomer.subscription.pausedcustomer.subscription.trial_will_endpayment_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:
- Loads the catalog configuration
- Ensures all products exist in Stripe
- Creates missing prices with proper metadata
- 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:
- Extract subscription data from Stripe webhook
- Update organization subscription in the database
- Synchronize OpenFGA tuples for enabled features
- Refresh Redis cache to reflect changes
- 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
-
Configure Stripe keys in your development environment:
export STRIPE_PRIVATE_KEY="sk_test_..."export STRIPE_WEBHOOK_SECRET="whsec_..." -
Set up webhook forwarding using Stripe CLI:
stripe loginstripe listen --forward-to localhost:17608/v1/stripe/webhook -
Test webhook events:
stripe trigger customer.subscription.updatedstripe trigger payment_intent.succeeded
Testing Environment
For testing, create a separate Stripe account and:
- Generate test API keys
- Create a clean catalog configuration
- Use the catalog CLI to seed products and prices
- 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"