Billing Service
The Billing service manages Stripe subscriptions and maps them to GospeLib entitlements. It receives Stripe webhook events, maintains subscription state in PostgreSQL, and caches entitlement lookups in Redis for the gateway.
Quick Reference
| Property | Value |
|---|---|
| Port | 8300 |
| Language | Go |
| Framework | Chi v5 |
| Module path | github.com/gospelib/main/services/billing |
| Entry point | cmd/server/main.go |
| Payment provider | Stripe |
| Data store | PostgreSQL (gl_subscriptions, gl_stripe_events) |
Responsibilities
- Stripe webhook handling — Receives and processes subscription lifecycle events
- Subscription management — Tracks plan status (active, trialing, canceled, past_due)
- Entitlement mapping — Derives feature access from subscription plans
- Entitlement caching — Pushes entitlements to Redis (60s TTL) for gateway lookups
- Idempotency — Deduplicates webhook events via
gl_stripe_eventstable
Subscription Plans
Plans are defined in configuration, not hardcoded:
| Plan | Price | Key Entitlements |
|---|---|---|
| Reader (free) | $0 | scriptures_read, basic_search, topical_guide_browse |
| Scholar | $7.99/mo or $79.99/yr | All Reader + interlinear_hebrew_greek, manuscript_witnesses, ai_features, knowledge_graph_explorer |
| Academic | $14.99/mo or $149.99/yr | All entitlements (unlimited) |
Rate Limits by Plan
| Resource | Free | Scholar | Academic |
|---|---|---|---|
| AI requests | 5/hour | 50/hour | 200/hour |
| Search | 20/min | 200/min | 1000/min |
Running Locally
cd services/billing
go run ./cmd/server
The service starts on port 8300 and requires PostgreSQL to be running.
Environment Variables
| Variable | Default | Description |
|---|---|---|
GOSPELIB_BILLING_PORT | 8300 | HTTP listen port |
GOSPELIB_BILLING_DATABASE_URL | postgres://localhost:5432/gospelib | PostgreSQL connection string |
GOSPELIB_BILLING_STRIPE_SECRET_KEY | — | Stripe API secret key |
GOSPELIB_BILLING_STRIPE_WEBHOOK_SECRET | — | Stripe webhook signing secret |
GOSPELIB_BILLING_REDIS_URL | redis://localhost:6380 | Redis for entitlement caching |
Health Check
curl http://localhost:8300/health
# {"status": "ok"}
Entry Point Pattern
Standard Go service entry point at cmd/server/main.go:
- Configure Zerolog (JSON structured logging)
- Load config from environment variables
- Initialize PostgreSQL connection + run migrations
- Load plan configuration
- Create Chi router with middleware
- Register routes — health, webhook (public), billing endpoints (authenticated)
- Start HTTP server in a goroutine
- Wait for
SIGINT/SIGTERM - Graceful shutdown with 10-second context
PostgreSQL Schema
CREATE TABLE gl_subscriptions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES gl_users(id),
stripe_sub_id TEXT UNIQUE NOT NULL,
plan_id TEXT NOT NULL,
status TEXT NOT NULL, -- active | trialing | canceled | past_due
current_period_start TIMESTAMPTZ NOT NULL,
current_period_end TIMESTAMPTZ NOT NULL,
cancel_at_period_end BOOLEAN DEFAULT FALSE,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE gl_stripe_events (
stripe_event_id TEXT PRIMARY KEY, -- idempotency key
event_type TEXT NOT NULL,
processed_at TIMESTAMPTZ DEFAULT NOW()
);
Docker
FROM golang:1.25-alpine AS builder
WORKDIR /build
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /app ./cmd/server
FROM alpine:3.20
RUN apk --no-cache add ca-certificates
COPY --from=builder /app /app
EXPOSE 8300
ENTRYPOINT ["/app"]
Related Pages
- Stripe Integration — webhook handling and entitlement mapping
- Auth Service — user management and JWT middleware
- Architecture > Security > Entitlements — plan-based authorization