Files
fizzy/saas/app/controllers/stripe/webhooks_controller.rb
T

70 lines
2.5 KiB
Ruby

class Stripe::WebhooksController < ApplicationController
allow_unauthenticated_access
skip_before_action :require_account
skip_before_action :verify_authenticity_token
def create
event = verify_webhook_signature
return head :bad_request unless event
dispatch_stripe_event(event)
head :ok
end
private
def dispatch_stripe_event(event)
case event.type
when "checkout.session.completed"
sync_new_subscription(event.data.object.subscription, plan_key: event.data.object.metadata["plan_key"]) if event.data.object.mode == "subscription"
when "customer.subscription.updated", "customer.subscription.deleted"
sync_subscription(event.data.object.id)
end
end
def verify_webhook_signature
payload = request.body.read
sig_header = request.env["HTTP_STRIPE_SIGNATURE"]
Stripe::Webhook.construct_event(payload, sig_header, ENV["STRIPE_WEBHOOK_SECRET"])
rescue Stripe::SignatureVerificationError => e
Rails.logger.error "Stripe webhook signature verification failed: #{e.message}"
nil
end
def sync_new_subscription(stripe_subscription_id, plan_key:)
sync_subscription(stripe_subscription_id) do |subscription_properties|
subscription_properties[:plan_key] = plan_key if plan_key
end
end
# Always fetch fresh subscription data from Stripe to handle out-of-order
# event delivery. Not relying on payload data.
def sync_subscription(stripe_subscription_id)
stripe_subscription = Stripe::Subscription.retrieve(stripe_subscription_id)
if subscription = find_subscription_by_customer(stripe_subscription.customer)
subscription_properties = {
stripe_subscription_id: stripe_subscription.id,
status: stripe_subscription.status,
current_period_end: current_period_end_for(stripe_subscription),
cancel_at: stripe_subscription.cancel_at ? Time.at(stripe_subscription.cancel_at) : nil
}
yield subscription_properties if block_given?
subscription_properties[:stripe_subscription_id] = nil if stripe_subscription.status == "canceled"
subscription.update!(subscription_properties)
end
end
def find_subscription_by_customer(customer_id)
Account::Subscription.find_by(stripe_customer_id: customer_id)
end
def current_period_end_for(stripe_subscription)
timestamp = stripe_subscription.items.data.first&.current_period_end
Time.at(timestamp) if timestamp
end
end