diff --git a/enterprise/app/services/enterprise/billing/switch_currency_service.rb b/enterprise/app/services/enterprise/billing/switch_currency_service.rb index d8739de62..3b6eb9143 100644 --- a/enterprise/app/services/enterprise/billing/switch_currency_service.rb +++ b/enterprise/app/services/enterprise/billing/switch_currency_service.rb @@ -42,6 +42,12 @@ class Enterprise::Billing::SwitchCurrencyService # The Stripe swap self-reverted, so drop the pending marker and surface a single error type. clear_pending raise Error, e.message + rescue Stripe::StripeError + # A raw Stripe failure in the preflight (payment-method/location sync) happens before any + # subscription change, so drop the marker too — otherwise a transient blip locks out switching + # until the stale timeout. A post-success persist failure isn't a Stripe error and is left for the webhook. + clear_pending + raise end end diff --git a/spec/enterprise/services/enterprise/billing/switch_currency_service_spec.rb b/spec/enterprise/services/enterprise/billing/switch_currency_service_spec.rb index 100c888ec..d208b4c30 100644 --- a/spec/enterprise/services/enterprise/billing/switch_currency_service_spec.rb +++ b/spec/enterprise/services/enterprise/billing/switch_currency_service_spec.rb @@ -127,6 +127,13 @@ describe Enterprise::Billing::SwitchCurrencyService do expect(account.reload.custom_attributes['billing_currency_switch_pending']).to be_present end + it 'clears the pending marker when a stripe preflight call fails' do + allow(Stripe::Customer).to receive(:retrieve).and_raise(Stripe::StripeError.new('customer temporarily unavailable')) + + expect { service.perform }.to raise_error(Stripe::StripeError) + expect(account.reload.custom_attributes).not_to have_key('billing_currency_switch_pending') + end + it 'proceeds when the in-progress marker is stale' do account.update!(custom_attributes: account.custom_attributes.merge( 'billing_currency_switch_pending' => { 'currency' => 'brl', 'started_at' => 1.hour.ago.to_i }