stripe-ruby-mock 2.5.8 → 3.0.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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/.travis.yml +6 -6
  4. data/README.md +8 -8
  5. data/lib/stripe_mock.rb +3 -0
  6. data/lib/stripe_mock/api/errors.rb +31 -28
  7. data/lib/stripe_mock/api/webhooks.rb +3 -0
  8. data/lib/stripe_mock/data.rb +140 -13
  9. data/lib/stripe_mock/instance.rb +10 -3
  10. data/lib/stripe_mock/request_handlers/balance_transactions.rb +2 -2
  11. data/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb +1 -1
  12. data/lib/stripe_mock/request_handlers/helpers/token_helpers.rb +1 -1
  13. data/lib/stripe_mock/request_handlers/invoices.rb +6 -1
  14. data/lib/stripe_mock/request_handlers/payment_intents.rb +175 -0
  15. data/lib/stripe_mock/request_handlers/payment_methods.rb +138 -0
  16. data/lib/stripe_mock/request_handlers/products.rb +1 -0
  17. data/lib/stripe_mock/request_handlers/setup_intents.rb +93 -0
  18. data/lib/stripe_mock/request_handlers/subscriptions.rb +17 -4
  19. data/lib/stripe_mock/request_handlers/validators/param_validators.rb +95 -9
  20. data/lib/stripe_mock/test_strategies/base.rb +42 -8
  21. data/lib/stripe_mock/test_strategies/live.rb +23 -12
  22. data/lib/stripe_mock/test_strategies/mock.rb +6 -2
  23. data/lib/stripe_mock/version.rb +1 -1
  24. data/lib/stripe_mock/webhook_fixtures/charge.failed.json +166 -38
  25. data/lib/stripe_mock/webhook_fixtures/customer.created.json +1 -0
  26. data/lib/stripe_mock/webhook_fixtures/customer.updated.json +1 -0
  27. data/lib/stripe_mock/webhook_fixtures/product.created.json +34 -0
  28. data/lib/stripe_mock/webhook_fixtures/product.deleted.json +34 -0
  29. data/lib/stripe_mock/webhook_fixtures/product.updated.json +38 -0
  30. data/spec/instance_spec.rb +6 -6
  31. data/spec/server_spec.rb +2 -1
  32. data/spec/shared_stripe_examples/account_examples.rb +1 -1
  33. data/spec/shared_stripe_examples/balance_transaction_examples.rb +3 -3
  34. data/spec/shared_stripe_examples/bank_examples.rb +3 -3
  35. data/spec/shared_stripe_examples/card_examples.rb +4 -4
  36. data/spec/shared_stripe_examples/charge_examples.rb +9 -22
  37. data/spec/shared_stripe_examples/coupon_examples.rb +1 -1
  38. data/spec/shared_stripe_examples/customer_examples.rb +44 -30
  39. data/spec/shared_stripe_examples/dispute_examples.rb +2 -2
  40. data/spec/shared_stripe_examples/error_mock_examples.rb +8 -7
  41. data/spec/shared_stripe_examples/external_account_examples.rb +3 -3
  42. data/spec/shared_stripe_examples/invoice_examples.rb +41 -39
  43. data/spec/shared_stripe_examples/invoice_item_examples.rb +1 -1
  44. data/spec/shared_stripe_examples/payment_intent_examples.rb +137 -0
  45. data/spec/shared_stripe_examples/payment_method_examples.rb +185 -0
  46. data/spec/shared_stripe_examples/payout_examples.rb +2 -2
  47. data/spec/shared_stripe_examples/plan_examples.rb +135 -92
  48. data/spec/shared_stripe_examples/product_examples.rb +155 -0
  49. data/spec/shared_stripe_examples/refund_examples.rb +25 -21
  50. data/spec/shared_stripe_examples/setup_intent_examples.rb +68 -0
  51. data/spec/shared_stripe_examples/subscription_examples.rb +281 -310
  52. data/spec/shared_stripe_examples/subscription_items_examples.rb +3 -2
  53. data/spec/shared_stripe_examples/transfer_examples.rb +6 -6
  54. data/spec/shared_stripe_examples/webhook_event_examples.rb +11 -11
  55. data/spec/spec_helper.rb +3 -4
  56. data/spec/stripe_mock_spec.rb +2 -2
  57. data/spec/support/shared_contexts/stripe_validator_spec.rb +8 -0
  58. data/spec/support/stripe_examples.rb +3 -0
  59. data/stripe-ruby-mock.gemspec +2 -2
  60. metadata +47 -27
  61. data/spec/shared_stripe_examples/product_example.rb +0 -65
