Skip to content

Commit bd62916

Browse files
committed
Adding webhooks stuff
1 parent 6489674 commit bd62916

22 files changed

+259
-27
lines changed

Gemfile

+1
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,4 @@ gem "stimulus-rails", "~> 0.6.0"
6464
gem "tailwindcss-rails", "~> 0.4.3"
6565
gem 'image_processing', '~> 1.2'
6666
gem 'stripe'
67+
gem 'resque'

Gemfile.lock

+20
Original file line numberDiff line numberDiff line change
@@ -137,10 +137,13 @@ GEM
137137
mini_mime (1.1.2)
138138
mini_portile2 (2.6.1)
139139
minitest (5.14.4)
140+
mono_logger (1.1.1)
140141
msgpack (1.4.2)
141142
multi_json (1.15.0)
142143
multi_xml (0.6.0)
143144
multipart-post (2.1.1)
145+
mustermann (1.1.1)
146+
ruby2_keywords (~> 0.0.1)
144147
nio4r (2.5.8)
145148
nokogiri (1.12.5)
146149
mini_portile2 (~> 2.6.1)
@@ -217,9 +220,18 @@ GEM
217220
rb-fsevent (0.11.0)
218221
rb-inotify (0.10.1)
219222
ffi (~> 1.0)
223+
redis (4.5.1)
224+
redis-namespace (1.8.1)
225+
redis (>= 3.0.4)
220226
responders (3.0.1)
221227
actionpack (>= 5.0)
222228
railties (>= 5.0)
229+
resque (2.2.0)
230+
mono_logger (~> 1.0)
231+
multi_json (~> 1.0)
232+
redis-namespace (~> 1.6)
233+
sinatra (>= 0.9.2)
234+
vegas (~> 0.1.2)
223235
ruby-vips (2.1.4)
224236
ffi (~> 1.12)
225237
ruby2_keywords (0.0.5)
@@ -234,6 +246,11 @@ GEM
234246
sprockets-rails
235247
tilt
236248
semantic_range (3.0.0)
249+
sinatra (2.1.0)
250+
mustermann (~> 1.0)
251+
rack (~> 2.2)
252+
rack-protection (= 2.1.0)
253+
tilt (~> 2.0)
237254
spring (3.0.0)
238255
sprockets (4.0.2)
239256
concurrent-ruby (~> 1.0)
@@ -254,6 +271,8 @@ GEM
254271
turbolinks-source (5.2.0)
255272
tzinfo (2.0.4)
256273
concurrent-ruby (~> 1.0)
274+
vegas (0.1.11)
275+
rack (>= 1.0.0)
257276
warden (1.2.9)
258277
rack (>= 2.0.9)
259278
web-console (4.1.0)
@@ -291,6 +310,7 @@ DEPENDENCIES
291310
puma (~> 5.0)
292311
rack-mini-profiler (~> 2.0)
293312
rails (~> 7.0.0.alpha2)
313+
resque
294314
sass-rails (>= 6)
295315
spring
296316
stimulus-rails (~> 0.6.0)

Rakefile

+1
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
33

44
require_relative "config/application"
5+
require 'resque/tasks'
56

67
Rails.application.load_tasks

app/controllers/reservations_controller.rb

+11-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@ class ReservationsController < ApplicationController
44
def index
55
end
66

7+
def cancel
8+
@reservation = current_user.reservations.find(params[:id])
9+
# make api call to refund payment
10+
refund = Stripe::Refund.create({
11+
payment_intent: @reservation.stripe_payment_intent_id,
12+
})
13+
@reservation.update(status: :canceling, stripe_refund_id: refund.id)
14+
redirect_to reservation_path(@reservation)
15+
end
16+
717
def create
818
@reservation = current_user.reservations.new(reservation_params)
919
if @reservation.save
@@ -47,7 +57,7 @@ def create
4757
end
4858

4959
def show
50-
60+
@reservation = current_user.reservations.find(params[:id])
5161
end
5262

5363
private
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
class WebhooksController < ApplicationController
2+
skip_before_action :verify_authenticity_token
3+
4+
def create
5+
event = Event.create!(
6+
request_body: request.raw_post,
7+
source: params[:source],
8+
)
9+
EventJob.perform_later(event)
10+
render json: { message: "success" }
11+
end
12+
end

app/helpers/webhooks_helper.rb

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
module WebhooksHelper
2+
end

app/jobs/event_job.rb

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
class EventJob < ApplicationJob
2+
queue_as :default
3+
4+
def perform(event)
5+
case event.source
6+
when "stripe"
7+
stripe_event = Stripe::Event.construct_from(
8+
JSON.parse(event.request_body, symbolize_names: true)
9+
)
10+
begin
11+
handle_stripe(stripe_event)
12+
event.update(
13+
event_type: stripe_event.type,
14+
error_message: '',
15+
status: :processed
16+
)
17+
rescue => e
18+
event.update(
19+
error_message: e.message,
20+
status: :failed
21+
)
22+
end
23+
else
24+
event.update(
25+
error_message: "Unknown source #{event.source}",
26+
status: :failed
27+
)
28+
end
29+
end
30+
31+
def handle_stripe(event)
32+
case event.type
33+
when "checkout.session.completed"
34+
# do something with the checkout session and the reservation here.
35+
checkout_session = event.data.object
36+
reservation = Reservation.find_by(session_id: checkout_session.id)
37+
if reservation.nil?
38+
raise "No reservation found with Checkout Session ID #{checkout_session.id}"
39+
end
40+
reservation.update(status: :booked, stripe_payment_intent_id: checkout_session.payment_intent)
41+
when "charge.refunded"
42+
charge = event.data.object
43+
reservation = Reservation.find_by(stripe_payment_intent_id: charge.payment_intent)
44+
if reservation.nil?
45+
raise "No reservation found with Payment Intent ID #{charge.payment_intent}"
46+
end
47+
reservation.update(status: :cancelled)
48+
end
49+
end
50+
end

