From 1a84d6ae42bd520e3b68a908f2c89a3a9a4cea33 Mon Sep 17 00:00:00 2001 From: Emiliano Frascaro Date: Tue, 2 Jun 2026 19:11:42 +0200 Subject: [PATCH] Fix checkout Stripe: ricrea customer se ID test su ambiente live. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Evita errore «No such customer» dopo passaggio da sk_test a sk_live. Co-authored-by: Cursor --- .../billing/stripe/checkout_session.rb | 17 +----- .../billing/stripe/ensure_customer.rb | 59 +++++++++++++++++++ .../billing/stripe/ensure_customer_spec.rb | 29 +++++++++ 3 files changed, 90 insertions(+), 15 deletions(-) create mode 100644 backend/app/services/billing/stripe/ensure_customer.rb create mode 100644 backend/spec/services/billing/stripe/ensure_customer_spec.rb diff --git a/backend/app/services/billing/stripe/checkout_session.rb b/backend/app/services/billing/stripe/checkout_session.rb index c36223c..af22860 100644 --- a/backend/app/services/billing/stripe/checkout_session.rb +++ b/backend/app/services/billing/stripe/checkout_session.rb @@ -16,7 +16,7 @@ module Billing price_id = PriceCatalog.price_id(plan_slug: @plan_slug, interval: @interval) - customer_id = ensure_customer_id! + customer_id = EnsureCustomer.call(club: @club, user: @user) CustomerSync.call(club: @club, customer_id: customer_id) session = ::Stripe::Checkout::Session.create( @@ -48,7 +48,7 @@ module Billing def portal_url raise "Stripe non configurato" unless MatchLiveTv.stripe_enabled? - customer_id = ensure_customer_id! + customer_id = EnsureCustomer.call(club: @club, user: @user) CustomerSync.call(club: @club, customer_id: customer_id) session = ::Stripe::BillingPortal::Session.create( customer: customer_id, @@ -59,19 +59,6 @@ module Billing private - def ensure_customer_id! - sub = @club.subscription || @club.build_subscription(plan: Plan["free"], status: "active") - return sub.stripe_customer_id if sub.stripe_customer_id.present? - - customer = ::Stripe::Customer.create( - email: @user.email, - name: @club.billing_legal_name.presence || @club.name, - metadata: { club_id: @club.id } - ) - sub.update!(stripe_customer_id: customer.id) - customer.id - end - def success_url "#{billing_url}?checkout=success&session_id={CHECKOUT_SESSION_ID}" end diff --git a/backend/app/services/billing/stripe/ensure_customer.rb b/backend/app/services/billing/stripe/ensure_customer.rb new file mode 100644 index 0000000..3ecf5eb --- /dev/null +++ b/backend/app/services/billing/stripe/ensure_customer.rb @@ -0,0 +1,59 @@ +module Billing + module Stripe + # Garantisce un Customer Stripe valido per la modalità API attiva (test vs live). + class EnsureCustomer + def self.call(club:, user:) + new(club: club, user: user).call + end + + def initialize(club:, user:) + @club = club + @user = user + end + + def call + sub = @club.subscription || @club.build_subscription(plan: Plan["free"], status: "active") + + if sub.stripe_customer_id.present? && !customer_valid?(sub.stripe_customer_id) + reset_stripe_billing!(sub) + end + + return sub.stripe_customer_id if sub.stripe_customer_id.present? + + customer = ::Stripe::Customer.create( + email: billing_email, + name: @club.billing_legal_name.presence || @club.name, + metadata: { club_id: @club.id } + ) + sub.update!(stripe_customer_id: customer.id) + customer.id + end + + private + + def billing_email + @club.billing_email.presence || @user.email + end + + def customer_valid?(customer_id) + ::Stripe::Customer.retrieve(customer_id) + true + rescue ::Stripe::InvalidRequestError => e + return false if e.code == "resource_missing" || e.message.include?("No such customer") + + raise + end + + def reset_stripe_billing!(sub) + Rails.logger.info( + "[Stripe] Customer #{sub.stripe_customer_id} non valido per club #{@club.id} — reset ID Stripe locali" + ) + sub.update!( + stripe_customer_id: nil, + stripe_subscription_id: nil, + cancel_at_period_end: false + ) + end + end + end +end diff --git a/backend/spec/services/billing/stripe/ensure_customer_spec.rb b/backend/spec/services/billing/stripe/ensure_customer_spec.rb new file mode 100644 index 0000000..b5baafe --- /dev/null +++ b/backend/spec/services/billing/stripe/ensure_customer_spec.rb @@ -0,0 +1,29 @@ +require "rails_helper" + +RSpec.describe Billing::Stripe::EnsureCustomer do + let(:user) { User.create!(email: "ensure@test.it", name: "U", password: "password123", role: "coach") } + let(:club) do + Club.create!(name: "Ensure Club", sport: "volleyball", primary_color: "#e53935", secondary_color: "#ffffff") + end + + before do + load Rails.root.join("db/seeds/plans.rb") + Billing::AssignPlan.call(club: club, plan_slug: "free") + club.subscription.update!(stripe_customer_id: "cus_stale_test_mode") + end + + it "ricrea il customer se l'id salvato non esiste su Stripe" do + err = ::Stripe::InvalidRequestError.new("No such customer", 404, code: "resource_missing") + allow(Stripe::Customer).to receive(:retrieve).with("cus_stale_test_mode").and_raise(err) + allow(Stripe::Customer).to receive(:create).and_return( + double(id: "cus_live_new", email: user.email) + ) + allow(Stripe::Customer).to receive(:update) + + id = described_class.call(club: club, user: user) + + expect(id).to eq("cus_live_new") + expect(club.subscription.reload.stripe_customer_id).to eq("cus_live_new") + expect(club.subscription.stripe_subscription_id).to be_nil + end +end