stripe-ruby-mock 2.3.1 → 2.5.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/.env +2 -0
  3. data/.travis.yml +8 -4
  4. data/README.md +12 -5
  5. data/lib/stripe_mock/api/account_balance.rb +14 -0
  6. data/lib/stripe_mock/api/client.rb +4 -4
  7. data/lib/stripe_mock/api/conversion_rate.rb +14 -0
  8. data/lib/stripe_mock/api/errors.rb +25 -13
  9. data/lib/stripe_mock/api/instance.rb +16 -6
  10. data/lib/stripe_mock/api/webhooks.rb +8 -1
  11. data/lib/stripe_mock/client.rb +18 -2
  12. data/lib/stripe_mock/data/list.rb +14 -2
  13. data/lib/stripe_mock/data.rb +398 -58
  14. data/lib/stripe_mock/instance.rb +105 -9
  15. data/lib/stripe_mock/request_handlers/accounts.rb +41 -2
  16. data/lib/stripe_mock/request_handlers/balance.rb +17 -0
  17. data/lib/stripe_mock/request_handlers/balance_transactions.rb +18 -2
  18. data/lib/stripe_mock/request_handlers/charges.rb +44 -33
  19. data/lib/stripe_mock/request_handlers/country_spec.rb +22 -0
  20. data/lib/stripe_mock/request_handlers/coupons.rb +5 -4
  21. data/lib/stripe_mock/request_handlers/customers.rb +29 -11
  22. data/lib/stripe_mock/request_handlers/ephemeral_key.rb +13 -0
  23. data/lib/stripe_mock/request_handlers/external_accounts.rb +55 -0
  24. data/lib/stripe_mock/request_handlers/helpers/bank_account_helpers.rb +1 -1
  25. data/lib/stripe_mock/request_handlers/helpers/card_helpers.rb +9 -7
  26. data/lib/stripe_mock/request_handlers/helpers/coupon_helpers.rb +10 -6
  27. data/lib/stripe_mock/request_handlers/helpers/external_account_helpers.rb +49 -0
  28. data/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb +59 -16
  29. data/lib/stripe_mock/request_handlers/helpers/token_helpers.rb +3 -2
  30. data/lib/stripe_mock/request_handlers/invoices.rb +93 -14
  31. data/lib/stripe_mock/request_handlers/orders.rb +5 -5
  32. data/lib/stripe_mock/request_handlers/payouts.rb +32 -0
  33. data/lib/stripe_mock/request_handlers/plans.rb +1 -0
  34. data/lib/stripe_mock/request_handlers/products.rb +43 -0
  35. data/lib/stripe_mock/request_handlers/recipients.rb +12 -0
  36. data/lib/stripe_mock/request_handlers/refunds.rb +91 -0
  37. data/lib/stripe_mock/request_handlers/subscription_items.rb +36 -0
  38. data/lib/stripe_mock/request_handlers/subscriptions.rb +101 -39
  39. data/lib/stripe_mock/request_handlers/tax_rates.rb +36 -0
  40. data/lib/stripe_mock/request_handlers/tokens.rb +9 -3
  41. data/lib/stripe_mock/request_handlers/transfers.rb +11 -5
  42. data/lib/stripe_mock/request_handlers/validators/param_validators.rb +15 -1
  43. data/lib/stripe_mock/server.rb +14 -1
  44. data/lib/stripe_mock/test_strategies/base.rb +10 -5
  45. data/lib/stripe_mock/test_strategies/live.rb +5 -0
  46. data/lib/stripe_mock/test_strategies/mock.rb +8 -0
  47. data/lib/stripe_mock/util.rb +8 -2
  48. data/lib/stripe_mock/version.rb +1 -1
  49. data/lib/stripe_mock/webhook_fixtures/account.external_account.created.json +27 -0
  50. data/lib/stripe_mock/webhook_fixtures/account.external_account.deleted.json +27 -0
  51. data/lib/stripe_mock/webhook_fixtures/account.external_account.updated.json +27 -0
  52. data/lib/stripe_mock/webhook_fixtures/account.updated.json +1 -1
  53. data/lib/stripe_mock/webhook_fixtures/charge.dispute.funds_reinstated.json +88 -0
  54. data/lib/stripe_mock/webhook_fixtures/charge.dispute.funds_withdrawn.json +88 -0
  55. data/lib/stripe_mock/webhook_fixtures/charge.updated.json +58 -0
  56. data/lib/stripe_mock/webhook_fixtures/customer.subscription.created.json +40 -10
  57. data/lib/stripe_mock/webhook_fixtures/customer.subscription.deleted.json +39 -10
  58. data/lib/stripe_mock/webhook_fixtures/customer.subscription.trial_will_end.json +39 -10
  59. data/lib/stripe_mock/webhook_fixtures/customer.subscription.updated.json +40 -11
  60. data/lib/stripe_mock/webhook_fixtures/invoice.created.json +3 -2
  61. data/lib/stripe_mock/webhook_fixtures/invoice.payment_failed.json +1 -1
  62. data/lib/stripe_mock/webhook_fixtures/invoice.payment_succeeded.json +92 -85
  63. data/lib/stripe_mock/webhook_fixtures/invoice.updated.json +3 -2
  64. data/lib/stripe_mock/webhook_fixtures/plan.created.json +1 -1
  65. data/lib/stripe_mock/webhook_fixtures/plan.deleted.json +1 -1
  66. data/lib/stripe_mock/webhook_fixtures/plan.updated.json +1 -1
  67. data/lib/stripe_mock.rb +15 -0
  68. data/spec/api/instance_spec.rb +30 -0
  69. data/spec/instance_spec.rb +54 -4
  70. data/spec/integration_examples/prepare_error_examples.rb +6 -6
  71. data/spec/list_spec.rb +27 -10
  72. data/spec/readme_spec.rb +2 -0
  73. data/spec/server_spec.rb +7 -3
  74. data/spec/shared_stripe_examples/account_examples.rb +46 -0
  75. data/spec/shared_stripe_examples/balance_examples.rb +11 -0
  76. data/spec/shared_stripe_examples/balance_transaction_examples.rb +28 -0
  77. data/spec/shared_stripe_examples/bank_examples.rb +28 -1
  78. data/spec/shared_stripe_examples/card_examples.rb +23 -5
  79. data/spec/shared_stripe_examples/card_token_examples.rb +1 -0
  80. data/spec/shared_stripe_examples/charge_examples.rb +131 -26
  81. data/spec/shared_stripe_examples/country_specs_examples.rb +18 -0
  82. data/spec/shared_stripe_examples/coupon_examples.rb +8 -2
  83. data/spec/shared_stripe_examples/customer_examples.rb +90 -0
  84. data/spec/shared_stripe_examples/dispute_examples.rb +19 -8
  85. data/spec/shared_stripe_examples/ephemeral_key_examples.rb +17 -0
  86. data/spec/shared_stripe_examples/error_mock_examples.rb +15 -5
  87. data/spec/shared_stripe_examples/external_account_examples.rb +170 -0
  88. data/spec/shared_stripe_examples/extra_features_examples.rb +2 -0
  89. data/spec/shared_stripe_examples/invoice_examples.rb +314 -51
  90. data/spec/shared_stripe_examples/payout_examples.rb +68 -0
  91. data/spec/shared_stripe_examples/plan_examples.rb +47 -4
  92. data/spec/shared_stripe_examples/product_example.rb +65 -0
  93. data/spec/shared_stripe_examples/recipient_examples.rb +13 -7
  94. data/spec/shared_stripe_examples/refund_examples.rb +453 -84
  95. data/spec/shared_stripe_examples/subscription_examples.rb +477 -32
  96. data/spec/shared_stripe_examples/subscription_items_examples.rb +75 -0
  97. data/spec/shared_stripe_examples/tax_rate_examples.rb +42 -0
  98. data/spec/shared_stripe_examples/transfer_examples.rb +72 -23
  99. data/spec/shared_stripe_examples/webhook_event_examples.rb +74 -5
  100. data/spec/spec_helper.rb +7 -6
  101. data/spec/stripe_mock_spec.rb +16 -3
  102. data/spec/support/stripe_examples.rb +8 -1
  103. data/spec/util_spec.rb +35 -1
  104. data/stripe-ruby-mock.gemspec +1 -1
  105. metadata +44 -8
  106. data/ChangeLog.rdoc +0 -4
