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>
77 lines
2.4 KiB
Ruby
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
|