stripe-ruby-mock 2.5.4 → 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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -1
  3. data/README.md +1 -1
  4. data/lib/stripe_mock.rb +4 -0
  5. data/lib/stripe_mock/api/webhooks.rb +2 -0
  6. data/lib/stripe_mock/data.rb +78 -17
  7. data/lib/stripe_mock/data/list.rb +7 -2
  8. data/lib/stripe_mock/instance.rb +37 -3
  9. data/lib/stripe_mock/request_handlers/accounts.rb +16 -0
  10. data/lib/stripe_mock/request_handlers/charges.rb +7 -8
  11. data/lib/stripe_mock/request_handlers/customers.rb +2 -2
  12. data/lib/stripe_mock/request_handlers/ephemeral_key.rb +13 -0
  13. data/lib/stripe_mock/request_handlers/helpers/card_helpers.rb +1 -0
  14. data/lib/stripe_mock/request_handlers/helpers/coupon_helpers.rb +10 -11
  15. data/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb +20 -2
  16. data/lib/stripe_mock/request_handlers/invoices.rb +1 -1
  17. data/lib/stripe_mock/request_handlers/products.rb +43 -0
  18. data/lib/stripe_mock/request_handlers/refunds.rb +6 -3
  19. data/lib/stripe_mock/request_handlers/subscription_items.rb +36 -0
  20. data/lib/stripe_mock/request_handlers/subscriptions.rb +40 -22
  21. data/lib/stripe_mock/request_handlers/tax_rates.rb +36 -0
  22. data/lib/stripe_mock/request_handlers/tokens.rb +2 -2
  23. data/lib/stripe_mock/request_handlers/transfers.rb +10 -4
  24. data/lib/stripe_mock/request_handlers/validators/param_validators.rb +3 -0
  25. data/lib/stripe_mock/test_strategies/base.rb +4 -2
  26. data/lib/stripe_mock/version.rb +1 -1
  27. data/lib/stripe_mock/webhook_fixtures/charge.dispute.funds_reinstated.json +88 -0
  28. data/lib/stripe_mock/webhook_fixtures/charge.dispute.funds_withdrawn.json +88 -0
  29. data/lib/stripe_mock/webhook_fixtures/customer.subscription.created.json +2 -2
  30. data/lib/stripe_mock/webhook_fixtures/customer.subscription.deleted.json +2 -2
  31. data/lib/stripe_mock/webhook_fixtures/customer.subscription.trial_will_end.json +2 -2
  32. data/lib/stripe_mock/webhook_fixtures/customer.subscription.updated.json +3 -3
  33. data/lib/stripe_mock/webhook_fixtures/invoice.created.json +3 -2
  34. data/lib/stripe_mock/webhook_fixtures/invoice.payment_failed.json +1 -1
  35. data/lib/stripe_mock/webhook_fixtures/invoice.payment_succeeded.json +1 -1
  36. data/lib/stripe_mock/webhook_fixtures/invoice.updated.json +3 -2
  37. data/lib/stripe_mock/webhook_fixtures/plan.created.json +1 -1
  38. data/lib/stripe_mock/webhook_fixtures/plan.deleted.json +1 -1
  39. data/lib/stripe_mock/webhook_fixtures/plan.updated.json +1 -1
  40. data/spec/instance_spec.rb +31 -0
  41. data/spec/shared_stripe_examples/account_examples.rb +27 -0
  42. data/spec/shared_stripe_examples/charge_examples.rb +23 -14
  43. data/spec/shared_stripe_examples/customer_examples.rb +11 -1
  44. data/spec/shared_stripe_examples/ephemeral_key_examples.rb +17 -0
  45. data/spec/shared_stripe_examples/invoice_examples.rb +5 -5
  46. data/spec/shared_stripe_examples/plan_examples.rb +19 -4
  47. data/spec/shared_stripe_examples/product_example.rb +65 -0
  48. data/spec/shared_stripe_examples/refund_examples.rb +16 -10
  49. data/spec/shared_stripe_examples/subscription_examples.rb +176 -18
  50. data/spec/shared_stripe_examples/subscription_items_examples.rb +75 -0
  51. data/spec/shared_stripe_examples/tax_rate_examples.rb +42 -0
  52. data/spec/shared_stripe_examples/transfer_examples.rb +61 -30
  53. data/spec/support/stripe_examples.rb +4 -1
  54. metadata +16 -2