@@ -20,12 +20,15 @@ module StripeMock
20
20
  @@handlers.find {|h| method_url =~ h[:route] }
21
21
  end
22
22
 
23
+ include StripeMock::RequestHandlers::ExternalAccounts
23
24
  include StripeMock::RequestHandlers::Accounts
25
+ include StripeMock::RequestHandlers::Balance
24
26
  include StripeMock::RequestHandlers::BalanceTransactions
25
27
  include StripeMock::RequestHandlers::Charges
26
28
  include StripeMock::RequestHandlers::Cards
27
29
  include StripeMock::RequestHandlers::Sources
28
30
  include StripeMock::RequestHandlers::Subscriptions # must be before Customers
31
+ include StripeMock::RequestHandlers::SubscriptionItems
29
32
  include StripeMock::RequestHandlers::Customers
30
33
  include StripeMock::RequestHandlers::Coupons
31
34
  include StripeMock::RequestHandlers::Disputes
@@ -34,19 +37,26 @@ module StripeMock
34
37
  include StripeMock::RequestHandlers::InvoiceItems
35
38
  include StripeMock::RequestHandlers::Orders
36
39
  include StripeMock::RequestHandlers::Plans
40
+ include StripeMock::RequestHandlers::Products
41
+ include StripeMock::RequestHandlers::Refunds
37
42
  include StripeMock::RequestHandlers::Recipients
38
43
  include StripeMock::RequestHandlers::Transfers
39
44
  include StripeMock::RequestHandlers::Tokens
