Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Stripe Cancellation Webhook #1682

Merged
merged 6 commits into from Feb 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions Envfile
Expand Up @@ -130,6 +130,7 @@ variable :SLACK_WEBHOOK_URL, :String, default: "Optional"
# Stripe for payment system
variable :STRIPE_PUBLISHABLE_KEY, :String, default: "Optional"
variable :STRIPE_SECRET_KEY, :String, default: "Optional"
variable :STRIPE_CANCELLATION_SECRET, :String, default: "Optional"

# For browser webpush notifications (webpush gem) (totally random base64 nums)
variable :VAPID_PUBLIC_KEY, :String, default: "dGhpcyBpcyBkZXYudG8gdGVzdCBkYXRh"
Expand Down
39 changes: 39 additions & 0 deletions app/controllers/stripe_cancellations_controller.rb
@@ -0,0 +1,39 @@
class StripeCancellationsController < ApplicationController
skip_before_action :verify_authenticity_token, only: :create

def create
verify_stripe_signature
unsubscribe_customer
end

def verify_stripe_signature
payload = request.body.read
sig_header = request.env["HTTP_STRIPE_SIGNATURE"]

begin
Stripe::Webhook.construct_event(
payload, sig_header, Rails.configuration.stripe[:stripe_cancellation_secret]
)
rescue JSON::ParserError
# Invalid payload
status 400
return
rescue Stripe::SignatureVerificationError
# Invalid signature
status 400
return
end
end

def unsubscribe_customer
event = Stripe::Event.retrieve(params[:id])
stripe_id = event.data.object.customer
customer = Stripe::Customer.retrieve(stripe_id)
monthly_dues = event.data.object.items.data[0].plan.amount
user = User.where(stripe_id_code: stripe_id).first
MembershipService.new(customer, user, monthly_dues).unsubscribe_customer
render body: nil, status: 200
rescue Stripe::APIConnectionError, Stripe::StripeError
render body: nil, status: 400
end
end
2 changes: 2 additions & 0 deletions app/services/membership_service.rb
Expand Up @@ -42,6 +42,8 @@ def create_subscription
end

def cancel_subscription
return true if customer.subscriptions.none?

subscription.delete
end

Expand Down
3 changes: 2 additions & 1 deletion config/initializers/stripe.rb
@@ -1,6 +1,7 @@
Rails.configuration.stripe = {
publishable_key: ApplicationConfig["STRIPE_PUBLISHABLE_KEY"],
secret_key: ApplicationConfig["STRIPE_SECRET_KEY"]
secret_key: ApplicationConfig["STRIPE_SECRET_KEY"],
stripe_cancellation_secret: ApplicationConfig["STRIPE_CANCELLATION_SECRET"]
}

Stripe.api_key = Rails.configuration.stripe[:secret_key]
1 change: 1 addition & 0 deletions config/routes.rb
Expand Up @@ -112,6 +112,7 @@
resources :tags, only: [:index]
resources :stripe_subscriptions, only: %i[create update destroy]
resources :stripe_active_cards, only: %i[create update destroy]
resources :stripe_cancellations, only: [:create]
resources :live_articles, only: [:index]
resources :github_repos, only: %i[create update]
resources :buffered_articles, only: [:index]
Expand Down
32 changes: 32 additions & 0 deletions spec/requests/stripe_cancellations_spec.rb
@@ -0,0 +1,32 @@
require "rails_helper"

RSpec.describe "StripeCancellations", type: :request do
let(:user) { create(:user) }
let(:stripe_helper) { StripeMock.create_test_helper }

before do
StripeMock.start
allow_any_instance_of(StripeCancellationsController).to receive(:verify_stripe_signature).and_return(true)
end

after { StripeMock.stop }

it "mocks a stripe cancellation webhook" do
customer = Stripe::Customer.create(
email: user.email,
source: stripe_helper.generate_card_token,
)
MembershipService.new(customer, user, 12).subscribe_customer
user.reload
expect(user.monthly_dues).not_to eq(0)

event = StripeMock.mock_webhook_event(
"customer.subscription.deleted",
customer: user.stripe_id_code, total_count: 1,
)
post "/stripe_cancellations", params: event.as_json
user.reload
expect(user.monthly_dues).to eq(0)
expect(response).to have_http_status(200)
end
end