Skip to content

Commit

Permalink
Support updating subscription with multiple plan items
Browse files Browse the repository at this point in the history
  • Loading branch information
mattagra committed Mar 13, 2018
1 parent a11cc55 commit 53ef2fd
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 70 deletions.
2 changes: 1 addition & 1 deletion lib/stripe_mock/request_handlers/customers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def new_customer(route, method_url, params, headers)
end

subscription = Data.mock_subscription({ id: new_id('su') })
subscription.merge!(custom_subscription_params(plan, customers[ params[:id] ], params))
subscription = resolve_subscription_changes(subscription, [plan], customers[ params[:id] ], params)
add_subscription_to_customer(customers[ params[:id] ], subscription)
subscriptions[subscription[:id]] = subscription
elsif params[:trial_end]
Expand Down
21 changes: 11 additions & 10 deletions lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,22 @@ def get_customer_subscription(customer, sub_id)
customer[:subscriptions][:data].find{|sub| sub[:id] == sub_id }
end

def custom_subscription_params(plan, cus, options = {})
def resolve_subscription_changes(subscription, plans, customer, options = {})
subscription.merge!(custom_subscription_params(plans, customer, options))
subscription[:items][:data] = plans.map { |plan| Data.mock_subscription_item({ plan: plan }) }
subscription
end

def custom_subscription_params(plans, cus, options = {})
verify_trial_end(options[:trial_end]) if options[:trial_end]

plan = plans.first if plans.size == 1

now = Time.now.utc.to_i
created_time = options[:created] || now
start_time = options[:current_period_start] || now
params = { plan: plan, customer: cus[:id], current_period_start: start_time, created: created_time }
params = { customer: cus[:id], current_period_start: start_time, created: created_time }
params.merge!({ :plan => (plans.size == 1 ? plans.first : nil) })
params.merge! options.select {|k,v| k =~ /application_fee_percent|quantity|metadata|tax_percent/}
# TODO: Implement coupon logic

Expand Down Expand Up @@ -87,14 +96,6 @@ def total_items_amount(items)
items.each { |i| total += (i[:quantity] || 1) * i[:plan][:amount] }
total
end

def mock_subscription_items(items = [])
data = []
items.each do |i|
data << Data.mock_subscription_item(i.merge(plan: plans[i[:plan].to_s]))
end
data
end
end
end
end
2 changes: 1 addition & 1 deletion lib/stripe_mock/request_handlers/invoices.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def upcoming_invoice(route, method_url, params, headers)
invoice_date = Time.now.to_i
subscription_plan = assert_existence :plan, subscription_plan_id, plans[subscription_plan_id.to_s]
preview_subscription = Data.mock_subscription
preview_subscription.merge!(custom_subscription_params(subscription_plan, customer, { trial_end: params[:subscription_trial_end] }))
preview_subscription = resolve_subscription_changes(preview_subscription, [subscription_plan], customer, { trial_end: params[:subscription_trial_end] })
preview_subscription[:id] = subscription[:id]
preview_subscription[:quantity] = subscription_quantity
subscription_proration_date = params[:subscription_proration_date] || Time.now
Expand Down
87 changes: 34 additions & 53 deletions lib/stripe_mock/request_handlers/subscriptions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,24 +32,10 @@ def retrieve_customer_subscriptions(route, method_url, params, headers)
customer[:subscriptions]
end

def plan_id_from_params(params)
if params[:plan]
params[:plan].to_s
elsif params[:items]
item = params[:items].values.find { |item| item[:plan] }
if item
item[:plan].to_s
end
end
end

def create_customer_subscription(route, method_url, params, headers)
route =~ method_url

plan_id = plan_id_from_params(params)

plan = assert_existence :plan, plan_id, plans[plan_id]

subscription_plans = get_subscription_plans_from_params(params)
customer = assert_existence :customer, $1, customers[$1]

if params[:source]
Expand All @@ -59,10 +45,11 @@ def create_customer_subscription(route, method_url, params, headers)
end

subscription = Data.mock_subscription({ id: (params[:id] || new_id('su')) })
subscription.merge!(custom_subscription_params(plan, customer, params))
subscription = resolve_subscription_changes(subscription, subscription_plans, customer, params)

# Ensure customer has card to charge if plan has no trial and is not free
verify_card_present(customer, plan, subscription, params)
# Note: needs updating for subscriptions with multiple plans
verify_card_present(customer, subscription_plans.first, subscription, params)

