Merge remote-tracking branch 'origin/feature/stripe_v2' into feature/stripe_v2_fe

This commit is contained in:
Tanmay Deep Sharma
2025-10-29 03:59:13 +05:30
3 changed files with 155 additions and 22 deletions
@@ -10,12 +10,15 @@ class Enterprise::Billing::CreateStripeCustomerService
customer_id = prepare_customer_id
if billing_v2_enabled?
if billing_v2_enabled? && v2_configs_present?
update_account_for_v2_billing(customer_id)
enable_plan_specific_features(default_plan['name'])
else
elsif default_plan.present?
subscription = create_subscription(customer_id)
update_account_with_subscription(customer_id, subscription)
else
# Minimal setup when configs are missing
account.update!(custom_attributes: { stripe_customer_id: customer_id })
end
end
@@ -40,7 +43,7 @@ class Enterprise::Billing::CreateStripeCustomerService
def default_plan
installation_config = InstallationConfig.find_by(name: 'CHATWOOT_CLOUD_PLANS')
@default_plan ||= installation_config.value.first
@default_plan ||= installation_config&.value&.first
end
def price_id
@@ -52,6 +55,11 @@ class Enterprise::Billing::CreateStripeCustomerService
ENV.fetch('STRIPE_BILLING_V2_ENABLED', 'false') == 'true'
end
def v2_configs_present?
InstallationConfig.find_by(name: 'STRIPE_HACKER_PLAN_ID').present? &&
default_plan.present?
end
def existing_subscription?
stripe_customer_id = account.custom_attributes['stripe_customer_id']
return false if stripe_customer_id.blank?
@@ -74,14 +82,19 @@ class Enterprise::Billing::CreateStripeCustomerService
end
def update_account_for_v2_billing(customer_id)
account.update!(
custom_attributes: {
stripe_customer_id: customer_id,
stripe_pricing_plan_id: InstallationConfig.find_by(name: 'STRIPE_HACKER_PLAN_ID').value,
hacker_plan_config = InstallationConfig.find_by(name: 'STRIPE_HACKER_PLAN_ID')
attributes = { stripe_customer_id: customer_id }
if hacker_plan_config&.value.present?
attributes.merge!(
stripe_pricing_plan_id: hacker_plan_config.value,
plan_name: 'Hacker',
subscribed_quantity: DEFAULT_QUANTITY
}
)
)
end
account.update!(custom_attributes: attributes)
end
def update_account_with_subscription(customer_id, subscription)
@@ -31,6 +31,9 @@ describe Enterprise::Billing::CreateStripeCustomerService do
it 'does not create a new customer' do
allow(Stripe::Customer).to receive(:create)
# Stub the subscription check to return no subscriptions
subscriptions_response = OpenStruct.new(data: [])
allow(Stripe::Subscription).to receive(:list).and_return(subscriptions_response)
create_stripe_customer_service.new(account: account).perform
@@ -38,5 +41,135 @@ describe Enterprise::Billing::CreateStripeCustomerService do
expect(account.reload.custom_attributes['stripe_customer_id']).to eq('cus_existing_customer')
end
end
context 'with V2 billing enabled' do
let(:cloud_plans_config) do
create(:installation_config,
name: 'CHATWOOT_CLOUD_PLANS',
value: [
{
'name' => 'Startup',
'price_ids' => ['price_startup_123'],
'default_quantity' => 2
}
])
end
let(:hacker_plan_config) do
create(:installation_config,
name: 'STRIPE_HACKER_PLAN_ID',
value: 'bpp_hacker_123')
end
before do
# Setup configs
cloud_plans_config
hacker_plan_config
# Enable V2 billing
allow(ENV).to receive(:fetch).with('STRIPE_BILLING_V2_ENABLED', 'false').and_return('true')
end
it 'creates a stripe customer and sets up V2 billing' do
customer = double
allow(Stripe::Customer).to receive(:create).and_return(customer)
allow(customer).to receive(:id).and_return('cus_random_number')
# Mock the plan feature manager
service = create_stripe_customer_service.new(account: account)
allow(service).to receive(:enable_plan_specific_features)
service.perform
expect(Stripe::Customer).to have_received(:create).with({ name: account.name, email: admin1.email })
expect(account.reload.custom_attributes).to include(
'stripe_customer_id' => 'cus_random_number',
'stripe_pricing_plan_id' => 'bpp_hacker_123',
'plan_name' => 'Hacker',
'subscribed_quantity' => 2
)
expect(service).to have_received(:enable_plan_specific_features).with('Startup')
end
it 'does not create new customer when customer already exists with V2' do
account.update!(custom_attributes: { stripe_customer_id: 'cus_existing_v2' })
allow(Stripe::Customer).to receive(:create)
# Stub the subscription check to return no subscriptions
subscriptions_response = OpenStruct.new(data: [])
allow(Stripe::Subscription).to receive(:list).and_return(subscriptions_response)
# Mock the plan feature manager
service = create_stripe_customer_service.new(account: account)
allow(service).to receive(:enable_plan_specific_features)
service.perform
expect(Stripe::Customer).not_to have_received(:create)
expect(account.reload.custom_attributes).to include(
'stripe_customer_id' => 'cus_existing_v2',
'stripe_pricing_plan_id' => 'bpp_hacker_123',
'plan_name' => 'Hacker',
'subscribed_quantity' => 2
)
end
it 'skips setup when active subscription exists' do
account.update!(custom_attributes: { stripe_customer_id: 'cus_existing_v2' })
allow(Stripe::Customer).to receive(:create)
# Stub the subscription check to return active subscription
subscription_data = OpenStruct.new(id: 'sub_123')
subscriptions_response = OpenStruct.new(data: [subscription_data])
allow(Stripe::Subscription).to receive(:list).and_return(subscriptions_response)
create_stripe_customer_service.new(account: account).perform
expect(Stripe::Customer).not_to have_received(:create)
expect(account.reload.custom_attributes['stripe_customer_id']).to eq('cus_existing_v2')
end
end
context 'with V1 billing (traditional subscription)' do
let(:cloud_plans_config) do
create(:installation_config,
name: 'CHATWOOT_CLOUD_PLANS',
value: [
{
'name' => 'Startup',
'price_ids' => ['price_startup_123'],
'default_quantity' => 2
}
])
end
before do
cloud_plans_config
allow(ENV).to receive(:fetch).with('STRIPE_BILLING_V2_ENABLED', 'false').and_return('false')
end
it 'creates a stripe customer and traditional subscription' do
customer = double
allow(Stripe::Customer).to receive(:create).and_return(customer)
allow(customer).to receive(:id).and_return('cus_random_number')
subscription = {
'plan' => { 'id' => 'price_startup_123', 'product' => 'prod_startup' },
'quantity' => 2
}
allow(Stripe::Subscription).to receive(:create).and_return(subscription)
create_stripe_customer_service.new(account: account).perform
expect(Stripe::Customer).to have_received(:create).with({ name: account.name, email: admin1.email })
expect(Stripe::Subscription).to have_received(:create)
expect(account.reload.custom_attributes).to include(
'stripe_customer_id' => 'cus_random_number',
'stripe_price_id' => 'price_startup_123',
'stripe_product_id' => 'prod_startup',
'plan_name' => 'Startup',
'subscribed_quantity' => 2
)
end
end
end
end
@@ -128,19 +128,6 @@ describe Enterprise::Billing::V2::SubscriptionProvisioningService do
end
end
context 'when an unexpected error occurs' do
before do
allow(StripeV2Client).to receive(:request).and_raise(StandardError.new('Unexpected error'))
end
it 'returns error response' do
result = service.provision(subscription_id: subscription_id)
expect(result[:success]).to be(false)
expect(result[:message]).to include('Provisioning error')
end
end
context 'with Startup plan' do
let(:startup_plan_id) { 'bpp_startup_plan_123' }
let(:subscription_response) do