stripemeter
stripemeter copied to clipboard
Stripe-native usage metering with real-time cost projections, exactly-once processing, and invoice parity guarantees.
StripeMeter — pre-invoice parity for Stripe usage billing
v0.4.0: Production-readiness pack
See v0.4.0 Release Notes and Operator Runbook.
Open in Codespaces · 5-min Quickstart · Run the Parity Demo · Try the Stripe Test Clocks Demo
What’s new in v0.4.0
- End-of-cycle parity demo (Stripe Test Clocks)
-
Replay API —
POST /v1/replaywith dry-run/apply; watermark/cursor semantics - Shadow Mode — test-environment pushes with deterministic idempotency keys
- /metrics + Prometheus + Grafana dashboard
- ALERTS.md + RECONCILIATION.md runbook
Try in 5 minutes → Verify in 30 seconds
git clone https://github.com/geminimir/stripemeter && cd stripemeter
cp .env.example .env && docker compose up -d && pnpm -r build
pnpm db:migrate && pnpm dev
curl -fsS http://localhost:3000/health/ready | jq . || true
TENANT_ID=$(uuidgen 2>/dev/null || cat /proc/sys/kernel/random/uuid) bash examples/api-calls/send.sh
curl -fsS http://localhost:3000/metrics | head -n 30 # duplicate counted once
What StripeMeter isn't
- Not a pricing or entitlement layer (use a pricing stack like Autumn; StripeMeter ensures usage numbers are correct).
- Not a data warehouse.
- Throughput targets: laptop p95 ingest ≤ 25 ms, late-event replay (10k) ≤ 2 s. Scale with queue/workers for higher volumes.
What it is
A small service you run next to your app: it dedupes retries, handles late events with watermarks, keeps running counters, and pushes only the delta to Stripe so totals stay correct. A reconciliation loop + metrics catch drift before invoice close.
Who it’s for
- SaaS teams on Stripe usage-based pricing
- Engineers who need correct usage totals and early drift detection
What it is / isn’t
It is
- A metering pipeline: ingest → dedupe → aggregate → reconcile
- A correctness guard for Stripe usage billing (no surprise invoices)
-
Operator-ready:
/health,/metrics, drift tolerance, runbooks
It isn’t
- A payment processor or replacement for Stripe Billing
- A pricing engine/UI (those are optional extras; core is correctness)
Quickstart
(Optional preflight: bash scripts/preflight.sh)
pnpm i -w
cp .env.example .env
docker compose up -d
pnpm -r build
pnpm db:migrate
pnpm dev
After services are up:
- Readiness:
GET http://localhost:3000/health/ready - Metrics:
GET http://localhost:3000/metrics - List events:
curl -s "http://localhost:3000/v1/events?tenantId=your-tenant-id&limit=10" | jq
Verify it worked (30-sec demo)
# 1) Health (should be healthy or degraded)
curl -fsS http://localhost:3000/health/ready | jq . || true
# 2) Idempotency demo: send the SAME event twice (counts once)
# TENANT_ID will be generated if unset
TENANT_ID=$(uuidgen 2>/dev/null || cat /proc/sys/kernel/random/uuid) bash examples/api-calls/send.sh
# 3) Check metrics (should reflect one accepted ingest)
curl -fsS http://localhost:3000/metrics | head -n 30
If this clarified drift/idempotency, please ⭐ the repo and open an issue with what you tried — it guides the roadmap.
Micro-proof numbers (optional quick check)
- p95 ingest latency: ~10–25 ms
- Re-aggregation of 10k late events: ≤ 2 s
- Duplicate events inside 24 h idempotency window: 0 double-counts
Production checklist
- [x] Exact-once effect: idempotency window (duplicates won’t double-count)
- [x] Late events handled via watermarks + re-aggregation
- [x] Delta writes to Stripe (no over-reporting)
- [x] Health endpoints + structured logs +
/metrics - [x] Prometheus scrape + Grafana dashboard + alert recipes
- [x] Replay via API for safe reprocessing
- [x] Shadow Mode for safe test pushes
- [x] Triage & repair runbook with copy-paste commands
Reproduce locally:
# p95 for POST /v1/events/ingest (100 concurrent for 30s)
npx autocannon -m POST -H 'content-type: application/json' \
-b '{"events":[{"tenantId":"your-tenant-id","metric":"api_calls","customerRef":"c1","quantity":1,"ts":"2025-01-01T00:00:00Z"}]}' \
http://localhost:3000/v1/events/ingest
# Spot-check metrics after a short send
curl -s http://localhost:3000/metrics | grep -E "http_request_duration|process_" || true
Configure metrics (optional)
Put a tiny config in examples/config/stripemeter.config.ts to map metric → counter and choose a watermarkWindowSeconds.
Shadow Mode
Shadow Mode lets you post usage to Stripe’s test environment in parallel without affecting live invoices.
- Set
STRIPE_TEST_SECRET_KEYin your environment (in addition toSTRIPE_SECRET_KEY). - Mark a price mapping with
shadow=trueand provideshadowStripeAccount,shadowPriceId, and optionallyshadowSubscriptionItemId. - The writer routes these to the Stripe test client and uses deterministic idempotency keys.
- Live invoices remain unaffected; live write logs are not updated for shadow pushes.
- Metrics:
shadow_usage_posts_total,shadow_usage_post_failures_total. - Guardrails: if
shadow=truebutSTRIPE_TEST_SECRET_KEYis missing, pushes are skipped with warnings.
Pick your case (examples)
- API calls:
bash examples/api-calls/verify.sh - Seats:
bash examples/seats/verify.sh
Each script checks health, sends a duplicate event with an explicit idempotency key, and prints the first lines of /metrics so you can see it counted once.
StripeMeter is a Stripe-native usage metering system focused on correctness and operability. Built by developers who believe customers should be able to verify what they’re billed for.
Why StripeMeter?
- Correct usage totals via idempotent ingest and late-event handling
- Pre-invoice parity within ε, monitored by alerts; see the runbook
- Fresh counters and delta pushes to Stripe to avoid over-reporting
- Operator-grade: health, metrics, dashboards, and alert recipes
What Makes StripeMeter Special
Unlike other billing solutions, StripeMeter is designed around three core principles:
- Transparency First: Customers should never be surprised by their bill
- Developer Experience: Building usage-based pricing should be delightful
- Community Driven: Built by the community, for the community
Adopters wanted (2 slots this week).
If you run Stripe usage-based pricing, I’ll pair for 30 minutes to wire one meter in staging or set up a nightly replay. You’ll get priority on your must-have knobs and a thank-you in the next release notes.
→ Open a discussion: “Staging adopter” or DM via the repo email.
Architecture
┌─────────────┐ ┌──────────┐ ┌──────────────┐
│ Clients │────▶│ Ingest │────▶│ Events │
│ (SDK/HTTP) │ │ API │ │ (Postgres) │
└─────────────┘ └──────────┘ └──────────────┘
│ │
▼ ▼
┌──────────┐ ┌──────────────┐
│ Queue │────▶│ Aggregator │
│ (Redis) │ │ Worker │
└──────────┘ └──────────────┘
│
▼
┌──────────────┐
│ Counters │
│(Redis + PG) │
└──────────────┘
│
┌────────────────┼────────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Stripe │ │ Alerts │ │ Customer │
│ Writer │ │ & Caps │ │ Widget │
└──────────┘ └──────────┘ └──────────┘
Project Structure
stripemeter/
├── packages/
│ ├── core/ # Shared types, schemas, utilities
│ ├── database/ # Database layer (Drizzle ORM + Redis)
│ ├── pricing-lib/ # Pricing calculation engine
│ ├── sdk-node/ # Node.js SDK
│ └── sdk-python/ # Python SDK
├── apps/
│ ├── api/ # REST API (Fastify)
│ ├── workers/ # Background workers (BullMQ)
│ ├── admin-ui/ # Admin dashboard (React)
│ └── customer-widget/ # Embeddable widget (React)
└── infra/ # Infrastructure configs
Quick Start
Get StripeMeter running in under 5 minutes
One-Command Setup
# Clone and setup everything automatically
git clone https://github.com/geminimir/stripemeter.git
cd stripemeter && ./scripts/setup.sh
That's it! The setup script will:
- Check prerequisites (Node.js 20+, pnpm, Docker)
- Install dependencies
- Start infrastructure services
- Run database migrations
- Create example configuration
Manual Setup (if you prefer)
Click to expand manual installation steps
- Prerequisites: Node.js 20+, pnpm 8+, Docker
-
Install:
pnpm install -
Configure: Copy
.env.exampleto.envand add your Stripe keys -
Infrastructure:
docker compose up -d -
Database:
pnpm db:migrate -
Start:
pnpm dev
You're Ready!
-
API:
http://localhost:3000(with Swagger docs at/docs) -
Admin Dashboard:
http://localhost:3001 -
Customer Widget Demo:
http://localhost:3002
Try the Interactive Demo
Experience StripeMeter in action with our realistic SaaS demo:
cd demo/cloudapi-saas
./demo-start.sh
The demo showcases:
- Real-time usage tracking with live cost updates
- Multiple pricing tiers (Free, Pro, Enterprise)
- Interactive API testing with immediate billing feedback
- Usage simulation tools for different traffic patterns
- Complete billing transparency that customers love
Perfect for understanding how StripeMeter integrates with your SaaS application!
Core Concepts
Events (Immutable Ledger)
Every usage event is stored with a deterministic idempotency key. Events are never deleted or modified - corrections are made through adjustments.
Counters (Materialized Aggregations)
Pre-computed aggregations (sum/max/last) by tenant, metric, customer, and period. Updated in near-real-time by the aggregator worker.
Watermarks (Late Event Handling)
Each counter maintains a watermark timestamp. Events arriving within the lateness window (default 48h) trigger re-aggregation. Later events become adjustments.
Delta Push (Stripe Synchronization)
The writer tracks pushed_total per subscription item and only sends the delta to Stripe, ensuring idempotent updates even after retries.
Reconciliation (Trust but Verify)
Hourly comparison of local totals vs Stripe reported usage. Differences beyond epsilon (0.5%) trigger investigation and suggested adjustments.
Pricing Simulator
Test and optimize your pricing strategy before going live
The StripeMeter pricing simulator helps you validate billing logic, compare pricing models, and ensure customers are never surprised by their bills.
Quick Example
import { InvoiceSimulator } from '@stripemeter/pricing-lib';
const simulator = new InvoiceSimulator();
// Compare tiered vs volume pricing for 25,000 API calls
const tieredPrice = simulator.simulate({
customerId: 'test',
periodStart: '2024-01-01',
periodEnd: '2024-02-01',
usageItems: [{
metric: 'api_calls',
quantity: 25000,
priceConfig: {
model: 'tiered',
currency: 'USD',
tiers: [
{ upTo: 10000, unitPrice: 0.01 },
{ upTo: 50000, unitPrice: 0.008 },
{ upTo: null, unitPrice: 0.005 }
]
}
}]
});
console.log(`Tiered pricing: $${tieredPrice.total}`); // $220
📖 Complete Documentation
- Simulator Getting Started - Complete guide with examples
- Pricing Scenarios - Real-world use cases and comparisons
- Example Code - Runnable examples for all pricing models
Why Use the Simulator?
✅ Validate pricing accuracy - Test before customers see bills
✅ Compare pricing models - Tiered vs Volume vs Graduated
✅ Optimize revenue - Find the best pricing for your segments
✅ Handle edge cases - Test zero usage, tier boundaries, credits
✅ Enterprise scenarios - Multi-metric billing with commitments
Usage Examples
Track Usage with SDKs
Node.js SDK
import { createClient } from '@stripemeter/sdk-node';
const client = createClient({
apiUrl: 'http://localhost:3000',
tenantId: 'your-tenant-id',
customerId: 'cus_ABC123'
});
// Track a single event
await client.track({
metric: 'api_calls',
customerRef: 'cus_ABC123',
quantity: 100,
meta: { endpoint: '/v1/search', region: 'us-east-1' }
});
// Get live usage and cost projection
const usage = await client.getUsage('cus_ABC123');
const projection = await client.getProjection('cus_ABC123');
console.log(`Current usage: ${usage.metrics[0].current}`);
console.log(`Projected cost: $${projection.total}`);
Python SDK
from stripemeter import StripeMeterClient
client = StripeMeterClient(
api_url="http://localhost:3000",
tenant_id="your-tenant-id",
customer_id="cus_ABC123"
)
# Track usage
client.track(
metric="api_calls",
customer_ref="cus_ABC123",
quantity=100,
meta={"endpoint": "/v1/search", "region": "us-east-1"}
)
# Get projections
projection = client.get_projection("cus_ABC123")
print(f"Projected cost: ${projection.total}")
REST API
# Ingest usage events
curl -X POST http://localhost:3000/v1/events/ingest \
-H "Content-Type: application/json" \
-d '{
"events": [{
"tenantId": "your-tenant-id",
"metric": "api_calls",
"customerRef": "cus_ABC123",
"quantity": 100,
"ts": "2025-01-16T14:30:00Z"
}]
}'
# Get cost projection
curl -X POST http://localhost:3000/v1/usage/projection \
-H "Content-Type: application/json" \
-d '{"tenantId": "your-tenant-id", "customerRef": "cus_ABC123"}'
Embed the Customer Widget
<!-- Add to your customer dashboard -->
<div id="usage-widget"></div>
<script src="https://cdn.stripemeter.io/widget/v1/stripemeter-widget.umd.js"></script>
<script>
StripeMeterWidget.initStripeMeterWidget('usage-widget', {
apiUrl: 'https://api.stripemeter.io',
tenantId: 'your-tenant-id',
customerId: 'cus_ABC123',
theme: 'light' // or 'dark'
});
</script>
Contributing
StripeMeter is built by the community, for the community.
Ways to Contribute
- Found a bug? Open an issue
- Have an idea? Start a discussion
- Improve docs - Even fixing typos helps!
- Add tests - Help us improve reliability
- Design improvements - Make StripeMeter more beautiful
- New features - Check our roadmap
Quick Contribution Guide
-
Fork the repo and create your branch:
git checkout -b my-amazing-feature - Make your changes and add tests if needed
-
Run the tests:
pnpm test -
Commit with a clear message:
git commit -m "Add amazing feature" - Push and create a PR - we'll review it quickly!
Testing & Quality
We maintain high code quality standards:
# Run all tests
pnpm test
# Type checking
pnpm typecheck
# Linting
pnpm lint
# End-to-end tests
pnpm test:e2e
Deployment
One-Click Deploy
Docker Production
# Production deployment
docker compose -f docker-compose.prod.yml up -d
# With monitoring stack
docker compose -f docker-compose.prod.yml --profile monitoring up -d
Kubernetes
# Apply all manifests
kubectl apply -k infra/k8s/
# Or use Helm
helm install stripemeter ./charts/stripemeter
Performance & Monitoring
Production SLOs:
- Ingest latency p99 ≤ 200ms
- Projection staleness ≤ 60s
- Reconciliation accuracy ≥ 99.5%
- Uptime ≥ 99.9%
Built-in Observability:
- Prometheus metrics
- Structured logging
- Distributed tracing
- Health check endpoints
- Alerts: see ops/ALERTS.md and the Reconciliation Runbook
License
StripeMeter is MIT licensed. Use it, modify it, distribute it - we believe in open source!
Acknowledgments
Built with ❤️ by the open-source community. Special thanks to:
- Stripe for the amazing payments platform
- All our contributors
If StripeMeter helps your business, please give us a star!
Star on GitHub • Documentation • Community
Made with ❤️ by developers who believe in billing transparency