app/models/event.rb

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# == Schema Information
2+
#
3+
# Table name: events
4+
#
5+
# id :bigint not null, primary key
6+
# request_body :text
7+
# status :integer default("pending")
8+
# error_message :text
9+
# source :string
10+
# event_type :string
11+
# created_at :datetime not null
12+
# updated_at :datetime not null
13+
#
14+
class Event < ApplicationRecord
15+
enum status: {pending: 0, processed: 1, failed: 2}
16+
end

app/models/reservation.rb

+9-8
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@
22
#
33
# Table name: reservations
44
#
5-
# id :bigint not null, primary key
6-
# listing_id :bigint not null
7-
# guest_id :bigint not null
8-
# session_id :string
9-
# status :integer default(0)
10-
# created_at :datetime not null
11-
# updated_at :datetime not null
5+
# id :bigint not null, primary key
6+
# listing_id :bigint not null
7+
# guest_id :bigint not null
8+
# session_id :string
9+
# status :integer default("pending")
10+
# created_at :datetime not null
11+
# updated_at :datetime not null
12+
# stripe_payment_intent_id :string
1213
#
1314
class Reservation < ApplicationRecord
1415
belongs_to :listing
1516
belongs_to :guest, class_name: 'User'
16-
enum status: {pending: 0, booked: 1, cancelled: 2}
17+
enum status: {pending: 0, booked: 1, canceling: 3, cancelled: 2}
1718
end

app/views/reservations/show.html.erb

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,8 @@
1-
<h1>Reservations#show</h1>
2-
<p>Find me in app/views/reservations/show.html.erb</p>
1+
<h1 class="text-2xl mb-4 font-bold"><%= @reservation.listing.title %></h1>
2+
3+
<pre><%= JSON.pretty_generate(@reservation.as_json) %></pre>
4+
5+
<form action="<%= cancel_reservation_path(@reservation) %>" method="post">
6+
<input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>">
7+
<input type="submit" value="Cancel">
8+
</form>

config/application.rb

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class Application < Rails::Application
1212
config.load_defaults 6.1
1313

1414
Rails.application.default_url_options = { host: 'localhost', port: 3000 }
15+
config.active_job.queue_adapter = :resque
1516

1617
# Configuration for the application, engines, and railties goes here.
1718
#

config/routes.rb

+15-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1+
require 'resque/server'
12
Rails.application.routes.draw do
23
root to: 'static_pages#home'
34

45
resources :listings, only: [:index, :show]
5-
resources :reservations
6+
resources :reservations do
7+
member do
8+
post '/cancel' => 'reservations#cancel'
9+
end
10+
end
11+
post '/webhooks/:source' => 'webhooks#create'
612

713
namespace :host do
814
resources :listings do
@@ -12,4 +18,12 @@
1218
end
1319

1420
devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' }
21+
22+
resque_web_constraint = lambda do |request|
23+
current_user = request.env['warden'].user
24+
current_user.id == 1
25+
end
26+
constraints resque_web_constraint do
27+
mount Resque::Server, at: '/jobs'
28+
end
1529
end
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
class CreateEvents < ActiveRecord::Migration[7.0]
2+
def change
3+
create_table :events do |t|
4+
t.text :request_body
5+
t.integer :status, default: 0
6+
t.text :error_message
7+
t.string :source
8+
t.string :event_type
9+
10+
t.timestamps
11+
end
12+
end
13+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
class AddStripePaymentIntentIdToReservations < ActiveRecord::Migration[7.0]
2+
def change
3+
add_column :reservations, :stripe_payment_intent_id, :string
4+
end
5+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
class AddStripeRefundIdToReservations < ActiveRecord::Migration[7.0]
2+
def change
3+
add_column :reservations, :stripe_refund_id, :string
4+
end
5+
end

db/schema.rb

+13-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
require "test_helper"
2+
3+
class WebhooksControllerTest < ActionDispatch::IntegrationTest
4+
# test "the truth" do
5+
# assert true
6+
# end
7+
end

test/fixtures/events.yml

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# == Schema Information
2+
#
3+
# Table name: events
4+
#
5+
# id :bigint not null, primary key
6+
# request_body :text
7+
# status :integer default("pending")
8+
# error_message :text
9+
# source :string
10+
# event_type :string
11+
# created_at :datetime not null
12+
# updated_at :datetime not null
13+
#
14+
15+
one:
16+
request_body: MyText
17+
status: 1
18+
error_message: MyText
19+
source: MyString
20+
event_type: MyString
21+
22+
two:
23+
request_body: MyText
24+
status: 1
25+
error_message: MyText
26+
source: MyString
27+
event_type: MyString

test/fixtures/reservations.yml

+8-7
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22
#
33
# Table name: reservations
44
#
5-
# id :bigint not null, primary key
6-
# listing_id :bigint not null
7-
# guest_id :bigint not null
8-
# session_id :string
9-
# status :integer default(0)
10-
# created_at :datetime not null
11-
# updated_at :datetime not null
5+
# id :bigint not null, primary key
6+
# listing_id :bigint not null
7+
# guest_id :bigint not null
8+
# session_id :string
9+
# status :integer default("pending")
10+
# created_at :datetime not null
11+
# updated_at :datetime not null
12+
# stripe_payment_intent_id :string
1213
#
1314

1415
one:

test/jobs/event_job_test.rb

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
require "test_helper"
2+
3+
class EventJobTest < ActiveJob::TestCase
4+
# test "the truth" do
5+
# assert true
6+
# end
7+
end

0 commit comments

Comments
 (0)