if params[:coupon]
coupon_id = params[:coupon]
Expand All @@ -82,25 +69,23 @@ def create_customer_subscription(route, method_url, params, headers)
subscriptions[subscription[:id]] = subscription
add_subscription_to_customer(customer, subscription)

clear_top_level_plan_if_multiple_items(subscription)

subscriptions[subscription[:id]]
end

def create_subscription(route, method_url, params, headers)
route =~ method_url

plan_id = plan_id_from_params(params)

plan = plan_id ? assert_existence(:plan, plan_id, plans[plan_id]) : nil
subscription_plans = get_subscription_plans_from_params(params)

customer = params[:customer]
customer_id = customer.is_a?(Stripe::Customer) ? customer[:id] : customer.to_s
customer = assert_existence :customer, customer_id, customers[customer_id]

if plan && customer
unless customer[:currency].to_s == plan[:currency].to_s
raise Stripe::InvalidRequestError.new('lol', 'currency', http_status: 400)
if subscription_plans && customer
subscription_plans.each do |plan|
unless customer[:currency].to_s == plan[:currency].to_s
raise Stripe::InvalidRequestError.new('lol', 'currency', http_status: 400)
end
end
end

Expand All @@ -117,11 +102,11 @@ def create_subscription(route, method_url, params, headers)
end

subscription = Data.mock_subscription({ id: (params[:id] || new_id('su')) })
subscription.merge!(custom_subscription_params(plan, customer, params))
subscription[:items][:data] = mock_subscription_items(params[:items].values) if params[:items]
subscription = resolve_subscription_changes(subscription, subscription_plans, customer, params)

# Ensure customer has card to charge if plan has no trial and is not free
verify_card_present(customer, plan, subscription, params)
# Note: needs updating for subscriptions with multiple plans
verify_card_present(customer, subscription_plans.first, subscription, params)

if params[:coupon]
coupon_id = params[:coupon]
Expand All @@ -141,8 +126,6 @@ def create_subscription(route, method_url, params, headers)
subscriptions[subscription[:id]] = subscription
add_subscription_to_customer(customer, subscription)

clear_top_level_plan_if_multiple_items(subscription)

subscriptions[subscription[:id]]
end

Expand Down Expand Up @@ -176,21 +159,12 @@ def update_subscription(route, method_url, params, headers)
customer[:default_source] = new_card[:id]
end

# expand the plan for addition to the customer object
plan_id = plan_id_from_params(params)

unless plan_id
plan_id = if subscription[:plan]
subscription[:plan][:id]
elsif subscription[:items]
row = subscription[:items][:data].find { |item| item[:plan] }
if row
row[:plan][:id]
end
end
end
subscription_plans = get_subscription_plans_from_params(params)

plan = plans[plan_id]
# subscription plans are not being updated but load them for the response
if subscription_plans.empty?
subscription_plans = subscription[:items][:data].map { |item| item[:plan] }
end

if params[:coupon]
coupon_id = params[:coupon]
Expand All @@ -207,25 +181,20 @@ def update_subscription(route, method_url, params, headers)
raise Stripe::InvalidRequestError.new("No such coupon: #{coupon_id}", 'coupon', http_status: 400)
end
end

assert_existence :plan, plan_id, plan
params[:plan] = plan if params[:plan]
verify_card_present(customer, plan, subscription)
verify_card_present(customer, subscription_plans.first, subscription)

if subscription[:cancel_at_period_end]
subscription[:cancel_at_period_end] = false
subscription[:canceled_at] = nil
end

params[:current_period_start] = subscription[:current_period_start]
subscription.merge!(custom_subscription_params(plan, customer, params))
subscription = resolve_subscription_changes(subscription, subscription_plans, customer, params)

# delete the old subscription, replace with the new subscription
customer[:subscriptions][:data].reject! { |sub| sub[:id] == subscription[:id] }
customer[:subscriptions][:data] << subscription

clear_top_level_plan_if_multiple_items(subscription)

subscription
end

Expand Down Expand Up @@ -259,8 +228,20 @@ def cancel_subscription(route, method_url, params, headers)

private

def clear_top_level_plan_if_multiple_items(subscription)
subscription[:plan] = nil if subscription[:items][:data].size > 1
def get_subscription_plans_from_params(params)
plan_ids = if params[:plan]
[params[:plan].to_s]
elsif params[:items]
items = params[:items]
items = items.values if items.respond_to?(:values)
items.map { |item| item[:plan].to_s if item[:plan] }
else
[]
end
plan_ids.each do |plan_id|
assert_existence :plan, plan_id, plans[plan_id]
end
plan_ids.map { |plan_id| plans[plan_id] }
end

