stripe-ruby-mock 3.0.0 → 3.1.0

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 (137) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/rspec_tests.yml +38 -0
  3. data/.rspec +2 -1
  4. data/CHANGELOG.md +69 -0
  5. data/Gemfile +1 -0
  6. data/README.md +13 -5
  7. data/lib/stripe_mock/api/client.rb +1 -1
  8. data/lib/stripe_mock/api/errors.rb +3 -0
  9. data/lib/stripe_mock/api/instance.rb +1 -1
  10. data/lib/stripe_mock/api/webhooks.rb +66 -25
  11. data/lib/stripe_mock/client.rb +2 -1
  12. data/lib/stripe_mock/data/list.rb +42 -9
  13. data/lib/stripe_mock/data.rb +242 -31
  14. data/lib/stripe_mock/instance.rb +14 -3
  15. data/lib/stripe_mock/request_handlers/account_links.rb +15 -0
  16. data/lib/stripe_mock/request_handlers/accounts.rb +17 -6
  17. data/lib/stripe_mock/request_handlers/charges.rb +11 -4
  18. data/lib/stripe_mock/request_handlers/checkout_session.rb +179 -0
  19. data/lib/stripe_mock/request_handlers/customers.rb +35 -18
  20. data/lib/stripe_mock/request_handlers/ephemeral_key.rb +1 -1
  21. data/lib/stripe_mock/request_handlers/events.rb +30 -3
  22. data/lib/stripe_mock/request_handlers/express_login_links.rb +15 -0
  23. data/lib/stripe_mock/request_handlers/helpers/coupon_helpers.rb +6 -0
  24. data/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb +36 -12
  25. data/lib/stripe_mock/request_handlers/invoices.rb +10 -4
  26. data/lib/stripe_mock/request_handlers/payment_intents.rb +19 -1
  27. data/lib/stripe_mock/request_handlers/payment_methods.rb +10 -24
  28. data/lib/stripe_mock/request_handlers/plans.rb +1 -1
  29. data/lib/stripe_mock/request_handlers/prices.rb +62 -0
  30. data/lib/stripe_mock/request_handlers/promotion_codes.rb +43 -0
  31. data/lib/stripe_mock/request_handlers/refunds.rb +13 -2
  32. data/lib/stripe_mock/request_handlers/setup_intents.rb +16 -9
  33. data/lib/stripe_mock/request_handlers/sources.rb +12 -6
  34. data/lib/stripe_mock/request_handlers/subscriptions.rb +120 -21
  35. data/lib/stripe_mock/request_handlers/tokens.rb +6 -4
  36. data/lib/stripe_mock/request_handlers/transfers.rb +12 -1
  37. data/lib/stripe_mock/request_handlers/validators/param_validators.rb +33 -4
  38. data/lib/stripe_mock/server.rb +2 -2
  39. data/lib/stripe_mock/test_strategies/base.rb +62 -10
  40. data/lib/stripe_mock/version.rb +1 -1
  41. data/lib/stripe_mock/webhook_fixtures/account.updated.json +1 -1
  42. data/lib/stripe_mock/webhook_fixtures/balance.available.json +27 -15
  43. data/lib/stripe_mock/webhook_fixtures/charge.captured.json +143 -0
  44. data/lib/stripe_mock/webhook_fixtures/charge.dispute.created.json +63 -16
  45. data/lib/stripe_mock/webhook_fixtures/charge.failed.json +49 -120
  46. data/lib/stripe_mock/webhook_fixtures/charge.refund.updated.json +35 -0
  47. data/lib/stripe_mock/webhook_fixtures/charge.refunded.json +145 -50
  48. data/lib/stripe_mock/webhook_fixtures/charge.succeeded.json +114 -43
  49. data/lib/stripe_mock/webhook_fixtures/checkout.session.completed.json +79 -0
  50. data/lib/stripe_mock/webhook_fixtures/checkout.session.completed.payment_mode.json +53 -0
  51. data/lib/stripe_mock/webhook_fixtures/checkout.session.completed.setup_mode.json +45 -0
  52. data/lib/stripe_mock/webhook_fixtures/customer.created.json +37 -46
  53. data/lib/stripe_mock/webhook_fixtures/customer.deleted.json +36 -32
  54. data/lib/stripe_mock/webhook_fixtures/customer.source.created.json +31 -22
  55. data/lib/stripe_mock/webhook_fixtures/customer.source.updated.json +36 -25
  56. data/lib/stripe_mock/webhook_fixtures/customer.subscription.created.json +135 -47
  57. data/lib/stripe_mock/webhook_fixtures/customer.subscription.deleted.json +134 -45
  58. data/lib/stripe_mock/webhook_fixtures/customer.subscription.updated.json +135 -56
  59. data/lib/stripe_mock/webhook_fixtures/customer.updated.json +38 -47
  60. data/lib/stripe_mock/webhook_fixtures/invoice.created.json +176 -49
  61. data/lib/stripe_mock/webhook_fixtures/invoice.finalized.json +171 -0
  62. data/lib/stripe_mock/webhook_fixtures/invoice.paid.json +171 -0
  63. data/lib/stripe_mock/webhook_fixtures/invoice.payment_action_required.json +171 -0
  64. data/lib/stripe_mock/webhook_fixtures/invoice.payment_failed.json +149 -83
  65. data/lib/stripe_mock/webhook_fixtures/invoice.payment_succeeded.json +149 -90
  66. data/lib/stripe_mock/webhook_fixtures/invoice.upcoming.json +70 -0
  67. data/lib/stripe_mock/webhook_fixtures/invoice.updated.json +178 -50
  68. data/lib/stripe_mock/webhook_fixtures/invoiceitem.created.json +87 -13
  69. data/lib/stripe_mock/webhook_fixtures/invoiceitem.updated.json +88 -14
  70. data/lib/stripe_mock/webhook_fixtures/mandate.updated.json +34 -0
  71. data/lib/stripe_mock/webhook_fixtures/payment_intent.amount_capturable_updated.json +170 -0
  72. data/lib/stripe_mock/webhook_fixtures/payment_intent.canceled.json +73 -0
  73. data/lib/stripe_mock/webhook_fixtures/payment_intent.created.json +86 -0
  74. data/lib/stripe_mock/webhook_fixtures/payment_intent.payment_failed.json +225 -0
  75. data/lib/stripe_mock/webhook_fixtures/payment_intent.processing.json +162 -0
  76. data/lib/stripe_mock/webhook_fixtures/payment_intent.requires_action.json +191 -0
  77. data/lib/stripe_mock/webhook_fixtures/payment_intent.succeeded.json +196 -0
  78. data/lib/stripe_mock/webhook_fixtures/payment_link.created.json +47 -0
  79. data/lib/stripe_mock/webhook_fixtures/payment_link.updated.json +50 -0
  80. data/lib/stripe_mock/webhook_fixtures/payment_method.attached.json +63 -0
  81. data/lib/stripe_mock/webhook_fixtures/payment_method.detached.json +62 -0
  82. data/lib/stripe_mock/webhook_fixtures/payout.created.json +40 -0
  83. data/lib/stripe_mock/webhook_fixtures/payout.paid.json +40 -0
  84. data/lib/stripe_mock/webhook_fixtures/payout.updated.json +46 -0
  85. data/lib/stripe_mock/webhook_fixtures/plan.created.json +30 -13
  86. data/lib/stripe_mock/webhook_fixtures/plan.deleted.json +30 -13
  87. data/lib/stripe_mock/webhook_fixtures/plan.updated.json +34 -14
  88. data/lib/stripe_mock/webhook_fixtures/price.created.json +42 -0
  89. data/lib/stripe_mock/webhook_fixtures/price.deleted.json +42 -0
  90. data/lib/stripe_mock/webhook_fixtures/price.updated.json +48 -0
  91. data/lib/stripe_mock/webhook_fixtures/product.created.json +19 -13
  92. data/lib/stripe_mock/webhook_fixtures/product.deleted.json +20 -14
  93. data/lib/stripe_mock/webhook_fixtures/product.updated.json +24 -15
  94. data/lib/stripe_mock/webhook_fixtures/quote.accepted.json +92 -0
  95. data/lib/stripe_mock/webhook_fixtures/quote.canceled.json +92 -0
  96. data/lib/stripe_mock/webhook_fixtures/quote.created.json +92 -0
  97. data/lib/stripe_mock/webhook_fixtures/quote.finalized.json +92 -0
  98. data/lib/stripe_mock/webhook_fixtures/setup_intent.canceled.json +46 -0
  99. data/lib/stripe_mock/webhook_fixtures/setup_intent.created.json +51 -0
  100. data/lib/stripe_mock/webhook_fixtures/setup_intent.setup_failed.json +100 -0
  101. data/lib/stripe_mock/webhook_fixtures/setup_intent.succeeded.json +46 -0
  102. data/lib/stripe_mock/webhook_fixtures/subscription_schedule.canceled.json +119 -0
  103. data/lib/stripe_mock/webhook_fixtures/subscription_schedule.created.json +114 -0
  104. data/lib/stripe_mock/webhook_fixtures/subscription_schedule.released.json +111 -0
  105. data/lib/stripe_mock/webhook_fixtures/subscription_schedule.updated.json +125 -0
  106. data/lib/stripe_mock/webhook_fixtures/tax_rate.created.json +32 -0
  107. data/lib/stripe_mock/webhook_fixtures/tax_rate.updated.json +37 -0
  108. data/lib/stripe_mock.rb +7 -0
  109. data/spec/instance_spec.rb +7 -7
  110. data/spec/integration_examples/completing_checkout_sessions_example.rb +37 -0
  111. data/spec/list_spec.rb +38 -0
  112. data/spec/readme_spec.rb +1 -1
  113. data/spec/server_spec.rb +4 -2
  114. data/spec/shared_stripe_examples/account_examples.rb +9 -1
  115. data/spec/shared_stripe_examples/account_link_examples.rb +16 -0
  116. data/spec/shared_stripe_examples/balance_examples.rb +6 -0
  117. data/spec/shared_stripe_examples/card_token_examples.rb +17 -21
  118. data/spec/shared_stripe_examples/checkout_session_examples.rb +99 -0
  119. data/spec/shared_stripe_examples/customer_examples.rb +49 -23
  120. data/spec/shared_stripe_examples/express_login_link_examples.rb +12 -0
  121. data/spec/shared_stripe_examples/invoice_examples.rb +29 -8
  122. data/spec/shared_stripe_examples/payment_intent_examples.rb +84 -0
  123. data/spec/shared_stripe_examples/payment_method_examples.rb +336 -67
  124. data/spec/shared_stripe_examples/price_examples.rb +223 -0
  125. data/spec/shared_stripe_examples/product_examples.rb +1 -9
  126. data/spec/shared_stripe_examples/promotion_code_examples.rb +68 -0
  127. data/spec/shared_stripe_examples/refund_examples.rb +13 -0
  128. data/spec/shared_stripe_examples/setup_intent_examples.rb +17 -0
  129. data/spec/shared_stripe_examples/subscription_examples.rb +361 -9
  130. data/spec/shared_stripe_examples/transfer_examples.rb +10 -1
  131. data/spec/shared_stripe_examples/webhook_event_examples.rb +51 -5
  132. data/spec/spec_helper.rb +4 -0
  133. data/spec/stripe_mock_spec.rb +2 -2
  134. data/spec/support/stripe_examples.rb +8 -1
  135. data/stripe-ruby-mock.gemspec +7 -2
  136. metadata +73 -12
  137. data/.travis.yml +0 -28
