stripe-ruby-mock 2.5.6 → 3.1.0.rc2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -1
  3. data/.travis.yml +6 -9
  4. data/CHANGELOG.md +35 -0
  5. data/Gemfile +1 -0
  6. data/README.md +17 -11
  7. data/lib/stripe_mock.rb +11 -0
  8. data/lib/stripe_mock/api/client.rb +1 -1
  9. data/lib/stripe_mock/api/errors.rb +34 -28
  10. data/lib/stripe_mock/api/instance.rb +1 -1
  11. data/lib/stripe_mock/api/webhooks.rb +7 -0
  12. data/lib/stripe_mock/client.rb +2 -1
  13. data/lib/stripe_mock/data.rb +323 -13
  14. data/lib/stripe_mock/data/list.rb +42 -9
  15. data/lib/stripe_mock/instance.rb +52 -5
  16. data/lib/stripe_mock/request_handlers/account_links.rb +15 -0
  17. data/lib/stripe_mock/request_handlers/balance_transactions.rb +2 -2
  18. data/lib/stripe_mock/request_handlers/charges.rb +13 -12
  19. data/lib/stripe_mock/request_handlers/checkout.rb +15 -0
  20. data/lib/stripe_mock/request_handlers/checkout_session.rb +16 -0
  21. data/lib/stripe_mock/request_handlers/customers.rb +35 -18
  22. data/lib/stripe_mock/request_handlers/ephemeral_key.rb +1 -1
  23. data/lib/stripe_mock/request_handlers/express_login_links.rb +15 -0
  24. data/lib/stripe_mock/request_handlers/helpers/coupon_helpers.rb +14 -10
  25. data/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb +22 -7
  26. data/lib/stripe_mock/request_handlers/helpers/token_helpers.rb +1 -1
  27. data/lib/stripe_mock/request_handlers/invoices.rb +11 -5
  28. data/lib/stripe_mock/request_handlers/payment_intents.rb +182 -0
  29. data/lib/stripe_mock/request_handlers/payment_methods.rb +120 -0
  30. data/lib/stripe_mock/request_handlers/plans.rb +1 -1
  31. data/lib/stripe_mock/request_handlers/prices.rb +44 -0
  32. data/lib/stripe_mock/request_handlers/products.rb +44 -0
  33. data/lib/stripe_mock/request_handlers/refunds.rb +6 -3
  34. data/lib/stripe_mock/request_handlers/setup_intents.rb +93 -0
  35. data/lib/stripe_mock/request_handlers/sources.rb +12 -6
  36. data/lib/stripe_mock/request_handlers/subscription_items.rb +36 -0
  37. data/lib/stripe_mock/request_handlers/subscriptions.rb +82 -41
  38. data/lib/stripe_mock/request_handlers/tax_rates.rb +36 -0
  39. data/lib/stripe_mock/request_handlers/tokens.rb +8 -6
  40. data/lib/stripe_mock/request_handlers/validators/param_validators.rb +130 -9
  41. data/lib/stripe_mock/test_strategies/base.rb +68 -8
  42. data/lib/stripe_mock/test_strategies/live.rb +23 -12
  43. data/lib/stripe_mock/test_strategies/mock.rb +6 -2
  44. data/lib/stripe_mock/version.rb +1 -1
  45. data/lib/stripe_mock/webhook_fixtures/balance.available.json +6 -0
  46. data/lib/stripe_mock/webhook_fixtures/charge.dispute.funds_reinstated.json +88 -0
  47. data/lib/stripe_mock/webhook_fixtures/charge.dispute.funds_withdrawn.json +88 -0
  48. data/lib/stripe_mock/webhook_fixtures/charge.failed.json +166 -38
  49. data/lib/stripe_mock/webhook_fixtures/customer.created.json +1 -0
  50. data/lib/stripe_mock/webhook_fixtures/customer.updated.json +1 -0
  51. data/lib/stripe_mock/webhook_fixtures/invoice.created.json +2 -1
  52. data/lib/stripe_mock/webhook_fixtures/invoice.payment_succeeded.json +1 -1
  53. data/lib/stripe_mock/webhook_fixtures/invoice.updated.json +2 -1
  54. data/lib/stripe_mock/webhook_fixtures/payment_intent.payment_failed.json +186 -0
  55. data/lib/stripe_mock/webhook_fixtures/payment_intent.succeeded.json +164 -0
  56. data/lib/stripe_mock/webhook_fixtures/product.created.json +34 -0
  57. data/lib/stripe_mock/webhook_fixtures/product.deleted.json +34 -0
  58. data/lib/stripe_mock/webhook_fixtures/product.updated.json +38 -0
  59. data/spec/instance_spec.rb +10 -12
  60. data/spec/list_spec.rb +38 -0
  61. data/spec/server_spec.rb +6 -3
  62. data/spec/shared_stripe_examples/account_examples.rb +1 -1
  63. data/spec/shared_stripe_examples/account_link_examples.rb +16 -0
  64. data/spec/shared_stripe_examples/balance_examples.rb +6 -0
  65. data/spec/shared_stripe_examples/balance_transaction_examples.rb +3 -3
  66. data/spec/shared_stripe_examples/bank_examples.rb +3 -3
  67. data/spec/shared_stripe_examples/card_examples.rb +4 -4
  68. data/spec/shared_stripe_examples/card_token_examples.rb +17 -21
  69. data/spec/shared_stripe_examples/charge_examples.rb +32 -36
  70. data/spec/shared_stripe_examples/checkout_examples.rb +38 -0
  71. data/spec/shared_stripe_examples/coupon_examples.rb +1 -1
  72. data/spec/shared_stripe_examples/customer_examples.rb +103 -53
  73. data/spec/shared_stripe_examples/dispute_examples.rb +2 -2
  74. data/spec/shared_stripe_examples/error_mock_examples.rb +8 -7
  75. data/spec/shared_stripe_examples/express_login_link_examples.rb +12 -0
  76. data/spec/shared_stripe_examples/external_account_examples.rb +3 -3
  77. data/spec/shared_stripe_examples/invoice_examples.rb +43 -41
  78. data/spec/shared_stripe_examples/invoice_item_examples.rb +1 -1
  79. data/spec/shared_stripe_examples/payment_intent_examples.rb +147 -0
  80. data/spec/shared_stripe_examples/payment_method_examples.rb +449 -0
  81. data/spec/shared_stripe_examples/payout_examples.rb +2 -2
  82. data/spec/shared_stripe_examples/plan_examples.rb +135 -77
  83. data/spec/shared_stripe_examples/price_examples.rb +183 -0
  84. data/spec/shared_stripe_examples/product_examples.rb +155 -0
  85. data/spec/shared_stripe_examples/refund_examples.rb +41 -31
  86. data/spec/shared_stripe_examples/setup_intent_examples.rb +68 -0
  87. data/spec/shared_stripe_examples/subscription_examples.rb +546 -295
  88. data/spec/shared_stripe_examples/subscription_items_examples.rb +76 -0
  89. data/spec/shared_stripe_examples/tax_rate_examples.rb +42 -0
  90. data/spec/shared_stripe_examples/transfer_examples.rb +9 -9
  91. data/spec/shared_stripe_examples/webhook_event_examples.rb +11 -11
  92. data/spec/spec_helper.rb +7 -4
  93. data/spec/stripe_mock_spec.rb +4 -4
  94. data/spec/support/shared_contexts/stripe_validator_spec.rb +8 -0
  95. data/spec/support/stripe_examples.rb +12 -2
  96. data/stripe-ruby-mock.gemspec +8 -3
  97. metadata +81 -32
