• Stars
    star
    816
  • Rank 55,881 (Top 2 %)
  • Language
    Ruby
  • License
    MIT License
  • Created over 12 years ago
  • Updated about 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Stripe webhook integration for Rails applications.

StripeEvent

Build Status Gem Version Code Climate Gem Downloads

StripeEvent is built on the ActiveSupport::Notifications API. Incoming webhook requests are authenticated with the webhook signature. Define subscribers to handle specific event types. Subscribers can be a block or an object that responds to #call.

Install

# Gemfile
gem 'stripe_event'
# config/routes.rb
mount StripeEvent::Engine, at: '/my-chosen-path' # provide a custom path

Usage

# config/initializers/stripe.rb
Stripe.api_key             = ENV['STRIPE_SECRET_KEY']     # e.g. sk_live_...
StripeEvent.signing_secret = ENV['STRIPE_SIGNING_SECRET'] # e.g. whsec_...

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

Subscriber objects that respond to #call

class CustomerCreated
  def call(event)
    # Event handling
  end
end

class BillingEventLogger
  def initialize(logger)
    @logger = logger
  end

  def call(event)
    @logger.info "BILLING:#{event.type}:#{event.id}"
  end
end
StripeEvent.configure do |events|
  events.all BillingEventLogger.new(Rails.logger)
  events.subscribe 'customer.created', CustomerCreated.new
end

Subscribing to a namespace of event types

StripeEvent.subscribe 'customer.card.' do |event|
  # Will be triggered for any customer.card.* events
end

Securing your webhook endpoint

Authenticating webhooks with signatures

Stripe will cryptographically sign webhook payloads with a signature that is included in a special header sent with the request. Verifying this signature lets your application properly authenticate the request originated from Stripe. As of v2.0.0, StripeEvent now mandates that this feature be used. Please set the signing_secret configuration value:

StripeEvent.signing_secret = Rails.application.secrets.stripe_signing_secret

Please refer to Stripe's documentation for more details: https://stripe.com/docs/webhooks#signatures

Support for multiple signing secrets

Sometimes, you'll have multiple Stripe webhook subscriptions pointing at your application each with a different signing secret. For example, you might have both a main Account webhook and a webhook for a Connect application point at the same endpoint. It's possible to configure an array of signing secrets using the signing_secrets configuration option. The first one that successfully matches for each incoming webhook will be used to verify your incoming events.

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

(NOTE: signing_secret= and signing_secrets= are just aliases for one another)

Configuration

If you have built an application that has multiple Stripe accounts--say, each of your customers has their own--you may want to define your own way of retrieving events from Stripe (e.g. perhaps you want to use the account parameter from the top level to detect the customer for the event, then grab their specific API key). You can do this:

class EventFilter
  def call(event)
    event.api_key = lookup_api_key(event.account)
    event
  end

  def lookup_api_key(account_id)
    Account.find_by!(stripe_account_id: account_id).api_key
  rescue ActiveRecord::RecordNotFound
    # whoops something went wrong - error handling
  end
end

StripeEvent.event_filter = EventFilter.new

If you'd like to ignore particular webhook events (perhaps to ignore test webhooks in production, or to ignore webhooks for a non-paying customer), you can do so by returning nil in your custom event_filter. For example:

StripeEvent.event_filter = lambda do |event|
  return nil if Rails.env.production? && !event.livemode
  event
end
StripeEvent.event_filter = lambda do |event|
  account = Account.find_by!(stripe_account_id: event.account)
  return nil if account.delinquent?
  event
end

Note: Older versions of Stripe used event.user_id to reference the Connect Account ID.

Without Rails

StripeEvent can be used outside of Rails applications as well. Here is a basic Sinatra implementation:

require 'json'
require 'sinatra'
require 'stripe_event'

Stripe.api_key = ENV['STRIPE_SECRET_KEY']

StripeEvent.subscribe 'charge.failed' do |event|
  # Look ma, no Rails!
end

post '/_billing_events' do
  data = JSON.parse(request.body.read, symbolize_names: true)
  StripeEvent.instrument(data)
  200
end

Testing

Handling webhooks is a critical piece of modern billing systems. Verifying the behavior of StripeEvent subscribers can be done fairly easily by stubbing out the HTTP signature header used to authenticate the webhook request. Tools like Webmock and VCR work well. RequestBin is great for collecting the payloads. For exploratory phases of development, UltraHook and other tools can forward webhook requests directly to localhost. You can check out test-hooks, an example Rails application to see how to test StripeEvent subscribers with RSpec request specs and Webmock. A quick look:

# spec/requests/billing_events_spec.rb
require 'rails_helper'

describe "Billing Events" do
  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

  describe "customer.created" do
    let(:payload) { file_fixture('evt_customer_created.json').read }
    before(:each) { bypass_event_signature(payload) }

    it "is successful" do
      post '/_billing_events', params: payload
      expect(response).to have_http_status(:success)
      # Additional expectations...
    end
  end
end

Maintainers

Special thanks to all the contributors.

Versioning

Semantic Versioning 2.0 as defined at http://semver.org.

License

MIT License. Copyright 2012-2015 Integrallis Software.