@@ -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
@@ -97,6 +97,7 @@ module StripeMock
97
97
  get_bank_by_token(params[:source])
98
98
  end
99
99
  end
100
+ source[:metadata].merge!(params[:metadata]) if params[:metadata]
100
101
  add_source_to_object(type, source, resource)
101
102
  end
102
103
 
@@ -1,18 +1,17 @@
1
1
  module StripeMock
2
2
  module RequestHandlers
3
3
  module Helpers
4
+ def add_coupon_to_object(object, coupon)
5
+ discount_attrs = {}.tap do |attrs|
6
+ attrs[object[:object]] = object[:id]
7
+ attrs[:coupon] = coupon
8
+ attrs[:start] = Time.now.to_i
9
+ attrs[:end] = (DateTime.now >> coupon[:duration_in_months].to_i).to_time.to_i if coupon[:duration] == 'repeating'
10
+ end
4
11
 
5
- def add_coupon_to_customer(customer, coupon)
6
- customer[:discount] = {
7
- coupon: coupon,
8
- customer: customer[:id],
9
- start: Time.now.to_i,
10
- }
11
- customer[:discount][:end] = (DateTime.now >> coupon[:duration_in_months]).to_time.to_i if coupon[:duration].to_sym == :repeating && coupon[:duration_in_months]
12
-
13
- customer
12
+ object[:discount] = Stripe::Discount.construct_from(discount_attrs)
13
+ object
14
14
  end
15
-
16
15
  end
17
16
  end
18
- end
17
+ end
@@ -8,7 +8,17 @@ module StripeMock
8
8
 
9
9
  def resolve_subscription_changes(subscription, plans, customer, options = {})
10
10
  subscription.merge!(custom_subscription_params(plans, customer, options))
11
- subscription[:items][:data] = plans.map { |plan| Data.mock_subscription_item({ plan: plan }) }
11
+ items = options[:items]
12
+ items = items.values if items.respond_to?(:values)
13
+ subscription[:items][:data] = plans.map do |plan|
14
+ if items && items.size == plans.size
15
+ quantity = items &&
16
+ items.detect { |item| item[:plan] == plan[:id] }[:quantity] || 1
17
+ Data.mock_subscription_item({ plan: plan, quantity: quantity })
18
+ else
19
+ Data.mock_subscription_item({ plan: plan })
20
+ end
21
+ end
12
22
  subscription
13
23
  end
14
24
 
@@ -22,7 +32,15 @@ module StripeMock
22
32
  start_time = options[:current_period_start] || now
23
33
  params = { customer: cus[:id], current_period_start: start_time, created: created_time }
24
34
  params.merge!({ :plan => (plans.size == 1 ? plans.first : nil) })
25
- params.merge! options.select {|k,v| k =~ /application_fee_percent|quantity|metadata|tax_percent/}
35
+ keys_to_merge = /application_fee_percent|quantity|metadata|tax_percent|billing|days_until_due/
36
+ params.merge! options.select {|k,v| k =~ keys_to_merge}
37
+
38
+ if options[:cancel_at_period_end] == true
39
+ params.merge!(cancel_at_period_end: true, canceled_at: now)
40
+ elsif options[:cancel_at_period_end] == false
41
+ params.merge!(cancel_at_period_end: false, canceled_at: nil)
42
+ end
43
+
26
44
  # TODO: Implement coupon logic
27
45
 
28
46
  if (((plan && plan[:trial_period_days]) || 0) == 0 && options[:trial_end].nil?) || options[:trial_end] == "now"
@@ -138,7 +138,7 @@ module StripeMock
138
138
  id: new_id('in'),
139
139
  customer: customer[:id],
140
140
  discount: customer[:discount],
141
- date: invoice_date,
141
+ created: invoice_date,
142
142
  starting_balance: customer[:account_balance],
143
143
  subscription: preview_subscription[:id],
144
144
  period_start: prorating ? invoice_date : preview_subscription[:current_period_start],
@@ -0,0 +1,43 @@
1
+ module StripeMock
2
+ module RequestHandlers
3
+ module Products
4
+ def self.included(base)
5
+ base.add_handler 'post /v1/products', :create_product
6
+ base.add_handler 'get /v1/products/(.*)', :retrieve_product
7
+ base.add_handler 'post /v1/products/(.*)', :update_product
8
+ base.add_handler 'get /v1/products', :list_products
9
+ base.add_handler 'delete /v1/products/(.*)', :destroy_product
10
+ end
11
+
12
+ def create_product(_route, _method_url, params, _headers)
13
+ params[:id] ||= new_id('prod')
14
+ products[params[:id]] = Data.mock_product(params)
15
+ end
16
+
17
+ def retrieve_product(route, method_url, _params, _headers)
18
+ id = method_url.match(route).captures.first
19
+ assert_existence :product, id, products[id]
20
+ end
21
+
22
+ def update_product(route, method_url, params, _headers)
23
+ id = method_url.match(route).captures.first
24
+ product = assert_existence :product, id, products[id]
25
+
26
+ product.merge!(params)
27
+ end
28
+
29
+ def list_products(_route, _method_url, params, _headers)
30
+ limit = params[:limit] || 10
31
+ Data.mock_list_object(products.values.take(limit), params)
32
+ end
33
+
34
+ def destroy_product(route, method_url, _params, _headers)
35
+ id = method_url.match(route).captures.first
36
+ assert_existence :product, id, products[id]
37
+
38
+ products.delete(id)
39
+ { id: id, object: 'product', deleted: true }
40
+ end
41
+ end
42
+ end
43
+ end
@@ -10,9 +10,12 @@ module StripeMock
10
10
  end
11
11
 
12
12
  def new_refund(route, method_url, params, headers)
13
- if params[:idempotency_key] && refunds.any?
14
- original_refund = refunds.values.find { |c| c[:idempotency_key] == params[:idempotency_key]}
15
- return refunds[original_refund[:id]] if original_refund
13
+ if headers && headers[:idempotency_key]
14
+ params[:idempotency_key] = headers[:idempotency_key]
15
+ if refunds.any?
16
+ original_refund = refunds.values.find { |c| c[:idempotency_key] == headers[:idempotency_key]}
17
+ return refunds[original_refund[:id]] if original_refund
18
+ end
16
19
  end
17
20
 
18
21
  charge = assert_existence :charge, params[:charge], charges[params[:charge]]
@@ -0,0 +1,36 @@
1
+ module StripeMock
2
+ module RequestHandlers
3
+ module SubscriptionItems
4
+
5
+ def SubscriptionItems.included(klass)
6
+ klass.add_handler 'get /v1/subscription_items', :retrieve_subscription_items
7
+ klass.add_handler 'post /v1/subscription_items/([^/]*)', :update_subscription_item
8
+ klass.add_handler 'post /v1/subscription_items', :create_subscription_items
9
+ end
10
+
11
+ def retrieve_subscription_items(route, method_url, params, headers)
12
+ route =~ method_url
13
+
14
+ require_param(:subscription) unless params[:subscription]
15
+
16
+ Data.mock_list_object(subscriptions_items, params)
17
+ end
18
+
19
+ def create_subscription_items(route, method_url, params, headers)
20
+ params[:id] ||= new_id('si')
21
+
22
+ require_param(:subscription) unless params[:subscription]
23
+ require_param(:plan) unless params[:plan]
24
+
25
+ subscriptions_items[params[:id]] = Data.mock_subscription_item(params.merge(plan: plans[params[:plan]]))
26
+ end
27
+
28
+ def update_subscription_item(route, method_url, params, headers)
29
+ route =~ method_url
30
+
31
+ subscription_item = assert_existence :subscription_item, $1, subscriptions_items[$1]
32
+ subscription_item.merge!(params.merge(plan: plans[params[:plan]]))
33
+ end
34
+ end
35
+ end
36
+ end
@@ -60,7 +60,7 @@ module StripeMock
60
60
  coupon = coupons[coupon_id]
61
61
 
62
62
  if coupon
63
- subscription[:discount] = Stripe::Util.convert_to_stripe_object({ coupon: coupon }, {})
63
+ add_coupon_to_object(subscription, coupon)
64
64
  else
65
65
  raise Stripe::InvalidRequestError.new("No such coupon: #{coupon_id}", 'coupon', http_status: 400)
66
66
  end
@@ -73,6 +73,13 @@ module StripeMock
73
73
  end
74
74
 
75
75
  def create_subscription(route, method_url, params, headers)
76
+ if headers && headers[:idempotency_key]
77
+ if subscriptions.any?
78
+ original_subscription = subscriptions.values.find { |c| c[:idempotency_key] == headers[:idempotency_key]}
79
+ puts original_subscription
80
+ return subscriptions[original_subscription[:id]] if original_subscription
81
+ end
82
+ end
76
83
  route =~ method_url