45
+ include StripeMock::RequestHandlers::CountrySpec
46
+ include StripeMock::RequestHandlers::Payouts
47
+ include StripeMock::RequestHandlers::EphemeralKey
48
+ include StripeMock::RequestHandlers::TaxRates
40
49
 
41
-
42
- attr_reader :accounts, :balance_transactions, :bank_tokens, :charges, :coupons, :customers,
50
+ attr_reader :accounts, :balance, :balance_transactions, :bank_tokens, :charges, :coupons, :customers,
43
51
  :disputes, :events, :invoices, :invoice_items, :orders, :plans, :recipients,
44
- :transfers, :subscriptions
52
+ :refunds, :transfers, :payouts, :subscriptions, :country_spec, :subscriptions_items,
53
+ :products, :tax_rates
45
54
 
46
- attr_accessor :error_queue, :debug
55
+ attr_accessor :error_queue, :debug, :conversion_rate, :account_balance
47
56
 
48
57
  def initialize
49
58
  @accounts = {}
59
+ @balance = Data.mock_balance
50
60
  @balance_transactions = Data.mock_balance_transactions(['txn_05RsQX2eZvKYlo2C0FRTGSSA','txn_15RsQX2eZvKYlo2C0ERTYUIA', 'txn_25RsQX2eZvKYlo2C0ZXCVBNM', 'txn_35RsQX2eZvKYlo2C0QAZXSWE', 'txn_45RsQX2eZvKYlo2C0EDCVFRT', 'txn_55RsQX2eZvKYlo2C0OIKLJUY', 'txn_65RsQX2eZvKYlo2C0ASDFGHJ', 'txn_75RsQX2eZvKYlo2C0EDCXSWQ', 'txn_85RsQX2eZvKYlo2C0UJMCDET', 'txn_95RsQX2eZvKYlo2C0EDFRYUI'])
51
61
  @bank_tokens = {}
52
62
  @card_tokens = {}
@@ -59,20 +69,29 @@ module StripeMock
59
69
  @invoice_items = {}
60
70
  @orders = {}
61
71
  @plans = {}
72
+ @products = {}
62
73
  @recipients = {}
74
+ @refunds = {}
63
75
  @transfers = {}
76
+ @payouts = {}
64
77
  @subscriptions = {}
78
+ @subscriptions_items = {}
79
+ @country_spec = {}
80
+ @tax_rates = {}
65
81
 
66
82
  @debug = false
67
83
  @error_queue = ErrorQueue.new
68
84
  @id_counter = 0
69
85
  @balance_transaction_counter = 0
86
+ @dispute_counter = 0
87
+ @conversion_rate = 1.0
88
+ @account_balance = 10000
70
89
 
71
90
  # This is basically a cache for ParamValidators
72
91
  @base_strategy = TestStrategies::Base.new
73
92
  end
74
93
 
75
- def mock_request(method, url, api_key, params={}, headers={}, api_base_url=nil)
94
+ def mock_request(method, url, api_key: nil, api_base: nil, params: {}, headers: {})
76
95
  return {} if method == :xtest
77
96
 
78
97
  api_key ||= (Stripe.api_key || DUMMY_API_KEY)
@@ -95,7 +114,7 @@ module StripeMock
95
114
  else
96
115
  res = self.send(handler[:name], handler[:route], method_url, params, headers)
97
116
  puts " [res] #{res}" if @debug == true
98
- [res, api_key]
117
+ [to_faraday_hash(res), api_key]
99
118
  end
100
119
  else
101
120
  puts "[StripeMock] Warning : Unrecognized endpoint + method : [#{method} #{url}]"
@@ -109,12 +128,44 @@ module StripeMock
109
128
  @events[ event_data[:id] ] = symbolize_names(event_data)
110
129
  end
111
130
 
131
+ def upsert_stripe_object(object, attributes)
132
+ # Most Stripe entities can be created via the API. However, some entities are created when other Stripe entities are
133
+ # created - such as when BalanceTransactions are created when Charges are created. This method provides the ability
134
+ # to create these internal entities.
135
+ # It also provides the ability to modify existing Stripe entities.
136
+ id = attributes[:id]
137
+ if id.nil? || id == ""
138
+ # Insert new Stripe object
139
+ case object
140
+ when :balance_transaction
141
+ id = new_balance_transaction('txn', attributes)
142
+ when :dispute
143
+ id = new_dispute('dp', attributes)
144
+ else
145
+ raise UnsupportedRequestError.new "Unsupported stripe object `#{object}`"
146
+ end
147
+ else
148
+ # Update existing Stripe object
149
+ case object
150
+ when :balance_transaction
151
+ btxn = assert_existence :balance_transaction, id, @balance_transactions[id]
152
+ btxn.merge!(attributes)
153
+ when :dispute
154
+ dispute = assert_existence :dispute, id, @disputes[id]
155
+ dispute.merge!(attributes)
156
+ else
157
+ raise UnsupportedRequestError.new "Unsupported stripe object `#{object}`"
158
+ end
159
+ end
160
+ id
161
+ end
162
+
112
163
  private
