From 125ae5523a521517d525c2be2039fba40a655364 Mon Sep 17 00:00:00 2001 From: Tanmay Sharma Date: Thu, 9 Oct 2025 09:52:56 +0200 Subject: [PATCH] fix stripe v2 calls --- .../v2/pricing_plan_component_builder.rb | 30 ++++-- .../billing/v2/pricing_plan_service.rb | 28 ++++-- enterprise/lib/stripe_v2_client.rb | 76 +++++++++++++++ .../billing/v2/pricing_plan_service_spec.rb | 97 +++++++++---------- 4 files changed, 162 insertions(+), 69 deletions(-) create mode 100644 enterprise/lib/stripe_v2_client.rb 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 index 78a5d1436..d001c9cc3 100644 --- a/enterprise/app/services/enterprise/billing/v2/pricing_plan_component_builder.rb +++ b/enterprise/app/services/enterprise/billing/v2/pricing_plan_component_builder.rb @@ -58,14 +58,18 @@ class Enterprise::Billing::V2::PricingPlanComponentBuilder < Enterprise::Billing private def create_licensed_item(display_name:, lookup_key:, unit_label:) - Stripe::V2::Billing::LicensedItem.create( + StripeV2Client.request( + :post, + '/v2/billing/licensed_items', { display_name: display_name, lookup_key: lookup_key, unit_label: unit_label }, { api_key: ENV.fetch('STRIPE_SECRET_KEY', nil), stripe_version: '2025-08-27.preview' } ) end def create_license_fee(display_name:, unit_amount:, licensed_item_id:) - Stripe::V2::Billing::LicenseFee.create( + StripeV2Client.request( + :post, + '/v2/billing/license_fees', { display_name: display_name, currency: 'usd', @@ -80,7 +84,9 @@ class Enterprise::Billing::V2::PricingPlanComponentBuilder < Enterprise::Billing end def create_service_action(lookup_key:, credit_amount:, cpu_id:) - Stripe::V2::Billing::ServiceAction.create( + StripeV2Client.request( + :post, + '/v2/billing/service_actions', { lookup_key: lookup_key, service_interval: 'month', @@ -101,7 +107,9 @@ class Enterprise::Billing::V2::PricingPlanComponentBuilder < Enterprise::Billing end def create_rate_card(display_name:) - Stripe::V2::Billing::RateCard.create( + StripeV2Client.request( + :post, + '/v2/billing/rate_cards', { display_name: display_name, currency: 'usd', @@ -114,15 +122,18 @@ class Enterprise::Billing::V2::PricingPlanComponentBuilder < Enterprise::Billing end def create_metered_item(display_name:, lookup_key:, meter_id:) - Stripe::V2::Billing::MeteredItem.create( + StripeV2Client.request( + :post, + '/v2/billing/metered_items', { display_name: display_name, lookup_key: lookup_key, meter: meter_id }, { api_key: ENV.fetch('STRIPE_SECRET_KEY', nil), stripe_version: '2025-08-27.preview' } ) end def add_rate(card_id:, item_id:, cpu_id:, value:) - Stripe::V2::Billing::RateCard::Rate.create( - card_id, + StripeV2Client.request( + :post, + "/v2/billing/rate_cards/#{card_id}/rates", { metered_item: item_id, custom_pricing_unit_amount: { id: cpu_id, value: value.to_s } @@ -141,8 +152,9 @@ class Enterprise::Billing::V2::PricingPlanComponentBuilder < Enterprise::Billing { type: 'rate_card', rate_card: data } end - Stripe::V2::Billing::PricingPlan::Component.create( - plan_id, + StripeV2Client.request( + :post, + "/v2/billing/pricing_plans/#{plan_id}/components", params, { api_key: ENV.fetch('STRIPE_SECRET_KEY', nil), stripe_version: '2025-08-27.preview' } ) diff --git a/enterprise/app/services/enterprise/billing/v2/pricing_plan_service.rb b/enterprise/app/services/enterprise/billing/v2/pricing_plan_service.rb index 97f89b4e1..c01da49bf 100644 --- a/enterprise/app/services/enterprise/billing/v2/pricing_plan_service.rb +++ b/enterprise/app/services/enterprise/billing/v2/pricing_plan_service.rb @@ -1,26 +1,34 @@ class Enterprise::Billing::V2::PricingPlanService < Enterprise::Billing::V2::BaseService def create_custom_pricing_unit(display_name:, lookup_key:) - Stripe::V2::Billing::CustomPricingUnit.create( + StripeV2Client.request( + :post, + '/v2/billing/custom_pricing_units', { display_name: display_name, lookup_key: lookup_key }, { api_key: ENV.fetch('STRIPE_SECRET_KEY', nil), stripe_version: '2025-08-27.preview' } ) end def create_meter(display_name:, event_name:) - Stripe::Billing::Meter.create( - { - display_name: display_name, - event_name: event_name, - default_aggregation: { formula: 'sum' }, - customer_mapping: { type: 'by_id', event_payload_key: 'stripe_customer_id' }, - value_settings: { event_payload_key: 'value' } - }, + 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' + } + StripeV2Client.request( + :post, + '/v1/billing/meters', + params, { api_key: ENV.fetch('STRIPE_SECRET_KEY', nil), stripe_version: '2025-08-27.preview' } ) end def create_pricing_plan(display_name:, currency: 'usd', tax_behavior: 'exclusive') - Stripe::V2::Billing::PricingPlan.create( + StripeV2Client.request( + :post, + '/v2/billing/pricing_plans', { display_name: display_name, currency: currency, tax_behavior: tax_behavior }, { api_key: ENV.fetch('STRIPE_SECRET_KEY', nil), stripe_version: '2025-08-27.preview' } ) diff --git a/enterprise/lib/stripe_v2_client.rb b/enterprise/lib/stripe_v2_client.rb new file mode 100644 index 000000000..376ec8550 --- /dev/null +++ b/enterprise/lib/stripe_v2_client.rb @@ -0,0 +1,76 @@ +module StripeV2Client + class << self + def request(method, path, params = {}, options = {}) + api_key = options[:api_key] || ENV.fetch('STRIPE_SECRET_KEY', nil) + stripe_version = options[:stripe_version] || '2025-08-27.preview' + + uri = URI("https://api.stripe.com#{path}") + + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = true + + request = build_request(method, uri, params, path) + request['Authorization'] = "Bearer #{api_key}" + request['Stripe-Version'] = stripe_version + + response = http.request(request) + parse_response(response) + end + + private + + def build_request(method, uri, params, path) + case method + when :get + Net::HTTP::Get.new(uri) + when :post + req = Net::HTTP::Post.new(uri) + if v2_endpoint?(path) + req.body = params.to_json unless params.empty? + req['Content-Type'] = 'application/json' + else + req.set_form_data(params) unless params.empty? + req['Content-Type'] = 'application/x-www-form-urlencoded' + end + req + when :delete + Net::HTTP::Delete.new(uri) + end + end + + def v2_endpoint?(path) + path.start_with?('/v2/') + end + + def parse_response(response) + body = JSON.parse(response.body) + + # Convert to OpenStruct for dot notation access (mimicking Stripe SDK objects) + case body + when Hash + recursive_to_struct(body) + when Array + body.map { |item| recursive_to_struct(item) } + else + body + end + rescue JSON::ParserError + response.body + end + + def recursive_to_struct(hash) + return hash unless hash.is_a?(Hash) + + OpenStruct.new(hash.transform_values do |value| + case value + when Hash + recursive_to_struct(value) + when Array + value.map { |item| item.is_a?(Hash) ? recursive_to_struct(item) : item } + else + value + end + 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 index efbf4bb5b..5ee89060e 100644 --- a/spec/enterprise/services/enterprise/billing/v2/pricing_plan_service_spec.rb +++ b/spec/enterprise/services/enterprise/billing/v2/pricing_plan_service_spec.rb @@ -10,95 +10,92 @@ RSpec.describe Enterprise::Billing::V2::PricingPlanService do describe '#create_custom_pricing_unit' do it 'creates a custom pricing unit' do - cpu_double = instance_double(Stripe::V2::Billing::CustomPricingUnit, id: 'cpu_123') - allow(Stripe::V2::Billing::CustomPricingUnit).to receive(:create).and_return(cpu_double) + 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_double = instance_double(Stripe::Billing::Meter, id: 'meter_123') - allow(Stripe::Billing::Meter).to receive(:create).and_return(meter_double) + 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_double = instance_double(Stripe::V2::Billing::PricingPlan, id: 'plan_123') - allow(Stripe::V2::Billing::PricingPlan).to receive(:create).and_return(plan_double) + 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') expect(result.id).to eq('plan_123') - end - end - - describe '#create_service_action' do - it 'creates a service action for monthly credit grants' do - action_double = instance_double(Stripe::V2::Billing::ServiceAction, id: 'sa_123') - allow(Stripe::V2::Billing::ServiceAction).to receive(:create).and_return(action_double) - - result = service.create_service_action( - lookup_key: 'monthly_credits_001', - cpu_id: 'cpu_123', - credit_amount: 2000 + expect(StripeV2Client).to have_received(:request).with( + :post, + '/v2/billing/pricing_plans', + { display_name: 'Business Plan', currency: 'usd', tax_behavior: 'exclusive' }, + { api_key: 'sk_test_123', stripe_version: '2025-08-27.preview' } ) - - expect(result.id).to eq('sa_123') end end describe '#create_complete_pricing_plan' do let(:config) do { - cpu_display_name: 'Captain Credits', - cpu_lookup_key: 'captain_credits_001', - meter_display_name: 'Captain Prompts', - meter_event_name: 'captain_prompts_001', - plan_display_name: 'Business Plan - 2000 Credits', + cpu_display_name: 'Credits', + cpu_lookup_key: 'cpu_001', + meter_display_name: 'Prompts', + meter_event_name: 'prompts_001', + plan_display_name: 'Business Plan', include_license_fee: true, - licensed_item_display_name: 'Business Seat', - licensed_item_lookup_key: 'business_seat_001', + licensed_item_display_name: 'Seat', + licensed_item_lookup_key: 'seat_001', licensed_item_unit_label: 'per agent', - license_fee_display_name: 'Business Monthly Fee', + license_fee_display_name: 'Fee', license_fee_amount: '3900', - service_action_lookup_key: 'monthly_credits_001', + service_action_lookup_key: 'credits_001', monthly_credit_amount: 2000, - rate_card_display_name: 'Usage Rates', - metered_item_display_name: 'Captain Prompt', - metered_item_lookup_key: 'captain_prompt_001', + rate_card_display_name: 'Rates', + metered_item_display_name: 'Prompt', + metered_item_lookup_key: 'prompt_001', rate_value: 1 } end it 'creates a complete pricing plan with all components' do - cpu = instance_double(Stripe::V2::Billing::CustomPricingUnit, id: 'cpu_123') - meter = instance_double(Stripe::Billing::Meter, id: 'meter_123') - plan = instance_double(Stripe::V2::Billing::PricingPlan, id: 'plan_123') - licensed_item = instance_double(Stripe::V2::Billing::LicensedItem, id: 'li_123') - license_fee = instance_double(Stripe::V2::Billing::LicenseFee, id: 'lf_123', latest_version: 'v1') - service_action = instance_double(Stripe::V2::Billing::ServiceAction, id: 'sa_123') - rate_card = instance_double(Stripe::V2::Billing::RateCard, id: 'rc_123', latest_version: 'v1') - metered_item = instance_double(Stripe::V2::Billing::MeteredItem, id: 'mi_123') + cpu = OpenStruct.new(id: 'cpu_123') + meter = OpenStruct.new(id: 'meter_123') + plan = OpenStruct.new(id: 'plan_123') + service_action = OpenStruct.new(id: 'sa_123') + rate_card = OpenStruct.new(id: 'rc_123', latest_version: 'v1') - allow(service).to receive(:create_custom_pricing_unit).and_return(cpu) - allow(service).to receive(:create_meter).and_return(meter) - allow(service).to receive(:create_pricing_plan).and_return(plan) - allow(service).to receive(:create_licensed_item).and_return(licensed_item) - allow(service).to receive(:create_license_fee).and_return(license_fee) - allow(service).to receive(:create_service_action).and_return(service_action) - allow(service).to receive(:create_rate_card).and_return(rate_card) - allow(service).to receive(:create_metered_item).and_return(metered_item) - allow(service).to receive(:add_rate_to_rate_card).and_return(true) - allow(service).to receive(:add_component_to_plan).and_return(true) + allow(StripeV2Client).to receive(:request).and_return(cpu, meter, plan, service_action, rate_card) + allow_any_instance_of(Enterprise::Billing::V2::PricingPlanComponentBuilder) + .to receive(:add_license_fee_component).and_return(true) + allow_any_instance_of(Enterprise::Billing::V2::PricingPlanComponentBuilder) + .to receive(:add_service_action_component).and_return(service_action) + allow_any_instance_of(Enterprise::Billing::V2::PricingPlanComponentBuilder) + .to receive(:add_rate_card_component).and_return(rate_card) result = service.create_complete_pricing_plan(config)