77
84
 
78
85
  subscription_plans = get_subscription_plans_from_params(params)
@@ -95,7 +102,7 @@ module StripeMock
95
102
  customer[:default_source] = new_card[:id]
96
103
  end
97
104
 
98
- 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)
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)
99
106
  unknown_params = params.keys - allowed_params.map(&:to_sym)
100
107
  if unknown_params.length > 0
101
108
  raise Stripe::InvalidRequestError.new("Received unknown parameter: #{unknown_params.join}", unknown_params.first.to_s, http_status: 400)
@@ -103,6 +110,9 @@ module StripeMock
103
110
 
104
111
  subscription = Data.mock_subscription({ id: (params[:id] || new_id('su')) })
105
112
  subscription = resolve_subscription_changes(subscription, subscription_plans, customer, params)
113
+ if headers[:idempotency_key]
114
+ subscription[:idempotency_key] = headers[:idempotency_key]
115
+ end
106
116
 
107
117
  # Ensure customer has card to charge if plan has no trial and is not free
108
118
  # Note: needs updating for subscriptions with multiple plans
@@ -117,7 +127,7 @@ module StripeMock
117
127
  coupon = coupons[coupon_id]
118
128
 
119
129
  if coupon
120
- subscription[:discount] = Stripe::Util.convert_to_stripe_object({ coupon: coupon }, {})
130
+ add_coupon_to_object(subscription, coupon)
121
131
  else
122
132
  raise Stripe::InvalidRequestError.new("No such coupon: #{coupon_id}", 'coupon', http_status: 400)
123
133
  end
@@ -174,9 +184,9 @@ module StripeMock
174
184
 
175
185
  coupon = coupons[coupon_id]
176
186
  if coupon
177
- subscription[:discount] = Stripe::Util.convert_to_stripe_object({ coupon: coupon }, {})
187
+ add_coupon_to_object(subscription, coupon)
178
188
  elsif coupon_id == ""
179
- subscription[:discount] = Stripe::Util.convert_to_stripe_object(nil, {})
189
+ subscription[:discount] = nil
180
190
  else
181
191
  raise Stripe::InvalidRequestError.new("No such coupon: #{coupon_id}", 'coupon', http_status: 400)
182
192
  end
@@ -189,6 +199,7 @@ module StripeMock
189
199
  end
190
200
 
191
201
  params[:current_period_start] = subscription[:current_period_start]
202
+ params[:trial_end] = params[:trial_end] || subscription[:trial_end]
192
203
  subscription = resolve_subscription_changes(subscription, subscription_plans, customer, params)
193
204
 
194
205
  # delete the old subscription, replace with the new subscription
@@ -244,26 +255,33 @@ module StripeMock
244
255
  plan_ids.map { |plan_id| plans[plan_id] }
245
256
  end
246
257
 
258
+ # Ensure customer has card to charge unless one of the following criterias is met:
259
+ # 1) is in trial
260
+ # 2) is free
261
+ # 3) has billing set to send invoice
247
262
  def verify_card_present(customer, plan, subscription, params={})
248
- if customer[:default_source].nil? && customer[:trial_end].nil? &&
249
- (plan.nil? ||
250
- ((plan[:trial_period_days] || 0) == 0 &&
251
- plan[:amount] != 0 &&
252
- plan[:trial_end].nil?)) &&
253
- params[:trial_end].nil? &&
254
- (subscription.nil? || subscription[:trial_end].nil? || subscription[:trial_end] == 'now')
255
-
256
- if subscription[:items]
257
- trial = subscription[:items][:data].none? do |item|
258
- plan = item[:plan]
259
- (plan[:trial_period_days].nil? || plan[:trial_period_days] == 0) &&
260
- (plan[:trial_end].nil? || plan[:trial_end] == 'now')
261
- end
262
- return if trial
263
- end
263
+ return if customer[:default_source]
264
+ return if customer[:trial_end]
265
+ return if params[:trial_end]
264
266
 
265
- raise Stripe::InvalidRequestError.new('You must supply a valid card xoxo', nil, http_status: 400)
267
+ plan_trial_period_days = plan[:trial_period_days] || 0
268
+ plan_has_trial = plan_trial_period_days != 0 || plan[:amount] == 0 || plan[:trial_end]
269
+ return if plan && plan_has_trial
270
+
271
+ return if subscription && subscription[:trial_end] && subscription[:trial_end] != 'now'
272
+
273
+ if subscription[:items]
274
+ trial = subscription[:items][:data].none? do |item|
275
+ plan = item[:plan]
276
+ (plan[:trial_period_days].nil? || plan[:trial_period_days] == 0) &&
277
+ (plan[:trial_end].nil? || plan[:trial_end] == 'now')
278
+ end
279
+ return if trial
266
280
  end
