When something important happens on an external service (think Stripe), their system can send a POST
request to your app.1 This is called a webhook. Webhooks help automate operations without us having to do anything other than set them up. In this post, I’ll show you how to consume these webhooks with your Rails 5 application.
1. Create a POST
route inside routes.rb.
post '/hooks/:integration_name' => 'webhooks#receive'
Let’s say you’re setting up Stripe. The code above would allow them to post to yoursite.com/hooks/stripe.
2. Generate a webhooks controller with a receive
method2:
rails g controller Webhooks receive
3. Let’s work with the data we receive.
class WebhooksController < ApplicationController
skip_before_action :verify_authenticity_token
def receive
# Parse the data received as JSON
data = JSON.parse(request.body.read)
# Now you can use the data however you'd like.
puts data['xxx']
# Respond that we received the hook
head :ok
end
end
4. Technically, you can be done after Step 3. However, if you’re consuming multiple types of events, wouldn’t it be nice to have a separate action for each one? We can change the receive
method to call a different function for each type of event:3
class WebhooksController < ApplicationController
skip_before_action :verify_authenticity_token
def receive
data = JSON.parse(request.body.read)
method = "handle_" + data['type'].tr('.', '_')
self.send method, data
head :ok
end
def handle_charge_succeeded(data)
# puts data['xxx']
end
def handle_charge_failed(data)
# puts data['xxx']
end
def handle_customer_subscription_created(data)
# puts data['xxx']
end
end
-
This
POST
request can contain data about what just happened – i.e. “Customer RSwanson was successfully charged for their monthly subscription.” Your application could consume this data to update RSwanson’saccount_standing
to:active
. ↩︎ -
Alternatively, you could add a
receive
method to one of your existing controllers. If you do this, you’ll also need to point your route from Step 1 to the appropriate controller. ↩︎ -
This is a pattern from Laravel Cashier and was introduced to me by Arandi López’s post. ↩︎