@@ -34,7 +34,7 @@ module StripeMock
34
34
 
35
35
  def list_plans(route, method_url, params, headers)
36
36
  limit = params[:limit] ? params[:limit] : 10
37
- Data.mock_list_object(plans.values.first(limit), limit: limit)
37
+ Data.mock_list_object(plans.values.first(limit), params.merge!(limit: limit))
38
38
  end
39
39
 
40
40
  end
@@ -0,0 +1,44 @@
1
+ module StripeMock
2
+ module RequestHandlers
3
+ module Prices
4
+
5
+ def Prices.included(klass)
6
+ klass.add_handler 'post /v1/prices', :new_price
7
+ klass.add_handler 'post /v1/prices/(.*)', :update_price
8
+ klass.add_handler 'get /v1/prices/(.*)', :get_price
9
+ klass.add_handler 'get /v1/prices', :list_prices
10
+ end
11
+
12
+ def new_price(route, method_url, params, headers)
13
+ params[:id] ||= new_id('price')
14
+ validate_create_price_params(params)
15
+ prices[ params[:id] ] = Data.mock_price(params)
16
+ end
17
+
18
+ def update_price(route, method_url, params, headers)
19
+ route =~ method_url
20
+ assert_existence :price, $1, prices[$1]
21
+ prices[$1].merge!(params)
22
+ end
23
+
24
+ def get_price(route, method_url, params, headers)
25
+ route =~ method_url
26
+ assert_existence :price, $1, prices[$1]
27
+ end
28
+
29
+ def list_prices(route, method_url, params, headers)
30
+ limit = params[:limit] ? params[:limit] : 10
31
+ price_data = prices.values
32
+ validate_list_prices_params(params)
33
+
34
+ if params.key?(:lookup_keys)
35
+ price_data.select! do |price|
36
+ params[:lookup_keys].include?(price[:lookup_key])
37
+ end
38
+ end
39
+
40
+ Data.mock_list_object(price_data.first(limit), params.merge!(limit: limit))
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,44 @@
1
+ module StripeMock
2
+ module RequestHandlers
3
+ module Products
4
+ def self.included(base)
5
+ base.add_handler 'post /v1/products', :create_product
6
+ base.add_handler 'get /v1/products/(.*)', :retrieve_product
7
+ base.add_handler 'post /v1/products/(.*)', :update_product
8
+ base.add_handler 'get /v1/products', :list_products
9
+ base.add_handler 'delete /v1/products/(.*)', :destroy_product
10
+ end
11
+
12
+ def create_product(_route, _method_url, params, _headers)
13
+ params[:id] ||= new_id('prod')
14
+ validate_create_product_params(params)
15
+ products[params[:id]] = Data.mock_product(params)
16
+ end
17
+
18
+ def retrieve_product(route, method_url, _params, _headers)
19
+ id = method_url.match(route).captures.first
20
+ assert_existence :product, id, products[id]
21
+ end
22
+
23
+ def update_product(route, method_url, params, _headers)
24
+ id = method_url.match(route).captures.first
25
+ product = assert_existence :product, id, products[id]
26
+
27
+ product.merge!(params)
28
+ end
29
+
30
+ def list_products(_route, _method_url, params, _headers)
31
+ limit = params[:limit] || 10
32
+ Data.mock_list_object(products.values.take(limit), params)
33
+ end
34
+
35
+ def destroy_product(route, method_url, _params, _headers)
36
+ id = method_url.match(route).captures.first
37
+ assert_existence :product, id, products[id]
38
+
39
+ products.delete(id)
40
+ { id: id, object: 'product', deleted: true }
41
+ end
42
+ end
43
+ end
44
+ end
@@ -10,9 +10,12 @@ module StripeMock
10
10
  end
