6895135977
Replace deprecated `skip_before_action :verify_authenticity_token` with `skip_forgery_protection` to resolve Rails deprecation warnings. Changes: - app/controllers/notifications/unsubscribes_controller.rb - saas/app/controllers/stripe/webhooks_controller.rb
86 lines
3.1 KiB
Ruby
86 lines
3.1 KiB
Ruby
class Stripe::WebhooksController < ApplicationController
|
|
allow_unauthenticated_access
|
|
skip_before_action :require_account
|
|
skip_forgery_protection
|
|
|
|
def create
|
|
if event = verify_webhook_signature
|
|
dispatch_stripe_event(event)
|
|
head :ok
|
|
else
|
|
head :bad_request
|
|
end
|
|
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_stripe_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,
|
|
next_amount_due_in_cents: next_amount_due_for(stripe_subscription),
|
|
plan_key: plan_key_for(stripe_subscription)
|
|
}
|
|
|
|
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_stripe_customer(id)
|
|
Account::Subscription.find_by(stripe_customer_id: id)
|
|
end
|
|
|
|
def current_period_end_for(stripe_subscription)
|
|
timestamp = stripe_subscription.items.data.first&.current_period_end
|
|
Time.at(timestamp) if timestamp
|
|
end
|
|
|
|
def next_amount_due_for(stripe_subscription)
|
|
return nil if stripe_subscription.status == "canceled"
|
|
|
|
preview = Stripe::Invoice.create_preview(customer: stripe_subscription.customer, subscription: stripe_subscription.id)
|
|
preview.amount_due
|
|
rescue Stripe::InvalidRequestError
|
|
nil
|
|
end
|
|
|
|
def plan_key_for(stripe_subscription)
|
|
price_id = stripe_subscription.items.data.first&.price&.id
|
|
Plan.find_by_price_id(price_id)&.key
|
|
end
|
|
end
|