113
164
 
114
165
  def assert_existence(type, id, obj, message=nil)
115
166
  if obj.nil?
116
167
  msg = message || "No such #{type}: #{id}"
117
- raise Stripe::InvalidRequestError.new(msg, type.to_s, 404)
168
+ raise Stripe::InvalidRequestError.new(msg, type.to_s, http_status: 404)
118
169
  end
119
170
  obj
120
171
  end
@@ -124,14 +175,59 @@ module StripeMock
124
175
  "#{StripeMock.global_id_prefix}#{prefix}_#{@id_counter += 1}"
125
176
  end
126
177
 
127
- def new_balance_transaction(prefix)
178
+ def new_balance_transaction(prefix, params = {})
128
179
  # balance transaction ids must be strings
129
- "#{StripeMock.global_id_prefix}#{prefix}_#{@balance_transaction_counter += 1}"
180
+ id = "#{StripeMock.global_id_prefix}#{prefix}_#{@balance_transaction_counter += 1}"
181
+ amount = params[:amount]
182
+ unless amount.nil?
183
+ # Fee calculation
184
+ calculate_fees(params) unless params[:fee]
185
+ params[:net] = amount - params[:fee]
186
+ params[:amount] = amount * @conversion_rate
187
+ end
188
+ @balance_transactions[id] = Data.mock_balance_transaction(params.merge(id: id))
189
+ id
190
+ end
191
+
192
+ def new_dispute(prefix, params = {})
193
+ id = "#{StripeMock.global_id_prefix}#{prefix}_#{@dispute_counter += 1}"
194
+ @disputes[id] = Data.mock_dispute(params.merge(id: id))
195
+ id
130
196
  end
131
197
 
132
198
  def symbolize_names(hash)
133
199
  Stripe::Util.symbolize_names(hash)
134
200
  end
135
201
 
202
+ def to_faraday_hash(hash)
203
+ response = Struct.new(:data)
204
+ response.new(hash)
205
+ end
206
+
207
+ def calculate_fees(params)
208
+ application_fee = params[:application_fee] || 0
209
+ params[:fee] = processing_fee(params[:amount]) + application_fee
210
+ params[:fee_details] = [
211
+ {
212
+ amount: processing_fee(params[:amount]),
213
+ application: nil,
214
+ currency: params[:currency] || StripeMock.default_currency,
215
+ description: "Stripe processing fees",
216
+ type: "stripe_fee"
217
+ }
218
+ ]
219
+ if application_fee
220
+ params[:fee_details] << {
221
+ amount: application_fee,
222
+ currency: params[:currency] || StripeMock.default_currency,
223
+ description: "Application fee",
224
+ type: "application_fee"
225
+ }
226
+ end
227
+ end
228
+
229
+ def processing_fee(amount)
230
+ (30 + (amount.abs * 0.029).ceil) * (amount > 0 ? 1 : -1)
231
+ end
136
232
  end
137
233
  end
@@ -1,6 +1,7 @@
1
1
  module StripeMock
2
2
  module RequestHandlers
3
3
  module Accounts
4
+ VALID_START_YEAR = 2009
4
5
 
5
6
  def Accounts.included(klass)
6
7
  klass.add_handler 'post /v1/accounts', :new_account
@@ -8,6 +9,7 @@ module StripeMock
8
9
  klass.add_handler 'get /v1/accounts/(.*)', :get_account
9
10
  klass.add_handler 'post /v1/accounts/(.*)', :update_account
10
11
  klass.add_handler 'get /v1/accounts', :list_accounts
12
+ klass.add_handler 'post /oauth/deauthorize',:deauthorize
11
13
  end
12
14
 
13
15
  def new_account(route, method_url, params, headers)
@@ -25,8 +27,14 @@ module StripeMock
25
27
 
26
28
  def update_account(route, method_url, params, headers)
27
29
  route =~ method_url
28
- assert_existence :account, $1, accounts[$1]
29
- accounts[$1].merge!(params)
30
+ account = assert_existence :account, $1, accounts[$1]
31
+ account.merge!(params)
32
+ if blank_value?(params[:tos_acceptance], :date)
33
+ raise Stripe::InvalidRequestError.new("Invalid integer: ", "tos_acceptance[date]", http_status: 400)
34
+ elsif params[:tos_acceptance] && params[:tos_acceptance][:date]
35
+ validate_acceptance_date(params[:tos_acceptance][:date])
36
+ end
37
+ account
30
38
  end
31
39
 
32
40
  def list_accounts(route, method_url, params, headers)
@@ -34,6 +42,12 @@ module StripeMock
34
42
  Data.mock_list_object(accounts.values, params)
35
43
  end
36
44
 
45
+ def deauthorize(route, method_url, params, headers)
46
+ init_account
47
+ route =~ method_url
48
+ Stripe::StripeObject.construct_from(:stripe_user_id => params[:stripe_user_id])
49
+ end
50
+
37
51
  private
38
52
 
39
53
  def init_account
@@ -42,6 +56,31 @@ module StripeMock
42
56
  accounts[acc[:id]] = acc
