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:
76
backend/app/services/billing/stripe/apply_pending_plan.rb
Normal file
76
backend/app/services/billing/stripe/apply_pending_plan.rb
Normal file
@@ -0,0 +1,76 @@
|
||||
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
|
||||
Reference in New Issue
Block a user