@@ -0,0 +1,62 @@
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
+
15
+ if params[:product_data]
16
+ params[:product] = create_product(nil, nil, params[:product_data], nil)[:id] unless params[:product]
17
+ params.delete(:product_data)
18
+ end
19
+
20
+ validate_create_price_params(params)
21
+ prices[ params[:id] ] = Data.mock_price(params)
22
+ end
23
+
24
+ def update_price(route, method_url, params, headers)
25
+ route =~ method_url
26
+ assert_existence :price, $1, prices[$1]
27
+ prices[$1].merge!(params)
28
+ end
29
+
30
+ def get_price(route, method_url, params, headers)
31
+ route =~ method_url
32
+ assert_existence :price, $1, prices[$1]
33
+ end
34
+
35
+ def list_prices(route, method_url, params, headers)
36
+ limit = params[:limit] ? params[:limit] : 10
37
+ price_data = prices.values
38
+ validate_list_prices_params(params)
39
+
40
+ if params.key?(:lookup_keys)
41
+ price_data.select! do |price|
42
+ params[:lookup_keys].include?(price[:lookup_key])
43
+ end
44
+ end
45
+
46
+ if params.key?(:currency)
47
+ price_data.select! do |price|
48
+ params[:currency] == price[:currency]
49
+ end
50
+ end
51
+
52
+ if params.key?(:product)
53
+ price_data.select! do |price|
54
+ params[:product] == price[:product]
55
+ end
56
+ end
57
+
58
+ Data.mock_list_object(price_data.first(limit), params.merge!(limit: limit))
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,43 @@
1
+ module StripeMock
2
+ module RequestHandlers
3
+ module PromotionCodes
4
+
5
+ def PromotionCodes.included(klass)
6
+ klass.add_handler 'post /v1/promotion_codes', :new_promotion_code
7
+ klass.add_handler 'post /v1/promotion_codes/([^/]*)', :update_promotion_code
8
+ klass.add_handler 'get /v1/promotion_codes/([^/]*)', :get_promotion_code
9
+ klass.add_handler 'get /v1/promotion_codes', :list_promotion_code
10
+ end
11
+
12
+ def new_promotion_code(route, method_url, params, headers)
13
+ params[:id] ||= new_id("promo")
14
+ raise Stripe::InvalidRequestError.new("Missing required param: coupon", "promotion_code", http_status: 400) unless params[:coupon]
15
+
16
+ if params[:restrictions]
17
+ if params[:restrictions][:minimum_amount] && !params[:restrictions][:minimum_amount_currency]
18
+ raise Stripe::InvalidRequestError.new(
19
+ "You must pass minimum_amount_currency when passing minimum_amount", "minimum_amount_currency", http_status: 400
20
+ )
21
+ end
22
+ end
23
+
24
+ promotion_codes[ params[:id] ] = Data.mock_promotion_code(params)
25
+ end
26
+
27
+ def update_promotion_code(route, method_url, params, headers)
28
+ route =~ method_url
29
+ assert_existence :promotion_code, $1, promotion_codes[$1]
30
+ promotion_codes[$1].merge!(params)
31
+ end
32
+
33
+ def get_promotion_code(route, method_url, params, headers)
34
+ route =~ method_url
35
+ assert_existence :promotion_code, $1, promotion_codes[$1]
36
+ end
37
+
38
+ def list_promotion_code(route, method_url, params, headers)
39
+ Data.mock_list_object(promotion_codes.values, params)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -18,7 +18,18 @@ module StripeMock
18
18
  end
