From e5698a1570e1f76e31db0eae8e5c89f4868264de Mon Sep 17 00:00:00 2001 From: mattagra Date: Tue, 13 Mar 2018 15:41:06 -0700 Subject: [PATCH 1/9] Revert "Support updating subscription with multiple plan items" This reverts commit 53ef2fd14a2ea96c5f225ff48b864fc24c108b8c. --- lib/stripe_mock/request_handlers/customers.rb | 2 +- .../helpers/subscription_helpers.rb | 21 +++-- lib/stripe_mock/request_handlers/invoices.rb | 2 +- .../request_handlers/subscriptions.rb | 87 +++++++++++-------- .../subscription_examples.rb | 61 ++----------- 5 files changed, 70 insertions(+), 103 deletions(-) diff --git a/lib/stripe_mock/request_handlers/customers.rb b/lib/stripe_mock/request_handlers/customers.rb index 4928715df..c35930bc2 100644 --- a/lib/stripe_mock/request_handlers/customers.rb +++ b/lib/stripe_mock/request_handlers/customers.rb @@ -40,7 +40,7 @@ def new_customer(route, method_url, params, headers) end subscription = Data.mock_subscription({ id: new_id('su') }) - subscription = resolve_subscription_changes(subscription, [plan], customers[ params[:id] ], params) + subscription.merge!(custom_subscription_params(plan, customers[ params[:id] ], params)) add_subscription_to_customer(customers[ params[:id] ], subscription) subscriptions[subscription[:id]] = subscription elsif params[:trial_end] diff --git a/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb b/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb index 14df93f23..d63c4f4b5 100644 --- a/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb +++ b/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb @@ -6,22 +6,13 @@ def get_customer_subscription(customer, sub_id) customer[:subscriptions][:data].find{|sub| sub[:id] == sub_id } end - 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 = {}) + def custom_subscription_params(plan, 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 = { customer: cus[:id], current_period_start: start_time, created: created_time } - params.merge!({ :plan => (plans.size == 1 ? plans.first : nil) }) + params = { plan: plan, customer: cus[:id], current_period_start: start_time, created: created_time } params.merge! options.select {|k,v| k =~ /application_fee_percent|quantity|metadata|tax_percent/} # TODO: Implement coupon logic @@ -96,6 +87,14 @@ 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 diff --git a/lib/stripe_mock/request_handlers/invoices.rb b/lib/stripe_mock/request_handlers/invoices.rb index 3e4434082..8f8e242bc 100644 --- a/lib/stripe_mock/request_handlers/invoices.rb +++ b/lib/stripe_mock/request_handlers/invoices.rb @@ -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 = resolve_subscription_changes(preview_subscription, [subscription_plan], customer, { trial_end: params[:subscription_trial_end] }) + preview_subscription.merge!(custom_subscription_params(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 diff --git a/lib/stripe_mock/request_handlers/subscriptions.rb b/lib/stripe_mock/request_handlers/subscriptions.rb index 8a4fbd192..28933c1e9 100644 --- a/lib/stripe_mock/request_handlers/subscriptions.rb +++ b/lib/stripe_mock/request_handlers/subscriptions.rb @@ -32,10 +32,24 @@ 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 - subscription_plans = get_subscription_plans_from_params(params) + plan_id = plan_id_from_params(params) + + plan = assert_existence :plan, plan_id, plans[plan_id] + customer = assert_existence :customer, $1, customers[$1] if params[:source] @@ -45,11 +59,10 @@ def create_customer_subscription(route, method_url, params, headers) end subscription = Data.mock_subscription({ id: (params[:id] || new_id('su')) }) - subscription = resolve_subscription_changes(subscription, subscription_plans, customer, params) + subscription.merge!(custom_subscription_params(plan, customer, params)) # Ensure customer has card to charge if plan has no trial and is not free - # Note: needs updating for subscriptions with multiple plans - verify_card_present(customer, subscription_plans.first, subscription, params) + verify_card_present(customer, plan, subscription, params) if params[:coupon] coupon_id = params[:coupon] @@ -69,23 +82,25 @@ 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 - subscription_plans = get_subscription_plans_from_params(params) + plan_id = plan_id_from_params(params) + + plan = plan_id ? assert_existence(:plan, plan_id, plans[plan_id]) : nil 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 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 + if plan && customer + unless customer[:currency].to_s == plan[:currency].to_s + raise Stripe::InvalidRequestError.new('lol', 'currency', http_status: 400) end end @@ -102,11 +117,11 @@ def create_subscription(route, method_url, params, headers) end subscription = Data.mock_subscription({ id: (params[:id] || new_id('su')) }) - subscription = resolve_subscription_changes(subscription, subscription_plans, customer, params) + subscription.merge!(custom_subscription_params(plan, customer, params)) + subscription[:items][:data] = mock_subscription_items(params[:items].values) if params[:items] # Ensure customer has card to charge if plan has no trial and is not free - # Note: needs updating for subscriptions with multiple plans - verify_card_present(customer, subscription_plans.first, subscription, params) + verify_card_present(customer, plan, subscription, params) if params[:coupon] coupon_id = params[:coupon] @@ -126,6 +141,8 @@ 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 @@ -159,13 +176,22 @@ def update_subscription(route, method_url, params, headers) customer[:default_source] = new_card[:id] end - subscription_plans = get_subscription_plans_from_params(params) - - # 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] } + # 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 + plan = plans[plan_id] + if params[:coupon] coupon_id = params[:coupon] @@ -181,7 +207,10 @@ def update_subscription(route, method_url, params, headers) raise Stripe::InvalidRequestError.new("No such coupon: #{coupon_id}", 'coupon', http_status: 400) end end - verify_card_present(customer, subscription_plans.first, subscription) + + assert_existence :plan, plan_id, plan + params[:plan] = plan if params[:plan] + verify_card_present(customer, plan, subscription) if subscription[:cancel_at_period_end] subscription[:cancel_at_period_end] = false @@ -189,12 +218,14 @@ def update_subscription(route, method_url, params, headers) end params[:current_period_start] = subscription[:current_period_start] - subscription = resolve_subscription_changes(subscription, subscription_plans, customer, params) + subscription.merge!(custom_subscription_params(plan, 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 @@ -228,20 +259,8 @@ def cancel_subscription(route, method_url, params, headers) private - 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] } + def clear_top_level_plan_if_multiple_items(subscription) + subscription[:plan] = nil if subscription[:items][:data].size > 1 end def verify_card_present(customer, plan, subscription, params={}) diff --git a/spec/shared_stripe_examples/subscription_examples.rb b/spec/shared_stripe_examples/subscription_examples.rb index 6f3dd9d51..e5ba12dcb 100644 --- a/spec/shared_stripe_examples/subscription_examples.rb +++ b/spec/shared_stripe_examples/subscription_examples.rb @@ -504,57 +504,6 @@ 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 @@ -908,13 +857,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('test_txn_default') - expect(subscription.items.data.first.created).to eq(1504716183) + expect(subscription.items.data.first.id).to eq('si_1AwFf62eZvKYlo2C9u6Dhf9') + expect(subscription.items.data.first.created).to eq(1504035973) expect(subscription.items.data.first.object).to eq('subscription_item') - 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.amount).to eq(999) + expect(subscription.items.data.first.plan.created).to eq(1504035972) expect(subscription.items.data.first.plan.currency).to eq('usd') - expect(subscription.items.data.first.quantity).to eq(2) + expect(subscription.items.data.first.quantity).to eq(1) end end From 6de6013c67f6e158a5db4d6dcbb7e5e76db4387e Mon Sep 17 00:00:00 2001 From: mattagra Date: Mon, 12 Mar 2018 16:33:02 -0700 Subject: [PATCH 2/9] Update List sorting to avoid reversing if created matches --- lib/stripe_mock/data/list.rb | 3 +-- .../shared_stripe_examples/charge_examples.rb | 2 +- .../shared_stripe_examples/refund_examples.rb | 2 +- .../webhook_event_examples.rb | 22 +++++++++---------- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/lib/stripe_mock/data/list.rb b/lib/stripe_mock/data/list.rb index 4214c0e08..e5effdbfd 100644 --- a/lib/stripe_mock/data/list.rb +++ b/lib/stripe_mock/data/list.rb @@ -8,8 +8,7 @@ def initialize(data, options = {}) @limit = [[options[:limit] || 10, 100].min, 1].max # restrict @limit to 1..100 @starting_after = options[:starting_after] if @data.first.is_a?(Hash) && @data.first[:created] - @data.sort_by! { |x| x[:created] } - @data.reverse! + @data.sort! { |x, y| y[:created] <=> x[:created] } elsif @data.first.respond_to?(:created) @data.sort_by { |x| x.created } @data.reverse! diff --git a/spec/shared_stripe_examples/charge_examples.rb b/spec/shared_stripe_examples/charge_examples.rb index f437ba1ff..bd1357a50 100644 --- a/spec/shared_stripe_examples/charge_examples.rb +++ b/spec/shared_stripe_examples/charge_examples.rb @@ -357,7 +357,7 @@ end it "stores all charges in memory" do - expect(Stripe::Charge.all.data.map(&:id).reverse).to eq([@charge.id, @charge2.id]) + expect(Stripe::Charge.all.data.map(&:id)).to eq([@charge.id, @charge2.id]) end it "defaults count to 10 charges" do diff --git a/spec/shared_stripe_examples/refund_examples.rb b/spec/shared_stripe_examples/refund_examples.rb index 99ae9dc1a..8bc14dc78 100644 --- a/spec/shared_stripe_examples/refund_examples.rb +++ b/spec/shared_stripe_examples/refund_examples.rb @@ -262,7 +262,7 @@ end it "stores all charges in memory" do - expect(Stripe::Refund.all.data.map(&:id)).to eq([@refund2.id, @refund.id]) + expect(Stripe::Refund.all.data.map(&:id)).to eq([@refund.id, @refund2.id]) end it "defaults count to 10 charges" do diff --git a/spec/shared_stripe_examples/webhook_event_examples.rb b/spec/shared_stripe_examples/webhook_event_examples.rb index 6685dba00..900c5b501 100644 --- a/spec/shared_stripe_examples/webhook_event_examples.rb +++ b/spec/shared_stripe_examples/webhook_event_examples.rb @@ -199,14 +199,14 @@ events = Stripe::Event.all(limit: 3) expect(events.count).to eq(3) - expect(events.map &:id).to include(invoice_item_created_event.id, invoice_created_event.id, coupon_created_event.id) - expect(events.map &:type).to include('invoiceitem.created', 'invoice.created', 'coupon.created') + expect(events.map &:id).to include(customer_created_event.id, plan_created_event.id, coupon_created_event.id) + expect(events.map &:type).to include('customer.created', 'plan.created', 'coupon.created') end end - - describe 'Subscription events' do - it "Checks for billing items in customer.subscription.created" do + + describe 'Subscription events' do + it "Checks for billing items in customer.subscription.created" do subscription_created_event = StripeMock.mock_webhook_event('customer.subscription.created') expect(subscription_created_event).to be_a(Stripe::Event) expect(subscription_created_event.id).to_not be_nil @@ -216,7 +216,7 @@ expect(subscription_created_event.data.object.items.data.first.id).to eq('si_00000000000000') end - it "Checks for billing items in customer.subscription.deleted" do + it "Checks for billing items in customer.subscription.deleted" do subscription_deleted_event = StripeMock.mock_webhook_event('customer.subscription.deleted') expect(subscription_deleted_event).to be_a(Stripe::Event) expect(subscription_deleted_event.id).to_not be_nil @@ -225,8 +225,8 @@ expect(subscription_deleted_event.data.object.items.data.first).to respond_to(:plan) expect(subscription_deleted_event.data.object.items.data.first.id).to eq('si_00000000000000') end - - it "Checks for billing items in customer.subscription.updated" do + + it "Checks for billing items in customer.subscription.updated" do subscription_updated_event = StripeMock.mock_webhook_event('customer.subscription.updated') expect(subscription_updated_event).to be_a(Stripe::Event) expect(subscription_updated_event.id).to_not be_nil @@ -235,8 +235,8 @@ expect(subscription_updated_event.data.object.items.data.first).to respond_to(:plan) expect(subscription_updated_event.data.object.items.data.first.id).to eq('si_00000000000000') end - - it "Checks for billing items in customer.subscription.trial_will_end" do + + it "Checks for billing items in customer.subscription.trial_will_end" do subscription_trial_will_end_event = StripeMock.mock_webhook_event('customer.subscription.trial_will_end') expect(subscription_trial_will_end_event).to be_a(Stripe::Event) expect(subscription_trial_will_end_event.id).to_not be_nil @@ -247,7 +247,7 @@ end end - describe 'Invoices events' do + describe 'Invoices events' do it "Checks for billing items in invoice.payment_succeeded" do invoice_payment_succeeded = StripeMock.mock_webhook_event('invoice.payment_succeeded') expect(invoice_payment_succeeded).to be_a(Stripe::Event) From 7bcda96b5fb8da8f2b6c17aade49cfe9a26ce954 Mon Sep 17 00:00:00 2001 From: mattagra Date: Mon, 12 Mar 2018 16:43:52 -0700 Subject: [PATCH 3/9] Support updating subscription with multiple plan items --- .../helpers/subscription_helpers.rb | 13 +++++ .../subscription_examples.rb | 55 +++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb b/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb index d63c4f4b5..bea78dede 100644 --- a/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb +++ b/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb @@ -24,6 +24,19 @@ def custom_subscription_params(plan, cus, options = {}) params.merge!({status: 'trialing', current_period_end: end_time, trial_start: start_time, trial_end: end_time}) end + items = options[:items] + items = items.values if items.respond_to?(:values) + if items + items_data = [] + items.each do |item| + plan = assert_existence(:plan, item[:plan], plans[item[:plan]]) + quantity = item[:quantity] || 1 + items_data.push(Data.mock_subscription_item(plan: plan, quantity: quantity, created: Time.now.utc.to_i)) + end + params[:items] = Data.mock_list_object(items_data) + params.delete(:plan) if items_data.size > 1 + end + params end diff --git a/spec/shared_stripe_examples/subscription_examples.rb b/spec/shared_stripe_examples/subscription_examples.rb index e5ba12dcb..dce2d9e1a 100644 --- a/spec/shared_stripe_examples/subscription_examples.rb +++ b/spec/shared_stripe_examples/subscription_examples.rb @@ -504,6 +504,61 @@ 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: 4 }, { plan: addon_plan.id, quantity: 5 }] + 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[0].quantity).to eq(4) + expect(customer.subscriptions.data.first.items.data[1].plan.to_hash).to eq(addon_plan.to_hash) + expect(customer.subscriptions.data.first.items.data[1].quantity).to eq(5) + 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: 1 }, { plan: addon2_plan.id, quantity: 5 }] + 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[0].quantity).to eq(1) + expect(customer.subscriptions.data.first.items.data[1].plan.to_hash).to eq(addon2_plan.to_hash) + expect(customer.subscriptions.data.first.items.data[1].quantity).to eq(5) + 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 From 520ef631fa1f273000dad96bea0909e5b34cc283 Mon Sep 17 00:00:00 2001 From: mattagra Date: Tue, 13 Mar 2018 15:47:05 -0700 Subject: [PATCH 4/9] Avoid calling values on items if it is an array --- lib/stripe_mock/request_handlers/subscriptions.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/stripe_mock/request_handlers/subscriptions.rb b/lib/stripe_mock/request_handlers/subscriptions.rb index 28933c1e9..b41384eab 100644 --- a/lib/stripe_mock/request_handlers/subscriptions.rb +++ b/lib/stripe_mock/request_handlers/subscriptions.rb @@ -36,7 +36,9 @@ def plan_id_from_params(params) if params[:plan] params[:plan].to_s elsif params[:items] - item = params[:items].values.find { |item| item[:plan] } + items = params[:items] + items = items.values if items.respond_to?(:values) + item = items.find { |item| item[:plan] } if item item[:plan].to_s end From 4833892ec45700fde5197bd68f1458d1e65b5a08 Mon Sep 17 00:00:00 2001 From: mattagra Date: Wed, 14 Mar 2018 17:42:33 -0700 Subject: [PATCH 5/9] Set subscription quantity to nil if subscription has multiple items --- lib/stripe_mock/request_handlers/subscriptions.rb | 5 ++++- spec/shared_stripe_examples/subscription_examples.rb | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/stripe_mock/request_handlers/subscriptions.rb b/lib/stripe_mock/request_handlers/subscriptions.rb index b41384eab..9e0ef0efc 100644 --- a/lib/stripe_mock/request_handlers/subscriptions.rb +++ b/lib/stripe_mock/request_handlers/subscriptions.rb @@ -262,7 +262,10 @@ 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 + if subscription[:items][:data].size > 1 + subscription[:plan] = nil + subscription[:quantity] = nil + end end def verify_card_present(customer, plan, subscription, params={}) diff --git a/spec/shared_stripe_examples/subscription_examples.rb b/spec/shared_stripe_examples/subscription_examples.rb index dce2d9e1a..7985edd30 100644 --- a/spec/shared_stripe_examples/subscription_examples.rb +++ b/spec/shared_stripe_examples/subscription_examples.rb @@ -516,6 +516,7 @@ def gen_card_tk expect(sub.object).to eq('subscription') expect(sub.plan).to be_nil + expect(sub.quantity).to be_nil customer = Stripe::Customer.retrieve('test_customer_sub') expect(customer.subscriptions.data).to_not be_empty @@ -524,6 +525,7 @@ def gen_card_tk expect(customer.subscriptions.data.first.id).to eq(sub.id) expect(customer.subscriptions.data.first.plan).to be_nil + expect(customer.subscriptions.data.first.quantity).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[0].quantity).to eq(4) @@ -544,6 +546,7 @@ def gen_card_tk expect(sub.object).to eq('subscription') expect(sub.plan).to be_nil + expect(sub.quantity).to be_nil customer = Stripe::Customer.retrieve('test_customer_sub') expect(customer.subscriptions.data).to_not be_empty @@ -552,6 +555,7 @@ def gen_card_tk expect(customer.subscriptions.data.first.id).to eq(sub.id) expect(customer.subscriptions.data.first.plan).to be_nil + expect(customer.subscriptions.data.first.quantity).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[0].quantity).to eq(1) From 8b838d15fd0ae8eccbe291aef2e8379d528a2ad3 Mon Sep 17 00:00:00 2001 From: mattagra Date: Fri, 16 Mar 2018 14:38:28 -0700 Subject: [PATCH 6/9] Raise InvalidRequestError when updating subscription quantity and subscription has multiple items --- lib/stripe_mock/request_handlers/subscriptions.rb | 4 ++++ .../subscription_examples.rb | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/lib/stripe_mock/request_handlers/subscriptions.rb b/lib/stripe_mock/request_handlers/subscriptions.rb index 9e0ef0efc..b99c9e07c 100644 --- a/lib/stripe_mock/request_handlers/subscriptions.rb +++ b/lib/stripe_mock/request_handlers/subscriptions.rb @@ -219,6 +219,10 @@ def update_subscription(route, method_url, params, headers) subscription[:canceled_at] = nil end + if params[:quantity] && subscription[:items][:data].size > 1 + raise Stripe::InvalidRequestError.new('Cannot update using quantity parameter when multiple plans exist on the subscription. Updates must be made to individual items instead.', nil, http_status: 400) + end + params[:current_period_start] = subscription[:current_period_start] subscription.merge!(custom_subscription_params(plan, customer, params)) diff --git a/spec/shared_stripe_examples/subscription_examples.rb b/spec/shared_stripe_examples/subscription_examples.rb index 7985edd30..77e94d4e9 100644 --- a/spec/shared_stripe_examples/subscription_examples.rb +++ b/spec/shared_stripe_examples/subscription_examples.rb @@ -641,6 +641,20 @@ def gen_card_tk expect(customer.subscriptions.data.first.plan.to_hash).to eq(free.to_hash) end + it "throws an error when updating quantity and subscription has multiple plans" do + gold_plan = stripe_helper.create_plan(id: 'gold') + addon_plan = stripe_helper.create_plan(id: 'addon') + customer = Stripe::Customer.create(id: 'test_customer_sub', source: gen_card_tk) + sub = Stripe::Subscription.create(customer: customer.id, items: [{ plan: gold_plan.id }, { plan: addon_plan.id }]) + + sub.quantity = 5 + expect { sub.save }.to raise_error {|e| + expect(e).to be_a Stripe::InvalidRequestError + expect(e.http_status).to eq(400) + expect(e.message).to eq('Cannot update using quantity parameter when multiple plans exist on the subscription. Updates must be made to individual items instead.') + } + end + [nil, 0].each do |trial_period_days| it "throws an error when updating a customer with no card, and plan trail_period_days = #{trial_period_days}", live: true do begin From a3fca8fd8c7af1de777b53dcc26ec9f577a6cd20 Mon Sep 17 00:00:00 2001 From: mattagra Date: Fri, 16 Mar 2018 16:24:49 -0700 Subject: [PATCH 7/9] Remove unnecessary mock_subscription_items helper --- .../request_handlers/helpers/subscription_helpers.rb | 8 -------- lib/stripe_mock/request_handlers/subscriptions.rb | 1 - 2 files changed, 9 deletions(-) diff --git a/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb b/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb index bea78dede..c99def177 100644 --- a/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb +++ b/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb @@ -100,14 +100,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 diff --git a/lib/stripe_mock/request_handlers/subscriptions.rb b/lib/stripe_mock/request_handlers/subscriptions.rb index b99c9e07c..8324b3fc0 100644 --- a/lib/stripe_mock/request_handlers/subscriptions.rb +++ b/lib/stripe_mock/request_handlers/subscriptions.rb @@ -120,7 +120,6 @@ def create_subscription(route, method_url, params, headers) 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] # Ensure customer has card to charge if plan has no trial and is not free verify_card_present(customer, plan, subscription, params) From 7e8187bdc7cef1a03c50693af623490d33482f16 Mon Sep 17 00:00:00 2001 From: mattagra Date: Fri, 16 Mar 2018 17:41:43 -0700 Subject: [PATCH 8/9] Expect items to be an array --- .../request_handlers/helpers/subscription_helpers.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb b/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb index c99def177..68253897c 100644 --- a/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb +++ b/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb @@ -24,9 +24,9 @@ def custom_subscription_params(plan, cus, options = {}) params.merge!({status: 'trialing', current_period_end: end_time, trial_start: start_time, trial_end: end_time}) end - items = options[:items] + items = options[:items] || [] items = items.values if items.respond_to?(:values) - if items + if items.any? items_data = [] items.each do |item| plan = assert_existence(:plan, item[:plan], plans[item[:plan]]) From 103a76d577a6febfe4ee5bc26cea127d2623df1c Mon Sep 17 00:00:00 2001 From: mattagra Date: Fri, 16 Mar 2018 17:54:22 -0700 Subject: [PATCH 9/9] Updating items with quantity update subscription quantity correctly --- .../request_handlers/helpers/subscription_helpers.rb | 7 ++++++- spec/shared_stripe_examples/subscription_examples.rb | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb b/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb index 68253897c..f6ec36bd2 100644 --- a/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb +++ b/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb @@ -34,7 +34,12 @@ def custom_subscription_params(plan, cus, options = {}) items_data.push(Data.mock_subscription_item(plan: plan, quantity: quantity, created: Time.now.utc.to_i)) end params[:items] = Data.mock_list_object(items_data) - params.delete(:plan) if items_data.size > 1 + if items_data.size == 1 + params.merge!(:plan => items_data[0][:plan], :quantity => items_data[0][:quantity]) + elsif items_data.size > 1 + params.delete(:plan) + params.delete(:quantity) + end end params diff --git a/spec/shared_stripe_examples/subscription_examples.rb b/spec/shared_stripe_examples/subscription_examples.rb index 77e94d4e9..c9140b79b 100644 --- a/spec/shared_stripe_examples/subscription_examples.rb +++ b/spec/shared_stripe_examples/subscription_examples.rb @@ -14,7 +14,7 @@ def gen_card_tk expect(customer.subscriptions.data).to be_empty expect(customer.subscriptions.count).to eq(0) - sub = Stripe::Subscription.create({ items: [{ plan: 'silver' }], + sub = Stripe::Subscription.create({ items: [{ plan: 'silver', :quantity => 4 }], customer: customer.id, metadata: { foo: "bar", example: "yes" } }) expect(sub.object).to eq('subscription') @@ -31,6 +31,7 @@ def gen_card_tk expect(customer.subscriptions.data.first.id).to eq(sub.id) expect(customer.subscriptions.data.first.plan.to_hash).to eq(plan.to_hash) + expect(customer.subscriptions.data.first.quantity).to eq(4) expect(customer.subscriptions.data.first.customer).to eq(customer.id) expect(customer.subscriptions.data.first.metadata.foo).to eq( "bar" ) expect(customer.subscriptions.data.first.metadata.example).to eq( "yes" )