Open-source project
integrallis/stripe_event avatar
integrallis/stripe_event

StripeEvent turns Stripe webhooks into ActiveSupport notifications

Stripe webhook integration for Rails applications.

855 stars107 forksRubyMIT

At a glance

What is it?
stripe_event is a small Rails gem that mounts a webhook endpoint, verifies the Stripe signature, and republishes each event through ActiveSupport::Notifications so subscribers can be blocks or objects. It suits Rails shops that need several event types handled by separate classes.
Who is it for?
Adopt stripe_event when a Rails application already uses ActiveSupport::Notifications and wants one mountable endpoint with mandatory signature verification, a per-tenant `event_filter` and testable subscribers. Do not adopt it for a non-Rails service that only needs a single webhook verified once, since the Sinatra path in the README is a sketch rather than a framework integration.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 156 days ago.
What is it written in?
Mainly Ruby, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A webhook endpoint that republishes through Rails notifications

The central idea is small and worth understanding before anything else. StripeEvent is built on the ActiveSupport::Notifications API, so instead of exposing one controller method you must write, it mounts a Rails engine, decodes the incoming payload, and instruments a notification per event. Subscribers then attach to that notification the way any other Rails instrumentation subscriber would. The README describes subscribers as either a block or an object that responds to `#call`. For a Rails team this means a webhook handler behaves like the rest of the application's instrumentation: it can be observed, it can be faked in tests, and it does not need its own request plumbing.

Signature verification stopped being optional at v2.0.0

The security posture is the detail that decides whether you can deploy this. Stripe signs webhook payloads cryptographically and includes the signature in a special header on the request, and verifying it is how your application authenticates that the payload really came from Stripe. The README states plainly that as of v2.0.0, StripeEvent now mandates that this feature be used, and instructs you to set the `signing_secret` configuration value. That is a breaking change the gem owner chose to enforce rather than warn about. The consequence for a new installation is simple: there is no configuration where an unsigned payload is accepted, so a deployment that forgets the secret fails loudly instead of quietly trusting the internet.

Mounting the engine and pointing it at your secrets

Setup is three edits. Add the gem to your Gemfile:

ruby
gem 'stripe_event'

Mount the engine at a path you choose in `config/routes.rb`:

ruby
mount StripeEvent::Engine, at: '/my-chosen-path'

Then set the API key and the signing secret in an initializer, ideally from the environment:

ruby
Stripe.api_key             = ENV['STRIPE_SECRET_KEY']     # e.g. sk_live_...
StripeEvent.signing_secret = ENV['STRIPE_SIGNING_SECRET'] # e.g. whsec_...

That path is what you configure as the webhook endpoint inside Stripe, and the signature secret has to match the one Stripe shows you for that endpoint. The README also shows the same secret read from `Rails.application.secrets.stripe_signing_secret`, so older applications that keep secrets in the Rails secrets store can stay on that pattern.

Subscribers as blocks, objects and namespaces

How you bind a handler is your choice, and the README shows all three forms. The simplest is a block inside the configuration block, where the event object exposes the parsed Stripe types:

ruby
StripeEvent.configure do |events|
  events.subscribe 'charge.failed' do |event|
    # Define subscriber behavior based on the event object
    event.class       #=> Stripe::Event
    event.type        #=> "charge.failed"
    event.data.object #=> #<Stripe::Charge:0x3fcb34c115f8>
  end

  events.all do |event|
    # Handle all event types - logging, etc.
  end
end

For anything with state, a class that responds to `#call` works just as well, and the README includes one that takes a logger in its constructor. Namespace subscriptions are supported too, with a trailing dot matching a family of event types such as `customer.card.`, which is triggered for any `customer.card.*` event.

Several signing secrets and a per-tenant event filter

Plural accounts need more than one secret. The README describes the case of multiple Stripe webhook subscriptions pointing at the same application, each with its own signing secret, such as a main account webhook and a Connect application webhook aimed at the same endpoint. For that, `signing_secrets` accepts an array, and the first entry that successfully matches an incoming webhook is the one used to verify it:

ruby
StripeEvent.signing_secrets = [
  Rails.application.secrets.stripe_account_signing_secret,
  Rails.application.secrets.stripe_connect_signing_secret,
]

