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>
This commit is contained in:
2026-06-02 17:10:12 +02:00
parent 148402a97c
commit a5e781729c
78 changed files with 2457 additions and 424 deletions

View File

@@ -5,8 +5,21 @@
<h1>Dati di fatturazione</h1>
<p style="color:#aaa">Società: <strong><%= @club.name %></strong>. Inserisci i dati per lintestazione delle fatture e linvio tramite SDI o PEC.</p>
<% plan_return = params[:plan].presence_in(%w[premium_light premium_full]) %>
<% unless @club.billing_profile_complete? %>
<div class="flash alert" style="margin:16px 0">
<strong>Obbligatori per abbonarti a un piano premium.</strong>
<%= billing_profile_incomplete_message(@club) %>
<% if plan_return %>
Dopo il salvataggio potrai procedere al pagamento di <strong><%= Plan[plan_return].name %></strong>.
<% end %>
</div>
<% end %>
<div class="card">
<%= form_with url: public_club_billing_profile_path(@club), method: :patch do %>
<%= hidden_field_tag :plan, plan_return if plan_return %>
<%= hidden_field_tag :interval, params[:interval] if params[:interval].present? %>
<%= render "shared/billing_profile_fields", record: @club %>
<%= submit_tag "Salva dati di fatturazione", class: "btn btn-primary" %>
<% end %>

View File

@@ -3,12 +3,13 @@
<div class="wrap" style="padding-top:20px">
<h1>Abbonamento</h1>
<%= render "shared/club_subscription_status", club: @club, entitlements: @entitlements, subscription: @subscription %>
<%= render "shared/club_subscription_status", club: @club, entitlements: @entitlements, subscription: @subscription, on_billing_page: true %>
<%= render "shared/stripe_secure_payment" %>
<%= render "shared/plan_cards", show_stripe_portal: true %>
<%= render "shared/plan_cards", show_stripe_portal: false %>
<%= render "shared/subscription_cancel", club: @club, subscription: @subscription %>
<%= render "shared/billing_documents", club: @club, payments: @payments, invoices: @invoices %>
<%= render "shared/billing_documents", club: @club, payments: @payments %>
<p style="margin-top:24px"><%= link_to "← Società", public_club_path(@club) %></p>
</div>

View File

@@ -22,9 +22,16 @@
<%= label_tag :plan, "Piano iniziale (per tutta la società)" %>
<%= select_tag :plan, options_for_select([
["Free — 1 responsabile trasmissione per squadra, 1 live", "free"],
["Premium Light — €40/anno o €5/mese", "premium_light"],
["Premium Full — €200/anno o €20/mese", "premium_full"]
], params[:plan] || "free") %>
["Premium Light", "premium_light"],
["Premium Full", "premium_full"]
], params[:plan] || "free"), id: "club_plan_select" %>
<div id="club_plan_interval" style="margin-top:10px">
<%= label_tag :interval, "Fatturazione premium" %>
<%= select_tag :interval, options_for_select([
["Annuale — €40/anno (Light) o €200/anno (Full)", "yearly"],
["Mensile — €5/mese (Light) o €20/mese (Full)", "monthly"]
], params[:interval] || "yearly") %>
</div>
<%= render "shared/stripe_secure_payment", compact: true %>
<%= submit_tag "Crea società", class: "btn btn-primary" %>
<% end %>

View File

@@ -37,7 +37,7 @@
<h2>Diretta terminata</h2>
<p>Lo streaming di questa partita è stato chiuso.</p>
<% if @session.ended_at %>
<p class="stream-ended-meta">Chiusa il <%= l(@session.ended_at.in_time_zone, format: :long) %></p>
<p class="stream-ended-meta">Chiusa il <%= l_local(@session.ended_at) %></p>
<% end %>
<p class="stream-ended-hint">Il punteggio finale resta visibile sopra.</p>
<%= link_to "Tutte le dirette", public_live_index_path, class: "btn btn-secondary stream-ended-btn" %>

View File

@@ -38,7 +38,7 @@
<td><strong><%= match.opponent_name %></strong></td>
<td>
<% if match.scheduled_at %>
<%= l match.scheduled_at.in_time_zone, format: :long %>
<%= l_local(match.scheduled_at) %>
<% else %>
<span style="color:#888">Senza orario</span>
<% end %>

View File

@@ -32,7 +32,7 @@
<p>Registrazione indicizzata. Il playback dipende dalla retention del piano Premium.</p>
<% end %>
<% if @recording.expires_at %>
<p class="stream-ended-meta" style="margin-top:12px">Disponibile fino al <%= l @recording.expires_at, format: :long %></p>
<p class="stream-ended-meta" style="margin-top:12px">Disponibile fino al <%= l_local(@recording.expires_at) %></p>
<% end %>
</div>
<% end %>

View File

@@ -16,7 +16,6 @@
<%= render "shared/input_toggle",
name: :password,
label: "Password",
input_type: "text",
required: true,
autocomplete: "current-password" %>
<p class="auth-forgot"><%= link_to "Password dimenticata?", public_password_forgot_path %></p>

View File

@@ -29,7 +29,7 @@
<% elsif plan.slug == "free" %>
<p style="color:#888;font-size:0.85rem">Contatta il supporto per downgrade.</p>
<% elsif MatchLiveTv.stripe_enabled? %>
<%= link_to "Attiva #{plan.name}", public_club_checkout_path(@team.club, plan: plan.slug), class: "btn btn-primary" %>
<%= link_to "Gestisci abbonamento società", public_club_billing_path(@team.club), class: "btn btn-primary" %>
<% else %>
<p style="color:#888">Stripe non configurato</p>
<% end %>
@@ -41,9 +41,7 @@
<p style="color:#aaa">Esigenze diverse? <%= mail_to "info@matchlive.it", "Contattaci" %> per un'offerta su misura.</p>
</div>
<% if @entitlements.premium_active? && MatchLiveTv.stripe_enabled? %>
<p style="margin-top:20px"><%= button_to "Gestisci su Stripe", public_club_portal_path(@team.club), class: "btn btn-secondary" %></p>
<% end %>
<p style="margin-top:20px"><%= link_to "Pagamenti e fatture della società", public_club_billing_path(@team.club) %></p>
<p><%= link_to "← Dettagli squadra", public_team_details_path(@team) %></p>
</div>