Exclusive Discount Deal
Upto 50% OFF
Offer ends in:
23 DAYS
|
21 HOURS
|
29 MINS
|
47 SECS
Home / Blog / Metered SaaS Billing with Stripe Connect and Webhook Queues
SaaS & Platforms • Oct 8, 2026

Metered SaaS Billing with Stripe Connect and Webhook Queues

Architect an idempotent, event-driven usage metering pipeline using Redis, Worker queues, and Stripe Connect to process high-concurrency API usage billing.

UPTO 50% OFF
Trending:
BrickTry

Requirement Scope

AI is analyzing your requirement...

Generating custom modules, implementation options, and dynamic clarification questions.

Add Custom Requirement or Module

Add your own specific features, integrations, or components. AI will incorporate them to dynamically generate the next relevant options.

1. Progressive Clarifications

Click to expand & answer

2. Scope Modules & Features (/ Selected)

Click row to expand details · Customize options
✓
✕
Completeness:

Building a metered, consumption-based pricing tier requires a deterministic approach to usage tracking. When thousands of concurrent API requests or compute operations flow through your platform, counting events directly inside a relational database will cause database connection exhaustion, race conditions, and billing drift.

To maintain financial accuracy without sacrificing system performance, you must decouple event ingestion from Stripe's API. This requires an event-driven architecture using Redis as an ingestion buffer, a worker queue for asynchronous aggregation, and Stripe Connect or Stripe Metered Billing to execute monthly invoicing.


Architectural Topology

High-concurrency usage tracking relies on a three-tier design:

  1. Ingestion Layer: API gateways or middleware that push incremental counters to an in-memory datastore with sub-millisecond latency.
  2. Aggregation Layer: Background workers that flush, window, and roll up raw metric events into billable aggregates.
  3. Synchronization Layer: Idempotent cron or event-driven sync jobs that push finalized aggregates to Stripe via the Usage Records API.
[ Client Request ]
       │
       ▼
[ API Gateway / Middleware ]
       │ (Atomic INCRBY)
       ▼
┌─────────────────────────┐
│     Redis Cluster       │ ◄── [ Periodic Flush Worker (Cron / BullMQ) ]
└─────────────────────────┘
                                       │
                                       ▼
                       ┌───────────────────────────────┐
                       │   PostgreSQL Billing Ledger   │
                       └───────────────────────────────┘
                                       │
                                       ▼
                       ┌───────────────────────────────┐
                       │     Stripe Metered API        │
                       └───────────────────────────────┘

1. High-Performance Ingestion with Redis

To capture usage without blocking client threads, avoid writing individual usage rows to PostgreSQL or MySQL on every API call. Instead, utilize Redis HINCRBY or sorted sets to increment counters in memory.

The following TypeScript snippet demonstrates a fast middleware function that records metered feature consumption (e.g., LLM tokens or API call counts) atomically in Redis:

import { Redis } from 'ioredis';
import { Request, Response, NextFunction } from 'express';

const redis = new Redis(process.env.REDIS_URL || 'redis://localhost:6379');

interface MeteredRequest extends Request {
  tenantId?: string;
  meterKey?: string;
  usageQuantity?: number;
}

export async function meterUsageMiddleware(req: MeteredRequest, res: Response, next: NextFunction) {
  const tenantId = req.tenantId;
  const meterKey = req.meterKey || 'api_calls';
  const quantity = req.usageQuantity || 1;

  if (!tenantId) {
    return res.status(400).json({ error: 'Missing tenant context for metering.' });
  }

  const timestampWindow = Math.floor(Date.now() / 3600000); // Hourly window
  const redisKey = `usage:${tenantId}:${meterKey}:${timestampWindow}`;

  try {
    // Atomic increment in Redis; O(1) time complexity
    await redis.hincrby(redisKey, 'count', quantity);
    // Set a TTL of 7 days to prevent memory leaks from stale tenants
    await redis.expire(redisKey, 604800);
    next();
  } catch (error) {
    // Fail open or closed depending on business requirements;
    // here we log and proceed to prevent billing downtime from breaking core app UX.
    console.error(`[Metering Error] Failed to record usage for tenant ${tenantId}:`, error);
    next();
  }
}

2. Idempotent Aggregation & Stripe Sync Worker

At the end of each billing cycle or hourly window, a background worker must flush Redis metrics into a durable ledger and transmit them to Stripe. Idempotency is critical here; network drops or worker retries must never double-bill a customer.

Below is a robust Node.js worker implementation using Stripe's Node SDK and cryptographic idempotency keys derived from the tenant, meter, and time window:

import Stripe from 'stripe';
import { Redis } from 'ioredis';
import crypto from 'crypto';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, { apiVersion: '2023-10-16' });
const redis = new Redis(process.env.REDIS_URL || 'redis://localhost:6379');

