fix: add renewal identification for credit flow (#12999)
This commit is contained in:
committed by
GitHub
parent
399c91adaa
commit
3051da1e44
@@ -50,15 +50,19 @@ class Enterprise::Billing::HandleStripeEventService
|
|||||||
previous_usage = capture_previous_usage
|
previous_usage = capture_previous_usage
|
||||||
update_account_attributes(subscription, plan)
|
update_account_attributes(subscription, plan)
|
||||||
update_plan_features
|
update_plan_features
|
||||||
|
|
||||||
|
if billing_period_renewed?
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
handle_subscription_credits(plan, previous_usage)
|
handle_subscription_credits(plan, previous_usage)
|
||||||
account.reset_response_usage
|
account.reset_response_usage
|
||||||
end
|
end
|
||||||
|
elsif plan_changed?
|
||||||
|
handle_plan_change_credits(plan, previous_usage)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def capture_previous_usage
|
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
|
end
|
||||||
|
|
||||||
def current_plan_credits
|
def current_plan_credits
|
||||||
@@ -71,15 +75,15 @@ class Enterprise::Billing::HandleStripeEventService
|
|||||||
def update_account_attributes(subscription, plan)
|
def update_account_attributes(subscription, plan)
|
||||||
# https://stripe.com/docs/api/subscriptions/object
|
# https://stripe.com/docs/api/subscriptions/object
|
||||||
account.update(
|
account.update(
|
||||||
custom_attributes: {
|
custom_attributes: account.custom_attributes.merge(
|
||||||
stripe_customer_id: subscription.customer,
|
'stripe_customer_id' => subscription.customer,
|
||||||
stripe_price_id: subscription['plan']['id'],
|
'stripe_price_id' => subscription['plan']['id'],
|
||||||
stripe_product_id: subscription['plan']['product'],
|
'stripe_product_id' => subscription['plan']['product'],
|
||||||
plan_name: plan['name'],
|
'plan_name' => plan['name'],
|
||||||
subscribed_quantity: subscription['quantity'],
|
'subscribed_quantity' => subscription['quantity'],
|
||||||
subscription_status: subscription['status'],
|
'subscription_status' => subscription['status'],
|
||||||
subscription_ends_on: Time.zone.at(subscription['current_period_end'])
|
'subscription_ends_on' => Time.zone.at(subscription['current_period_end'])
|
||||||
}
|
)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -131,6 +135,18 @@ class Enterprise::Billing::HandleStripeEventService
|
|||||||
account.update!(limits: current_limits.merge('captain_responses' => updated_credits))
|
account.update!(limits: current_limits.merge('captain_responses' => updated_credits))
|
||||||
end
|
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)
|
def get_plan_credits(plan_name)
|
||||||
config = InstallationConfig.find_by(name: CAPTAIN_CLOUD_PLAN_LIMITS).value
|
config = InstallationConfig.find_by(name: CAPTAIN_CLOUD_PLAN_LIMITS).value
|
||||||
config = JSON.parse(config) if config.is_a?(String)
|
config = JSON.parse(config) if config.is_a?(String)
|
||||||
@@ -141,20 +157,12 @@ class Enterprise::Billing::HandleStripeEventService
|
|||||||
plan_name = account.custom_attributes['plan_name']
|
plan_name = account.custom_attributes['plan_name']
|
||||||
return if plan_name.blank?
|
return if plan_name.blank?
|
||||||
|
|
||||||
# Enable features based on plan hierarchy
|
|
||||||
case plan_name
|
case plan_name
|
||||||
when 'Startups'
|
when 'Startups' then account.enable_features(*STARTUP_PLAN_FEATURES)
|
||||||
# Startups plan gets the basic features
|
|
||||||
account.enable_features(*STARTUP_PLAN_FEATURES)
|
|
||||||
when 'Business'
|
when 'Business'
|
||||||
# Business plan gets Startups features + Business features
|
account.enable_features(*STARTUP_PLAN_FEATURES, *BUSINESS_PLAN_FEATURES)
|
||||||
account.enable_features(*STARTUP_PLAN_FEATURES)
|
|
||||||
account.enable_features(*BUSINESS_PLAN_FEATURES)
|
|
||||||
when 'Enterprise'
|
when 'Enterprise'
|
||||||
# Enterprise plan gets all features
|
account.enable_features(*STARTUP_PLAN_FEATURES, *BUSINESS_PLAN_FEATURES, *ENTERPRISE_PLAN_FEATURES)
|
||||||
account.enable_features(*STARTUP_PLAN_FEATURES)
|
|
||||||
account.enable_features(*BUSINESS_PLAN_FEATURES)
|
|
||||||
account.enable_features(*ENTERPRISE_PLAN_FEATURES)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -162,6 +170,25 @@ class Enterprise::Billing::HandleStripeEventService
|
|||||||
@subscription ||= @event.data.object
|
@subscription ||= @event.data.object
|
||||||
end
|
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
|
def account
|
||||||
@account ||= Account.where("custom_attributes->>'stripe_customer_id' = ?", subscription.customer).first
|
@account ||= Account.where("custom_attributes->>'stripe_customer_id' = ?", subscription.customer).first
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ describe Enterprise::Billing::HandleStripeEventService do
|
|||||||
# Setup common subscription mocks
|
# Setup common subscription mocks
|
||||||
allow(event).to receive(:data).and_return(data)
|
allow(event).to receive(:data).and_return(data)
|
||||||
allow(data).to receive(:object).and_return(subscription)
|
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('quantity').and_return('10')
|
||||||
allow(subscription).to receive(:[]).with('status').and_return('active')
|
allow(subscription).to receive(:[]).with('status').and_return('active')
|
||||||
allow(subscription).to receive(:[]).with('current_period_end').and_return(1_686_567_520)
|
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')
|
expect(account).not_to be_feature_enabled('audit_logs')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'resets captain usage on subscription update' do
|
it 'resets captain usage on billing period renewal' do
|
||||||
# Prime the account with some usage
|
# Prime the account with some usage
|
||||||
5.times { account.increment_response_usage }
|
5.times { account.increment_response_usage }
|
||||||
expect(account.custom_attributes['captain_responses_usage']).to eq(5)
|
expect(account.custom_attributes['captain_responses_usage']).to eq(5)
|
||||||
@@ -70,6 +71,10 @@ describe Enterprise::Billing::HandleStripeEventService do
|
|||||||
# Setup for any plan
|
# Setup for any plan
|
||||||
allow(subscription).to receive(:[]).with('plan')
|
allow(subscription).to receive(:[]).with('plan')
|
||||||
.and_return({ 'id' => 'test', 'product' => 'plan_id_startups', 'name' => 'Startups' })
|
.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)
|
stripe_event_service.new.perform(event: event)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user