require "rails_helper" RSpec.describe "Public club billing", type: :request do let!(:coach) do User.create!(email: "billing-coach@test.it", name: "Coach", password: "password123", role: "coach") end let!(:club) do c = Club.create!(name: "Billing HTTP Club", sport: "volleyball", primary_color: "#e53935", secondary_color: "#ffffff", billing_entity_type: "company", billing_legal_name: "ASD Billing", billing_email: "bill@test.it", billing_address_line: "Via 1", billing_city: "Milano", billing_province: "MI", billing_postal_code: "20100", billing_country: "IT", billing_vat_number: "12345678901", billing_recipient_code: "ABCDEFG") ClubMembership.create!(user: coach, club: c, role: "owner") c.teams.create!(name: "U15", sport: "volleyball") load Rails.root.join("db/seeds/plans.rb") Billing::AssignPlan.call(club: c, plan_slug: "free") c end def login! post public_login_path, params: { email: coach.email, password: "password123" } end it "mostra pagamenti e fatture in billing" do club.billing_payments.create!( amount_cents: 4000, currency: "eur", status: "paid", paid_at: Time.current, description: "Premium Light", stripe_invoice_id: "in_spec_#{SecureRandom.hex(6)}" ) payment = club.billing_payments.reload.first club.billing_invoices.create!( number: "2026/100", issued_on: Date.current, amount_cents: 4000, status: "draft", source: "manual", billing_payment: payment ) login! get public_club_billing_path(club) expect(response).to have_http_status(:ok) expect(response.body).to include("Pagamenti e fatture") expect(response.body).not_to include("Ricevuta Stripe") expect(response.body).not_to include("Sincronizza pagamenti") expect(response.body).not_to include("Gestisci fatturazione su Stripe") expect(response.body).to include("Premium Light") expect(response.body).to include("2026/100") expect(response.body).to include("In preparazione") end it "salva profilo fatturazione" do club.update!(billing_recipient_code: nil, billing_pec: nil) login! patch public_club_billing_profile_path(club), params: { club: { billing_recipient_code: "XYZABC1", billing_pec: "" } } expect(response).to redirect_to(public_club_billing_path(club)) expect(club.reload.billing_recipient_code).to eq("XYZABC1") end it "blocca checkout premium senza profilo completo" do club.update!(billing_vat_number: nil, billing_fiscal_code: nil) allow(MatchLiveTv).to receive(:stripe_enabled?).and_return(true) login! get public_club_checkout_path(club, plan: "premium_light") expect(response).to redirect_to(public_club_billing_profile_path(club, plan: "premium_light")) end it "non mostra pulsanti checkout se profilo incompleto" do club.update!(billing_vat_number: nil) allow(MatchLiveTv).to receive(:stripe_enabled?).and_return(true) login! get public_club_billing_path(club) expect(response.body).to include("Completa dati di fatturazione") expect(response.body).not_to include("Attiva Premium Light — €40/anno") end it "dopo profilo completo reindirizza al checkout" do club.update!( billing_entity_type: "company", billing_vat_number: nil, billing_fiscal_code: nil, billing_recipient_code: nil, billing_pec: nil ) login! patch public_club_billing_profile_path(club, plan: "premium_light", interval: "yearly"), params: { club: { billing_entity_type: "company", billing_legal_name: "ASD Billing", billing_vat_number: "12345678901", billing_email: "bill@test.it", billing_address_line: "Via 1", billing_city: "Milano", billing_province: "MI", billing_postal_code: "20100", billing_country: "IT", billing_recipient_code: "ABCDEFG" } } expect(response).to redirect_to(public_club_checkout_path(club, plan: "premium_light", interval: "yearly")) end it "mostra pulsanti per intervallo e avvia checkout annuale" do allow(MatchLiveTv).to receive(:stripe_enabled?).and_return(true) allow(MatchLiveTv).to receive(:stripe_premium_light_yearly_price_id).and_return("price_y") allow(MatchLiveTv).to receive(:stripe_premium_light_monthly_price_id).and_return("price_m") allow(MatchLiveTv).to receive(:stripe_premium_full_yearly_price_id).and_return("price_fy") allow(MatchLiveTv).to receive(:stripe_premium_full_monthly_price_id).and_return("price_fm") login! get public_club_billing_path(club) expect(response.body).to include("Attiva Premium Light — €40/anno") expect(response.body).to include("Attiva Premium Light — €5/mese") allow(Billing::Stripe::CheckoutSession).to receive(:new).and_return( instance_double(Billing::Stripe::CheckoutSession, url: "https://checkout.stripe.com/test") ) get public_club_checkout_path(club, plan: "premium_light", interval: "yearly") expect(response).to redirect_to("https://checkout.stripe.com/test") expect(Billing::Stripe::CheckoutSession).to have_received(:new).with( hash_including(club: club, plan_slug: "premium_light", interval: "yearly") ) end it "sincronizza il piano al ritorno da checkout success" do allow(MatchLiveTv).to receive(:stripe_enabled?).and_return(true) allow(Billing::Stripe::FinalizeSubscription).to receive(:call).and_return(false) allow(Billing::Stripe::CheckoutSync).to receive(:call) do |**kwargs| Billing::AssignPlan.call( club: kwargs[:club].reload, plan_slug: "premium_light", status: "active", stripe_attrs: { stripe_subscription_id: "sub_ui", billing_interval: "monthly" } ) end login! get public_club_billing_path(club, checkout: "success", session_id: "cs_test") expect(response).to have_http_status(:ok) expect(club.reload.subscription.plan.slug).to eq("premium_light") expect(response.body).to include("Piano attuale: Premium Light") expect(Billing::Stripe::CheckoutSync).to have_received(:call).with( club: club, session_id: "cs_test" ) end end