Files
MatchLiveTv/backend/app/services/billing/stripe/apply_pending_plan.rb
Emiliano Frascaro 566104aff4 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>
2026-06-02 22:15:27 +02:00

77 lines
2.4 KiB
Ruby

module Billing
module Stripe
# Allinea piano locale quando scatta un downgrade programmato (o dopo sync Stripe).
class ApplyPendingPlan
def self.call(club:)
new(club: club).call
end
def initialize(club:)
@club = club
@sub = club.subscription
end
def call
return false unless @sub&.pending_plan_id.present?
pending_slug = @sub.pending_plan.slug
interval = @sub.pending_billing_interval.presence || @sub.billing_interval
if @sub.stripe_subscription_id.present?
stripe_sub = ::Stripe::Subscription.retrieve(@sub.stripe_subscription_id)
current_price_id = stripe_sub.items.data.first.price.id
expected_price_id = PriceCatalog.price_id(plan_slug: pending_slug, interval: interval)
return false unless current_price_id == expected_price_id
inferred = PriceCatalog.infer_from_price_id(current_price_id)
if inferred
pending_slug, interval = inferred
end
period_start, period_end = SubscriptionPeriod.times(stripe_sub)
Billing::AssignPlan.call(
club: @club,
plan_slug: pending_slug,
status: map_status(stripe_sub.status),
stripe_attrs: {
stripe_customer_id: stripe_sub.customer,
stripe_subscription_id: stripe_sub.id,
current_period_start: period_start,
current_period_end: period_end,
cancel_at_period_end: stripe_sub.cancel_at_period_end,
billing_interval: interval,
pending_plan_id: nil,
pending_billing_interval: nil,
stripe_schedule_id: nil
}
)
else
Billing::AssignPlan.call(
club: @club,
plan_slug: pending_slug,
stripe_attrs: {
billing_interval: interval,
pending_plan_id: nil,
pending_billing_interval: nil,
stripe_schedule_id: nil
}
)
end
true
end
private
def map_status(stripe_status)
case stripe_status
when "trialing" then "trialing"
when "active" then "active"
when "past_due", "unpaid" then "past_due"
when "canceled", "incomplete_expired" then "canceled"
else "incomplete"
end
end
end
end
end