43
57
  end
44
58
  end
59
+
60
+ # Checks if setting a blank value
61
+ #
62
+ # returns true if the key is included in the hash
63
+ # and its value is empty or nil
64
+ def blank_value?(hash, key)
65
+ if hash.key?(key)
66
+ value = hash[key]
67
+ return true if value.nil? || "" == value
68
+ end
69
+ false
70
+ end
71
+
72
+ def validate_acceptance_date(unix_date)
73
+ unix_now = Time.now.strftime("%s").to_i
74
+ formatted_date = Time.at(unix_date)
75
+
76
+ return if formatted_date.year >= VALID_START_YEAR && unix_now >= unix_date
77
+
78
+ raise Stripe::InvalidRequestError.new(
79
+ "ToS acceptance date is not valid. Dates are expected to be integers, measured in seconds, not in the future, and after 2009",
80
+ "tos_acceptance[date]",
81
+ http_status: 400
82
+ )
83
+ end
45
84
  end
46
85
  end
47
86
  end
@@ -0,0 +1,17 @@
1
+ module StripeMock
2
+ module RequestHandlers
3
+ module Balance
4
+
5
+ def Balance.included(klass)
6
+ klass.add_handler 'get /v1/balance', :get_balance
7
+ end
8
+
9
+ def get_balance(route, method_url, params, headers)
10
+ route =~ method_url
11
+
12
+ return_balance = Data.mock_balance(account_balance)
13
+ return_balance
14
+ end
15
+ end
16
+ end
17
+ end
@@ -9,11 +9,27 @@ module StripeMock
9
9
 
10
10
  def get_balance_transaction(route, method_url, params, headers)
11
11
  route =~ method_url
12
- assert_existence :balance_transaction, $1, balance_transactions[$1]
12
+ assert_existence :balance_transaction, $1, hide_additional_attributes(balance_transactions[$1])
13
13
  end
14
14
 
15
15
  def list_balance_transactions(route, method_url, params, headers)
16
- Data.mock_list_object(balance_transactions.values, params)
16
+ values = balance_transactions.values
17
+ if params.has_key?(:transfer)
18
+ # If transfer supplied as params, need to filter the btxns returned to only include those with the specified transfer id
19
+ values = values.select{|btxn| btxn[:transfer] == params[:transfer]}
20
+ end
21
+ Data.mock_list_object(values.map{|btxn| hide_additional_attributes(btxn)}, params)
22
+ end
23
+
24
+ private
25
+
26
+ def hide_additional_attributes(btxn)
27
+ # For automatic Stripe transfers, the transfer attribute on balance_transaction stores the transfer which
28
+ # included this balance_transaction. However, it is not exposed as a field returned on a balance_transaction.
29
+ # Therefore, need to not show this attribute if it exists.
30
+ if !btxn.nil?
31
+ btxn.reject{|k,v| k == :transfer }
32
+ end
17
33
  end
18
34
 
19
35
  end
@@ -8,14 +8,17 @@ module StripeMock
8
8
  klass.add_handler 'get /v1/charges/(.*)', :get_charge
9
9
  klass.add_handler 'post /v1/charges/(.*)/capture', :capture_charge
10
10
  klass.add_handler 'post /v1/charges/(.*)/refund', :refund_charge
11
- klass.add_handler 'post /v1/charges/(.*)/refunds', :create_refund
11
+ klass.add_handler 'post /v1/charges/(.*)/refunds', :refund_charge
12
12
  klass.add_handler 'post /v1/charges/(.*)', :update_charge
13
13
  end
14
14
 
15
15
  def new_charge(route, method_url, params, headers)
16
- if params[:idempotency_key] && charges.any?
17
- original_charge = charges.values.find { |c| c[:idempotency_key] == params[:idempotency_key]}
18
- return charges[original_charge[:id]] if original_charge
16
+ if headers && headers[:idempotency_key]
17
+ params[:idempotency_key] = headers[:idempotency_key]
18
+ if charges.any?
19
+ original_charge = charges.values.find { |c| c[:idempotency_key] == headers[:idempotency_key]}
20
+ return charges[original_charge[:id]] if original_charge
21
+ end
19
22
  end
20
23
 
21
24
  id = new_id('ch')
@@ -28,10 +31,10 @@ module StripeMock
28
31
  if params[:customer]
29
32
  params[:source] = get_card(customers[params[:customer]], params[:source])
30
33
  else
31
- params[:source] = get_card_by_token(params[:source])
34
+ params[:source] = get_card_or_bank_by_token(params[:source])
32
35
  end
33
36
  elsif params[:source][:id]
34
- raise Stripe::InvalidRequestError.new("Invalid token id: #{params[:source]}", 'card', 400)
37
+ raise Stripe::InvalidRequestError.new("Invalid token id: #{params[:source]}", 'card', http_status: 400)
35
38
  end
36
39
  elsif params[:customer]
37
40
  customer = customers[params[:customer]]
@@ -41,8 +44,21 @@ module StripeMock
41
44
  end