281
+
282
+ return if params[:billing] == 'send_invoice'
283
+
284
+ raise Stripe::InvalidRequestError.new('You must supply a valid card xoxo', nil, http_status: 400)
267
285
  end
268
286
 
269
287
  def verify_active_status(subscription)
@@ -0,0 +1,36 @@
1
+ module StripeMock
2
+ module RequestHandlers
3
+ module TaxRates
4
+ def TaxRates.included(klass)
5
+ klass.add_handler 'post /v1/tax_rates', :new_tax_rate
6
+ klass.add_handler 'post /v1/tax_rates/([^/]*)', :update_tax_rate
7
+ klass.add_handler 'get /v1/tax_rates/([^/]*)', :get_tax_rate
8
+ klass.add_handler 'get /v1/tax_rates', :list_tax_rates
9
+ end
10
+
11
+ def update_tax_rate(route, method_url, params, headers)
12
+ route =~ method_url
13
+ rate = assert_existence :tax_rate, $1, tax_rates[$1]
14
+ rate.merge!(params)
15
+ rate
16
+ end
17
+
18
+ def new_tax_rate(route, method_url, params, headers)
19
+ params[:id] ||= new_id('txr')
20
+ tax_rates[ params[:id] ] = Data.mock_tax_rate(params)
21
+ tax_rates[ params[:id] ]
22
+ end
23
+
24
+ def list_tax_rates(route, method_url, params, headers)
25
+ Data.mock_list_object(tax_rates.values, params)
26
+ end
27
+
28
+ def get_tax_rate(route, method_url, params, headers)
29
+ route =~ method_url
30
+ tax_rate = assert_existence :tax_rate, $1, tax_rates[$1]
31
+ tax_rate.clone
32
+ end
33
+ end
34
+ end
35
+ end
36
+
@@ -46,12 +46,12 @@ module StripeMock
46
46
  end
47
47
 
48
48
  if bank_account
49
- token_id = generate_bank_token(bank_account)
49
+ token_id = generate_bank_token(bank_account.dup)
50
50
  bank_account = @bank_tokens[token_id]
51
51
 
52
52
  Data.mock_bank_account_token(params.merge :id => token_id, :bank_account => bank_account)
53
53
  else
54
- token_id = generate_card_token(customer_card)
54
+ token_id = generate_card_token(customer_card.dup)
55
55
  card = @card_tokens[token_id]
56
56
 
57
57
  Data.mock_card_token(params.merge :id => token_id, :card => card)
@@ -10,13 +10,19 @@ module StripeMock
10
10
  end
11
11
 
12
12
  def get_all_transfers(route, method_url, params, headers)
13
- if recipient = params[:recipient]
14
- assert_existence :recipient, recipient, recipients[recipient]
13
+ extra_params = params.keys - [:created, :destination, :ending_before,
14
+ :limit, :starting_after, :transfer_group]
15
+ unless extra_params.empty?
16
+ raise Stripe::InvalidRequestError.new("Received unknown parameter: #{extra_params[0]}", extra_params[0].to_s, http_status: 400)
17
+ end
18
+
19
+ if destination = params[:destination]
20
+ assert_existence :destination, destination, accounts[destination]
15
21
  end
16
22
 
17
23
  _transfers = transfers.each_with_object([]) do |(_, transfer), array|
18
- if recipient
19
- array << transfer if transfer[:recipient] == recipient
24
+ if destination
25
+ array << transfer if transfer[:destination] == destination
20
26
  else
21
27
  array << transfer
22
28
  end
@@ -24,6 +24,9 @@ module StripeMock
24
24
  end
25
25
  end
26
26
 
27
+ def require_param(param_name)
28
+ raise Stripe::InvalidRequestError.new("Missing required param: #{param_name}.", param_name.to_s, http_status: 400)
29
+ end
27
30
  end
28
31
  end
29
32
  end
@@ -6,7 +6,9 @@ module StripeMock
6
6
  currency = params[:currency] || StripeMock.default_currency