11
11
 
12
12
  def new_refund(route, method_url, params, headers)
13
- if params[:idempotency_key] && refunds.any?
14
- original_refund = refunds.values.find { |c| c[:idempotency_key] == params[:idempotency_key]}
15
- return refunds[original_refund[:id]] if original_refund
13
+ if headers && headers[:idempotency_key]
14
+ params[:idempotency_key] = headers[:idempotency_key]
15
+ if refunds.any?
16
+ original_refund = refunds.values.find { |c| c[:idempotency_key] == headers[:idempotency_key]}
17
+ return refunds[original_refund[:id]] if original_refund
18
+ end
16
19
  end
17
20
 
18
21
  charge = assert_existence :charge, params[:charge], charges[params[:charge]]
@@ -0,0 +1,93 @@
1
+ module StripeMock
2
+ module RequestHandlers
3
+ module SetupIntents
4
+ ALLOWED_PARAMS = [
5
+ :confirm,
6
+ :customer,
7
+ :description,
8
+ :metadata,
9
+ :on_behalf_of,
10
+ :payment_method,
11
+ :payment_method_options,
12
+ :payment_method_types,
13
+ :return_url,
14
+ :usage
15
+ ]
16
+
17
+ def SetupIntents.included(klass)
18
+ klass.add_handler 'post /v1/setup_intents', :new_setup_intent
19
+ klass.add_handler 'get /v1/setup_intents', :get_setup_intents
20
+ klass.add_handler 'get /v1/setup_intents/(.*)', :get_setup_intent
21
+ klass.add_handler 'post /v1/setup_intents/(.*)/confirm', :confirm_setup_intent
22
+ klass.add_handler 'post /v1/setup_intents/(.*)/cancel', :cancel_setup_intent
23
+ klass.add_handler 'post /v1/setup_intents/(.*)', :update_setup_intent
24
+ end
25
+
26
+ def new_setup_intent(route, method_url, params, headers)
27
+ id = new_id('si')
28
+
29
+ setup_intents[id] = Data.mock_setup_intent(
30
+ params.merge(
31
+ id: id
32
+ )
33
+ )
34
+
35
+ setup_intents[id].clone
36
+ end
37
+
38
+ def update_setup_intent(route, method_url, params, headers)
39
+ route =~ method_url
40
+ id = $1
41
+
42
+ setup_intent = assert_existence :setup_intent, id, setup_intents[id]
43
+ setup_intents[id] = Util.rmerge(setup_intent, params.select{ |k,v| ALLOWED_PARAMS.include?(k)})
44
+ end
45
+
46
+ def get_setup_intents(route, method_url, params, headers)
47
+ params[:offset] ||= 0
48
+ params[:limit] ||= 10
49
+
50
+ clone = setup_intents.clone
51
+
52
+ if params[:customer]
53
+ clone.delete_if { |k,v| v[:customer] != params[:customer] }
54
+ end
55
+
56
+ Data.mock_list_object(clone.values, params)
57
+ end
58
+
59
+ def get_setup_intent(route, method_url, params, headers)
60
+ route =~ method_url
61
+ setup_intent_id = $1 || params[:setup_intent]
62
+ setup_intent = assert_existence :setup_intent, setup_intent_id, setup_intents[setup_intent_id]
63
+
64
+ setup_intent = setup_intent.clone
65
+ setup_intent
66
+ end
67
+
68
+ def capture_setup_intent(route, method_url, params, headers)
69
+ route =~ method_url
70
+ setup_intent = assert_existence :setup_intent, $1, setup_intents[$1]
71
+
72
+ setup_intent[:status] = 'succeeded'
73
+ setup_intent
74
+ end
75
+
76
+ def confirm_setup_intent(route, method_url, params, headers)
77
+ route =~ method_url
78
+ setup_intent = assert_existence :setup_intent, $1, setup_intents[$1]
79
+
80
+ setup_intent[:status] = 'succeeded'
81
+ setup_intent
82
+ end
83
+
84
+ def cancel_setup_intent(route, method_url, params, headers)
85
+ route =~ method_url
86
+ setup_intent = assert_existence :setup_intent, $1, setup_intents[$1]
87
+
88
+ setup_intent[:status] = 'canceled'
89
+ setup_intent
90
+ end
91
+ end
92
+ end
93
+ end
@@ -12,30 +12,35 @@ module StripeMock
12
12
  end
