From 7babcfe6226d612a0d6f3ddb00b5c01f8cb8f32d Mon Sep 17 00:00:00 2001 From: Tanmay Deep Sharma Date: Mon, 10 Nov 2025 17:55:57 +0530 Subject: [PATCH 1/4] add updated rspecs --- .../api/v1/accounts_controller_spec.rb | 6 +- .../create_stripe_customer_service_spec.rb | 197 +++++++----------- .../handle_stripe_event_service_spec.rb | 142 +++++++++++++ 3 files changed, 226 insertions(+), 119 deletions(-) diff --git a/spec/enterprise/controllers/enterprise/api/v1/accounts_controller_spec.rb b/spec/enterprise/controllers/enterprise/api/v1/accounts_controller_spec.rb index 33941cee8..f56428d0a 100644 --- a/spec/enterprise/controllers/enterprise/api/v1/accounts_controller_spec.rb +++ b/spec/enterprise/controllers/enterprise/api/v1/accounts_controller_spec.rb @@ -205,8 +205,10 @@ RSpec.describe 'Enterprise Billing APIs', type: :request do }, 'conversation' => {}, 'captain' => { - 'documents' => { 'consumed' => 0, 'current_available' => ChatwootApp.max_limit, 'total_count' => ChatwootApp.max_limit }, - 'responses' => { 'consumed' => 0, 'current_available' => ChatwootApp.max_limit, 'total_count' => ChatwootApp.max_limit } + 'documents' => { 'consumed' => 0, 'current_available' => ChatwootApp.max_limit, 'total_count' => ChatwootApp.max_limit, + 'monthly' => nil, 'topup' => nil }, + 'responses' => { 'consumed' => 0, 'current_available' => ChatwootApp.max_limit, 'total_count' => ChatwootApp.max_limit, + 'monthly' => 0, 'topup' => 0 } }, 'non_web_inboxes' => {} } diff --git a/spec/enterprise/services/enterprise/billing/create_stripe_customer_service_spec.rb b/spec/enterprise/services/enterprise/billing/create_stripe_customer_service_spec.rb index f5b0bbe86..7b3a5c300 100644 --- a/spec/enterprise/services/enterprise/billing/create_stripe_customer_service_spec.rb +++ b/spec/enterprise/services/enterprise/billing/create_stripe_customer_service_spec.rb @@ -5,137 +5,100 @@ describe Enterprise::Billing::CreateStripeCustomerService do let(:account) { create(:account) } let!(:admin1) { create(:user, account: account, role: :administrator) } - let(:admin2) { create(:user, account: account, role: :administrator) } - let(:subscriptions_list) { double } describe '#perform' do - before do - create( - :installation_config, - { name: 'CHATWOOT_CLOUD_PLANS', value: [ - { 'name' => 'A Plan Name', 'product_id' => ['prod_hacker_random'], 'price_ids' => ['price_hacker_random'] } - ] } - ) + context 'when V2 configs are missing' do + it 'raises a configuration error' do + expect do + create_stripe_customer_service.new(account: account).perform + end.to raise_error(StandardError, /V2 billing configuration is required/) + end end - it 'does not call stripe methods if customer id is present' do - account.update!(custom_attributes: { stripe_customer_id: 'cus_random_number' }) - allow(subscriptions_list).to receive(:data).and_return([]) - allow(Stripe::Customer).to receive(:create) - allow(Stripe::Subscription).to receive(:list).and_return(subscriptions_list) - allow(Stripe::Subscription).to receive(:create) - .and_return( - { - plan: { id: 'price_random_number', product: 'prod_random_number' }, - quantity: 2 - }.with_indifferent_access - ) + context 'with V2 billing' 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 - create_stripe_customer_service.new(account: account).perform + let(:hacker_plan_config) do + create(:installation_config, + name: 'STRIPE_HACKER_PLAN_ID', + value: 'bpp_hacker_123') + end - expect(Stripe::Customer).not_to have_received(:create) - expect(Stripe::Subscription) - .to have_received(:create) - .with({ customer: 'cus_random_number', items: [{ price: 'price_hacker_random', quantity: 2 }] }) + before do + # Setup configs + cloud_plans_config + hacker_plan_config + end - expect(account.reload.custom_attributes).to eq( - { - stripe_customer_id: 'cus_random_number', - stripe_price_id: 'price_random_number', - stripe_product_id: 'prod_random_number', - subscribed_quantity: 2, - plan_name: 'A Plan Name' - }.with_indifferent_access - ) - end - - it 'calls stripe methods to create a customer and updates the account' do - customer = double - allow(Stripe::Customer).to receive(:create).and_return(customer) - allow(customer).to receive(:id).and_return('cus_random_number') - allow(Stripe::Subscription) - .to receive(:create) - .and_return( - { - plan: { id: 'price_random_number', product: 'prod_random_number' }, - quantity: 2 - }.with_indifferent_access - ) - - 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) - .with({ customer: customer.id, items: [{ price: 'price_hacker_random', quantity: 2 }] }) - - expect(account.reload.custom_attributes).to eq( - { - stripe_customer_id: customer.id, - stripe_price_id: 'price_random_number', - stripe_product_id: 'prod_random_number', - subscribed_quantity: 2, - plan_name: 'A Plan Name' - }.with_indifferent_access - ) - end - end - - describe 'when checking for existing subscriptions' do - before do - create( - :installation_config, - { name: 'CHATWOOT_CLOUD_PLANS', value: [ - { 'name' => 'A Plan Name', 'product_id' => ['prod_hacker_random'], 'price_ids' => ['price_hacker_random'] } - ] } - ) - end - - context 'when account has no stripe_customer_id' do - it 'creates a new subscription' do + 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') - allow(Stripe::Subscription).to receive(:create).and_return( - { - plan: { id: 'price_random_number', product: 'prod_random_number' }, - quantity: 2 - }.with_indifferent_access + + # 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_billing_version' => 2, + 'stripe_pricing_plan_id' => 'bpp_hacker_123', + 'plan_name' => 'Hacker', + 'subscribed_quantity' => 2 ) + expect(service).to have_received(:enable_plan_specific_features).with('Hacker') + 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_billing_version' => 2, + '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).to have_received(:create) - expect(Stripe::Subscription).to have_received(:create) - end - end - - context 'when account has stripe_customer_id' do - let(:stripe_customer_id) { 'cus_random_number' } - - before do - account.update!(custom_attributes: { stripe_customer_id: stripe_customer_id }) - end - - context 'when customer has active subscriptions' do - before do - allow(Stripe::Subscription).to receive(:list).and_return(subscriptions_list) - allow(subscriptions_list).to receive(:data).and_return(['subscription']) - allow(Stripe::Subscription).to receive(:create) - end - - it 'does not create a new subscription' do - create_stripe_customer_service.new(account: account).perform - - expect(Stripe::Subscription).not_to have_received(:create) - expect(Stripe::Subscription).to have_received(:list).with( - { - customer: stripe_customer_id, - status: 'active', - limit: 1 - } - ) - end + expect(Stripe::Customer).not_to have_received(:create) + expect(account.reload.custom_attributes['stripe_customer_id']).to eq('cus_existing_v2') end end end diff --git a/spec/enterprise/services/enterprise/billing/handle_stripe_event_service_spec.rb b/spec/enterprise/services/enterprise/billing/handle_stripe_event_service_spec.rb index 67ccd168b..51221ac5c 100644 --- a/spec/enterprise/services/enterprise/billing/handle_stripe_event_service_spec.rb +++ b/spec/enterprise/services/enterprise/billing/handle_stripe_event_service_spec.rb @@ -317,4 +317,146 @@ describe Enterprise::Billing::HandleStripeEventService do end end end + + describe 'credit grant handling' do + let(:credit_service) { instance_double(Enterprise::Billing::V2::CreditManagementService) } + + before do + allow(Enterprise::Billing::V2::CreditManagementService).to receive(:new) + .with(account: account).and_return(credit_service) + end + + context 'when handling monthly credit grant' do + it 'adds credits from Stripe' do + allow(credit_service).to receive(:add_response_topup_credits) + + # Webhook event object (minimal, just has ID) + grant_event_object = OpenStruct.new( + id: 'credgr_test_123', + customer: 'cus_123' + ) + allow(event).to receive(:type).and_return('billing.credit_grant.created') + allow(data).to receive(:object).and_return(grant_event_object) + + # Full grant object from API (has complete amount structure) + api_grant_response = OpenStruct.new( + id: 'credgr_test_123', + customer: 'cus_123', + metadata: { 'credits' => '2000' }, + amount: OpenStruct.new( + type: 'custom_pricing_unit', + custom_pricing_unit: OpenStruct.new(value: 2000) + ), + expires_at: Time.current + ) + allow(Stripe::Billing::CreditGrant).to receive(:retrieve) + .with('credgr_test_123') + .and_return(api_grant_response) + + stripe_event_service.new.perform(event: event) + + expect(credit_service).to have_received(:add_response_topup_credits).with(2000) + end + end + + context 'when handling topup credit grant' do + it 'adds topup credits' do + allow(credit_service).to receive(:add_response_topup_credits) + + # Webhook event object (minimal, just has ID) + grant_event_object = OpenStruct.new( + id: 'credgr_test_456', + customer: 'cus_123' + ) + allow(event).to receive(:type).and_return('billing.credit_grant.created') + allow(data).to receive(:object).and_return(grant_event_object) + + # Full grant object from API (has complete amount structure) + api_grant_response = OpenStruct.new( + id: 'credgr_test_456', + customer: 'cus_123', + metadata: { 'credits' => '500' }, + amount: OpenStruct.new( + type: 'custom_pricing_unit', + custom_pricing_unit: OpenStruct.new(value: 500) + ), + expires_at: nil + ) + allow(Stripe::Billing::CreditGrant).to receive(:retrieve) + .with('credgr_test_456') + .and_return(api_grant_response) + + stripe_event_service.new.perform(event: event) + + expect(credit_service).to have_received(:add_response_topup_credits).with(500) + end + end + + context 'when handling monetary type credit grant' do + it 'adds credits from monetary grant' do + allow(credit_service).to receive(:add_response_topup_credits) + + # Webhook event object (minimal, just has ID) + grant_event_object = OpenStruct.new( + id: 'credgr_test_monetary', + customer: 'cus_123' + ) + allow(event).to receive(:type).and_return('billing.credit_grant.created') + allow(data).to receive(:object).and_return(grant_event_object) + + # Full grant object from API with monetary amount + api_grant_response = OpenStruct.new( + id: 'credgr_test_monetary', + customer: 'cus_123', + metadata: { 'credits' => '1000' }, + amount: OpenStruct.new( + type: 'monetary', + monetary: OpenStruct.new( + currency: 'usd', + value: 1000 + ) + ), + expires_at: Time.current + ) + allow(Stripe::Billing::CreditGrant).to receive(:retrieve) + .with('credgr_test_monetary') + .and_return(api_grant_response) + + stripe_event_service.new.perform(event: event) + + expect(credit_service).to have_received(:add_response_topup_credits).with(1000) + end + end + + context 'when handling credit grant with zero amount' do + it 'does not call credit service' do + # Webhook event object (minimal, just has ID) + grant_event_object = OpenStruct.new( + id: 'credgr_test_zero', + customer: 'cus_123' + ) + allow(event).to receive(:type).and_return('billing.credit_grant.created') + allow(data).to receive(:object).and_return(grant_event_object) + + # Full grant object from API with zero amount + api_grant_response = OpenStruct.new( + id: 'credgr_test_zero', + customer: 'cus_123', + amount: OpenStruct.new( + type: 'custom_pricing_unit', + custom_pricing_unit: OpenStruct.new(value: 0) + ), + expires_at: Time.current + ) + allow(Stripe::Billing::CreditGrant).to receive(:retrieve) + .with('credgr_test_zero') + .and_return(api_grant_response) + + stripe_event_service.new.perform(event: event) + + # Ensure we don't accidentally call these methods + expect(Enterprise::Billing::V2::CreditManagementService).not_to have_received(:new) + end + end + end end From d230bb68d0fde2ceeca010349badaa3b2fbd574c Mon Sep 17 00:00:00 2001 From: Tanmay Deep Sharma Date: Mon, 10 Nov 2025 19:06:24 +0530 Subject: [PATCH 2/4] add updated rspecs --- .../create_stripe_customer_service_spec.rb | 161 +++++++++--------- 1 file changed, 79 insertions(+), 82 deletions(-) diff --git a/spec/enterprise/services/enterprise/billing/create_stripe_customer_service_spec.rb b/spec/enterprise/services/enterprise/billing/create_stripe_customer_service_spec.rb index 7b3a5c300..8987a15e8 100644 --- a/spec/enterprise/services/enterprise/billing/create_stripe_customer_service_spec.rb +++ b/spec/enterprise/services/enterprise/billing/create_stripe_customer_service_spec.rb @@ -1,104 +1,101 @@ require 'rails_helper' describe Enterprise::Billing::CreateStripeCustomerService do - subject(:create_stripe_customer_service) { described_class } + subject(:service) { described_class.new(account: account) } let(:account) { create(:account) } - let!(:admin1) { create(:user, account: account, role: :administrator) } + let!(:admin) { create(:user, account: account, role: :administrator) } + let(:stripe_customer_double) { instance_double(Stripe::Customer, id: 'cus_new') } + let(:subscriptions_list) { Stripe::ListObject.construct_from({ data: [] }) } + let(:hacker_plan_id) { 'price_hacker_random' } + + before do + create( + :installation_config, + name: 'CHATWOOT_CLOUD_PLANS', + value: [ + { + 'name' => 'Hacker', + 'limits' => { + 'captain_responses_monthly' => 5, + 'captain_responses_topup' => 0 + } + } + ] + ) + + create(:installation_config, name: 'STRIPE_HACKER_PLAN_ID', value: hacker_plan_id) + end describe '#perform' do - context 'when V2 configs are missing' do - it 'raises a configuration error' do - expect do - create_stripe_customer_service.new(account: account).perform - end.to raise_error(StandardError, /V2 billing configuration is required/) + context 'when account already has an active subscription' do + before do + account.update!(custom_attributes: { stripe_customer_id: 'cus_existing' }) + allow(Stripe::Subscription).to receive(:list) + .and_return(Stripe::ListObject.construct_from({ data: ['subscription'] })) + end + + it 'returns without modifying the account or contacting Stripe' do + expect(Stripe::Customer).not_to receive(:create) + + expect { service.perform }.not_to(change { account.reload.custom_attributes }) end end - context 'with V2 billing' 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 - + context 'when v2 billing configuration is missing' do before do - # Setup configs - cloud_plans_config - hacker_plan_config + InstallationConfig.find_by(name: 'STRIPE_HACKER_PLAN_ID').destroy! 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_billing_version' => 2, - 'stripe_pricing_plan_id' => 'bpp_hacker_123', - 'plan_name' => 'Hacker', - 'subscribed_quantity' => 2 - ) - expect(service).to have_received(:enable_plan_specific_features).with('Hacker') - 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_billing_version' => 2, - 'stripe_pricing_plan_id' => 'bpp_hacker_123', - 'plan_name' => 'Hacker', - 'subscribed_quantity' => 2 + it 'raises an informative error' do + expect { service.perform }.to raise_error( + StandardError, + 'V2 billing configuration is required. Please configure STRIPE_HACKER_PLAN_ID.' ) end + end - it 'skips setup when active subscription exists' do - account.update!(custom_attributes: { stripe_customer_id: 'cus_existing_v2' }) + context 'when account needs to be upgraded to v2 billing' do + context 'when stripe customer already exists' do + before do + account.update!(custom_attributes: { stripe_customer_id: 'cus_existing' }) + allow(Stripe::Subscription).to receive(:list).and_return(subscriptions_list) + allow(Stripe::Customer).to receive(:create) + end - 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) + it 'does not create a new customer but updates custom attributes for v2 billing' do + service.perform - create_stripe_customer_service.new(account: account).perform + expect(Stripe::Customer).not_to have_received(:create) - expect(Stripe::Customer).not_to have_received(:create) - expect(account.reload.custom_attributes['stripe_customer_id']).to eq('cus_existing_v2') + expect(account.reload.custom_attributes).to include( + 'stripe_customer_id' => 'cus_existing', + 'stripe_pricing_plan_id' => hacker_plan_id, + 'plan_name' => 'Hacker', + 'subscribed_quantity' => described_class::DEFAULT_QUANTITY, + 'stripe_billing_version' => 2 + ) + end + end + + context 'when stripe customer does not exist' do + before do + allow(Stripe::Customer).to receive(:create).and_return(stripe_customer_double) + end + + it 'creates a stripe customer and updates the account with v2 billing attributes' do + service.perform + + expect(Stripe::Customer).to have_received(:create).with({ name: account.name, email: admin.email }) + + expect(account.reload.custom_attributes).to include( + 'stripe_customer_id' => 'cus_new', + 'stripe_pricing_plan_id' => hacker_plan_id, + 'plan_name' => 'Hacker', + 'subscribed_quantity' => described_class::DEFAULT_QUANTITY, + 'stripe_billing_version' => 2 + ) + end end end end From eba12567e9f438113cbba1aa7f426397c82f3ea3 Mon Sep 17 00:00:00 2001 From: Tanmay Deep Sharma Date: Mon, 10 Nov 2025 19:15:20 +0530 Subject: [PATCH 3/4] cursor review commit fixes --- .../api/v1/accounts/concerns/billing_v2.rb | 2 +- .../enterprise/webhooks/stripe_controller.rb | 2 +- .../billing/handle_stripe_event_service.rb | 12 ------------ 3 files changed, 2 insertions(+), 14 deletions(-) diff --git a/enterprise/app/controllers/enterprise/api/v1/accounts/concerns/billing_v2.rb b/enterprise/app/controllers/enterprise/api/v1/accounts/concerns/billing_v2.rb index dc52958bd..c20c5f54c 100644 --- a/enterprise/app/controllers/enterprise/api/v1/accounts/concerns/billing_v2.rb +++ b/enterprise/app/controllers/enterprise/api/v1/accounts/concerns/billing_v2.rb @@ -34,7 +34,7 @@ module Enterprise::Api::V1::Accounts::Concerns::BillingV2 ) if result[:success] - render json: { success: true, redirect_url: result[:redirect_url], session_id: result[:session_id] } + render json: { success: true, redirect_url: result[:redirect_url] } else render json: { error: result[:message] }, status: :unprocessable_entity end diff --git a/enterprise/app/controllers/enterprise/webhooks/stripe_controller.rb b/enterprise/app/controllers/enterprise/webhooks/stripe_controller.rb index 7c248659d..0cc523889 100644 --- a/enterprise/app/controllers/enterprise/webhooks/stripe_controller.rb +++ b/enterprise/app/controllers/enterprise/webhooks/stripe_controller.rb @@ -44,6 +44,6 @@ class Enterprise::Webhooks::StripeController < ActionController::API def v2_billing_event?(event_type) Rails.logger.debug { "V2 billing event: #{event_type}" } - event_type.start_with?('v2.') + event_type.start_with?('v2.') if event_type.present? end end diff --git a/enterprise/app/services/enterprise/billing/handle_stripe_event_service.rb b/enterprise/app/services/enterprise/billing/handle_stripe_event_service.rb index 5343e9ef0..a1baa7b0f 100644 --- a/enterprise/app/services/enterprise/billing/handle_stripe_event_service.rb +++ b/enterprise/app/services/enterprise/billing/handle_stripe_event_service.rb @@ -105,22 +105,10 @@ class Enterprise::Billing::HandleStripeEventService return credits.to_i if credits.present? && credits.to_i.positive? end - # Fallback: extract from amount object - amount_data = extract_attribute(grant, :amount) - return 0 unless amount_data - 0 end def extract_attribute(object, attribute) object.respond_to?(attribute) ? object.public_send(attribute) : object[attribute.to_s] end - - def extract_amount_value(amount_data, unit_type) - unit = extract_attribute(amount_data, unit_type) - return 0 unless unit - - value = extract_attribute(unit, :value) - value.to_i - end end From 418d9cce27fb225110bf3458d0439d8cc5969505 Mon Sep 17 00:00:00 2001 From: Tanmay Deep Sharma Date: Mon, 10 Nov 2025 20:44:10 +0530 Subject: [PATCH 4/4] update stripe controller rspec --- .../webooks/stripe_controller_spec.rb | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/spec/enterprise/controllers/enterprise/webooks/stripe_controller_spec.rb b/spec/enterprise/controllers/enterprise/webooks/stripe_controller_spec.rb index bbcddeb29..96d868610 100644 --- a/spec/enterprise/controllers/enterprise/webooks/stripe_controller_spec.rb +++ b/spec/enterprise/controllers/enterprise/webooks/stripe_controller_spec.rb @@ -2,24 +2,34 @@ require 'rails_helper' RSpec.describe 'Enterprise::Webhooks::StripeController', type: :request do describe 'POST /enterprise/webhooks/stripe' do - let(:params) { { content: 'hello' } } + let(:payload) { { type: 'customer.subscription.updated', content: 'hello' }.to_json } + let(:event_object) { instance_double(Stripe::Event, type: 'customer.subscription.updated') } + + around do |example| + ENV['STRIPE_WEBHOOK_SECRET'] = 'test_secret' + ENV['STRIPE_WEBHOOK_SECRET_V2'] = 'test_secret_v2' + example.run + ENV.delete('STRIPE_WEBHOOK_SECRET') + ENV.delete('STRIPE_WEBHOOK_SECRET_V2') + end it 'call the Enterprise::Billing::HandleStripeEventService with the params' do handle_stripe = double - allow(Stripe::Webhook).to receive(:construct_event).and_return(params) + allow(Stripe::Webhook).to receive(:construct_event).and_return(event_object) allow(Enterprise::Billing::HandleStripeEventService).to receive(:new).and_return(handle_stripe) allow(handle_stripe).to receive(:perform) - post '/enterprise/webhooks/stripe', headers: { 'Stripe-Signature': 'test' }, params: params - expect(handle_stripe).to have_received(:perform).with(event: params) + post '/enterprise/webhooks/stripe', params: payload, headers: { 'Content-Type' => 'application/json', 'Stripe-Signature' => 'test' } + expect(handle_stripe).to have_received(:perform).with(event: event_object) end it 'returns a bad request if the headers are missing' do - post '/enterprise/webhooks/stripe', params: params + post '/enterprise/webhooks/stripe', params: payload, headers: { 'Content-Type' => 'application/json' } expect(response).to have_http_status(:bad_request) end it 'returns a bad request if the headers are invalid' do - post '/enterprise/webhooks/stripe', headers: { 'Stripe-Signature': 'test' }, params: params + allow(Stripe::Webhook).to receive(:construct_event).and_raise(Stripe::SignatureVerificationError.new('Invalid signature', 'sig')) + post '/enterprise/webhooks/stripe', params: payload, headers: { 'Content-Type' => 'application/json', 'Stripe-Signature' => 'test' } expect(response).to have_http_status(:bad_request) end end