billing

package
v0.1.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 22, 2026 License: MIT Imports: 12 Imported by: 0

Documentation

Index

Constants

View Source
const (
	StatusActive   = "active"
	StatusPastDue  = "past_due"
	StatusCanceled = "canceled"
	StatusTrialing = "trialing"
)

SubscriptionStatus constants for well-known Stripe subscription states.

Variables

View Source
var (
	PlanFree = Plan{
		ID:                  "free",
		Name:                "Free",
		PriceMonthly:        0,
		ExecutionsPerMonth:  1000,
		MaxPipelines:        5,
		MaxStepsPerPipeline: 20,
		RetentionDays:       7,
		MaxWorkers:          2,
	}

	PlanStarter = Plan{
		ID:                  "starter",
		Name:                "Starter",
		PriceMonthly:        4900,
		ExecutionsPerMonth:  50_000,
		MaxPipelines:        25,
		MaxStepsPerPipeline: 50,
		RetentionDays:       30,
		MaxWorkers:          8,
		Features:            []string{"email-support", "custom-domains"},
	}

	PlanProfessional = Plan{
		ID:                  "professional",
		Name:                "Professional",
		PriceMonthly:        19900,
		ExecutionsPerMonth:  500_000,
		MaxPipelines:        0,
		MaxStepsPerPipeline: 0,
		RetentionDays:       90,
		MaxWorkers:          32,
		Features:            []string{"email-support", "custom-domains", "priority-builds", "advanced-analytics"},
	}

	PlanEnterprise = Plan{
		ID:                  "enterprise",
		Name:                "Enterprise",
		PriceMonthly:        0,
		ExecutionsPerMonth:  0,
		MaxPipelines:        0,
		MaxStepsPerPipeline: 0,
		RetentionDays:       365,
		MaxWorkers:          0,
		Features: []string{
			"sso",
			"multi-region",
			"dedicated-infrastructure",
			"sla-guarantee",
			"priority-support",
			"custom-domains",
			"advanced-analytics",
			"audit-log-export",
		},
	}

	// AllPlans is the ordered list of available plans.
	AllPlans = []Plan{PlanFree, PlanStarter, PlanProfessional, PlanEnterprise}
)

Predefined billing plans.

View Source
var ErrLimitExceeded = billingError("execution limit exceeded for current billing period")

ErrLimitExceeded is returned by CheckLimit when a tenant has exhausted their plan's execution quota for the current billing period.

Functions

func CheckLimit added in v0.1.5

func CheckLimit(ctx context.Context, meter UsageMeter, tenantID string) error

CheckLimit is a convenience wrapper for non-HTTP enforcement paths (e.g. gRPC handlers or internal pipeline runners). It returns an error when the tenant has exceeded their plan limit.

Types

type BillingProvider

type BillingProvider interface {
	// CreateCustomer registers a new billing customer for the given tenant.
	CreateCustomer(ctx context.Context, tenantID, email string) (customerID string, err error)
	// CreateSubscription starts a subscription for the customer on the given plan.
	CreateSubscription(ctx context.Context, customerID, planID string) (subscriptionID string, err error)
	// CancelSubscription cancels an active subscription.
	CancelSubscription(ctx context.Context, subscriptionID string) error
	// ReportUsage reports metered usage for a subscription.
	ReportUsage(ctx context.Context, subscriptionID string, quantity int64) error
	// HandleWebhook processes an incoming webhook payload from the payment provider.
	HandleWebhook(ctx context.Context, payload []byte, signature string) error
}

BillingProvider abstracts payment and subscription management.

type EnforcementMiddleware added in v0.1.5

type EnforcementMiddleware struct {
	// contains filtered or unexported fields
}

EnforcementMiddleware wraps an HTTP handler and rejects requests from tenants that have exceeded their plan's execution limit.

func NewEnforcementMiddleware added in v0.1.5

func NewEnforcementMiddleware(meter UsageMeter, getTenantID TenantIDFunc) *EnforcementMiddleware

NewEnforcementMiddleware creates an EnforcementMiddleware. The meter is queried on every request; the getTenantID function is used to extract the current tenant from the request context or headers.

func (*EnforcementMiddleware) Wrap added in v0.1.5

Wrap returns an http.Handler that enforces the tenant's execution limit before delegating to next. Requests without a resolvable tenant ID pass through without enforcement.

type Handler

type Handler struct {
	// contains filtered or unexported fields
}

Handler exposes billing endpoints over HTTP.

func NewHandler

func NewHandler(meter UsageMeter, provider BillingProvider) *Handler

