SquareEvent
SquareEvent 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
.
The gem is based on the excellent Stripe Event work from Integrallis Software, it has been adapted and re-written to work with Square webhooks.
Install
# Gemfile
gem 'square_event'
# config/routes.rb
mount SquareEvent::Engine, at: '/my-chosen-path' # provide a custom path
Usage
# config/initializers/square_event.rb
SquareEvent.signing_secret = Rails.application.credentials.square[Rails.env][:webhook_secret]
SquareEvent.notification_url = Rails.application.credentials.square[Rails.env][:webhook_url]
SquareEvent.configure do |events|
events.subscribe 'payment.created' do |event|
# Define subscriber behavior based on the event object
event.class #=> SquareEvent::Event
event.type #=> "payment.created"
event.data #=> data": { "type": "payment", "id": "KkAkhdMs...
end
events.all do |event|
# Handle all event types - logging, etc.
end
end
Subscriber or interactor 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
SquareEvent.configure do |events|
events.all BillingEventLogger.new(Rails.logger)
events.subscribe 'customer.created', CustomerCreated.new
end
Subscribing to a namespace of event types
SquareEvent.subscribe 'customer.' do |event|
# Will be triggered for any customer.* events
end
Securing your webhook endpoint
Authenticating webhooks with signatures
Square 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 Square. SquareEvent mandates that this is used for every request. Please set the signing_secret
and notification_url
configuration values:
SquareEvent.signing_secret = Rails.application.credentials.square[Rails.env][:webhook_secret]
SquareEvent.notification_url = Rails.application.credentials.square[Rails.env][:notification_url]
Please refer to Square's documentation for more details: https://developer.squareup.com/docs/webhooks-api/validate-notifications
Sandbox and live mode
If you'd like to ignore particular webhook events (perhaps to ignore test webhooks in production, you can do so by returning nil
in your custom event_filter
. For example:
SquareEvent.event_filter = lambda do |event|
return nil if Rails.env.production? && !event.sandbox?
event
end
Without Rails
SquareEvent can be used outside of Rails applications as well. Here is a basic Sinatra implementation:
require 'json'
require 'sinatra'
require 'square_event'
SquareEvent.signing_secret = ENV['SQUARE_SIGNING_SECRET']
SquareEvent.notification_url = ENV['SQUARE_NOTIFICATION_URL']
SquareEvent.subscribe 'payment.created' do |event|
# Look ma, no Rails!
end
post '/_billing_events' do
data = JSON.parse(request.body.read, symbolize_names: true)
SquareEvent.instrument(data)
200
end
Testing
Handling webhooks is a critical piece of modern billing systems. Verifying the behavior of SquareEvent 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.
The Square ruby library does not currently offer an Event
object to use to create or refer to webhook with, so their testing in Ruby is harder than with Stripe.
Maintainers
Special thanks to all the contributors.
Versioning
Semantic Versioning 2.0 as defined at http://semver.org.
License
MIT License. Copyright 2020-2021 Andy Callaghan, Square work. Copyright 2012-2015 Integrallis Software original Stripe work.