19
19
  end
20
20
 
21
- charge = assert_existence :charge, params[:charge], charges[params[:charge]]
21
+ if params[:payment_intent]
22
+ payment_intent = assert_existence(
23
+ :payment_intent,
24
+ params[:payment_intent],
25
+ payment_intents[params[:payment_intent]]
26
+ )
27
+ charge = {}
28
+ else
29
+ charge = assert_existence :charge, params[:charge], charges[params[:charge]]
30
+ payment_intent = {}
31
+ end
32
+ params[:amount] ||= payment_intent[:amount]
22
33
  params[:amount] ||= charge[:amount]
23
34
  id = new_id('re')
24
35
  bal_trans_params = {
@@ -32,7 +43,7 @@ module StripeMock
32
43
  :id => id,
33
44
  :charge => charge[:id],
34
45
  )
35
- add_refund_to_charge(refund, charge)
46
+ add_refund_to_charge(refund, charge) unless charge.empty?
36
47
  refunds[id] = refund
37
48
 
38
49
  if params[:expand] == ['balance_transaction']
@@ -15,20 +15,22 @@ module StripeMock
15
15
  ]
16
16
 
17
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
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
24
  end
25
25
 
26
26
  def new_setup_intent(route, method_url, params, headers)
27
27
  id = new_id('si')
28
+ status = params[:payment_method] ? 'requires_action' : 'requires_payment_method'
28
29
 
29
30
  setup_intents[id] = Data.mock_setup_intent(
30
31
  params.merge(
31
- id: id
32
+ id: id,
33
+ status: status
32
34
  )
33
35
  )
34
36
 
@@ -40,7 +42,7 @@ module StripeMock
40
42
  id = $1
41
43
 
42
44
  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)})
45
+ setup_intents[id] = Util.rmerge(setup_intent, params.select { |k, v| ALLOWED_PARAMS.include?(k) })
44
46
  end
45
47
 
46
48
  def get_setup_intents(route, method_url, params, headers)
@@ -50,7 +52,7 @@ module StripeMock
50
52
  clone = setup_intents.clone
51
53
 
52
54
  if params[:customer]
53
- clone.delete_if { |k,v| v[:customer] != params[:customer] }
55
+ clone.delete_if { |k, v| v[:customer] != params[:customer] }
54
56
  end
55
57
 
56
58
  Data.mock_list_object(clone.values, params)
@@ -62,6 +64,11 @@ module StripeMock
62
64
  setup_intent = assert_existence :setup_intent, setup_intent_id, setup_intents[setup_intent_id]
63
65
 
64
66
  setup_intent = setup_intent.clone