NewHandler creates a new billing HTTP handler.

func (*Handler) RegisterRoutes

func (h *Handler) RegisterRoutes(mux *http.ServeMux)

RegisterRoutes registers billing endpoints on the given mux.

type InMemoryMeter

type InMemoryMeter struct {
	// contains filtered or unexported fields
}

InMemoryMeter is a thread-safe in-memory UsageMeter suitable for tests.

func NewInMemoryMeter

func NewInMemoryMeter() *InMemoryMeter

NewInMemoryMeter creates an InMemoryMeter.

func (*InMemoryMeter) CheckLimit

func (m *InMemoryMeter) CheckLimit(_ context.Context, tenantID string) (bool, int64, error)

CheckLimit checks whether the tenant may run another execution.

func (*InMemoryMeter) GetUsage

func (m *InMemoryMeter) GetUsage(_ context.Context, tenantID string, period time.Time) (*UsageReport, error)

GetUsage returns the usage report for the given period.

func (*InMemoryMeter) RecordExecution

func (m *InMemoryMeter) RecordExecution(_ context.Context, tenantID, pipelineName string) error

RecordExecution records a single execution for the tenant.

func (*InMemoryMeter) SetPlan

func (m *InMemoryMeter) SetPlan(tenantID, planID string)

SetPlan associates a tenant with a billing plan.

type MockBillingProvider

type MockBillingProvider struct {

	// Customers maps tenantID -> customerID.
	Customers map[string]string
	// Subscriptions maps subscriptionID -> planID.
	Subscriptions map[string]string
	// UsageReports collects (subscriptionID, quantity) pairs.
	UsageReports []UsageEntry
	// WebhookPayloads collects raw webhook bodies.
	WebhookPayloads [][]byte

	// Error fields allow tests to inject failures.
	CreateCustomerErr     error
	CreateSubscriptionErr error
	CancelSubscriptionErr error
	ReportUsageErr        error
	HandleWebhookErr      error
	// contains filtered or unexported fields
}

MockBillingProvider is a test double that records calls and returns configurable results.

func NewMockBillingProvider

func NewMockBillingProvider() *MockBillingProvider

NewMockBillingProvider creates a MockBillingProvider ready for use.

func (*MockBillingProvider) CancelSubscription

func (m *MockBillingProvider) CancelSubscription(_ context.Context, subscriptionID string) error

CancelSubscription cancels a mock subscription.

func (*MockBillingProvider) CreateCustomer

func (m *MockBillingProvider) CreateCustomer(_ context.Context, tenantID, _ string) (string, error)

CreateCustomer creates a mock customer.

func (*MockBillingProvider) CreateSubscription

func (m *MockBillingProvider) CreateSubscription(_ context.Context, customerID, planID string) (string, error)

CreateSubscription creates a mock subscription.

func (*MockBillingProvider) HandleWebhook

func (m *MockBillingProvider) HandleWebhook(_ context.Context, payload []byte, _ string) error

HandleWebhook records the webhook payload.

func (*MockBillingProvider) ReportUsage

func (m *MockBillingProvider) ReportUsage(_ context.Context, subscriptionID string, quantity int64) error

ReportUsage records a usage report.

type Plan

type Plan struct {
	ID                  string   `json:"id"`
	Name                string   `json:"name"`
	PriceMonthly        int      `json:"price_monthly"`          // cents
	ExecutionsPerMonth  int64    `json:"executions_per_month"`   // 0 = unlimited
	MaxPipelines        int      `json:"max_pipelines"`          // 0 = unlimited
	MaxStepsPerPipeline int      `json:"max_steps_per_pipeline"` // 0 = unlimited
	RetentionDays       int      `json:"retention_days"`
	MaxWorkers          int      `json:"max_workers"`
	Features            []string `json:"features,omitempty"`
}

Plan represents a billing plan with usage limits.

func PlanByID

func PlanByID(id string) *Plan

PlanByID looks up a plan by its identifier. Returns nil if not found.

func (Plan) IsUnlimited

func (p Plan) IsUnlimited() bool

IsUnlimited reports whether the plan has no execution limit.

type SQLiteMeter

type SQLiteMeter struct {
	// contains filtered or unexported fields
}

SQLiteMeter is a UsageMeter backed by a SQLite database.

func NewSQLiteMeter

func NewSQLiteMeter(db *sql.DB) (*SQLiteMeter, error)

NewSQLiteMeter creates a new SQLiteMeter and initialises the schema.

func (*SQLiteMeter) CheckLimit