The README notes that `signing_secret=` and `signing_secrets=` are aliases for one another, so the singular form still works for a single secret. The ordering of the array therefore matters, since matching stops at the first success. The related option for multi-tenant setups is `event_filter`. If each of your customers has their own Stripe account, you may want to detect the account from the event and then fetch that customer's API key, which the README demonstrates with a filter object whose `#call` method sets `event.api_key` and returns the event. The same hook is where you drop events you do not want to handle, because returning `nil` from the filter ignores the event. The README gives two examples of that, one ignoring test webhooks in production with `return nil if Rails.env.production? && !event.livemode`, and another ignoring events for a delinquent account. Note that the filter runs before your subscribers, so a filter that raises will block the whole delivery.

Testing subscribers by stubbing the signature

Webhook handling is worth testing properly, and the README is specific about how. It suggests stubbing out the HTTP signature header used to authenticate the webhook request, with Webmock and VCR named as tools that work well, and RequestBin for collecting payloads and UltraHook for forwarding webhook requests to localhost during exploratory development. It also points at test-hooks, an example Rails application showing how to test subscribers with RSpec request specs and Webmock. The pattern in the spec is to stub the event construction rather than the whole HTTP layer:

ruby
def bypass_event_signature(payload)
  event = Stripe::Event.construct_from(JSON.parse(payload, symbolize_names: true))
  expect(Stripe::Webhook).to receive(:construct_event).and_return(event)
end

That keeps the signature out of the test while leaving your subscriber logic real, which is the part you actually want covered.

Using the gem outside Rails, and what it costs to maintain

The gem is not Rails only, though the Rails path is the documented one. The README shows a Sinatra implementation that requires the gem, subscribes to a `charge.failed` event, parses the request body and calls `StripeEvent.instrument(data)` from a POST route. Treat that as a sketch of the mechanism rather than a framework integration. On maintenance, the project is MIT licensed and is not archived, with the last push on 2026-05-20 and three releases published the same day: v2.13.0, v2.14.0 and v2.15.0. The repository carries a gemspec, a Rakefile, an Appraisals file and a `gemfiles/` directory, which is the usual setup for testing against several Rails versions. The maintainers are listed as Ryan McGeary, Pete Keen and Danny Whalen.

The alternative: verifying the payload in your own controller

The obvious alternative is to skip the engine entirely. The Stripe library exposes `Stripe::Webhook.construct_event`, and the gem's own test suite uses that method, so an application could mount a plain Rails route, read the signature header itself, construct the event and dispatch it to its own code. The difference in approach is what you take on. With StripeEvent you get signature verification, ActiveSupport::Notifications integration, subscription by event type and by namespace, an `event_filter` hook and a testing path, all from a mounted engine. With the manual route you keep control of the request lifecycle but write the verification, the dispatch and the test scaffolding yourself. For an application that already routes every provider through ActiveSupport::Notifications, the manual route is a small amount of code. For an application with several event types and several tenant accounts, the gem has already made the decisions you would otherwise repeat.

Editorial conclusion

Adopt stripe_event when a Rails application already uses ActiveSupport::Notifications and wants one mountable endpoint with mandatory signature verification, a per-tenant `event_filter` and testable subscribers. Do not adopt it for a non-Rails service that only needs a single webhook verified once, since the Sinatra path in the README is a sketch rather than a framework integration. Verify first that your Stripe account issues a signing secret you can store, and remember that the signature check has been mandatory since v2.0.0, so an endpoint without `signing_secret` will not authenticate incoming payloads.

Frequently asked questions

How do I install and mount the stripe_event gem in Rails?

Add `gem 'stripe_event'` to the Gemfile and mount the engine in `config/routes.rb` with `mount StripeEvent::Engine, at: '/my-chosen-path'`. Then set `Stripe.api_key` and `StripeEvent.signing_secret` in an initializer.

Is a Stripe signing secret required to use stripe_event?

Yes. The README states that as of v2.0.0 the feature is mandated, so you must set the `signing_secret` configuration value for incoming payloads to be authenticated.

Can one endpoint handle several Stripe webhook signing secrets?

Yes. Set `StripeEvent.signing_secrets` to an array of secrets, for example a main account secret and a Connect application secret, and the first one that matches an incoming webhook is used to verify it.

How do I ignore certain Stripe events in stripe_event?

Set `StripeEvent.event_filter` and return `nil` for events you want skipped, such as test webhooks in production. The filter runs before subscribers and can also attach a per-account API key to the event.

How can I test stripe_event subscribers without calling Stripe?

Stub the signature by making `Stripe::Webhook.construct_event` return an event built from a fixture payload, as shown in the gem's RSpec example. The README also names Webmock, VCR, RequestBin and UltraHook as supporting tools.

Official sources

  1. integrallis/stripe_event on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/integrallis-stripe-event.svg)](https://hysenlabs.com/projects/integrallis-stripe-event)

Was this useful?