def verify_card_present(customer, plan, subscription, params={})
Expand Down
61 changes: 56 additions & 5 deletions spec/shared_stripe_examples/subscription_examples.rb
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,57 @@ def gen_card_tk
expect(customer.subscriptions.data.first.customer).to eq(customer.id)
end

it "updates a stripe customer's existing subscription with single plan when multiple plans inside of items" do
silver_plan = stripe_helper.create_plan(id: 'silver')
gold_plan = stripe_helper.create_plan(id: 'gold')
addon_plan = stripe_helper.create_plan(id: 'addon_plan')
customer = Stripe::Customer.create(id: 'test_customer_sub', source: gen_card_tk, plan: silver_plan.id)

sub = Stripe::Subscription.retrieve(customer.subscriptions.data.first.id)
sub.items = [{ plan: gold_plan.id, quantity: 2 }, { plan: addon_plan.id, quantity: 2 }]
expect(sub.save).to be_truthy

expect(sub.object).to eq('subscription')
expect(sub.plan).to be_nil

customer = Stripe::Customer.retrieve('test_customer_sub')
expect(customer.subscriptions.data).to_not be_empty
expect(customer.subscriptions.count).to eq(1)
expect(customer.subscriptions.data.length).to eq(1)

expect(customer.subscriptions.data.first.id).to eq(sub.id)
expect(customer.subscriptions.data.first.plan).to be_nil
expect(customer.subscriptions.data.first.customer).to eq(customer.id)
expect(customer.subscriptions.data.first.items.data[0].plan.to_hash).to eq(gold_plan.to_hash)
expect(customer.subscriptions.data.first.items.data[1].plan.to_hash).to eq(addon_plan.to_hash)
end

it "updates a stripe customer's existing subscription with multple plans when multiple plans inside of items" do
silver_plan = stripe_helper.create_plan(id: 'silver')
gold_plan = stripe_helper.create_plan(id: 'gold')
addon1_plan = stripe_helper.create_plan(id: 'addon1')
addon2_plan = stripe_helper.create_plan(id: 'addon2')
customer = Stripe::Customer.create(id: 'test_customer_sub', source: gen_card_tk)
sub = Stripe::Subscription.create(customer: customer.id, items: [{ plan: silver_plan.id }, { plan: addon1_plan.id }])

sub.items = [{ plan: gold_plan.id, quantity: 2 }, { plan: addon2_plan.id, quantity: 2 }]
expect(sub.save).to be_truthy

expect(sub.object).to eq('subscription')
expect(sub.plan).to be_nil

customer = Stripe::Customer.retrieve('test_customer_sub')
expect(customer.subscriptions.data).to_not be_empty
expect(customer.subscriptions.count).to eq(1)
expect(customer.subscriptions.data.length).to eq(1)

expect(customer.subscriptions.data.first.id).to eq(sub.id)
expect(customer.subscriptions.data.first.plan).to be_nil
expect(customer.subscriptions.data.first.customer).to eq(customer.id)
expect(customer.subscriptions.data.first.items.data[0].plan.to_hash).to eq(gold_plan.to_hash)
expect(customer.subscriptions.data.first.items.data[1].plan.to_hash).to eq(addon2_plan.to_hash)
end

it 'when adds coupon', live: true do
plan = stripe_helper.create_plan(id: 'plan_with_coupon2', name: 'One More Test Plan', amount: 777)
coupon = stripe_helper.create_coupon
Expand Down Expand Up @@ -857,13 +908,13 @@ def gen_card_tk
expect(subscription.items.object).to eq('list')
expect(subscription.items.data.class).to eq(Array)
expect(subscription.items.data.count).to eq(1)
expect(subscription.items.data.first.id).to eq('si_1AwFf62eZvKYlo2C9u6Dhf9')
expect(subscription.items.data.first.created).to eq(1504035973)
expect(subscription.items.data.first.id).to eq('test_txn_default')
expect(subscription.items.data.first.created).to eq(1504716183)
expect(subscription.items.data.first.object).to eq('subscription_item')
expect(subscription.items.data.first.plan.amount).to eq(999)
expect(subscription.items.data.first.plan.created).to eq(1504035972)
expect(subscription.items.data.first.plan.amount).to eq(0)
expect(subscription.items.data.first.plan.created).to eq(1466698898)
expect(subscription.items.data.first.plan.currency).to eq('usd')
expect(subscription.items.data.first.quantity).to eq(1)
expect(subscription.items.data.first.quantity).to eq(2)
end
end

Expand Down

0 comments on commit 53ef2fd

Please sign in to comment.