13
13
 
14
14
  def create_source(route, method_url, params, headers)
15
+ stripe_account = headers && headers[:stripe_account] || Stripe.api_key
15
16
  route =~ method_url
16
- add_source_to(:customer, $1, params, customers)
17
+ add_source_to(:customer, $1, params, customers[stripe_account])
17
18
  end
18
19
 
19
20
  def retrieve_sources(route, method_url, params, headers)
21
+ stripe_account = headers && headers[:stripe_account] || Stripe.api_key
20
22
  route =~ method_url
21
- retrieve_object_cards(:customer, $1, customers)
23
+ retrieve_object_cards(:customer, $1, customers[stripe_account])
22
24
  end
23
25
 
24
26
  def retrieve_source(route, method_url, params, headers)
27
+ stripe_account = headers && headers[:stripe_account] || Stripe.api_key
25
28
  route =~ method_url
26
- customer = assert_existence :customer, $1, customers[$1]
29
+ customer = assert_existence :customer, $1, customers[stripe_account][$1]
27
30
 
28
31
  assert_existence :card, $2, get_card(customer, $2)
29
32
  end
30
33
 
31
34
  def delete_source(route, method_url, params, headers)
35
+ stripe_account = headers && headers[:stripe_account] || Stripe.api_key
32
36
  route =~ method_url
33
- delete_card_from(:customer, $1, $2, customers)
37
+ delete_card_from(:customer, $1, $2, customers[stripe_account])
34
38
  end
35
39
 
36
40
  def update_source(route, method_url, params, headers)
41
+ stripe_account = headers && headers[:stripe_account] || Stripe.api_key
37
42
  route =~ method_url
38
- customer = assert_existence :customer, $1, customers[$1]
43
+ customer = assert_existence :customer, $1, customers[stripe_account][$1]
39
44
 
40
45
  card = assert_existence :card, $2, get_card(customer, $2)
41
46
  card.merge!(params)
@@ -43,8 +48,9 @@ module StripeMock
43
48
  end
44
49
 
45
50
  def verify_source(route, method_url, params, headers)
51
+ stripe_account = headers && headers[:stripe_account] || Stripe.api_key
46
52
  route =~ method_url
47
- customer = assert_existence :customer, $1, customers[$1]
53
+ customer = assert_existence :customer, $1, customers[stripe_account][$1]
48
54
 
49
55
  bank_account = assert_existence :bank_account, $2, verify_bank_account(customer, $2)
50
56
  bank_account
@@ -0,0 +1,36 @@
1
+ module StripeMock
2
+ module RequestHandlers
3
+ module SubscriptionItems
4
+
5
+ def SubscriptionItems.included(klass)
6
+ klass.add_handler 'get /v1/subscription_items', :retrieve_subscription_items
7
+ klass.add_handler 'post /v1/subscription_items/([^/]*)', :update_subscription_item
8
+ klass.add_handler 'post /v1/subscription_items', :create_subscription_items
9
+ end
10
+
11
+ def retrieve_subscription_items(route, method_url, params, headers)
12
+ route =~ method_url
13
+
14
+ require_param(:subscription) unless params[:subscription]
15
+
16
+ Data.mock_list_object(subscriptions_items, params)
17
+ end
18
+
19
+ def create_subscription_items(route, method_url, params, headers)
20
+ params[:id] ||= new_id('si')
21
+
22
+ require_param(:subscription) unless params[:subscription]
23
+ require_param(:plan) unless params[:plan]
24
+
25
+ subscriptions_items[params[:id]] = Data.mock_subscription_item(params.merge(plan: plans[params[:plan]]))
26
+ end
27
+
28
+ def update_subscription_item(route, method_url, params, headers)
29
+ route =~ method_url
30
+
31
+ subscription_item = assert_existence :subscription_item, $1, subscriptions_items[$1]
32
+ subscription_item.merge!(params.merge(plan: plans[params[:plan]]))
33
+ end
34
+ end
35
+ end
36
+ end
@@ -17,26 +17,29 @@ module StripeMock
17
17
  end
18
18
 
19
19
  def retrieve_customer_subscription(route, method_url, params, headers)
20
+ stripe_account = headers && headers[:stripe_account] || Stripe.api_key
20
21
  route =~ method_url
21
22
 
22
- customer = assert_existence :customer, $1, customers[$1]
23
+ customer = assert_existence :customer, $1, customers[stripe_account][$1]
23
24
  subscription = get_customer_subscription(customer, $2)
24
25
 
25
26
  assert_existence :subscription, $2, subscription
26
27
  end
27
28
 
28
29
  def retrieve_customer_subscriptions(route, method_url, params, headers)
30
+ stripe_account = headers && headers[:stripe_account] || Stripe.api_key
29
31
  route =~ method_url
30
32
 
31
- customer = assert_existence :customer, $1, customers[$1]
33
+ customer = assert_existence :customer, $1, customers[stripe_account][$1]
32
34
  customer[:subscriptions]
33
35
  end
34
36
 
35
37
  def create_customer_subscription(route, method_url, params, headers)
38
+ stripe_account = headers && headers[:stripe_account] || Stripe.api_key
36
39
  route =~ method_url
37
40
 
38
41
  subscription_plans = get_subscription_plans_from_params(params)
39
- customer = assert_existence :customer, $1, customers[$1]
42
+ customer = assert_existence :customer, $1, customers[stripe_account][$1]
40
43
 
41
44
  if params[:source]
42
45
  new_card = get_card_by_token(params.delete(:source))
@@ -60,7 +63,7 @@ module StripeMock
60
63
  coupon = coupons[coupon_id]
61
64
 
62
65
  if coupon
63
- subscription[:discount] = Stripe::Util.convert_to_stripe_object({ coupon: coupon }, {})
66
+ add_coupon_to_object(subscription, coupon)
64
67
  else
65
68
  raise Stripe::InvalidRequestError.new("No such coupon: #{coupon_id}", 'coupon', http_status: 400)
66
69
  end
@@ -73,21 +76,20 @@ module StripeMock
73
76
  end
74
77
 
75
78
  def create_subscription(route, method_url, params, headers)
79
+ stripe_account = headers && headers[:stripe_account] || Stripe.api_key
80
+ if headers && headers[:idempotency_key]
81
+ if subscriptions.any?
82
+ original_subscription = subscriptions.values.find { |c| c[:idempotency_key] == headers[:idempotency_key]}
83
+ return subscriptions[original_subscription[:id]] if original_subscription
84
+ end
85
+ end
76
86
  route =~ method_url
77
87
 
78
88
  subscription_plans = get_subscription_plans_from_params(params)
79
89
 
80
90
  customer = params[:customer]
81
91
  customer_id = customer.is_a?(Stripe::Customer) ? customer[:id] : customer.to_s
82
- customer = assert_existence :customer, customer_id, customers[customer_id]
83
-
84
- if subscription_plans && customer
85
- subscription_plans.each do |plan|
86
- unless customer[:currency].to_s == plan[:currency].to_s
87
- raise Stripe::InvalidRequestError.new("Customer's currency of #{customer[:currency]} does not match plan's currency of #{plan[:currency]}", 'currency', http_status: 400)
88
- end
89
- end
90
- end
92
+ customer = assert_existence :customer, customer_id, customers[stripe_account][customer_id]
91
93
 
92
94
  if params[:source]
93
95
  new_card = get_card_by_token(params.delete(:source))
@@ -95,7 +97,7 @@ module StripeMock
95
97
  customer[:default_source] = new_card[:id]
96
98
  end
97
99
 
98
- allowed_params = %w(customer application_fee_percent coupon items metadata plan quantity source tax_percent trial_end trial_period_days current_period_start created prorate billing_cycle_anchor)
100
+ allowed_params = %w(customer application_fee_percent coupon items metadata plan quantity source tax_percent trial_end trial_period_days current_period_start created prorate billing_cycle_anchor billing days_until_due idempotency_key enable_incomplete_payments cancel_at_period_end default_tax_rates payment_behavior pending_invoice_item_interval default_payment_method collection_method off_session trial_from_plan expand)
99
101
  unknown_params = params.keys - allowed_params.map(&:to_sym)
100
102
  if unknown_params.length > 0
101
103
  raise Stripe::InvalidRequestError.new("Received unknown parameter: #{unknown_params.join}", unknown_params.first.to_s, http_status: 400)
@@ -103,6 +105,9 @@ module StripeMock
103
105
 
104
106
  subscription = Data.mock_subscription({ id: (params[:id] || new_id('su')) })
105
107
  subscription = resolve_subscription_changes(subscription, subscription_plans, customer, params)
108
+ if headers[:idempotency_key]
109
+ subscription[:idempotency_key] = headers[:idempotency_key]
110
+ end
106
111
 
107
112
  # Ensure customer has card to charge if plan has no trial and is not free
108
113
  # Note: needs updating for subscriptions with multiple plans
@@ -117,12 +122,21 @@ module StripeMock
117
122
  coupon = coupons[coupon_id]
118
123
 
119
124
  if coupon
120
- subscription[:discount] = Stripe::Util.convert_to_stripe_object({ coupon: coupon }, {})
125
+ add_coupon_to_object(subscription, coupon)
121
126
  else
122
127
  raise Stripe::InvalidRequestError.new("No such coupon: #{coupon_id}", 'coupon', http_status: 400)
123
128
  end
124
129
  end
125
130
 
131
+ if params[:trial_period_days]
132
+ subscription[:status] = 'trialing'
133
+ end
134
+
135
+ if params[:cancel_at_period_end]
136
+ subscription[:cancel_at_period_end] = true
137
+ subscription[:canceled_at] = Time.now.utc.to_i
138
+ end
139
+
126
140
  subscriptions[subscription[:id]] = subscription
127
141
  add_subscription_to_customer(customer, subscription)
128
142
 
@@ -136,14 +150,16 @@ module StripeMock
136
150
  end
137
151
 
138
152
  def retrieve_subscriptions(route, method_url, params, headers)
153
+ # stripe_account = headers && headers[:stripe_account] || Stripe.api_key
139
154
  route =~ method_url
140
155
 
141
156
  Data.mock_list_object(subscriptions.values, params)
142
- #customer = assert_existence :customer, $1, customers[$1]
157
+ #customer = assert_existence :customer, $1, customers[stripe_account][$1]
143
158
  #customer[:subscriptions]
144
159
  end
145
160
 
146
161
  def update_subscription(route, method_url, params, headers)
162
+ stripe_account = headers && headers[:stripe_account] || Stripe.api_key
147
163
  route =~ method_url
148
164
 
149
165
  subscription_id = $2 ? $2 : $1
@@ -151,7 +167,7 @@ module StripeMock
151
167
  verify_active_status(subscription)
152
168
 
153
169
  customer_id = subscription[:customer]
154
- customer = assert_existence :customer, customer_id, customers[customer_id]
170
+ customer = assert_existence :customer, customer_id, customers[stripe_account][customer_id]
155
171
 
156
172
  if params[:source]
157
173
  new_card = get_card_by_token(params.delete(:source))
@@ -174,23 +190,35 @@ module StripeMock
174
190
 
175
191
  coupon = coupons[coupon_id]
176
192
  if coupon
177
- subscription[:discount] = Stripe::Util.convert_to_stripe_object({ coupon: coupon }, {})
193
+ add_coupon_to_object(subscription, coupon)
178
194
  elsif coupon_id == ""
179
- subscription[:discount] = Stripe::Util.convert_to_stripe_object(nil, {})
195
+ subscription[:discount] = nil
180
196
  else
181
197
  raise Stripe::InvalidRequestError.new("No such coupon: #{coupon_id}", 'coupon', http_status: 400)
182
198
  end
183
199
  end
184
- verify_card_present(customer, subscription_plans.first, subscription)
185
200
 
186
- if subscription[:cancel_at_period_end]
201
+ if params[:trial_period_days]
202
+ subscription[:status] = 'trialing'
203
+ end
204
+
205
+ if params[:cancel_at_period_end]
206
+ subscription[:cancel_at_period_end] = true
207
+ subscription[:canceled_at] = Time.now.utc.to_i
208
+ elsif params.has_key?(:cancel_at_period_end)
187
209
  subscription[:cancel_at_period_end] = false
188
210
  subscription[:canceled_at] = nil
189
211
  end
190
212
 
191
213
  params[:current_period_start] = subscription[:current_period_start]
214
+ params[:trial_end] = params[:trial_end] || subscription[:trial_end]
215
+
216
+ plan_amount_was = subscription.dig(:plan, :amount)
217
+
192
218
  subscription = resolve_subscription_changes(subscription, subscription_plans, customer, params)
193
219
 
220
+ verify_card_present(customer, subscription_plans.first, subscription, params) if plan_amount_was == 0 && subscription.dig(:plan, :amount) && subscription.dig(:plan, :amount) > 0
221
+
194
222
  # delete the old subscription, replace with the new subscription
195
223
  customer[:subscriptions][:data].reject! { |sub| sub[:id] == subscription[:id] }
196
224
  customer[:subscriptions][:data] << subscription
@@ -199,13 +227,14 @@ module StripeMock
199
227
  end
200
228
 
201
229
  def cancel_subscription(route, method_url, params, headers)
230
+ stripe_account = headers && headers[:stripe_account] || Stripe.api_key
202
231
  route =~ method_url
203
232
 
204
233
  subscription_id = $2 ? $2 : $1
205
234
  subscription = assert_existence :subscription, subscription_id, subscriptions[subscription_id]
206
235
 
207
236
  customer_id = subscription[:customer]
208
- customer = assert_existence :customer, customer_id, customers[customer_id]
237
+ customer = assert_existence :customer, customer_id, customers[stripe_account][customer_id]
209
238
 
210
239
  cancel_params = { canceled_at: Time.now.utc.to_i }
211
240
  cancelled_at_period_end = (params[:at_period_end] == true)
@@ -234,36 +263,48 @@ module StripeMock
234
263
  elsif params[:items]
235
264
  items = params[:items]
236
265
  items = items.values if items.respond_to?(:values)
237
- items.map { |item| item[:plan].to_s if item[:plan] }
266
+ items.map { |item| item[:plan] ? item[:plan] : item[:price] }
238
267
  else
239
268
  []
240
269
  end
270
+ plan_ids.compact!
241
271
  plan_ids.each do |plan_id|
242
272
  assert_existence :plan, plan_id, plans[plan_id]
273
+ rescue Stripe::InvalidRequestError
274
+ assert_existence :price, plan_id, prices[plan_id]
243
275
  end
244
- plan_ids.map { |plan_id| plans[plan_id] }
276
+ plan_ids.map { |plan_id| plans[plan_id] || prices[plan_id]}
245
277
  end
246
278
 
279
+ # Ensure customer has card to charge unless one of the following criterias is met:
280
+ # 1) is in trial
281
+ # 2) is free
282
+ # 3) has billing set to send invoice
247
283
  def verify_card_present(customer, plan, subscription, params={})
248
- if customer[:default_source].nil? && customer[:trial_end].nil? &&
249
- (plan.nil? ||
250
- ((plan[:trial_period_days] || 0) == 0 &&
251
- plan[:amount] != 0 &&
252
- plan[:trial_end].nil?)) &&
253
- params[:trial_end].nil? &&
254
- (subscription.nil? || subscription[:trial_end].nil? || subscription[:trial_end] == 'now')
255
-
256
- if subscription[:items]
257
- trial = subscription[:items][:data].none? do |item|
258
- plan = item[:plan]
259
- (plan[:trial_period_days].nil? || plan[:trial_period_days] == 0) &&
260
- (plan[:trial_end].nil? || plan[:trial_end] == 'now')
261
- end
262
- return if trial
284
+ return if customer[:default_source]
285
+ return if customer[:invoice_settings][:default_payment_method]
286
+ return if customer[:trial_end]
287
+ return if params[:trial_end]
288
+ return if subscription[:default_payment_method]
289
+
290
+ plan_trial_period_days = plan[:trial_period_days] || 0
291
+ plan_has_trial = plan_trial_period_days != 0 || plan[:amount] == 0 || plan[:trial_end]
292
+ return if plan && plan_has_trial
293
+
294
+ return if subscription && subscription[:trial_end] && subscription[:trial_end] != 'now'
295
+
296
+ if subscription[:items]
297
+ trial = subscription[:items][:data].none? do |item|
298
+ plan = item[:plan]
299
+ (plan[:trial_period_days].nil? || plan[:trial_period_days] == 0) &&
300
+ (plan[:trial_end].nil? || plan[:trial_end] == 'now')
263
301
  end
264
-
265
- raise Stripe::InvalidRequestError.new('You must supply a valid card xoxo', nil, http_status: 400)
302
+ return if trial
266
303
  end
304
+
305
+ return if params[:billing] == 'send_invoice'
306
+
307
+ raise Stripe::InvalidRequestError.new('This customer has no attached payment source', nil, http_status: 400)
267
308
  end
268
309
 
269
310
  def verify_active_status(subscription)