@@ -102,7 +102,7 @@ module StripeMock
102
102
  customer[:default_source] = new_card[:id]
103
103
  end
104
104
 
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)
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)
106
106
  unknown_params = params.keys - allowed_params.map(&:to_sym)
107
107
  if unknown_params.length > 0
108
108
  raise Stripe::InvalidRequestError.new("Received unknown parameter: #{unknown_params.join}", unknown_params.first.to_s, http_status: 400)
@@ -133,6 +133,11 @@ module StripeMock
133
133
  end
134
134
  end
135
135
 
136
+ if params[:cancel_at_period_end]
137
+ subscription[:cancel_at_period_end] = true
138
+ subscription[:canceled_at] = Time.now.utc.to_i
139
+ end
140
+
136
141
  subscriptions[subscription[:id]] = subscription
137
142
  add_subscription_to_customer(customer, subscription)
138
143
 
@@ -191,17 +196,24 @@ module StripeMock
191
196
  raise Stripe::InvalidRequestError.new("No such coupon: #{coupon_id}", 'coupon', http_status: 400)
192
197
  end
193
198
  end
194
- verify_card_present(customer, subscription_plans.first, subscription)
195
199
 
196
- if subscription[:cancel_at_period_end]
200
+ if params[:cancel_at_period_end]
201
+ subscription[:cancel_at_period_end] = true
202
+ subscription[:canceled_at] = Time.now.utc.to_i
203
+ elsif params.has_key?(:cancel_at_period_end)
197
204
  subscription[:cancel_at_period_end] = false
198
205
  subscription[:canceled_at] = nil
199
206
  end
200
207
 
201
208
  params[:current_period_start] = subscription[:current_period_start]
202
209
  params[:trial_end] = params[:trial_end] || subscription[:trial_end]
210
+
211
+ plan_amount_was = subscription.dig(:plan, :amount)
212
+
203
213
  subscription = resolve_subscription_changes(subscription, subscription_plans, customer, params)
204
214
 
215
+ verify_card_present(customer, subscription_plans.first, subscription, params) if plan_amount_was == 0 && subscription.dig(:plan, :amount) && subscription.dig(:plan, :amount) > 0
216
+
205
217
  # delete the old subscription, replace with the new subscription
206
218
  customer[:subscriptions][:data].reject! { |sub| sub[:id] == subscription[:id] }
207
219
  customer[:subscriptions][:data] << subscription
@@ -261,6 +273,7 @@ module StripeMock
261
273
  # 3) has billing set to send invoice
262
274
  def verify_card_present(customer, plan, subscription, params={})
263
275
  return if customer[:default_source]
276
+ return if customer[:invoice_settings][:default_payment_method]
264
277
  return if customer[:trial_end]
265
278
  return if params[:trial_end]
266
279
 
@@ -281,7 +294,7 @@ module StripeMock
281
294
 
282
295
  return if params[:billing] == 'send_invoice'
283
296
 
284
- raise Stripe::InvalidRequestError.new('You must supply a valid card xoxo', nil, http_status: 400)
297
+ raise Stripe::InvalidRequestError.new('This customer has no attached payment source', nil, http_status: 400)
285
298
  end
