stripe-ruby-mock 2.5.8 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }