module Billing module Stripe class ScheduleDowngrade def self.call(club:, plan_slug:, interval:) new(club: club, plan_slug: plan_slug, interval: interval).call end def initialize(club:, plan_slug:, interval:) @club = club @plan_slug = plan_slug.to_s @interval = PriceCatalog.normalize_interval(interval) @sub = club.subscription end def call release_existing_schedule! stripe_sub = ::Stripe::Subscription.retrieve(@sub.stripe_subscription_id) period_start, period_end = SubscriptionPeriod.times(stripe_sub) raise "Periodo di fatturazione non disponibile" if period_end.blank? current_item = stripe_sub.items.data.first new_price_id = PriceCatalog.price_id(plan_slug: @plan_slug, interval: @interval) schedule = ::Stripe::SubscriptionSchedule.create(from_subscription: stripe_sub.id) ::Stripe::SubscriptionSchedule.update( schedule.id, { end_behavior: "release", phases: [ { items: [{ price: current_item.price.id, quantity: 1 }], start_date: schedule_phase_start(stripe_sub, period_start), end_date: period_end.to_i }, { items: [{ price: new_price_id, quantity: 1 }], metadata: { club_id: @club.id, plan_slug: @plan_slug, billing_interval: @interval } } ] } ) pending_plan = Plan[@plan_slug] @sub.update!( pending_plan: pending_plan, pending_billing_interval: @interval, stripe_schedule_id: schedule.id ) ChangePlanResult.new( kind: :downgrade, plan_slug: @plan_slug, interval: @interval, effective_at: period_end ) end private def release_existing_schedule! return if @sub.stripe_schedule_id.blank? ::Stripe::SubscriptionSchedule.release(@sub.stripe_schedule_id) rescue ::Stripe::InvalidRequestError nil ensure @sub.update!(stripe_schedule_id: nil, pending_plan_id: nil, pending_billing_interval: nil) end def schedule_phase_start(stripe_sub, period_start) period_start&.to_i || stripe_sub.items.data.first.current_period_start end end end end