67
+
68
+ if params[:expand]&.include?("payment_method")
69
+ setup_intent[:payment_method] = assert_existence :payment_method, setup_intent[:payment_method], payment_methods[setup_intent[:payment_method]]
70
+ end
71
+
65
72
  setup_intent
66
73
  end
67
74
 
@@ -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
@@ -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))
@@ -66,6 +69,16 @@ module StripeMock
66
69
  end
67
70
  end
68
71
 
72
+ if params[:promotion_code]
73
+ promotion_code_id = params[:promotion_code]
74
+
75
+ promotion_code = promotion_codes[promotion_code_id]
76
+
77
+ unless promotion_code
78
+ raise Stripe::InvalidRequestError.new("No such promotion code: #{promotion_code_id}", 'promotion_code', http_status: 400)
79
+ end
80
+ end
81
+
69
82
  subscriptions[subscription[:id]] = subscription
70
83
  add_subscription_to_customer(customer, subscription)
71
84
 
@@ -73,10 +86,10 @@ module StripeMock
73
86
  end
74
87
 
75
88
  def create_subscription(route, method_url, params, headers)
89
+ stripe_account = headers && headers[:stripe_account] || Stripe.api_key
76
90
  if headers && headers[:idempotency_key]
77
91
  if subscriptions.any?
78
92
  original_subscription = subscriptions.values.find { |c| c[:idempotency_key] == headers[:idempotency_key]}
79
- puts original_subscription
80
93
  return subscriptions[original_subscription[:id]] if original_subscription
81
94
  end
82
95
  end
@@ -86,15 +99,7 @@ module StripeMock
86
99
 
87
100
  customer = params[:customer]
88
101
  customer_id = customer.is_a?(Stripe::Customer) ? customer[:id] : customer.to_s
89
- customer = assert_existence :customer, customer_id, customers[customer_id]
90
-
91
- if subscription_plans && customer
92
- subscription_plans.each do |plan|
93
- unless customer[:currency].to_s == plan[:currency].to_s
94
- raise Stripe::InvalidRequestError.new("Customer's currency of #{customer[:currency]} does not match plan's currency of #{plan[:currency]}", 'currency', http_status: 400)
95
- end
96
- end
97
- end
102
+ customer = assert_existence :customer, customer_id, customers[stripe_account][customer_id]
98
103
 
99
104
  if params[:source]
100
105
  new_card = get_card_by_token(params.delete(:source))
@@ -102,7 +107,7 @@ module StripeMock
102
107
  customer[:default_source] = new_card[:id]
103
108
  end
104
109
 
105
- 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)
110
+ allowed_params = %w(id 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 proration_behavior backdate_start_date transfer_data expand automatic_tax payment_settings trial_settings promotion_code)
106
111
  unknown_params = params.keys - allowed_params.map(&:to_sym)
107
112
  if unknown_params.length > 0
108
113
  raise Stripe::InvalidRequestError.new("Received unknown parameter: #{unknown_params.join}", unknown_params.first.to_s, http_status: 400)
@@ -118,6 +123,10 @@ module StripeMock
118
123
  # Note: needs updating for subscriptions with multiple plans
119
124
  verify_card_present(customer, subscription_plans.first, subscription, params)
120
125
 
126
+ if params[:coupon] && params[:promotion_code]
127
+ raise Stripe::InvalidRequestError.new("You may only specify one of these parameters: coupon, promotion_code", "coupon", http_status: 400)
128
+ end
129
+
121
130
  if params[:coupon]
122
131
  coupon_id = params[:coupon]
123
132
 
@@ -133,11 +142,50 @@ module StripeMock
133
142
  end
134
143
  end
135
144
 
145
+ if params[:promotion_code]
146
+ promotion_code_id = params[:promotion_code]
147
+
148
+ promotion_code = promotion_codes[promotion_code_id]
149
+
150
+ unless promotion_code
151
+ raise Stripe::InvalidRequestError.new("No such promotion code: #{promotion_code_id}", 'promotion_code', http_status: 400)
152
+ end
153
+ end
154
+
155
+ if params[:trial_period_days]
156
+ subscription[:status] = 'trialing'
157
+ end
158
+
159
+ if params[:payment_behavior] == 'default_incomplete'
160
+ subscription[:status] = 'incomplete'
161
+ end
162
+
136
163
  if params[:cancel_at_period_end]
