Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion lib/stripe_mock/data.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1269,7 +1269,9 @@ def self.mock_subscription_item(params = {})
trial_period_days: nil
},
quantity: 2,
price: mock_price
price: mock_price,
current_period_start: nil,
current_period_end: nil
}.merge(params)
end

Expand Down
28 changes: 23 additions & 5 deletions lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,32 @@ def resolve_subscription_changes(subscription, plans, customer, options = {})
subscription.merge!(custom_subscription_params(plans, customer, options))
items = options[:items]
items = items.values if items.respond_to?(:values)

# Calculate period fields for subscription items
# These should match the subscription's current_period_start and current_period_end
start_time = subscription[:current_period_start]
end_time = subscription[:current_period_end]

subscription[:items][:data] = plans.map do |plan|
matching_item = items && items.detect { |item| [item[:price], item[:plan]].include? plan[:id] }

if matching_item
matching_item[:quantity] ||= 1
matching_item[:id] ||= new_id('si')
params = matching_item.merge(plan: plan)
params = matching_item.merge(
plan: plan,
current_period_start: start_time,
current_period_end: end_time
)
params[:price] = plan if plan[:object] == "price"
Data.mock_subscription_item(params)
else
params = { plan: plan, id: new_id('si') }
params = {
plan: plan,
id: new_id('si'),
current_period_start: start_time,
current_period_end: end_time
}
params[:price] = plan if plan[:object] == "price"
Data.mock_subscription_item(params)
end
Expand All @@ -30,6 +46,8 @@ def resolve_subscription_changes(subscription, plans, customer, options = {})
def custom_subscription_params(plans, cus, options = {})
verify_trial_end(options[:trial_end]) if options[:trial_end]

# Use first plan for period calculation, but only set :plan attribute if single item
plan_for_period = plans.first unless plans.empty?
plan = plans.first if plans.size == 1

now = Time.now.utc.to_i
Expand Down Expand Up @@ -59,11 +77,11 @@ def custom_subscription_params(plans, cus, options = {})

# TODO: Implement coupon logic

if (((plan && plan[:trial_period_days]) || 0) == 0 && options[:trial_end].nil?) || options[:trial_end] == "now"
end_time = options[:billing_cycle_anchor] || get_ending_time(start_time, plan)
if (((plan_for_period && plan_for_period[:trial_period_days]) || 0) == 0 && options[:trial_end].nil?) || options[:trial_end] == "now"
end_time = options[:billing_cycle_anchor] || get_ending_time(start_time, plan_for_period)
params.merge!({status: 'active', current_period_end: end_time, trial_start: nil, trial_end: nil, billing_cycle_anchor: options[:billing_cycle_anchor] || created_time})
else
end_time = options[:trial_end] || (Time.now.utc.to_i + plan[:trial_period_days]*86400)
end_time = options[:trial_end] || (Time.now.utc.to_i + plan_for_period[:trial_period_days]*86400)
params.merge!({status: 'trialing', current_period_end: end_time, trial_start: start_time, trial_end: end_time, billing_cycle_anchor: options[:billing_cycle_anchor] || created_time})
end

Expand Down
3 changes: 3 additions & 0 deletions lib/stripe_mock/request_handlers/subscriptions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -298,9 +298,11 @@ def update_subscription(route, method_url, params, headers)
if params[:cancel_at_period_end]
subscription[:cancel_at_period_end] = true
subscription[:canceled_at] = Time.now.utc.to_i
subscription[:cancel_at] = Time.now.utc.to_i
elsif params.has_key?(:cancel_at_period_end)
subscription[:cancel_at_period_end] = false
subscription[:canceled_at] = nil
subscription[:cancel_at] = nil
end

params[:current_period_start] = subscription[:current_period_start]
Expand Down Expand Up @@ -333,6 +335,7 @@ def cancel_subscription(route, method_url, params, headers)
cancelled_at_period_end = (params[:at_period_end] == true)
if cancelled_at_period_end
cancel_params[:cancel_at_period_end] = true
cancel_params[:cancel_at] = nil
else
cancel_params[:status] = 'canceled'
cancel_params[:cancel_at_period_end] = false
Expand Down
11 changes: 11 additions & 0 deletions spec/integration_examples/completing_checkout_sessions_example.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,16 @@
subscription = test_helper.complete_checkout_session(session, payment_method)

expect(subscription.default_payment_method).to eq(payment_method.id)
expect(subscription.current_period_start).to_not be_nil
expect(subscription.current_period_end).to_not be_nil
expect(subscription.current_period_end).to be > subscription.current_period_start

# Check subscription items also have period fields
subscription.items.data.each do |item|
expect(item.current_period_start).to_not be_nil
expect(item.current_period_end).to_not be_nil
expect(item.current_period_start).to eq(subscription.current_period_start)
expect(item.current_period_end).to eq(subscription.current_period_end)
end
end
end
35 changes: 35 additions & 0 deletions spec/shared_stripe_examples/subscription_items_examples.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,39 @@
}
end
end

context 'subscription items have current_period fields' do
it 'items have current_period_start and current_period_end matching subscription' do
sub = Stripe::Subscription.create(customer: customer.id, items: [{ plan: plan.id }])

expect(sub.items.data.length).to eq(1)
item = sub.items.data.first

expect(item.current_period_start).to_not be_nil
expect(item.current_period_end).to_not be_nil
expect(item.current_period_start).to eq(sub.current_period_start)
expect(item.current_period_end).to eq(sub.current_period_end)
expect(item.current_period_end).to be > item.current_period_start
end

it 'multi-item subscriptions have matching period fields on all items' do
sub = Stripe::Subscription.create(
customer: customer.id,
items: [
{ plan: plan.id, quantity: 2 },
{ plan: plan2.id, quantity: 1 }
]
)

expect(sub.items.data.length).to eq(2)

sub.items.data.each do |item|
expect(item.current_period_start).to_not be_nil
expect(item.current_period_end).to_not be_nil
expect(item.current_period_start).to eq(sub.current_period_start)
expect(item.current_period_end).to eq(sub.current_period_end)
expect(item.current_period_end).to be > item.current_period_start
end
end
end
end
3 changes: 2 additions & 1 deletion stripe-ruby-mock.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@ Gem::Specification.new do |gem|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
gem.require_paths = ['lib']

gem.add_dependency 'stripe', '> 5', '< 14'
gem.add_dependency 'stripe', '> 5', '< 19'
gem.add_dependency 'multi_json', '~> 1.0'
gem.add_dependency 'dante', '>= 0.2.0'
gem.add_dependency 'drb', '>= 2.0.4', '< 3'
gem.add_dependency 'ostruct', '~> 0.6.3'

gem.add_development_dependency 'rspec', '~> 3.13.0'
gem.add_development_dependency 'thin', '~> 1.8.1'
Expand Down