From fcc6973245dc79b4dbd0a1183de1fd2458ae50e0 Mon Sep 17 00:00:00 2001 From: Tanmay Deep Sharma Date: Thu, 6 Nov 2025 02:02:35 +0530 Subject: [PATCH] remove pricing plan creation --- create_plans.rb | 142 ----------------- .../v2/pricing_plan_component_builder.rb | 142 ----------------- .../billing/v2/pricing_plan_service.rb | 104 ------------- .../ai/captain_credit_service_spec.rb | 39 ----- .../billing/v2/pricing_plan_service_spec.rb | 145 ------------------ 5 files changed, 572 deletions(-) delete mode 100644 create_plans.rb delete mode 100644 enterprise/app/services/enterprise/billing/v2/pricing_plan_component_builder.rb delete mode 100644 enterprise/app/services/enterprise/billing/v2/pricing_plan_service.rb delete mode 100644 spec/enterprise/services/enterprise/ai/captain_credit_service_spec.rb delete mode 100644 spec/enterprise/services/enterprise/billing/v2/pricing_plan_service_spec.rb diff --git a/create_plans.rb b/create_plans.rb deleted file mode 100644 index 3e3a85d71..000000000 --- a/create_plans.rb +++ /dev/null @@ -1,142 +0,0 @@ -# Validate environment before starting -if ENV['STRIPE_SECRET_KEY'].blank? - puts '❌ ERROR: STRIPE_SECRET_KEY environment variable is not set' - puts 'Please set it with: export STRIPE_SECRET_KEY=sk_test_...' - exit 1 -end - -account = Account.first -if account.nil? - puts '❌ ERROR: No accounts found in database' - puts 'Please create an account first' - exit 1 -end - -puts "Using account: #{account.name} (ID: #{account.id})" -puts "Stripe API: #{ENV.fetch('STRIPE_SECRET_KEY', nil)[0..10]}..." -puts '' - -configs = [ - { - cpu_display_name: 'Credits', - cpu_lookup_key: 'cpu_credits', - meter_display_name: 'Cw meter', - meter_event_name: 'chatwoot.usage', - plan_display_name: 'Chatwoot Hacker', - plan_lookup_key: 'chatwoot_hacker_v2', - lookup_key: 'chatwoot_hacker_license_fee_v2', - service_action_lookup_key: 'chatwoot_hacker_plan_v2_credits', - metered_item_lookup_key: 'chatwoot_hacker_plan_v2_usage', - licensed_item_display_name: 'Seat', - licensed_item_unit_label: 'per agent', - license_fee_display_name: 'Fee', - license_fee_amount: '0', - monthly_credit_amount: 0, - rate_card_display_name: 'Rates', - metered_item_display_name: 'Prompt', - rate_value: 1, - config_key: 'STRIPE_HACKER_PLAN_ID' - }, - { - cpu_display_name: 'Credits', - cpu_lookup_key: 'cpu_credits', - meter_display_name: 'Cw meter', - meter_event_name: 'chatwoot.usage', - plan_display_name: 'Chatwoot Startup', - plan_lookup_key: 'chatwoot_startup_v2', - lookup_key: 'chatwoot_startup_license_fee_v2', - service_action_lookup_key: 'chatwoot_startup_plan_v2_credits', - metered_item_lookup_key: 'chatwoot_startup_plan_v2_usage', - licensed_item_display_name: 'Seat', - licensed_item_unit_label: 'per agent', - license_fee_display_name: 'Fee', - license_fee_amount: '1900', - monthly_credit_amount: 10_000, - rate_card_display_name: 'Rates', - metered_item_display_name: 'Prompt', - rate_value: 1, - config_key: 'STRIPE_STARTUP_PLAN_ID' - }, - { - cpu_display_name: 'Credits', - cpu_lookup_key: 'cpu_credits', - meter_display_name: 'Cw meter', - meter_event_name: 'chatwoot.usage', - plan_display_name: 'Chatwoot Business', - plan_lookup_key: 'chatwoot_business_v2', - lookup_key: 'chatwoot_business_license_fee_v2', - service_action_lookup_key: 'chatwoot_business_plan_v2_credits', - metered_item_lookup_key: 'chatwoot_business_plan_v2_usage', - licensed_item_display_name: 'Seat', - licensed_item_unit_label: 'per agent', - license_fee_display_name: 'Fee', - license_fee_amount: '3900', - monthly_credit_amount: 50_000, - rate_card_display_name: 'Rates', - metered_item_display_name: 'Prompt', - rate_value: 1, - config_key: 'STRIPE_BUSINESS_PLAN_ID' - }, - { - cpu_display_name: 'Credits', - cpu_lookup_key: 'cpu_credits', - meter_display_name: 'Cw meter', - meter_event_name: 'chatwoot.usage', - plan_display_name: 'Chatwoot Enterprise', - plan_lookup_key: 'chatwoot_enterprise_plan_v2', - lookup_key: 'chatwoot_enterprise_license_fee_v2', - service_action_lookup_key: 'chatwoot_enterprise_plan_v2_credits', - metered_item_lookup_key: 'chatwoot_enterprise_plan_v2_usage', - licensed_item_display_name: 'Seat', - licensed_item_unit_label: 'per agent', - license_fee_display_name: 'Fee', - license_fee_amount: '9900', - monthly_credit_amount: 200_000, - rate_card_display_name: 'Rates', - metered_item_display_name: 'Prompt', - rate_value: 1, - config_key: 'STRIPE_ENTERPRISE_PLAN_ID' - } -] - -service = Enterprise::Billing::V2::PricingPlanService.new(account: account) - -puts 'Creating pricing plans...' -puts '' - -configs.each do |config| - begin - result = service.create_complete_pricing_plan(config) - rescue StandardError => e - result = { success: false, error: e.message } - puts "Exception occurred: #{e.class}" - puts "Message: #{e.message}" - puts e.backtrace.first(5).join("\n") - end - - if result[:success] - # Save or update the pricing plan ID in InstallationConfig - installation_config = InstallationConfig.find_or_initialize_by(name: config[:config_key]) - installation_config.value = result[:pricing_plan].id - installation_config.save! - - puts "✓ Created #{config[:plan_display_name]}" - puts " Plan ID: #{result[:pricing_plan].id}" - puts " CPU ID: #{result[:custom_pricing_unit].id}" - puts " Meter ID: #{result[:meter].id}" - else - puts "✗ Failed to create #{config[:plan_display_name]}" - puts " Error: #{result[:error]}" if result[:error] - end - puts '' -end - -puts 'Summary:' -puts '--------' -puts "CPU ID saved to: STRIPE_CUSTOM_PRICING_UNIT_ID = #{InstallationConfig.find_by(name: 'STRIPE_CUSTOM_PRICING_UNIT_ID')&.value}" -puts "Meter ID saved to: STRIPE_METER_ID = #{InstallationConfig.find_by(name: 'STRIPE_METER_ID')&.value}" -puts '' -configs.each do |config| - plan_id = InstallationConfig.find_by(name: config[:config_key])&.value - puts "#{config[:config_key]} = #{plan_id}" -end diff --git a/enterprise/app/services/enterprise/billing/v2/pricing_plan_component_builder.rb b/enterprise/app/services/enterprise/billing/v2/pricing_plan_component_builder.rb deleted file mode 100644 index c3a04ac14..000000000 --- a/enterprise/app/services/enterprise/billing/v2/pricing_plan_component_builder.rb +++ /dev/null @@ -1,142 +0,0 @@ -class Enterprise::Billing::V2::PricingPlanComponentBuilder < Enterprise::Billing::V2::BaseService - include Enterprise::Billing::Concerns::StripeV2ClientHelper - - def add_license_fee_component(plan, config) - licensed_item = create_licensed_item( - display_name: config[:licensed_item_display_name], - lookup_key: config[:lookup_key], - unit_label: config[:licensed_item_unit_label] - ) - - license_fee = create_license_fee( - display_name: config[:license_fee_display_name], - unit_amount: config[:license_fee_amount], - licensed_item_id: licensed_item.id, - lookup_key: config[:lookup_key] - ) - - add_component( - plan_id: plan.id, - type: 'license_fee', - data: { id: license_fee.id, version: license_fee.latest_version }, - lookup_key: config[:lookup_key] - ) - end - - def add_service_action_component(plan, config, cpu) - action = create_service_action( - lookup_key: config[:service_action_lookup_key], - credit_amount: config[:monthly_credit_amount], - cpu_id: cpu.id - ) - - add_component( - plan_id: plan.id, - type: 'service_action', - data: { id: action.id }, - lookup_key: config[:service_action_lookup_key] - ) - - action - end - - def add_rate_card_component(plan, config, meter, cpu) - card = create_rate_card(display_name: config[:rate_card_display_name]) - - item = create_metered_item( - display_name: config[:metered_item_display_name], - lookup_key: config[:metered_item_lookup_key], - meter_id: meter.id - ) - - add_rate(card_id: card.id, item_id: item.id, cpu_id: cpu.id, value: config[:rate_value] || 1) - - add_component( - plan_id: plan.id, - type: 'rate_card', - data: { id: card.id, version: card.latest_version }, - lookup_key: config[:metered_item_lookup_key] - ) - - card - end - - private - - def create_licensed_item(display_name:, lookup_key:, unit_label:) - super({ display_name: display_name, lookup_key: lookup_key, unit_label: unit_label }) - end - - def create_license_fee(display_name:, unit_amount:, licensed_item_id:, lookup_key:) - super({ - display_name: display_name, - currency: 'usd', - service_interval: 'month', - service_interval_count: 1, - tax_behavior: 'exclusive', - unit_amount: unit_amount.to_s, - licensed_item: licensed_item_id, - lookup_key: lookup_key - }) - end - - def create_service_action(lookup_key:, credit_amount:, cpu_id:) - super(service_action_params(lookup_key, credit_amount, cpu_id)) - end - - def service_action_params(lookup_key, credit_amount, cpu_id) - { - lookup_key: lookup_key, - service_interval: 'month', - service_interval_count: 1, - type: 'credit_grant', - credit_grant: credit_grant_config(credit_amount, cpu_id) - } - end - - def credit_grant_config(credit_amount, cpu_id) - { - name: 'Monthly Credits', - amount: { - type: 'custom_pricing_unit', - custom_pricing_unit: { id: cpu_id, value: credit_amount.to_s } - }, - expiry_config: { type: 'end_of_service_period' }, - applicability_config: { scope: { price_type: 'metered' } } - } - end - - def create_rate_card(display_name:) - super({ - display_name: display_name, - currency: 'usd', - service_interval: 'month', - service_interval_count: 1, - tax_behavior: 'exclusive' - }) - end - - def create_metered_item(display_name:, lookup_key:, meter_id:) - super({ display_name: display_name, lookup_key: lookup_key, meter: meter_id }) - end - - def add_rate(card_id:, item_id:, cpu_id:, value:) - add_rate_to_card(card_id, { - metered_item: item_id, - custom_pricing_unit_amount: { id: cpu_id, value: value.to_s } - }) - end - - def add_component(plan_id:, type:, data:, lookup_key:) - params = case type - when 'license_fee' - { type: 'license_fee', license_fee: data, lookup_key: lookup_key } - when 'service_action' - { type: 'service_action', service_action: data, lookup_key: lookup_key } - when 'rate_card' - { type: 'rate_card', rate_card: data, lookup_key: lookup_key } - end - - add_pricing_plan_component(plan_id, params) - end -end diff --git a/enterprise/app/services/enterprise/billing/v2/pricing_plan_service.rb b/enterprise/app/services/enterprise/billing/v2/pricing_plan_service.rb deleted file mode 100644 index 56d6c73d4..000000000 --- a/enterprise/app/services/enterprise/billing/v2/pricing_plan_service.rb +++ /dev/null @@ -1,104 +0,0 @@ -class Enterprise::Billing::V2::PricingPlanService < Enterprise::Billing::V2::BaseService - include Enterprise::Billing::Concerns::StripeV2ClientHelper - - def create_custom_pricing_unit(display_name:, lookup_key:) - super({ display_name: display_name, lookup_key: lookup_key }) - end - - def create_meter(display_name:, event_name:) - params = { - 'display_name' => display_name, - 'event_name' => event_name, - 'default_aggregation[formula]' => 'sum', - 'customer_mapping[type]' => 'by_id', - 'customer_mapping[event_payload_key]' => 'stripe_customer_id', - 'value_settings[event_payload_key]' => 'value' - } - super(params) - end - - def create_pricing_plan(display_name:, lookup_key:, currency: 'usd', tax_behavior: 'exclusive') - super({ display_name: display_name, currency: currency, tax_behavior: tax_behavior, lookup_key: lookup_key }) - end - - def create_complete_pricing_plan(config) - # Use shared CPU and meter if available, otherwise create new ones - cpu = get_or_create_cpu(config) - meter = get_or_create_meter(config) - - plan = create_pricing_plan(display_name: config[:plan_display_name], lookup_key: config[:plan_lookup_key]) - - builder = component_builder - builder.add_license_fee_component(plan, config) if config[:license_fee_amount].to_i.positive? - builder.add_service_action_component(plan, config, cpu) if config[:monthly_credit_amount].to_i.positive? - builder.add_rate_card_component(plan, config, meter, cpu) - - # Make the latest version live - make_plan_version_live(plan.id) - - build_plan_result(plan, cpu, meter) - end - - def get_or_create_cpu(config) - shared_cpu_id = InstallationConfig.find_by(name: 'STRIPE_CUSTOM_PRICING_UNIT_ID')&.value - - if shared_cpu_id - # Return existing CPU as OpenStruct to match create response - OpenStruct.new(id: shared_cpu_id) - else - # Create new CPU if not using shared - cpu = create_custom_pricing_unit( - display_name: config[:cpu_display_name], - lookup_key: config[:cpu_lookup_key] - ) - - installation_config = InstallationConfig.find_or_initialize_by(name: 'STRIPE_CUSTOM_PRICING_UNIT_ID') - installation_config.value = cpu.id - installation_config.save! - - cpu - end - end - - def get_or_create_meter(config) - # Check for shared meter first - shared_meter_id = InstallationConfig.find_by(name: 'STRIPE_METER_ID')&.value || - ENV.fetch('STRIPE_METER_ID', nil) - - if shared_meter_id - # Return existing meter as OpenStruct to match create response - OpenStruct.new(id: shared_meter_id) - else - # Create new meter if not using shared - meter = create_meter( - display_name: config[:meter_display_name], - event_name: config[:meter_event_name] - ) - - installation_config = InstallationConfig.find_or_initialize_by(name: 'STRIPE_METER_ID') - installation_config.value = meter.id - installation_config.save! - - meter - end - end - - private - - def component_builder - @component_builder ||= Enterprise::Billing::V2::PricingPlanComponentBuilder.new(account: account) - end - - def make_plan_version_live(plan_id) - update_pricing_plan(plan_id, { live_version: 'latest' }) - end - - def build_plan_result(plan, cpu, meter) - { - success: true, - pricing_plan: plan, - custom_pricing_unit: cpu, - meter: meter - } - end -end diff --git a/spec/enterprise/services/enterprise/ai/captain_credit_service_spec.rb b/spec/enterprise/services/enterprise/ai/captain_credit_service_spec.rb deleted file mode 100644 index 14ae0506f..000000000 --- a/spec/enterprise/services/enterprise/ai/captain_credit_service_spec.rb +++ /dev/null @@ -1,39 +0,0 @@ -require 'rails_helper' - -describe Enterprise::Ai::CaptainCreditService do - let(:account) { create(:account) } - let(:service) { described_class.new(account: account) } - let(:credit_service) { instance_double(Enterprise::Billing::V2::CreditManagementService) } - - before do - allow(Enterprise::Billing::V2::CreditManagementService).to receive(:new).and_return(credit_service) - end - - describe '#check_and_use_credits' do - context 'when account is not on v2 billing' do - it 'returns success without invoking credit service' do - result = service.check_and_use_credits - - expect(result[:success]).to be(true) - expect(Enterprise::Billing::V2::CreditManagementService).not_to have_received(:new) - end - end - - context 'when account uses v2 billing' do - before do - account.update!(custom_attributes: (account.custom_attributes || {}).merge('stripe_billing_version' => 2)) - allow(credit_service).to receive(:use_credit).and_return(success: true) - end - - it 'delegates to credit management service with metadata' do - metadata = { 'feature' => 'ai_reply', 'conversation_id' => 123 } - - result = service.check_and_use_credits(feature: 'ai_reply', metadata: metadata) - - expect(result[:success]).to be(true) - expect(credit_service).to have_received(:use_credit) - .with(feature: 'ai_reply', amount: 1, metadata: metadata) - end - end - end -end diff --git a/spec/enterprise/services/enterprise/billing/v2/pricing_plan_service_spec.rb b/spec/enterprise/services/enterprise/billing/v2/pricing_plan_service_spec.rb deleted file mode 100644 index af3584f02..000000000 --- a/spec/enterprise/services/enterprise/billing/v2/pricing_plan_service_spec.rb +++ /dev/null @@ -1,145 +0,0 @@ -require 'rails_helper' - -RSpec.describe Enterprise::Billing::V2::PricingPlanService do - let(:account) { create(:account) } - let(:service) { described_class.new(account: account) } - - before do - allow(ENV).to receive(:fetch).and_call_original - allow(ENV).to receive(:fetch).with('STRIPE_SECRET_KEY', nil).and_return('sk_test_123') - end - - describe '#create_custom_pricing_unit' do - it 'creates a custom pricing unit' do - cpu_response = OpenStruct.new(id: 'cpu_123') - allow(StripeV2Client).to receive(:request).and_return(cpu_response) - - result = service.create_custom_pricing_unit(display_name: 'Credits', lookup_key: 'credits_001') - - expect(result.id).to eq('cpu_123') - expect(StripeV2Client).to have_received(:request).with( - :post, - '/v2/billing/custom_pricing_units', - { display_name: 'Credits', lookup_key: 'credits_001' }, - { api_key: 'sk_test_123', stripe_version: '2025-08-27.preview' } - ) - end - end - - describe '#create_meter' do - it 'creates a billing meter' do - meter_response = OpenStruct.new(id: 'meter_123') - allow(StripeV2Client).to receive(:request).and_return(meter_response) - - result = service.create_meter(display_name: 'Prompts', event_name: 'prompts_001') - - expect(result.id).to eq('meter_123') - expect(StripeV2Client).to have_received(:request).with( - :post, - '/v1/billing/meters', - kind_of(Hash), - { api_key: 'sk_test_123', stripe_version: '2025-08-27.preview' } - ) - end - end - - describe '#create_pricing_plan' do - it 'creates a pricing plan' do - plan_response = OpenStruct.new(id: 'plan_123') - allow(StripeV2Client).to receive(:request).and_return(plan_response) - - result = service.create_pricing_plan(display_name: 'Business Plan', lookup_key: 'business_plan') - - expect(result.id).to eq('plan_123') - expect(StripeV2Client).to have_received(:request).with( - :post, - '/v2/billing/pricing_plans', - { display_name: 'Business Plan', currency: 'usd', tax_behavior: 'exclusive', lookup_key: 'business_plan' }, - { api_key: 'sk_test_123', stripe_version: '2025-08-27.preview' } - ) - end - end - - describe '#create_complete_pricing_plan' do - # Clean up any existing InstallationConfig entries before each test - before do - InstallationConfig.where(name: %w[STRIPE_CUSTOM_PRICING_UNIT_ID STRIPE_METER_ID]).destroy_all - # Also ensure ENV doesn't have STRIPE_METER_ID - allow(ENV).to receive(:fetch).with('STRIPE_METER_ID', nil).and_return(nil) - end - - let(:cpu) { OpenStruct.new(id: 'cpu_123') } - let(:meter) { OpenStruct.new(id: 'meter_123') } - let(:plan) { OpenStruct.new(id: 'plan_123') } - let(:service_action) { OpenStruct.new(id: 'sa_123') } - let(:rate_card) { OpenStruct.new(id: 'rc_123', latest_version: 'v1') } - let(:live_plan) { OpenStruct.new(id: 'plan_123', live_version: 'latest') } - - let(:config) do - { - # Shared resources - cpu_display_name: 'Credits', - cpu_lookup_key: 'cpu_001', - meter_display_name: 'Prompts', - meter_event_name: 'prompts_001', - - # Plan details - plan_display_name: 'Business Plan', - plan_lookup_key: 'business_plan', - - # Component-specific lookup keys (each must be unique) - lookup_key: 'chatwoot_business_agents', # Used for license fee - service_action_lookup_key: 'chatwoot_business_credits', - metered_item_lookup_key: 'chatwoot_business_usage', - - # License fee - licensed_item_display_name: 'Seat', - licensed_item_unit_label: 'per agent', - license_fee_display_name: 'Fee', - license_fee_amount: 3900, - - # Service action (monthly credits) - monthly_credit_amount: 2000, - - # Rate card (overage) - rate_card_display_name: 'Rates', - metered_item_display_name: 'Prompt', - rate_value: 1 - } - end - - def stub_stripe_client - allow(StripeV2Client).to receive(:request) do |_method, path, *_args| - case path - when '/v2/billing/custom_pricing_units' then cpu - when '/v1/billing/meters' then meter - when '/v2/billing/pricing_plans' then plan - when '/v2/billing/pricing_plans/plan_123' then live_plan - end - end - end - - def stub_component_builder - component_builder = instance_double(Enterprise::Billing::V2::PricingPlanComponentBuilder) - allow(component_builder).to receive(:add_license_fee_component).and_return(true) - allow(component_builder).to receive(:add_service_action_component).and_return(service_action) - allow(component_builder).to receive(:add_rate_card_component).and_return(rate_card) - allow(service).to receive(:component_builder).and_return(component_builder) - end - - it 'creates a complete pricing plan with all components' do - stub_stripe_client - stub_component_builder - - result = service.create_complete_pricing_plan(config) - - expect(result[:success]).to be true - expect(result[:pricing_plan]).to eq(plan) - expect(result[:custom_pricing_unit]).to eq(cpu) - expect(result[:meter]).to eq(meter) - expect(result[:service_action]).to eq(service_action) - expect(InstallationConfig.find_by(name: 'STRIPE_CUSTOM_PRICING_UNIT_ID')&.value).to eq('cpu_123') - expect(InstallationConfig.find_by(name: 'STRIPE_METER_ID')&.value).to eq('meter_123') - end - end -end