137
164
  subscription[:cancel_at_period_end] = true
138
165
  subscription[:canceled_at] = Time.now.utc.to_i
139
166
  end
140
167
 
168
+ if params[:transfer_data] && !params[:transfer_data].empty?
169
+ throw Stripe::InvalidRequestError.new(missing_param_message("transfer_data[destination]")) unless params[:transfer_data][:destination]
170
+ subscription[:transfer_data] = params[:transfer_data].dup
171
+ subscription[:transfer_data][:amount_percent] ||= 100
172
+ end
173
+
174
+ if (s = params[:expand]&.find { |s| s.start_with? 'latest_invoice' })
175
+ payment_intent = nil
176
+ unless subscription[:status] == 'trialing'
177
+ intent_status = subscription[:status] == 'incomplete' ? 'requires_payment_method' : 'succeeded'
178
+ intent = Data.mock_payment_intent({
179
+ status: intent_status,
180
+ amount: subscription[:plan][:amount],
181
+ currency: subscription[:plan][:currency]
182
+ })
183
+ payment_intent = s.include?('latest_invoice.payment_intent') ? intent : intent.id
184
+ end
185
+ invoice = Data.mock_invoice([], { payment_intent: payment_intent })
186
+ subscription[:latest_invoice] = invoice
187
+ end
188
+
141
189
  subscriptions[subscription[:id]] = subscription
142
190
  add_subscription_to_customer(customer, subscription)
143
191
 
@@ -151,22 +199,43 @@ module StripeMock
151
199
  end
152
200
 
153
201
  def retrieve_subscriptions(route, method_url, params, headers)
202
+ # stripe_account = headers && headers[:stripe_account] || Stripe.api_key
154
203
  route =~ method_url
155
204
 
156
- Data.mock_list_object(subscriptions.values, params)
157
- #customer = assert_existence :customer, $1, customers[$1]
158
- #customer[:subscriptions]
205
+ subs = subscriptions.values
206
+
207
+ case params[:status]
208
+ when nil
209
+ subs = subs.filter {|subscription| subscription[:status] != "canceled"}
210
+ when "all"
211
+ # Include all subscriptions
212
+ else
213
+ subs = subs.filter {|subscription| subscription[:status] == params[:status]}
214
+ end
215
+ if params[:current_period_end]
216
+ subs = filter_by_timestamp(subs, field: :current_period_end, value: params[:current_period_end])
217
+ end
218
+ if params[:current_period_start]
219
+ subs = filter_by_timestamp(subs, field: :current_period_start, value: params[:current_period_start])
220
+ end
221
+
222
+ Data.mock_list_object(subs, params)
159
223
  end
160
224
 
161
225
  def update_subscription(route, method_url, params, headers)
226
+ stripe_account = headers && headers[:stripe_account] || Stripe.api_key
162
227
  route =~ method_url
163
228
 
229
+ if params[:billing_cycle_anchor] == 'now'
230
+ params[:billing_cycle_anchor] = Time.now.utc.to_i
231
+ end
232
+
164
233
  subscription_id = $2 ? $2 : $1
165
234
  subscription = assert_existence :subscription, subscription_id, subscriptions[subscription_id]
166
235
  verify_active_status(subscription)
167
236
 
168
237
  customer_id = subscription[:customer]
169
- customer = assert_existence :customer, customer_id, customers[customer_id]
238
+ customer = assert_existence :customer, customer_id, customers[stripe_account][customer_id]
170
239
 
171
240
  if params[:source]
172
241
  new_card = get_card_by_token(params.delete(:source))
@@ -197,6 +266,30 @@ module StripeMock
197
266
  end
198
267
  end
199
268
 
269
+ if params[:promotion_code]
270
+ promotion_code_id = params[:promotion_code]
271
+
272
+ promotion_code = promotion_codes[promotion_code_id]
273
+
274
+ if promotion_code
275
+ # You can't apply a promotion code with amount restrictions on the Customer object or on a subscription
276
+ # update API call
277
+ if promotion_code[:restrictions][:minimum_amount]
278
+ raise Stripe::InvalidRequestError.new(
279
+ "This promotion code cannot be redeemed on a subcription update because it uses the `minimum_amount` restriction.",
280
+ "promotion_code",
281
+ http_status: 400
282
+ )
283
+ end
284
+ else
285
+ raise Stripe::InvalidRequestError.new("No such promotion code: #{promotion_code_id}", 'promotion_code', http_status: 400)
286
+ end
287
+ end
288
+
289
+ if params[:trial_period_days]
290
+ subscription[:status] = 'trialing'
291
+ end
292
+
200
293
  if params[:cancel_at_period_end]