func (m *SQLiteMeter) CheckLimit(ctx context.Context, tenantID string) (bool, int64, error)

CheckLimit checks whether the tenant may run another execution.

func (*SQLiteMeter) GetUsage

func (m *SQLiteMeter) GetUsage(ctx context.Context, tenantID string, period time.Time) (*UsageReport, error)

GetUsage returns the usage report for the given period from SQLite.

func (*SQLiteMeter) RecordExecution

func (m *SQLiteMeter) RecordExecution(ctx context.Context, tenantID, pipelineName string) error

RecordExecution records an execution in SQLite.

func (*SQLiteMeter) SetPlan

func (m *SQLiteMeter) SetPlan(tenantID, planID string)

SetPlan associates a tenant with a billing plan.

type StripePlanIDs added in v0.1.5

type StripePlanIDs map[string]string

StripePlanIDs maps plan IDs to Stripe price IDs (monthly). These must be configured to match Stripe dashboard price objects.

type StripeProvider added in v0.1.5

type StripeProvider struct {
	// contains filtered or unexported fields
}

StripeProvider implements BillingProvider using the Stripe API.

func NewStripeProvider added in v0.1.5

func NewStripeProvider(apiKey, webhookSecret string, planPriceIDs StripePlanIDs) *StripeProvider

NewStripeProvider creates a StripeProvider with the given API key, webhook secret, and mapping from plan IDs to Stripe price IDs.

func (*StripeProvider) CancelSubscription added in v0.1.5

func (p *StripeProvider) CancelSubscription(_ context.Context, subscriptionID string) error

CancelSubscription cancels a Stripe subscription at period end.

func (*StripeProvider) CreateCustomer added in v0.1.5

func (p *StripeProvider) CreateCustomer(_ context.Context, tenantID, email string) (string, error)

CreateCustomer creates a new Stripe customer for the given tenant.

func (*StripeProvider) CreateSubscription added in v0.1.5

func (p *StripeProvider) CreateSubscription(_ context.Context, customerID, planID string) (string, error)

CreateSubscription creates a new Stripe subscription for the customer on the given plan.

func (*StripeProvider) HandleWebhook added in v0.1.5

func (p *StripeProvider) HandleWebhook(_ context.Context, payload []byte, signature string) error

HandleWebhook validates the Stripe webhook signature and dispatches known events.

func (*StripeProvider) ReportUsage added in v0.1.5

func (p *StripeProvider) ReportUsage(_ context.Context, _ string, _ int64) error

ReportUsage is a no-op for Stripe fixed-price plans. For metered billing, override this to call the Stripe usage records API.

type Subscription added in v0.1.5

type Subscription struct {
	ID                   string    `json:"id"`
	CustomerID           string    `json:"customer_id"`
	PlanID               string    `json:"plan_id"`
	Status               string    `json:"status"` // active, past_due, canceled, trialing
	CurrentPeriodEnd     time.Time `json:"current_period_end"`
	StripeSubscriptionID string    `json:"stripe_subscription_id,omitempty"`
}

Subscription represents an active or historical billing subscription for a tenant.

type TenantIDFunc added in v0.1.5

type TenantIDFunc func(r *http.Request) string

TenantIDFunc extracts a tenant ID from an incoming HTTP request. The caller provides this so that enforcement is not coupled to any specific authentication scheme.

type UsageEntry

type UsageEntry struct {
	SubscriptionID string
	Quantity       int64
}

UsageEntry records a single usage report.

type UsageMeter

type UsageMeter interface {
	// RecordExecution records a single pipeline execution for the tenant.
	RecordExecution(ctx context.Context, tenantID, pipelineName string) error
	// GetUsage returns the usage report for the given billing period.
	GetUsage(ctx context.Context, tenantID string, period time.Time) (*UsageReport, error)
	// CheckLimit checks whether the tenant is allowed to run another execution
	// and returns the remaining executions in the current period.
	CheckLimit(ctx context.Context, tenantID string) (allowed bool, remaining int64, err error)
}

UsageMeter tracks and queries resource consumption per tenant.

type UsageReport

type UsageReport struct {
	TenantID       string    `json:"tenant_id"`
	Period         time.Time `json:"period"` // first day of billing period
	ExecutionCount int64     `json:"execution_count"`
	PipelineCount  int       `json:"pipeline_count"`
	StepCount      int       `json:"step_count"`
	WorkerPeak     int       `json:"worker_peak"`
}

UsageReport summarizes resource usage for a tenant during a billing period.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL