Services About Us Why Choose Us Our Team Development Workflow Technology Stack Case Studies Portfolio Blog Free Guides Shopify Audit ($499) Estimate Project Contact Us
Back to Insights
E-Commerce • Oct 15, 2026 • 5 min read

Building High-Throughput Stripe Webhook Ingestion in Rails 8 with Solid Queue Idempotency

High-throughput Stripe webhook ingestion in Rails 8 requires decoupling HTTP reception from processing using Solid Queue, implementing strict database-backed idempotency constraints via unique transaction hashes, and ...

TV
TechVinta Team
Specialized in Rails, React, Marketplace & Sharetribe Flex Architecture
Verified Technical Guide
Building High-Throughput Stripe Webhook Ingestion in Rails 8 with Solid Queue Idempotency

Direct Answer: Building High-Throughput Stripe Webhook Ingestion in Rails 8 with Solid Queue Idempotency

High-throughput Stripe webhook ingestion in Rails 8 requires decoupling HTTP reception from processing using Solid Queue, implementing strict database-backed idempotency constraints via unique transaction hashes, and returning an immediate 2xx status code to prevent Stripe timeout loops and event retries.

Scaling a SaaS platform to process over 10,000 webhook events per minute from Stripe requires an architecture that values both speed and data integrity. In this production-grade guide, we examine how to architect, secure, and scale a bulletproof Stripe webhook pipeline using the modern Ruby on Rails 8 ecosystem.

The Architecture of High-Volume Webhook Processing

When dealing with thousands of requests per second, your application server's main threadpool must not block on business logic (such as provisioning licenses, sending emails, or updating user subscriptions in PostgreSQL). The standard anti-pattern involves parsing the JSON payload and executing domain logic synchronously inside the controller action. This approach inevitably leads to HTTP 504 Gateway Timeouts, database connection pool exhaustion, and widespread dropped events from Stripe's delivery infrastructure.

Our production architecture solves this by enforcing a three-tier lifecycle:

  • Tier 1: Edge Ingestion & Cryptographic Verification. Nginx/Kamal 2 reverse proxy paired with a lightweight Rails 8 Action Controller that verifies the Stripe-Signature header using constant-time hashing.
  • Tier 2: Asynchronous Enqueueing via Solid Queue. The verified raw payload is handed off instantly to Rails 8’s default database-backed background execution engine (Solid Queue), persisting the event record within an atomic transaction.
  • Tier 3: Idempotent Worker Execution. Background workers safely process the event payload, guarded by strict database unique indexes and pessimistic/optimistic locking strategies to handle Stripe’s at-least-once delivery guarantee.

Step 1: Configuring Rails 8 and Solid Queue for Scale

Rails 8 removes the external Redis dependency by default, leveraging Solid Queue for robust database-backed background processing. For high-throughput webhooks, you must tune your database connection pool and Solid Queue worker concurrency to handle the incoming traffic spikes without exhausting your PostgreSQL or MySQL connection limits.

First, ensure your config/queues.yml is configured to handle webhook workloads on dedicated queues to prevent slow background jobs (like CSV exports) from starving your payment processing pipeline:

# config/queues.yml
production:
  dispatchers:
    - polling_interval: 1
  workers:
    - queues: [default, mailers]
      threads: 5
    - queues: [stripe_webhooks]
      threads: 20
      polling_interval: 0.5

Step 2: The Ingestion Controller and Cryptographic Verification

Never process unverified payloads. The following controller captures the raw request body, verifies the Stripe signature, and persists the raw event to the database inside a transaction before enqueuing the background job.

# app/controllers/webhooks/stripe_controller.rb
module Webhooks
  class StripeController < ActionController::API
    before_action :verify_stripe_signature!

    def create
      # Immediately persist raw event for auditing and deduplication
      event_record = StripeEventLog.create_or_find_by!(
        stripe_event_id: stripe_event.id
      ) do |log|
        log.event_type = stripe_event.type
        log.payload = stripe_event.to_json
        log.status = 'pending'
      end

      if event_record.status == 'processed'
        head :ok and return
      end

      # Enqueue to Solid Queue on the dedicated stripe_webhooks queue
      ProcessStripeEventJob.set(queue: :stripe_webhooks).perform_later(event_record.id)

      head :ok
    rescue ActiveRecord::RecordNotUnique
      # Handles race conditions under extreme concurrency
      head :ok
    end

    private

    def verify_stripe_signature!
      payload = request.body.read
      sig_header = request.env['HTTP_STRIPE_SIGNATURE']
      endpoint_secret = Rails.application.credentials.dig(:stripe, :webhook_secret)

      @stripe_event = Stripe::Webhook.construct_event(
        payload, sig_header, endpoint_secret
      )
    rescue JSON::ParserError => e
      render json: { error: "Invalid payload" }, status: :bad_request
    rescue Stripe::SignatureVerificationError => e
      render json: { error: "Invalid signature" }, status: :bad_request
    end

    attr_reader :stripe_event
  end
end

Step 3: Implementing Strict Idempotency in Solid Queue Workers

Stripe guarantees at-least-once delivery, meaning your application will frequently receive duplicate events for the same state change (e.g., invoice.payment_succeeded delivered twice within milliseconds due to network timeouts). To prevent double-charging or duplicate license creation, your Solid Queue worker must enforce transactional idempotency.

# app/jobs/process_stripe_event_job.rb
class ProcessStripeEventJob < ApplicationJob
  queue_as :stripe_webhooks

  sidekiq_options retry: 5 # If using Sidekiq, or configure Solid Queue retry rules

  def perform(event_log_id)
    event_log = StripeEventLog.find_by(id: event_log_id)
    return unless event_log
    return if event_log.status == 'processed'

    ActiveRecord::Base.transaction do
      # Lock the record row to prevent concurrent worker execution
      event_log.lock!

      if event_log.status == 'processed'
        return
      end

      stripe_event = Stripe::Event.construct_from(
        JSON.parse(event_log.payload)
      )

      case stripe_event.type
      when 'checkout.session.completed'
        handle_checkout_completed(stripe_event.data.object)
      when 'invoice.payment_succeeded'
        handle_payment_succeeded(stripe_event.data.object)
      else
        Rails.logger.info("Unhandled Stripe event type: #{stripe_event.type}")
      end

      event_log.update!(status: 'processed', processed_at: Time.current)
    end
  rescue StandardError => e
    event_log&.update!(status: 'failed', error_message: e.message)
    raise e # Re-raise to trigger Solid Queue retry mechanism
  end

  private

  def handle_checkout_completed(session)
    user = User.find_by(stripe_customer_id: session.customer)
    return unless user

    user.update!(subscription_status: 'active', stripe_subscription_id: session.subscription)
    # Additional business logic: provision resources, send welcome email
  end

  def handle_payment_succeeded(invoice)
    # Process invoice fulfillment safely
  end
end

2026 Engineering Cost, Timeline & Architecture Comparison

When planning or scaling enterprise payment ingestion pipelines in Ruby on Rails, architectural choices dictate long-term maintenance overhead and infrastructure expenses. The following matrix contrasts building custom high-throughput ingestion with legacy approaches and alternative agency execution models.

Metric / Architecture Custom Rails 8 + Solid Queue (TechVinta Standard) Legacy Rails 7 + Redis / Sidekiq Standard Offshore Agency Build
Throughput Capacity 15,000+ webhooks/min (Optimized DB pooling) 8,000–10,000 webhooks/min (Redis memory bound) 1,000–3,000 webhooks/min (Unoptimized N+1 queries)
Infrastructure Footprint Single VPS / Kamal 2 (No Redis required) App Server + Redis Cluster ($120/mo extra) Bloated Kubernetes clusters ($500+/mo)
Development Timeline 2 to 3 weeks (Production-ready hardening) 3 to 5 weeks (Redis configuration overhead) 8 to 12 weeks (Scope creep & handoffs)
Engineering Cost $35 – $65/hr (Senior US-Managed Architecture) $45 – $75/hr (Specialized DevOps required) $8k – $25k fixed bid (High technical debt)
Idempotency Guarantees Database-level uniqueness + Row locking Redis atomic sets (Vulnerable to TTL flushes) In-memory flags (Frequent race conditions)

Accelerate Your Engineering Roadmap with TechVinta

Architecting robust, fault-tolerant financial pipelines requires deep expertise in modern Ruby on Rails 8 primitives, database concurrency control, and zero-downtime deployment strategies. At TechVinta, our senior engineering team specializes in building high-throughput SaaS backends, custom marketplace solutions, and scalable payment infrastructure. We offer flexible fractional architecture and dedicated development squads with guaranteed 4 to 6-hour US timezone overlap, ensuring seamless communication and rapid delivery. Contact TechVinta today to audit your Stripe ingestion pipeline or accelerate your next Rails release.

Frequently Asked Questions

What happens if Stripe retries a webhook while my Rails 8 worker is still processing the previous delivery?

If Stripe retries an event while your worker is actively executing, the second request will hit the ingestion controller. The controller will attempt to create or find the StripeEventLog record. If it already exists and is marked as 'pending' or 'processed', the controller immediately returns a 200 OK. Inside the background worker, row-level locking via event_log.lock! inside an ActiveRecord::Base.transaction ensures that concurrent worker executions serialize safely, preventing duplicate business logic execution.

Why use Solid Queue over Sidekiq for high-volume Stripe webhooks in Rails 8?

Rails 8 ships with Solid Queue as its default backend, utilizing your existing relational database (PostgreSQL/MySQL) for job persistence. For webhook ingestion, this eliminates the need to manage and scale a separate Redis cluster. Because webhook records are already being written to PostgreSQL for auditing and idempotency, storing Solid Queue jobs in the same database simplifies disaster recovery, transactional rollbacks, and backup procedures without sacrificing performance when properly indexed.

How do I handle Stripe webhook testing and local development without exposing my local machine to the public internet?

To test high-throughput Stripe webhooks locally, use the official Stripe CLI. Run stripe listen --forward-to localhost:3000/webhooks/stripe in your terminal. The Stripe CLI securely tunnels incoming webhook events from Stripe's servers directly to your local Rails 8 development environment, providing real-time log output and signature verification matching production behavior.

Interactive Tool

Marketplace Take Rate & Stripe Connect Fee Calculator

Calculate your true net margins after Stripe Connect processing, cross-border surcharges (+1%), FX (~1%), and dispute reserves.

Cross-Border Volume 20%
Gross Platform Rev
$5,000
Total Fees
$1,925
Estimated Net Monthly Margin
$3,075
Effective Take: 6.15%
Senior Rails & Sharetribe Architecture

Building, Scaling, or Modernizing Your Platform?

TechVinta delivers senior engineering ($35–$65/hr) with guaranteed 4–6 hours daily US EST/PST overlap. We specialize in custom Sharetribe Flex marketplaces, Ruby on Rails 8 modernizations, and high-throughput Stripe Connect integrations.

⚡
$35–$65/hr Transparent RatesDirect senior talent, no agency bloat
🇺🇸
4–6h Daily US OverlapLive EST/PST syncs & sprint reviews
🔒
100% Client IP & NDAEnterprise standards & clean code
Book a 15-Min Intro Call Get Free Project Sizing →
Share this article:
TV

Written by TechVinta Team

We are a full-stack development agency specializing in Ruby on Rails, React.js, Vue.js, Flutter, Shopify, and Sharetribe. We write about web development, DevOps, and building scalable applications.

Need Senior Developers?
$35–$65/hr · 4–6h US overlap

Keep Reading

TechVinta Assistant

Online - Ready to help

Hi there!

Need help with your project? We're online and ready to assist.

🍪

We use cookies for analytics to improve your experience. See our Cookie Policy.