remove pricing plan creation

This commit is contained in:
Tanmay Deep Sharma
2025-11-06 02:02:35 +05:30
parent 2590c57893
commit fcc6973245
5 changed files with 0 additions and 572 deletions
-142
View File
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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