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>
130 lines
3.9 KiB
Ruby
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
|