286
299
 
287
300
  def verify_active_status(subscription)
@@ -2,26 +2,112 @@ module StripeMock
2
2
  module RequestHandlers
3
3
  module ParamValidators
4
4
 
5
- def validate_create_plan_params(params)
5
+ def already_exists_message(obj_class)
6
+ "#{obj_class.to_s.split("::").last} already exists."
7
+ end
8
+
9
+ def not_found_message(obj_class, obj_id)
10
+ "No such #{obj_class.to_s.split("::").last.downcase}: #{obj_id}"
11
+ end
12
+
13
+ def missing_param_message(attr_name)
14
+ "Missing required param: #{attr_name}."
15
+ end
16
+
17
+ def invalid_integer_message(my_val)
18
+ "Invalid integer: #{my_val}"
19
+ end
20
+
21
+ #
22
+ # ProductValidator
23
+ #
24
+
25
+
26
+ def validate_create_product_params(params)
6
27
  params[:id] = params[:id].to_s
28
+ @base_strategy.create_product_params.keys.reject{ |k,_| k == :id }.each do |k|
29
+ raise Stripe::InvalidRequestError.new(missing_param_message(k), k) if params[k].nil?
30
+ end
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
+ if products[ params[:id] ]
37
+ raise Stripe::InvalidRequestError.new(already_exists_message(Stripe::Product), :id)
38
+ end
39
+ end
40
+
41
+ #
42
+ # PlanValidator
43
+ #
7
44
 
8
- @base_strategy.create_plan_params.keys.each do |name|
45
+ def missing_plan_amount_message
46
+ "Plans require an `amount` parameter to be set."
47
+ end
48
+
49
+ SUPPORTED_PLAN_INTERVALS = ["month", "year", "week", "day"]
50
+
51
+ def invalid_plan_interval_message
52
+ "Invalid interval: must be one of day, month, week, or year"
53
+ end
54
+
55
+ SUPPORTED_CURRENCIES = [
56
+ "usd", "aed", "afn", "all", "amd", "ang", "aoa", "ars", "aud", "awg", "azn", "bam", "bbd", "bdt", "bgn",
57
+ "bif", "bmd", "bnd", "bob", "brl", "bsd", "bwp", "bzd", "cad", "cdf", "chf", "clp", "cny", "cop", "crc",
58
+ "cve", "czk", "djf", "dkk", "dop", "dzd", "egp", "etb", "eur", "fjd", "fkp", "gbp", "gel", "gip", "gmd",
59
+ "gnf", "gtq", "gyd", "hkd", "hnl", "hrk", "htg", "huf", "idr", "ils", "inr", "isk", "jmd", "jpy", "kes",
60
+ "kgs", "khr", "kmf", "krw", "kyd", "kzt", "lak", "lbp", "lkr", "lrd", "lsl", "mad", "mdl", "mga", "mkd",
61
+ "mmk", "mnt", "mop", "mro", "mur", "mvr", "mwk", "mxn", "myr", "mzn", "nad", "ngn", "nio", "nok", "npr",
62
+ "nzd", "pab", "pen", "pgk", "php", "pkr", "pln", "pyg", "qar", "ron", "rsd", "rub", "rwf", "sar", "sbd",
63
+ "scr", "sek", "sgd", "shp", "sll", "sos", "srd", "std", "szl", "thb", "tjs", "top", "try", "ttd", "twd",
64
+ "tzs", "uah", "ugx", "uyu", "uzs", "vnd", "vuv", "wst", "xaf", "xcd", "xof", "xpf", "yer", "zar", "zmw",
65
+ "eek", "lvl", "svc", "vef", "ltl"
66
+ ]
67
+
68
+ def invalid_currency_message(my_val)
69
+ "Invalid currency: #{my_val.downcase}. Stripe currently supports these currencies: #{SUPPORTED_CURRENCIES.join(", ")}"
70
+ end
71
+
72
+ def validate_create_plan_params(params)
73
+ plan_id = params[:id].to_s
74
+ product_id = params[:product]
75
+
76
+ @base_strategy.create_plan_params.keys.each do |attr_name|
9
77
  message =