More Repositories

1

hibernate-spring-struts-bookstore

Hibernate+Spring+Struts Training Application
Java
29
star
2

learn-rspec-capybara

A Simple Rails App to Learn RSpec & Capybara
Ruby
21
star
3

rm-tetris

A RubyMotion Tetris Clone
Ruby
17
star
4

rm-super-koalio

SuperKoalio - A RubyMotion/JoyBox Platform Game
Ruby
17
star
5

stripe_saas

Stripe SAAS Rails Engine
Ruby
15
star
6

okonawa

A Simple iOS ToDo App created TDD style w/ RubyMotion and Parse
CSS
13
star
7

drools-dsl

A Simple Drools DSL Example
Java
12
star
8

in_place_jquery_timepickr

A Rails plugin for In Place Time Editing based on Maxime Haineault JQuery Timepickr
JavaScript
12
star
9

server-side-push

Examples for Server-Side Push Presentation
Ruby
8
star
10

blogdiggity

A Github/Asciidoc Powered Blog Engine for Rails 4
Ruby
6
star
11

jqm-spine-sinatra-demo

jQuery Mobile / Spine.js / Sinatra Demo
JavaScript
5
star
12

drools-jbpm

Drools jBPM5 Example
Java
4
star
13

baruco-okonawa

RubyMotion Baruco 2014 Workshop Application
CSS
4
star
14

GAE-Bookmarks

Google App Engine Example Application
Java
4
star
15

geomodel-cassandra-demo

A Rails 4 Demo of the GeoModel Ruby (https://github.com/integrallis/geomodel) Library using Cassandra
Ruby
4
star
16

drools-dtable-upsell

Drools Decision Table Example
Java
4
star
17

integrallis-refinerycms-courses

A Refinery CMS Engine to maintain a training curriculum, schedule and sell courses on location and online
Ruby
3
star
18

Gaelyk-Bookmarks

Gaelyk Example Application
Groovy
3
star
19

Beginning-JBoss-Seam

Source code for the Beginning JBoss Seam Book
Java
3
star
20

excemel

JRuby DSL for XML Building and Manipulation with XPath & XQuery
Ruby
3
star
21

drools-loans

Drools Loan Calculator Example
Java
2
star
22

geomodel

A pure Ruby implementation of the GeoModel concept for enabling Geospatial Queries in Non-SQL environments
Ruby
2
star
23

using_active_scaffold

A 30 minute Active Scaffolding Demo
2
star
24

jboss-seam-bookstore

JBoss Seam Bookstore Sample Application
Java
2
star
25

drools-greeter-5

Basic Drools 5.X Example
Java
2
star
26

motion-todos

Todo Application with RubyMotion
Ruby
2
star
27

hibernate-bookstore

Hibernate Training Application
Java
2
star
28

drools-agenda-groups

Drools Agenda Group Example
Java
2
star
29

drools-accumulate

Example using Drools Accumulate DRL Element
Java
2
star
30

oink

A Sinatra / Cassandra Twitter Clone
Ruby
2
star
31

struts2-basic

Basic Struts 2 Book/Todo to be used with samples.
Java
1
star
32

drools-drl

Drools DRL examples
Java
1
star
33

hibernate-spring-bookstore

Hibernate+Spring Training Application
Java
1
star
34

rcp-tetris

An Eclipse RPC Tetris Clone
Java
1
star
35

drools-recipes

A Recipe Finder written in Drools
Java
1
star
36

weblab

A Rails 3 JavaScript/JQuery Snippet Lab
JavaScript
1
star
37

drools-genealogy-guvnor-client

Java Client for a Drools Guvnor Rule Package using KnowledgeAgent
Java
1
star
38

struts2-day1

Code for Day 1 of Struts 2 Class
Java
1
star
39

swing-and-a-miss

A TDD Refactoring of a Swing Application
Java
1
star
40

jruby-java3d

A JRuby example using the Java 3D API
Ruby
1
star
41

jquery-khronos

A jQuery Time Picker
JavaScript
1
star
42

drools-loans-lab

Drools Loans Lab Example
Java
1
star
43

drools-genealogy

A Simple JBoss Drools Genealogy Tree Walker
Java
1
star
44

drools-nim

JBoss Drools version of the Combinatory Children's Game Nim
Java
1
star
45

learning-ruby-metaprogramming

Simple Metaprogramming Examples in Ruby
Ruby
1
star
46

drools-oopath

An Example of Drools OO Path Usage
Java
1
star
47

GroovyStateMachine

A groovy port of the aasm (acts_as_state_machine) library
Groovy
1
star
48

rss_news_reader

RSS News Reader - Ruby on Rails Training Application
Ruby
1
star
49

rm-todos

RubyMotion Todos App
Ruby
1
star
50

learning-ruby

Basic Ruby Examples from Integrallis' Beginning Ruby Course
Ruby
1
star
51

grails-gsp-embed-resources

Plugin to embed dynamic content into resources.
1
star
52

drools-permutations

A Simple example of Cross-Products in Drools
Java
1
star
53

grails_web_flow_demo

This is a demo and presentation on using Grails Web Flow.
JavaScript
1
star