201
294
  subscription[:cancel_at_period_end] = true
202
295
  subscription[:canceled_at] = Time.now.utc.to_i
@@ -222,13 +315,14 @@ module StripeMock
222
315
  end
223
316
 
224
317
  def cancel_subscription(route, method_url, params, headers)
318
+ stripe_account = headers && headers[:stripe_account] || Stripe.api_key
225
319
  route =~ method_url
226
320
 
227
321
  subscription_id = $2 ? $2 : $1
228
322
  subscription = assert_existence :subscription, subscription_id, subscriptions[subscription_id]
229
323
 
230
324
  customer_id = subscription[:customer]
231
- customer = assert_existence :customer, customer_id, customers[customer_id]
325
+ customer = assert_existence :customer, customer_id, customers[stripe_account][customer_id]
232
326
 
233
327
  cancel_params = { canceled_at: Time.now.utc.to_i }
234
328
  cancelled_at_period_end = (params[:at_period_end] == true)
@@ -257,14 +351,17 @@ module StripeMock
257
351
  elsif params[:items]
258
352
  items = params[:items]
259
353
  items = items.values if items.respond_to?(:values)
260
- items.map { |item| item[:plan].to_s if item[:plan] }
354
+ items.map { |item| item[:plan] ? item[:plan] : item[:price] }
261
355
  else
262
356
  []
263
357
  end
358
+ plan_ids.compact!
264
359
  plan_ids.each do |plan_id|
265
360
  assert_existence :plan, plan_id, plans[plan_id]
361
+ rescue Stripe::InvalidRequestError
362
+ assert_existence :price, plan_id, prices[plan_id]
266
363
  end
267
- plan_ids.map { |plan_id| plans[plan_id] }
364
+ plan_ids.map { |plan_id| plans[plan_id] || prices[plan_id]}
268
365
  end
269
366
 
270
367
  # Ensure customer has card to charge unless one of the following criterias is met:
@@ -276,6 +373,8 @@ module StripeMock
276
373
  return if customer[:invoice_settings][:default_payment_method]
277
374
  return if customer[:trial_end]
278
375
  return if params[:trial_end]
376
+ return if params[:payment_behavior] == 'default_incomplete'
377
+ return if subscription[:default_payment_method]
279
378
 
280
379
  plan_trial_period_days = plan[:trial_period_days] || 0
281
380
  plan_has_trial = plan_trial_period_days != 0 || plan[:amount] == 0 || plan[:trial_end]
@@ -8,6 +8,8 @@ module StripeMock
8
8
  end
9
9
 
10
10
  def create_token(route, method_url, params, headers)
11
+ stripe_account = headers && headers[:stripe_account] || Stripe.api_key
12
+
11
13
  if params[:customer].nil? && params[:card].nil? && params[:bank_account].nil?
12
14
  raise Stripe::InvalidRequestError.new('You must supply either a card, customer, or bank account to create a token.', nil, http_status: 400)
13
15
  end
@@ -15,13 +17,13 @@ module StripeMock
15
17
  cus_id = params[:customer]
16
18
 
17
19
  if cus_id && params[:source]
18
- customer = assert_existence :customer, cus_id, customers[cus_id]
20
+ customer = assert_existence :customer, cus_id, customers[stripe_account][cus_id]
19
21
 
20
22
  # params[:card] is an id; grab it from the db
21
23
  customer_card = get_card(customer, params[:source])
22
24
  assert_existence :card, params[:source], customer_card
23
25
  elsif params[:card].is_a?(String)
24
- customer = assert_existence :customer, cus_id, customers[cus_id]
26
+ customer = assert_existence :customer, cus_id, customers[stripe_account][cus_id]
25
27
 
26
28
  # params[:card] is an id; grab it from the db
27
29
  customer_card = get_card(customer, params[:card])
@@ -32,7 +34,7 @@ module StripeMock
32
34
  params[:card][:last4] = params[:card][:number][-4,4]
33
35
  customer_card = params[:card]
34
36
  elsif params[:bank_account].is_a?(String)
