Understanding Stripe Webhooks for SaaS Billing

Understanding Stripe Webhooks for SaaS Billing

Stripe webhooks are the backbone of any SaaS billing system. Here's how to handle them correctly.

Essential Webhooks

These are the events you must handle:

  • customer.subscription.created
  • customer.subscription.updated
  • customer.subscription.deleted
  • invoice.payment_succeeded
  • invoice.payment_failed

Implementation Pattern

class Payment::WebhooksController < ApplicationController
  skip_before_action :verify_authenticity_token

  def create
    event = Stripe::Webhook.construct_event(
      request.body.read,
      request.env["HTTP_STRIPE_SIGNATURE"],
      Rails.application.credentials.dig(:stripe, :webhook_secret)
    )

    handle_event(event)
    head :ok
  end
end

Common Pitfalls

  1. Not verifying signatures — always verify webhook authenticity
  2. Not handling idempotency — Stripe may send the same event multiple times
  3. Ignoring failed payments — set up dunning emails

Testing Locally

Use the Stripe CLI to forward events to your local server:

stripe listen --forward-to localhost:3000/payment/webhooks