Billing upgrade/downgrade, inviti in app e diretta YouTube da mobile.
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>
This commit is contained in:
80
backend/app/services/billing/stripe/schedule_downgrade.rb
Normal file
80
backend/app/services/billing/stripe/schedule_downgrade.rb
Normal file
@@ -0,0 +1,80 @@
|
||||
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
|
||||
Reference in New Issue
Block a user