35
- customer = assert_existence :customer, cus_id, customers[cus_id]
37
+ customer = assert_existence :customer, cus_id, customers[stripe_account][cus_id]
36
38
 
37
39
  # params[:bank_account] is an id; grab it from the db
38
40
  bank_account = verify_bank_account(customer, params[:bank_account])
@@ -41,7 +43,7 @@ module StripeMock
41
43
  # params[:card] is a hash of cc info; "Sanitize" the card number
42
44
  bank_account = params[:bank_account]
43
45
  else
44
- customer = assert_existence :customer, cus_id, customers[cus_id]
46
+ customer = assert_existence :customer, cus_id, customers[stripe_account][cus_id] || customers[Stripe.api_key][cus_id]
45
47
  customer_card = get_card(customer, customer[:default_source])
46
48
  end
47
49
 
@@ -45,7 +45,18 @@ module StripeMock
45
45
  raise Stripe::InvalidRequestError.new("Invalid integer: #{params[:amount]}", 'amount', http_status: 400)
46
46
  end
47
47
 
48
- transfers[id] = Data.mock_transfer(params.merge :id => id)
48
+ bal_trans_params = { amount: params[:amount].to_i, source: id }
49
+
50
+ balance_transaction_id = new_balance_transaction('txn', bal_trans_params)
51
+
52
+ transfers[id] = Data.mock_transfer(params.merge(id: id, balance_transaction: balance_transaction_id))
53
+
54
+ transfer = transfers[id].clone
55
+ if params[:expand] == ['balance_transaction']
56
+ transfer[:balance_transaction] = balance_transactions[balance_transaction_id]
57
+ end
58
+
59
+ transfer
49
60
  end
50
61
 
51
62
  def get_transfer(route, method_url, params, headers)
@@ -29,10 +29,6 @@ module StripeMock
29
29
  raise Stripe::InvalidRequestError.new(missing_param_message(k), k) if params[k].nil?
30
30
  end
31
31
 
32
- if !%w[good service].include?(params[:type])
33
- raise Stripe::InvalidRequestError.new("Invalid type: must be one of good or service", :type)
34
- end
35
-
36
32
  if products[ params[:id] ]
37
33
  raise Stripe::InvalidRequestError.new(already_exists_message(Stripe::Product), :id)
38
34
  end
@@ -110,9 +106,42 @@ module StripeMock
110
106
 
111
107
  end
112
108
 
109
+ def validate_create_price_params(params)
110
+ price_id = params[:id].to_s
111
+
112
+ require_param(:currency) unless params[:currency]
113
+ unless params[:product] || params[:product_data]
114
+ raise Stripe::InvalidRequestError("Requires product or product_data")
115
+ end
116
+
117
+ product_id = params[:product] || create_product(nil, nil, params[:product_data], nil).id
118
+
119
+ if prices[price_id]
120
+ message = already_exists_message(Stripe::Price)
121
+ raise Stripe::InvalidRequestError.new(message, :id)
122
+ end
123
+
124
+ unless products[product_id]
125
+ message = not_found_message(Stripe::Product, product_id)
126
+ raise Stripe::InvalidRequestError.new(message, :product)
127
+ end
128
+
129
+ unless SUPPORTED_CURRENCIES.include?(params[:currency])
130
+ message = invalid_currency_message(params[:currency])
131
+ raise Stripe::InvalidRequestError.new(message, :currency)
132
+ end
133
+ end
134
+
135
+ def validate_list_prices_params(params)
136
+ if params[:lookup_keys] && !params[:lookup_keys].is_a?(Array)
137
+ raise Stripe::InvalidRequestError.new('Invalid array', :lookup_keys)
138
+ end
139
+ end
140
+
113
141
  def require_param(param_name)
114
142
  raise Stripe::InvalidRequestError.new("Missing required param: #{param_name}.", param_name.to_s, http_status: 400)
115
143
  end
144
+
116
145
  end
117
146
  end
118
147
  end
@@ -16,9 +16,9 @@ module StripeMock
16
16
  self.clear_data
17
17
  end
18
18
 
19
- def mock_request(*args)
19
+ def mock_request(*args, **kwargs)
20
20
  begin
21
- @instance.mock_request(*args)
21
+ @instance.mock_request(*args, **kwargs)
22
22
  rescue Stripe::InvalidRequestError => e
23
23
  {
24
24
  :error_raised => 'invalid_request',