fix: add renewal identification for credit flow (#12999)

This commit is contained in:
Tanmay Deep Sharma
2025-12-08 18:35:33 +05:30
committed by GitHub
parent 399c91adaa
commit 3051da1e44
2 changed files with 59 additions and 27 deletions

View File

@@ -50,15 +50,19 @@ class Enterprise::Billing::HandleStripeEventService
previous_usage = capture_previous_usage
update_account_attributes(subscription, plan)
update_plan_features
handle_subscription_credits(plan, previous_usage)
account.reset_response_usage
if billing_period_renewed?
ActiveRecord::Base.transaction do
handle_subscription_credits(plan, previous_usage)
account.reset_response_usage
end
elsif plan_changed?
handle_plan_change_credits(plan, previous_usage)
end
end
def capture_previous_usage
{
responses: account.custom_attributes['captain_responses_usage'].to_i,
monthly: current_plan_credits[:responses]
}
{ responses: account.custom_attributes['captain_responses_usage'].to_i, monthly: current_plan_credits[:responses] }
end
def current_plan_credits
@@ -71,15 +75,15 @@ class Enterprise::Billing::HandleStripeEventService
def update_account_attributes(subscription, plan)
# https://stripe.com/docs/api/subscriptions/object
account.update(
custom_attributes: {
stripe_customer_id: subscription.customer,
stripe_price_id: subscription['plan']['id'],
stripe_product_id: subscription['plan']['product'],
plan_name: plan['name'],
subscribed_quantity: subscription['quantity'],
subscription_status: subscription['status'],
subscription_ends_on: Time.zone.at(subscription['current_period_end'])
}
custom_attributes: account.custom_attributes.merge(
'stripe_customer_id' => subscription.customer,
'stripe_price_id' => subscription['plan']['id'],
'stripe_product_id' => subscription['plan']['product'],
'plan_name' => plan['name'],
'subscribed_quantity' => subscription['quantity'],
'subscription_status' => subscription['status'],
'subscription_ends_on' => Time.zone.at(subscription['current_period_end'])
)
)
end
@@ -131,6 +135,18 @@ class Enterprise::Billing::HandleStripeEventService
account.update!(limits: current_limits.merge('captain_responses' => updated_credits))
end
def handle_plan_change_credits(new_plan, previous_usage)
current_limits = account.limits || {}
current_credits = current_limits['captain_responses'].to_i
previous_plan_credits = previous_usage[:monthly]
new_plan_credits = get_plan_credits(new_plan['name'])[:responses]
updated_credits = current_credits - previous_plan_credits + new_plan_credits
account.update!(limits: current_limits.merge('captain_responses' => updated_credits))
end
def get_plan_credits(plan_name)
config = InstallationConfig.find_by(name: CAPTAIN_CLOUD_PLAN_LIMITS).value
config = JSON.parse(config) if config.is_a?(String)
@@ -141,20 +157,12 @@ class Enterprise::Billing::HandleStripeEventService
plan_name = account.custom_attributes['plan_name']
return if plan_name.blank?
# Enable features based on plan hierarchy
case plan_name
when 'Startups'
# Startups plan gets the basic features
account.enable_features(*STARTUP_PLAN_FEATURES)
when 'Startups' then account.enable_features(*STARTUP_PLAN_FEATURES)
when 'Business'
# Business plan gets Startups features + Business features
account.enable_features(*STARTUP_PLAN_FEATURES)
account.enable_features(*BUSINESS_PLAN_FEATURES)
account.enable_features(*STARTUP_PLAN_FEATURES, *BUSINESS_PLAN_FEATURES)
when 'Enterprise'
# Enterprise plan gets all features
account.enable_features(*STARTUP_PLAN_FEATURES)
account.enable_features(*BUSINESS_PLAN_FEATURES)
account.enable_features(*ENTERPRISE_PLAN_FEATURES)
account.enable_features(*STARTUP_PLAN_FEATURES, *BUSINESS_PLAN_FEATURES, *ENTERPRISE_PLAN_FEATURES)
end
end
@@ -162,6 +170,25 @@ class Enterprise::Billing::HandleStripeEventService
@subscription ||= @event.data.object
end
def previous_attributes
@previous_attributes ||= JSON.parse((@event.data.previous_attributes || {}).to_json)
end
def plan_changed?
return false if previous_attributes['plan'].blank?
previous_plan_id = previous_attributes.dig('plan', 'id')
current_plan_id = subscription['plan']['id']
previous_plan_id != current_plan_id
end
def billing_period_renewed?
return false if previous_attributes['current_period_start'].blank?
previous_attributes['current_period_start'] != subscription['current_period_start']
end
def account
@account ||= Account.where("custom_attributes->>'stripe_customer_id' = ?", subscription.customer).first
end

View File

@@ -32,6 +32,7 @@ describe Enterprise::Billing::HandleStripeEventService do
# Setup common subscription mocks
allow(event).to receive(:data).and_return(data)
allow(data).to receive(:object).and_return(subscription)
allow(data).to receive(:previous_attributes).and_return({})
allow(subscription).to receive(:[]).with('quantity').and_return('10')
allow(subscription).to receive(:[]).with('status').and_return('active')
allow(subscription).to receive(:[]).with('current_period_end').and_return(1_686_567_520)
@@ -62,7 +63,7 @@ describe Enterprise::Billing::HandleStripeEventService do
expect(account).not_to be_feature_enabled('audit_logs')
end
it 'resets captain usage on subscription update' do
it 'resets captain usage on billing period renewal' do
# Prime the account with some usage
5.times { account.increment_response_usage }
expect(account.custom_attributes['captain_responses_usage']).to eq(5)
@@ -70,6 +71,10 @@ describe Enterprise::Billing::HandleStripeEventService do
# Setup for any plan
allow(subscription).to receive(:[]).with('plan')
.and_return({ 'id' => 'test', 'product' => 'plan_id_startups', 'name' => 'Startups' })
allow(subscription).to receive(:[]).with('current_period_start').and_return(1_686_567_520)
# Simulate billing period renewal with previous_attributes showing old period
allow(data).to receive(:previous_attributes).and_return({ 'current_period_start' => 1_683_975_520 })
stripe_event_service.new.perform(event: event)