stripe-ruby-mock 2.3.1 → 2.5.8

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 (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