add default payment method for topup and change of subscription

This commit is contained in:
Tanmay Sharma
2025-11-24 19:09:00 +05:30
parent aeb708a053
commit d9348883ed
2 changed files with 145 additions and 8 deletions
@@ -9,6 +9,7 @@ class Enterprise::Billing::V2::InvoicePaymentService < Enterprise::Billing::V2::
# @return [Hash] { success:, invoice_id:, invoice_url:, amount:, status: }
# Validate that customer has a default payment method
# If no default is set but payment methods exist, automatically set the first one as default
# @return [Hash, nil] Returns error hash if validation fails, nil if success
def validate_payment_method
return { success: false, message: 'No Stripe customer ID found' } if stripe_customer_id.blank?
@@ -16,10 +17,9 @@ class Enterprise::Billing::V2::InvoicePaymentService < Enterprise::Billing::V2::
customer = Stripe::Customer.retrieve(stripe_customer_id)
if customer.invoice_settings.default_payment_method.nil? && customer.default_source.nil?
return {
success: false,
message: 'No default payment method found. Please add a default payment method before making a purchase.'
}
# No default payment method found - try to set one automatically
ensure_default_payment_method_result = ensure_default_payment_method(customer)
return ensure_default_payment_method_result unless ensure_default_payment_method_result.nil?
end
nil
@@ -28,6 +28,40 @@ class Enterprise::Billing::V2::InvoicePaymentService < Enterprise::Billing::V2::
{ success: false, message: "Error validating payment method: #{e.message}" }
end
# Ensure a default payment method is set for the customer
# If payment methods exist but none is default, set the first one as default
# @param customer [Stripe::Customer] The Stripe customer object
# @return [Hash, nil] Returns error hash if no payment methods exist, nil if default is set successfully
def ensure_default_payment_method(customer)
payment_methods = fetch_customer_payment_methods(customer.id)
return no_payment_methods_error if payment_methods.data.empty?
set_first_payment_method_as_default(customer.id, payment_methods.data.first)
nil
rescue Stripe::StripeError => e
Rails.logger.error("Failed to set default payment method: #{e.message}")
{ success: false, message: "Error setting default payment method: #{e.message}" }
end
def fetch_customer_payment_methods(customer_id)
Stripe::PaymentMethod.list(customer: customer_id, limit: 100)
end
def no_payment_methods_error
{
success: false,
message: 'No payment methods found. Please add a payment method before making a purchase.'
}
end
def set_first_payment_method_as_default(customer_id, payment_method)
Stripe::Customer.update(
customer_id,
invoice_settings: { default_payment_method: payment_method.id }
)
Rails.logger.info("Automatically set payment method #{payment_method.id} as default for customer #{customer_id}")
end
# Create invoice with line items and charge immediately
#
# @param line_items [Array<Hash>] Line items: [{ amount: (cents), description:, metadata: }]
@@ -25,11 +25,114 @@ describe Enterprise::Billing::V2::TopupService do
end
describe '#create_topup' do
it 'creates topup' do
result = service.create_topup(credits: 500)
context 'when customer has a default payment method' do
it 'creates topup successfully' do
result = service.create_topup(credits: 500)
expect(result[:success]).to be true
expect(result[:credits]).to eq(500)
expect(result[:success]).to be true
expect(result[:credits]).to eq(500)
expect(result[:amount]).to eq(50.0)
end
end
context 'when customer has no default payment method but has payment methods available' do
let(:payment_methods_list) { OpenStruct.new(data: [OpenStruct.new(id: 'pm_first')]) }
before do
allow(Stripe::Customer).to receive(:retrieve).and_return(
OpenStruct.new(
id: 'cus_123',
invoice_settings: OpenStruct.new(default_payment_method: nil),
default_source: nil
)
)
allow(Stripe::PaymentMethod).to receive(:list).and_return(payment_methods_list)
allow(Stripe::Customer).to receive(:update)
end
it 'automatically sets default payment method and creates topup' do
result = service.create_topup(credits: 500)
expect(Stripe::Customer).to have_received(:update).with(
'cus_123',
invoice_settings: { default_payment_method: 'pm_first' }
)
expect(result[:success]).to be true
expect(result[:credits]).to eq(500)
end
end
context 'when customer has no payment methods at all' do
let(:empty_payment_methods) { OpenStruct.new(data: []) }
before do
allow(Stripe::Customer).to receive(:retrieve).and_return(
OpenStruct.new(
id: 'cus_123',
invoice_settings: OpenStruct.new(default_payment_method: nil),
default_source: nil
)
)
allow(Stripe::PaymentMethod).to receive(:list).and_return(empty_payment_methods)
end
it 'returns an error' do
result = service.create_topup(credits: 500)
expect(result[:success]).to be false
expect(result[:message]).to include('No payment methods found')
end
end
context 'when on Hacker plan' do
let(:account) { create(:account, custom_attributes: { 'stripe_customer_id' => 'cus_123', 'plan_name' => 'Hacker' }) }
it 'returns an error' do
result = service.create_topup(credits: 500)
expect(result[:success]).to be false
expect(result[:message]).to include('only available for Startup, Business, and Enterprise plans')
end
end
context 'when no plan is set' do
let(:account) { create(:account, custom_attributes: { 'stripe_customer_id' => 'cus_123' }) }
it 'returns an error' do
result = service.create_topup(credits: 500)
expect(result[:success]).to be false
expect(result[:message]).to include('only available for Startup, Business, and Enterprise plans')
end
end
context 'with invalid topup amount' do
it 'returns an error for zero credits' do
result = service.create_topup(credits: 0)
expect(result[:success]).to be false
expect(result[:message]).to eq('Invalid topup amount')
end
it 'returns an error for negative credits' do
result = service.create_topup(credits: -100)
expect(result[:success]).to be false
expect(result[:message]).to eq('Invalid topup amount')
end
end
context 'when topup amount is not supported' do
before do
allow(Enterprise::Billing::V2::TopupCatalog).to receive(:find_option).with(999).and_return(nil)
end
it 'returns an error' do
result = service.create_topup(credits: 999)
expect(result[:success]).to be false
expect(result[:message]).to eq('Unsupported topup amount')
end
end
end
end