Upgrade Stripe con addebito immediato; downgrade programmato a fine periodo. UX billing più sobria con box info; icone piani. API inviti e OAuth YouTube per staff trasmissione; deep link join; scelta partita programmata o nuova. Co-authored-by: Cursor <cursoragent@cursor.com>
81 lines
2.4 KiB
Ruby
81 lines
2.4 KiB
Ruby
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
|