Files
MatchLiveTv/backend/app/services/billing/payment_description.rb
Emiliano Frascaro a5e781729c Billing: fatture manuali, profilo obbligatorio e piani Stripe mensile/annuale.
Rimuove link al portale Stripe in area cliente, aggiunge flusso admin per PDF
fattura e email, blocca checkout senza dati di fatturazione, allinea prezzi
a €4,90/€39,90 e €9,90/€69,90, locale italiano e documentazione deploy.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-02 17:10:12 +02:00

130 lines
3.9 KiB
Ruby

module Billing
# Etichette leggibili per lo storico pagamenti (mai testo grezzo Stripe / webhook).
class PaymentDescription
UGLY = /\A(webhook\s*test|test|x|—|-)\z/i
AMOUNT_HINTS = {
500 => %w[premium_light monthly],
4000 => %w[premium_light yearly],
2000 => %w[premium_full monthly],
20_000 => %w[premium_full yearly]
}.freeze
class << self
def for_stripe_invoice(invoice)
plan_slug, interval = extract_plan_and_interval(invoice)
build(plan_slug: plan_slug, interval: interval, amount_cents: invoice.amount_paid)
end
def for_payment(payment)
stored = payment.description.to_s.strip
return stored if stored.present? && !ugly?(stored)
build(
plan_slug: payment.plan_slug,
interval: nil,
amount_cents: payment.amount_cents
)
end
private
def build(plan_slug:, interval:, amount_cents:)
plan_slug, interval = resolve_plan_interval(plan_slug, interval, amount_cents)
return plan_label(plan_slug, interval) if plan_slug.present?
if amount_cents.present? && amount_cents.to_i.positive?
euros = format("%.2f", amount_cents.to_i / 100.0)
return "Pagamento Match Live TV — #{euros}"
end
"Abbonamento Match Live TV"
end
def resolve_plan_interval(plan_slug, interval, amount_cents)
plan_slug = plan_slug.presence
interval = interval.presence
if plan_slug.blank? && amount_cents.present?
hint = AMOUNT_HINTS[amount_cents.to_i]
plan_slug, interval = hint if hint
end
if plan_slug.present? && interval.blank? && amount_cents.present?
hint = AMOUNT_HINTS[amount_cents.to_i]
interval = hint&.last if hint&.first == plan_slug
end
interval ||= Billing::Stripe::PriceCatalog::DEFAULT_INTERVAL if plan_slug.present?
[plan_slug, interval]
end
def plan_label(plan_slug, interval)
plan = Plan[plan_slug]
price = Billing::Stripe::PriceCatalog.label(plan_slug: plan_slug, interval: interval)
"#{plan.name}#{price}"
rescue KeyError
plan_slug.to_s.humanize
end
def extract_plan_and_interval(invoice)
meta = merged_metadata(invoice)
plan_slug = meta["plan_slug"].presence
interval = normalize_interval(meta["billing_interval"])
line = invoice.lines&.data&.first
if line
line_meta = line_metadata(line)
plan_slug ||= line_meta["plan_slug"]
interval ||= normalize_interval(line_meta["billing_interval"])
price_id = line_price_id(line)
from_price = Billing::Stripe::PriceCatalog.infer_from_price_id(price_id)
plan_slug ||= from_price&.first
interval ||= from_price&.last
end
plan_slug = normalize_plan_slug(plan_slug)
[plan_slug, interval]
end
def merged_metadata(invoice)
Billing::Stripe::InvoiceMetadata.merged(invoice)
end
def line_metadata(line)
meta = Billing::Stripe::InvoicePaymentRefs.safe_get(line, :metadata)
meta.to_h.stringify_keys
rescue NoMethodError
{}
end
def line_price_id(line)
pricing = Billing::Stripe::InvoicePaymentRefs.safe_get(line, :pricing)
details = Billing::Stripe::InvoicePaymentRefs.safe_get(pricing, :price_details)
Billing::Stripe::InvoicePaymentRefs.safe_get(details, :price)
end
def normalize_interval(value)
return nil if value.blank?
Billing::Stripe::PriceCatalog.normalize_interval(value)
rescue ArgumentError
nil
end
def normalize_plan_slug(slug)
return nil if slug.blank?
slug = slug.to_s
slug = "premium_full" if slug == "premium"
slug.presence_in(%w[premium_light premium_full])
end
def ugly?(text)
UGLY.match?(text.to_s.strip)
end
end
end
end