7
7
  {
8
8
  :id => 'stripe_mock_default_plan_id',
9
- :name => 'StripeMock Default Plan ID',
9
+ :product => {
10
+ :name => 'StripeMock Default Plan ID'
11
+ },
10
12
  :amount => 1337,
11
13
  :currency => currency,
12
14
  :interval => 'month'
@@ -14,7 +16,7 @@ module StripeMock
14
16
  end
15
17
 
16
18
  def generate_card_token(card_params={})
17
- card_data = { :number => "4242424242424242", :exp_month => 9, :exp_year => 2018, :cvc => "999", :tokenization_method => nil }
19
+ card_data = { :number => "4242424242424242", :exp_month => 9, :exp_year => (Time.now.year + 5), :cvc => "999", :tokenization_method => nil }
18
20
  card = StripeMock::Util.card_merge(card_data, card_params)
19
21
  card[:fingerprint] = StripeMock::Util.fingerprint(card[:number]) if StripeMock.state == 'local'
20
22
 
@@ -1,4 +1,4 @@
1
1
  module StripeMock
2
2
  # stripe-ruby-mock version
3
- VERSION = "2.5.4"
3
+ VERSION = "2.5.8"
4
4
  end
@@ -0,0 +1,88 @@
1
+ {
2
+ "created": 1326853478,
3
+ "livemode": false,
4
+ "id": "evt_00000000000000",
5
+ "type": "charge.dispute.funds_reinstated",
6
+ "object": "event",
7
+ "request": null,
8
+ "pending_webhooks": 1,
9
+ "api_version": "2017-12-14",
10
+ "data": {
11
+ "object": {
12
+ "id": "dp_00000000000000",
13
+ "object": "dispute",
14
+ "amount": 25000,
15
+ "balance_transaction": "txn_00000000000000",
16
+ "balance_transactions": [
17
+ {
18
+ "id": "txn_1Bl3mMGWCopOTFn18p8iALq8",
19
+ "object": "balance_transaction",
20
+ "amount": -25000,
21
+ "available_on": 1516233600,
22
+ "created": 1516145022,
23
+ "currency": "usd",
24
+ "description": "Chargeback withdrawal for ch_1Bl3mKGWCopOTFn1LKoN557r",
25
+ "exchange_rate": null,
26
+ "fee": 1500,
27
+ "fee_details": [
28
+ {
29
+ "amount": 1500,
30
+ "application": null,
31
+ "currency": "usd",
32
+ "description": "Dispute fee",
33
+ "type": "stripe_fee"
34
+ }
35
+ ],
36
+ "net": -26500,
37
+ "source": "dp_1Bl3mMGWCopOTFn1jpVaJDrU",
38
+ "status": "pending",
39
+ "type": "adjustment"
40
+ }
41
+ ],
42
+ "charge": "ch_00000000000000",
43
+ "created": 1516145022,
44
+ "currency": "usd",
45
+ "evidence": {
46
+ "access_activity_log": null,
47
+ "billing_address": null,
48
+ "cancellation_policy": null,
49
+ "cancellation_policy_disclosure": null,
50
+ "cancellation_rebuttal": null,
51
+ "customer_communication": null,
52
+ "customer_email_address": "amitree.apu@gmail.com",
53
+ "customer_name": "amitree.apu@gmail.com",
54
+ "customer_purchase_ip": "157.131.133.10",
55
+ "customer_signature": null,
56
+ "duplicate_charge_documentation": null,
57
+ "duplicate_charge_explanation": null,
58
+ "duplicate_charge_id": null,
59
+ "product_description": null,
60
+ "receipt": null,
61
+ "refund_policy": null,
62
+ "refund_policy_disclosure": null,
63
+ "refund_refusal_explanation": null,
64
+ "service_date": null,
65
+ "service_documentation": null,
66
+ "shipping_address": null,
67
+ "shipping_carrier": null,
68
+ "shipping_date": null,
69
+ "shipping_documentation": null,
70
+ "shipping_tracking_number": null,
71
+ "uncategorized_file": null,
72
+ "uncategorized_text": null
73
+ },
74
+ "evidence_details": {
75
+ "due_by": 1517529599,
76
+ "has_evidence": false,
77
+ "past_due": false,
78
+ "submission_count": 0
79
+ },
80
+ "is_charge_refundable": false,
81
+ "livemode": false,
82
+ "metadata": {
83
+ },
84
+ "reason": "fraudulent",
85
+ "status": "needs_response"
86
+ }
87
+ }
88
+ }