10
- if name == :amount
11
- "Plans require an `#{name}` parameter to be set."
78
+ if attr_name == :amount
79
+ "Plans require an `#{attr_name}` parameter to be set."
12
80
  else
13
- "Missing required param: #{name}."
81
+ "Missing required param: #{attr_name}."
14
82
  end
15
- raise Stripe::InvalidRequestError.new(message, name) if params[name].nil?
83
+ raise Stripe::InvalidRequestError.new(message, attr_name) if params[attr_name].nil?
84
+ end
85
+
86
+ if plans[plan_id]
87
+ message = already_exists_message(Stripe::Plan)
88
+ raise Stripe::InvalidRequestError.new(message, :id)
89
+ end
90
+
91
+ unless products[product_id]
92
+ message = not_found_message(Stripe::Product, product_id)
93
+ raise Stripe::InvalidRequestError.new(message, :product)
94
+ end
95
+
96
+ unless SUPPORTED_PLAN_INTERVALS.include?(params[:interval])
97
+ message = invalid_plan_interval_message
98
+ raise Stripe::InvalidRequestError.new(message, :interval)
16
99
  end
17
100
 
18
- if plans[ params[:id] ]
19
- raise Stripe::InvalidRequestError.new("Plan already exists.", :id)
101
+ unless SUPPORTED_CURRENCIES.include?(params[:currency])
102
+ message = invalid_currency_message(params[:currency])
103
+ raise Stripe::InvalidRequestError.new(message, :currency)
20
104
  end
21
105
 
22
106
  unless params[:amount].integer?
23
- raise Stripe::InvalidRequestError.new("Invalid integer: #{params[:amount]}", :amount)
107
+ message = invalid_integer_message(params[:amount])
108
+ raise Stripe::InvalidRequestError.new(message, :amount)
24
109
  end
110
+
25
111
  end
26
112
 
27
113
  def require_param(param_name)
@@ -2,19 +2,51 @@ module StripeMock
2
2
  module TestStrategies
3
3
  class Base
4
4
 
5
+ def list_products(limit)
6
+ Stripe::Product.list(limit: limit)
7
+ end
8
+
9
+ def create_product(params = {})
10
+ Stripe::Product.create create_product_params(params)
11
+ end
12
+
13
+ def create_product_params(params={})
14
+ {
15
+ :id => 'stripe_mock_default_product_id',
16
+ :name => 'Default Product',
17
+ :type => 'service'
18
+ }.merge(params)
19
+ end
20
+
21
+ def retrieve_product(product_id)
22
+ Stripe::Product.retrieve(product_id)
23
+ end
24
+
25
+
26
+ def list_plans(limit)
27
+ Stripe::Plan.list(limit: limit)
28
+ end
29
+
30
+ def create_plan(params={})
31
+ Stripe::Plan.create create_plan_params(params)
32
+ end
33
+
5
34
  def create_plan_params(params={})
6
- currency = params[:currency] || StripeMock.default_currency
7
35
  {
8
36
  :id => 'stripe_mock_default_plan_id',
9
- :product => {
10
- :name => 'StripeMock Default Plan ID'
11
- },
12
- :amount => 1337,
13
- :currency => currency,
14
- :interval => 'month'
37
+ :interval => 'month',
38
+ :currency => StripeMock.default_currency,
39
+ :product => nil, # need to override yourself to pass validations
40
+ :amount => 1337
15
41
  }.merge(params)
16
42
  end
17
43
 
44
+
45
+ def list_subscriptions(limit)
46
+ Stripe::Subscription.list(limit: limit)
47
+ end
48
+
49
+
18
50
  def generate_card_token(card_params={})