42
45
 
43
46
  ensure_required_params(params)
47
+ bal_trans_params = { amount: params[:amount], source: id, application_fee: params[:application_fee] }
48
+
49
+ balance_transaction_id = new_balance_transaction('txn', bal_trans_params)
50
+
51
+ charges[id] = Data.mock_charge(
52
+ params.merge :id => id,
53
+ :balance_transaction => balance_transaction_id)
54
+
55
+ charge = charges[id].clone
56
+ if params[:expand] == ['balance_transaction']
57
+ charge[:balance_transaction] =
58
+ balance_transactions[balance_transaction_id]
59
+ end
44
60
 
45
- charges[id] = Data.mock_charge(params.merge :id => id, :balance_transaction => new_balance_transaction('txn'))
61
+ charge
46
62
  end
47
63
 
48
64
  def update_charge(route, method_url, params, headers)
@@ -53,7 +69,7 @@ module StripeMock
53
69
  allowed = allowed_params(params)
54
70
  disallowed = params.keys - allowed
55
71
  if disallowed.count > 0
56
- raise Stripe::InvalidRequestError.new("Received unknown parameters: #{disallowed.join(', ')}" , '', 400)
72
+ raise Stripe::InvalidRequestError.new("Received unknown parameters: #{disallowed.join(', ')}" , '', http_status: 400)
57
73
  end
58
74
 
59
75
  charges[id] = Util.rmerge(charge, params)
@@ -74,7 +90,16 @@ module StripeMock
74
90
 
75
91
  def get_charge(route, method_url, params, headers)
76
92
  route =~ method_url
77
- assert_existence :charge, $1, charges[$1]
93
+ charge_id = $1 || params[:charge]
94
+ charge = assert_existence :charge, charge_id, charges[charge_id]
95
+
96
+ charge = charge.clone
97
+ if params[:expand] == ['balance_transaction']
98
+ balance_transaction = balance_transactions[charge[:balance_transaction]]
99
+ charge[:balance_transaction] = balance_transaction
100
+ end
101
+
102
+ charge
78
103
  end
79
104
 
80
105
  def capture_charge(route, method_url, params, headers)
@@ -101,24 +126,12 @@ module StripeMock
101
126
  def refund_charge(route, method_url, params, headers)
102
127
  charge = get_charge(route, method_url, params, headers)
103
128
 
104
- refund = Data.mock_refund params.merge(
105
- :balance_transaction => new_balance_transaction('txn'),
106
- :id => new_id('re')
129
+ new_refund(
130
+ route,
131
+ method_url,
132
+ params.merge(:charge => charge[:id]),
133
+ headers
107
134
  )
108
- add_refund_to_charge(refund, charge)
109
- charge
110
- end
111
-
112
- def create_refund(route, method_url, params, headers)
113
- charge = get_charge(route, method_url, params, headers)
114
-
115
- refund = Data.mock_refund params.merge(
116
- :balance_transaction => new_balance_transaction('txn'),
117
- :id => new_id('re'),
118
- :charge => charge[:id]
119
- )
120
- add_refund_to_charge(refund, charge)
121
- refund
122
135
  end
123
136
 
124
137
  private
@@ -129,9 +142,11 @@ module StripeMock
129
142
  elsif params[:currency].nil?
130
143
  require_param(:currency)
131
144
  elsif non_integer_charge_amount?(params)
132
- raise Stripe::InvalidRequestError.new("Invalid integer: #{params[:amount]}", 'amount', 400)
145
+ raise Stripe::InvalidRequestError.new("Invalid integer: #{params[:amount]}", 'amount', http_status: 400)
133
146
  elsif non_positive_charge_amount?(params)
134
- raise Stripe::InvalidRequestError.new('Invalid positive integer', 'amount', 400)
147
+ raise Stripe::InvalidRequestError.new('Invalid positive integer', 'amount', http_status: 400)
148
+ elsif params[:source].nil? && params[:customer].nil?
149
+ raise Stripe::InvalidRequestError.new('Must provide source or customer.', http_status: nil)
135
150
  end
136
151
  end
137
152
 
@@ -143,12 +158,8 @@ module StripeMock
143
158
  params[:amount] && params[:amount] < 1
144
159
  end
145
160
 
146
- def require_param(param)
147
- raise Stripe::InvalidRequestError.new("Missing required param: #{param}", param.to_s, 400)
148
- end
149
-
150
161
  def allowed_params(params)
151
- allowed = [:description, :metadata, :receipt_email, :fraud_details, :shipping]
162
+ allowed = [:description, :metadata, :receipt_email, :fraud_details, :shipping, :destination]
152
163
 
153
164
  # This is a workaround for the way the Stripe API sends params even when they aren't modified.
154
165
  # Stipe will include those params even when they aren't modified.
@@ -0,0 +1,22 @@
1
+ module StripeMock
2
+ module RequestHandlers
3
+ module CountrySpec
4
+
5
+ def CountrySpec.included(klass)
6
+ klass.add_handler 'get /v1/country_specs/(.*)', :retrieve_country_spec
7
+ end
8
+
9
+ def retrieve_country_spec(route, method_url, params, headers)
10
+ route =~ method_url
11
+
12
+ unless ["AT", "AU", "BE", "CA", "DE", "DK", "ES", "FI", "FR", "GB", "IE", "IT", "JP", "LU", "NL", "NO", "SE", "SG", "US"].include?($1)
13
+ raise Stripe::InvalidRequestError.new("#{$1} is not currently supported by Stripe.", $1.to_s)
14
+ end
15
+
16
+ country_spec[$1] ||= Data.mock_country_spec($1)
17
+
18
+ assert_existence :country_spec, $1, country_spec[$1]
19
+ end
20
+ end
21
+ end
22
+ end
@@ -11,18 +11,19 @@ module StripeMock
11
11
 
12
12
  def new_coupon(route, method_url, params, headers)
13
13
  params[:id] ||= new_id('coupon')
14
- raise Stripe::InvalidRequestError.new('Missing required param: duration', 'coupon', 400) unless params[:duration]
15
- coupons[ params[:id] ] = Data.mock_coupon(params)
14
+ raise Stripe::InvalidRequestError.new('Missing required param: duration', 'coupon', http_status: 400) unless params[:duration]
15
+ raise Stripe::InvalidRequestError.new('You must pass currency when passing amount_off', 'coupon', http_status: 400) if params[:amount_off] && !params[:currency]
16
+ coupons[ params[:id] ] = Data.mock_coupon({amount_off: nil, percent_off:nil}.merge(params))
16
17
  end
17
18
 
18
19
  def get_coupon(route, method_url, params, headers)
19
20
  route =~ method_url
20
- assert_existence :id, $1, coupons[$1]
21
+ assert_existence :coupon, $1, coupons[$1]
21
22
  end
22
23
 
23
24
  def delete_coupon(route, method_url, params, headers)
24
25
  route =~ method_url
25
- assert_existence :id, $1, coupons.delete($1)
26
+ assert_existence :coupon, $1, coupons.delete($1)
26
27
  end
27
28
 
28
29
  def list_coupons(route, method_url, params, headers)
@@ -4,10 +4,11 @@ module StripeMock
4
4
 
5
5
  def Customers.included(klass)
6
6
  klass.add_handler 'post /v1/customers', :new_customer
7
- klass.add_handler 'post /v1/customers/(.*)', :update_customer
8
- klass.add_handler 'get /v1/customers/(.*)', :get_customer
9
- klass.add_handler 'delete /v1/customers/(.*)', :delete_customer
7
+ klass.add_handler 'post /v1/customers/([^/]*)', :update_customer
8
+ klass.add_handler 'get /v1/customers/([^/]*)', :get_customer
9
+ klass.add_handler 'delete /v1/customers/([^/]*)', :delete_customer
10
10
  klass.add_handler 'get /v1/customers', :list_customers
11
+ klass.add_handler 'delete /v1/customers/([^/]*)/discount', :delete_customer_discount
11
12
  end
12
13
 
13
14
  def new_customer(route, method_url, params, headers)
@@ -18,7 +19,7 @@ module StripeMock
18
19
  new_card =
19
20
  if params[:source].is_a?(Hash)
20
21
  unless params[:source][:object] && params[:source][:number] && params[:source][:exp_month] && params[:source][:exp_year]
21
- raise Stripe::InvalidRequestError.new('You must supply a valid card', nil, 400)
22
+ raise Stripe::InvalidRequestError.new('You must supply a valid card', nil, http_status: 400)
22
23
  end
23
24
  card_from_params(params[:source])
24
25
  else
@@ -35,22 +36,22 @@ module StripeMock
35
36
  plan = assert_existence :plan, plan_id, plans[plan_id]
36
37
 
37
38
  if params[:default_source].nil? && params[:trial_end].nil? && plan[:trial_period_days].nil? && plan[:amount] != 0
38
- raise Stripe::InvalidRequestError.new('You must supply a valid card', nil, 400)
39
+ raise Stripe::InvalidRequestError.new('You must supply a valid card', nil, http_status: 400)
39
40
  end
40
41
 
41
42
  subscription = Data.mock_subscription({ id: new_id('su') })
42
- subscription.merge!(custom_subscription_params(plan, customers[ params[:id] ], params))
43
+ subscription = resolve_subscription_changes(subscription, [plan], customers[ params[:id] ], params)
43
44
  add_subscription_to_customer(customers[ params[:id] ], subscription)
44
45
  subscriptions[subscription[:id]] = subscription
45
46
  elsif params[:trial_end]
46
- raise Stripe::InvalidRequestError.new('Received unknown parameter: trial_end', nil, 400)
47
+ raise Stripe::InvalidRequestError.new('Received unknown parameter: trial_end', nil, http_status: 400)
47
48
  end
48
49
 
49
50
  if params[:coupon]
50
51
  coupon = coupons[ params[:coupon] ]
51
52
  assert_existence :coupon, params[:coupon], coupon
52
53
 
53
- add_coupon_to_customer(customers[params[:id]], coupon)
54
+ add_coupon_to_object(customers[params[:id]], coupon)
54
55
  end
55
56
 
56
57
  customers[ params[:id] ]
@@ -77,7 +78,7 @@ module StripeMock
77
78
  new_card = get_card_or_bank_by_token(params.delete(:source))
78
79
  elsif params[:source].is_a?(Hash)
79
80
  unless params[:source][:object] && params[:source][:number] && params[:source][:exp_month] && params[:source][:exp_year]
80
- raise Stripe::InvalidRequestError.new('You must supply a valid card', nil, 400)
81
+ raise Stripe::InvalidRequestError.new('You must supply a valid card', nil, http_status: 400)
81
82
  end
82
83
  new_card = card_from_params(params.delete(:source))
83
84
  end
@@ -89,7 +90,7 @@ module StripeMock
89
90
  coupon = coupons[ params[:coupon] ]
90
91
  assert_existence :coupon, params[:coupon], coupon
91
92
 
92
- add_coupon_to_customer(cus, coupon)
93
+ add_coupon_to_object(cus, coupon)
93
94
  end
94
95
 
95
96
  cus
@@ -107,13 +108,30 @@ module StripeMock
107
108
 
108
109
  def get_customer(route, method_url, params, headers)
109
110
  route =~ method_url
110
- assert_existence :customer, $1, customers[$1]
111
+ customer = assert_existence :customer, $1, customers[$1]
112
+
113
+ customer = customer.clone
114
+ if params[:expand] == ['default_source']
115
+ customer[:default_source] = customer[:sources][:data].detect do |source|
116
+ source[:id] == customer[:default_source]
117
+ end
118
+ end
119
+
120
+ customer
111
121
  end
112
122
 
113
123
  def list_customers(route, method_url, params, headers)
114
124
  Data.mock_list_object(customers.values, params)
115
125
  end
116
126
 
127
+ def delete_customer_discount(route, method_url, params, headers)
128
+ route =~ method_url
129
+ cus = assert_existence :customer, $1, customers[$1]
130
+
131
+ cus[:discount] = nil
132
+
133
+ cus
134
+ end
117
135
  end
118
136
  end
119
137
  end
@@ -0,0 +1,13 @@
1
+ module StripeMock
2
+ module RequestHandlers
3
+ module EphemeralKey
4
+ def self.included(klass)
5
+ klass.add_handler 'post /v1/ephemeral_keys', :create_ephemeral_key
6
+ end
7
+
8
+ def create_ephemeral_key(route, method_url, params, headers)
9
+ Data.mock_ephemeral_key(params)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,55 @@
1
+ module StripeMock
2
+ module RequestHandlers
3
+ module ExternalAccounts
4
+
5
+ def ExternalAccounts.included(klass)
6
+ klass.add_handler 'get /v1/accounts/(.*)/external_accounts', :retrieve_external_accounts
7
+ klass.add_handler 'post /v1/accounts/(.*)/external_accounts', :create_external_account
8
+ klass.add_handler 'post /v1/accounts/(.*)/external_accounts/(.*)/verify', :verify_external_account
9
+ klass.add_handler 'get /v1/accounts/(.*)/external_accounts/(.*)', :retrieve_external_account
10
+ klass.add_handler 'delete /v1/accounts/(.*)/external_accounts/(.*)', :delete_external_account
11
+ klass.add_handler 'post /v1/accounts/(.*)/external_accounts/(.*)', :update_external_account
12
+ end
13
+
14
+ def create_external_account(route, method_url, params, headers)
15
+ route =~ method_url
16
+ add_external_account_to(:account, $1, params, accounts)
17
+ end
18
+
19
+ def retrieve_external_accounts(route, method_url, params, headers)
20
+ route =~ method_url
21
+ retrieve_object_cards(:account, $1, accounts)
22
+ end
23
+
24
+ def retrieve_external_account(route, method_url, params, headers)
25
+ route =~ method_url
26
+ account = assert_existence :account, $1, accounts[$1]
27
+
28
+ assert_existence :card, $2, get_card(account, $2)
29
+ end
30
+
31
+ def delete_external_account(route, method_url, params, headers)
32
+ route =~ method_url
33
+ delete_card_from(:account, $1, $2, accounts)
34
+ end
35
+
36
+ def update_external_account(route, method_url, params, headers)
37
+ route =~ method_url
38
+ account = assert_existence :account, $1, accounts[$1]
39
+
40
+ card = assert_existence :card, $2, get_card(account, $2)
41
+ card.merge!(params)
42
+ card
43
+ end
44
+
45
+ def verify_external_account(route, method_url, params, headers)
46
+ route =~ method_url
47
+ account = assert_existence :account, $1, accounts[$1]
48
+
49
+ external_account = assert_existence :bank_account, $2, verify_bank_account(account, $2)
50
+ external_account
51
+ end
52
+
53
+ end
54
+ end
55
+ end