interface FlushJob {
  tenantId: string;
  stripeSubscriptionItemId: string;
  meterKey: string;
  windowTimestamp: number;
}

export async function processUsageFlush(job: FlushJob): Promise<void> {
  const { tenantId, stripeSubscriptionItemId, meterKey, windowTimestamp } = job;
  const redisKey = `usage:${tenantId}:${meterKey}:${windowTimestamp}`;

  // Fetch current accumulated count
  const rawCount = await redis.hget(redisKey, 'count');
  const quantity = rawCount ? parseInt(rawCount, 10) : 0;

  if (quantity === 0) return;

  // Generate a deterministic idempotency key to prevent duplicate Stripe billing events
  const idempotencyKey = crypto
    .createHash('sha256')
    .update(`${tenantId}-${stripeSubscriptionItemId}-${windowTimestamp}`)
    .digest('hex');

  try {
    // Send usage record to Stripe Billing API
    await stripe.subscriptionItems.createUsageRecord(
      stripeSubscriptionItemId,
      {
        quantity,
        timestamp: windowTimestamp,
        action: 'increment',
      },
      {
        idempotencyKey,
      }
    );

    // Mark key as synchronized in Redis or delete/archive it
    await redis.hset(redisKey, 'synced', '1');
    console.log(`[Stripe Sync] Successfully billed ${quantity} units for tenant ${tenantId}`);
  } catch (error: any) {
    if (error.code === 'idempotency_key_in_use') {
      console.warn(`[Stripe Sync] Duplicate sync attempt avoided via idempotency key: ${idempotencyKey}`);
      return;
    }
    console.error(`[Stripe Sync Failed] Tenant ${tenantId}:`, error.message);
    throw error; // Trigger worker retry queue
  }
}

3. Data Architecture & Index Strategies

To reconcile discrepancies between Redis, your internal relational ledger, and Stripe's invoices, maintain a persistent audit table in PostgreSQL.

Database Table Column Name Data Type Index Strategy Purpose
usage_ledgers id UUID Primary Key Unique row identifier
usage_ledgers tenant_id VARCHAR(64) B-Tree (Composite) Tenant query partitioning
usage_ledgers meter_key VARCHAR(64) B-Tree (Composite) Feature classification
usage_ledgers window_start TIMESTAMP B-Tree Temporal range queries
usage_ledgers stripe_synced BOOLEAN Partial Index (WHERE synced = false) Fast lookup for failed sync queues

How BrickTry Accelerates & Powers This

Building enterprise-grade metered billing pipelines from scratch requires orchestrating Redis clusters, asynchronous queue workers, Stripe webhooks, and secure database migrations. BrickTry eliminates this infrastructure boilerplate so your engineering team can focus on core product value.

  • BrickTry Lab Sandbox (/lab): Test your Redis ingestion middleware and Stripe webhook handlers instantly in an isolated zero-setup in-browser Node/Vite virtual container runtime, complete with live logs and terminal access.
  • AI-Human Dev Pairing: Let autonomous AI scaffolding generate initial database schemas, Stripe Connect onboarding flows, and BullMQ worker configurations, while our senior full-stack engineering pods review your architecture for edge cases, race conditions, and security vulnerabilities.
  • Interactive Scoping Engine: Break down complex multi-tenant billing requirements into modular architectural milestones, automated schema generators, and production-ready deployment checklists.
  • Unified Importer: Seamlessly import existing GitHub repositories or legacy CodeCanyon billing scripts, automatically refactoring monolithic codebases into modern clean architecture.
  • 100% Source Code Ownership: Maintain complete sovereignty over your intellectual property with full ownership of GitHub repositories, Docker configurations, and PostgreSQL schemas, ensuring zero vendor lock-in.

Build, Test, and Scale This on BrickTry

BrickTry pairs you with autonomous AI scaffolding supervised by dedicated senior full-stack software engineers in an interactive in-browser development sandbox. Test, build, and deploy production-grade software with 100% source code ownership and zero vendor lock-in.

Launch Interactive Requirement Builder →

❤️

Support BrickTry Platform & Engineering Development

Help us build, maintain, and advance our AI engineering platform. Every donation fuels open-source tooling, infrastructure, and continuous improvements.

$
Donor Details
Promote Your Brand / Link Wall

UPI / Credit & Debit Cards / Netbanking
Razorpay
Secure 256-bit encrypted checkout
View Leaderboard & Wall

Hey!

Welcome, Let's chat —
start a new conversation
below.

Recent conversations
See all

Hi ,We’d like to inform you that the Integ...

Abhishek A Agrawal • 1d ago

Abhishek A Agrawal

Back in a few hours