19
51
  card_data = { :number => "4242424242424242", :exp_month => 9, :exp_year => (Time.now.year + 5), :cvc => "999", :tokenization_method => nil }
20
52
  card = StripeMock::Util.card_merge(card_data, card_params)
@@ -40,6 +72,7 @@ module StripeMock
40
72
  stripe_token.id
41
73
  end
42
74
 
75
+
43
76
  def create_coupon_params(params = {})
44
77
  currency = params[:currency] || StripeMock.default_currency
45
78
  {
@@ -69,13 +102,14 @@ module StripeMock
69
102
  end
70
103
 
71
104
  def delete_all_coupons
72
- coupons = Stripe::Coupon.all
105
+ coupons = Stripe::Coupon.list
73
106
  coupons.data.map(&:delete) if coupons.data.count > 0
74
107
  end
75
108
 
76
109
  def prepare_card_error
77
110
  StripeMock.prepare_card_error(:card_error, :new_customer) if StripeMock.state == 'local'
78
111
  end
112
+
79
113
  end
80
114
  end
81
115
  end
@@ -2,6 +2,21 @@ module StripeMock
2
2
  module TestStrategies
3
3
  class Live < Base
4
4
 
5
+ def create_product(params={})
6
+ params = create_product_params(params)
7
+ raise "create_product requires an :id" if params[:id].nil?
8
+ delete_product(params[:id])
9
+ Stripe::Product.create params
10
+ end
11
+
12
+ def delete_product(product_id)
13
+ product = Stripe::Product.retrieve(product_id)
14
+ Stripe::Plan.list(product: product_id).each(&:delete) if product.type == 'service'
15
+ product.delete
16
+ rescue Stripe::StripeError => e
17
+ # do nothing
18
+ end
19
+
5
20
  def create_plan(params={})
6
21
  raise "create_plan requires an :id" if params[:id].nil?
7
22
  delete_plan(params[:id])
@@ -9,12 +24,10 @@ module StripeMock
9
24
  end
10
25
 
11
26
  def delete_plan(plan_id)
12
- begin
13
- plan = Stripe::Plan.retrieve(plan_id)
14
- plan.delete
15
- rescue Stripe::StripeError => e
16
- # Do nothing; we just want to make sure this plan ceases to exists
17
- end
27
+ plan = Stripe::Plan.retrieve(plan_id)
28
+ plan.delete
29
+ rescue Stripe::StripeError => e
30
+ # do nothing
18
31
  end
19
32
 
20
33
  def create_coupon(params={})
@@ -23,12 +36,10 @@ module StripeMock
23
36
  end
24
37
 
25
38
  def delete_coupon(id)
26
- begin
27
- coupon = Stripe::Coupon.retrieve(id)
28
- coupon.delete
29
- rescue Stripe::StripeError
30
- # do nothing
31
- end
39
+ coupon = Stripe::Coupon.retrieve(id)
40
+ coupon.delete
41
+ rescue Stripe::StripeError
42
+ # do nothing
32
43
  end
33
44
 
34
45
  def upsert_stripe_object(object, attributes)
@@ -2,8 +2,12 @@ module StripeMock
2
2
  module TestStrategies
3
3
  class Mock < Base
4
4
 
5
- def create_plan(params={})
6
- Stripe::Plan.create create_plan_params(params)
5
+ def delete_product(product_id)
6
+ if StripeMock.state == 'remote'
7
+ StripeMock.client.destroy_resource('products', product_id)
8
+ elsif StripeMock.state == 'local'
9
+ StripeMock.instance.products.delete(product_id)
10
+ end
7
11
  end
8
12
 
9
13
  def delete_plan(plan_id)
@@ -1,4 +1,4 @@
1
1
  module StripeMock
2
2
  # stripe-ruby-mock version
3
- VERSION = "2.5.8"
3
+ VERSION = "3.0.0"
4
4
  end
@@ -4,53 +4,181 @@
4
4
  "id": "evt_00000000000000",
5
5
  "type": "charge.failed",
6
6
  "object": "event",
7
+ "request": null,
8
+ "pending_webhooks": 1,
9
+ "api_version": "2018-02-28",
7
10
  "data": {
8
11
  "object": {
9
12
  "id": "ch_00000000000000",
10
13
  "object": "charge",
11
- "created": 1380933505,
14
+ "amount": 100,
15
+ "amount_refunded": 0,
16
+ "application": null,
17
+ "application_fee": null,
18
+ "application_fee_amount": null,
19
+ "balance_transaction": "txn_00000000000000",
20
+ "billing_details": {
21
+ "address": {
22
+ "city": null,
23
+ "country": null,
24
+ "line1": null,
25
+ "line2": null,
26
+ "postal_code": null,
27
+ "state": null
28
+ },
29
+ "email": null,
30
+ "name": "Jenny Rosen",
31
+ "phone": null
32
+ },
33
+ "captured": false,
34
+ "created": 1572389205,
35
+ "currency": "cad",
36
+ "customer": null,
37
+ "description": "My First Test Charge (created for API docs)",
38
+ "dispute": null,
39
+ "failure_code": null,
40
+ "failure_message": null,
41
+ "fraud_details": {},
42
+ "invoice": null,
12
43
  "livemode": false,
44
+ "metadata": {},
45
+ "on_behalf_of": null,
46
+ "order": null,
47
+ "outcome": null,
13
48
  "paid": false,
14
- "amount": 1000,
15
- "currency": "usd",
16
- "refunded": false,
17
- "source": {
18
- "id": "cc_00000000000000",
19
- "object": "card",
20
- "last4": "4242",
21
- "type": "Visa",
22
- "brand": "Visa",
23
- "funding": "credit",
24
- "exp_month": 12,
25
- "exp_year": 2013,
26
- "fingerprint": "wXWJT135mEK107G8",
27
- "customer": "cus_00000000000000",
28
- "country": "US",
29
- "name": "Actual Nothing",
30
- "address_line1": null,
31
- "address_line2": null,
32
- "address_city": null,
33
- "address_state": null,
34
- "address_zip": null,
35
- "address_country": null,
36
- "cvc_check": null,
37
- "address_line1_check": null,
38
- "address_zip_check": null
49
+ "payment_intent": null,
50
+ "payment_method": "card_00000000000000",
51
+ "payment_method_details": {
52
+ "card": {
53
+ "brand": "visa",
54
+ "checks": {
55
+ "address_line1_check": null,
56
+ "address_postal_code_check": null,
57
+ "cvc_check": null
58
+ },
59
+ "country": "US",
60
+ "exp_month": 8,
61
+ "exp_year": 2019,
62
+ "fingerprint": "C8aRpBae2T8GeJcn",
63
+ "funding": "credit",
64
+ "installments": null,
65
+ "last4": "4242",
66
+ "network": "visa",
67
+ "three_d_secure": null,
68
+ "wallet": null
69
+ },
70
+ "type": "card"
39
71
  },
40
- "captured": true,
72
+ "receipt_email": null,
73
+ "receipt_number": null,
74
+ "receipt_url": "https://pay.stripe.com/receipts/acct_1C0lZEHvOcc4e36o/ch_1FZ3SbHsssvOcc4e36o87NHFK7i/rcpt_G5DuglJFhskXjsEDnLzxF6ESuGQe0Qj",
75
+ "refunded": false,
41
76
  "refunds": {
42
-
77
+ "object": "list",
78
+ "data": [],
79
+ "has_more": false,
80
+ "url": "/v1/charges/ch_1FZ3SbHvOcc4e36o87NHFK7i/refunds"
43
81
  },
44
- "balance_transaction": "txn_00000000000000",
45
- "failure_message": null,
46
- "failure_code": null,
47
- "amount_refunded": 0,
48
- "customer": "cus_00000000000000",
49
- "invoice": "in_00000000000000",
50
- "description": null,
51
- "dispute": null,
52
- "metadata": {
82
+ "review": null,
83
+ "shipping": null,
84
+ "source_transfer": null,
85
+ "statement_descriptor": null,
86
+ "statement_descriptor_suffix": null,
87
+ "status": "failed",
88
+ "transfer_data": null,
89
+ "transfer_group": null
90
+ }
91
+ },
92
+ "webhook": {
93
+ "created": 1326853478,
94
+ "livemode": false,
95
+ "id": "evt_00000000000000",
96
+ "type": "charge.failed",
97
+ "object": "event",
98
+ "request": null,
99
+ "pending_webhooks": 1,
100
+ "api_version": "2018-02-28",
101
+ "data": {
102
+ "object": {
103
+ "id": "ch_00000000000000",
104
+ "object": "charge",
105
+ "amount": 100,
106
+ "amount_refunded": 0,
107
+ "application": null,
108
+ "application_fee": null,
109
+ "application_fee_amount": null,
110
+ "balance_transaction": "txn_00000000000000",
111
+ "billing_details": {
112
+ "address": {
113
+ "city": null,
114
+ "country": null,
115
+ "line1": null,
116
+ "line2": null,
117
+ "postal_code": null,
118
+ "state": null
119
+ },
120
+ "email": null,
121
+ "name": "Jenny Rosen",
122
+ "phone": null
123
+ },
124
+ "captured": false,
125
+ "created": 1572389205,
126
+ "currency": "cad",
127
+ "customer": null,
128
+ "description": "My First Test Charge (created for API docs)",
129
+ "dispute": null,
130
+ "failure_code": null,
131
+ "failure_message": null,
132
+ "fraud_details": {},
133
+ "invoice": null,
134
+ "livemode": false,
135
+ "metadata": {},
136
+ "on_behalf_of": null,
137
+ "order": null,
138
+ "outcome": null,
139
+ "paid": false,
140
+ "payment_intent": null,
141
+ "payment_method": "card_00000000000000",
142
+ "payment_method_details": {
143
+ "card": {
144
+ "brand": "visa",
145
+ "checks": {
146
+ "address_line1_check": null,
147
+ "address_postal_code_check": null,
148
+ "cvc_check": null
149
+ },
150
+ "country": "US",
151
+ "exp_month": 8,
152
+ "exp_year": 2019,
153
+ "fingerprint": "C8aRpBae2T8GeJcn",
154
+ "funding": "credit",
155
+ "installments": null,
156
+ "last4": "4242",
157
+ "network": "visa",
158
+ "three_d_secure": null,
159
+ "wallet": null
160
+ },
161
+ "type": "card"
162
+ },
163
+ "receipt_email": null,
164
+ "receipt_number": null,
165
+ "receipt_url": "https://pay.stripe.com/receipts/acct_1C0lZEHvOcc4e36o/ch_1FZ3SbHvOcc4sssse36o87NHFK7i/rcpt_G5DuglJFhskXjsEDnLzxF6ESuGQe0Qj",
166
+ "refunded": false,
167
+ "refunds": {
168
+ "object": "list",
169
+ "data": [],
170
+ "has_more": false,
171
+ "url": "/v1/charges/ch_1FZ3SbHvOccssssss4e36o87NHFK7i/refunds"
172
+ },
173
+ "review": null,
174
+ "shipping": null,
175
+ "source_transfer": null,
176
+ "statement_descriptor": null,
177
+ "statement_descriptor_suffix": null,
178
+ "status": "failed",
179
+ "transfer_data": null,
180
+ "transfer_group": null
53
181
  }
54
182
  }
55
183
  }
56
- }
184
+ }