epopia-stripe-ruby-mock 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 (171) hide show
  1. checksums.yaml +7 -0
  2. data/.env +2 -0
  3. data/.gitignore +8 -0
  4. data/.rspec +1 -0
  5. data/.travis.yml +28 -0
  6. data/Gemfile +12 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +413 -0
  9. data/Rakefile +14 -0
  10. data/bin/stripe-mock-server +19 -0
  11. data/lib/stripe_mock.rb +95 -0
  12. data/lib/stripe_mock/api/account_balance.rb +14 -0
  13. data/lib/stripe_mock/api/bank_tokens.rb +13 -0
  14. data/lib/stripe_mock/api/card_tokens.rb +13 -0
  15. data/lib/stripe_mock/api/client.rb +41 -0
  16. data/lib/stripe_mock/api/conversion_rate.rb +14 -0
  17. data/lib/stripe_mock/api/debug.rb +11 -0
  18. data/lib/stripe_mock/api/errors.rb +65 -0
  19. data/lib/stripe_mock/api/global_id_prefix.rb +22 -0
  20. data/lib/stripe_mock/api/instance.rb +38 -0
  21. data/lib/stripe_mock/api/live.rb +15 -0
  22. data/lib/stripe_mock/api/server.rb +39 -0
  23. data/lib/stripe_mock/api/test_helpers.rb +24 -0
  24. data/lib/stripe_mock/api/webhooks.rb +88 -0
  25. data/lib/stripe_mock/client.rb +127 -0
  26. data/lib/stripe_mock/data.rb +1193 -0
  27. data/lib/stripe_mock/data/list.rb +73 -0
  28. data/lib/stripe_mock/error_queue.rb +27 -0
  29. data/lib/stripe_mock/errors/closed_client_connection_error.rb +9 -0
  30. data/lib/stripe_mock/errors/server_timeout_error.rb +12 -0
  31. data/lib/stripe_mock/errors/stripe_mock_error.rb +15 -0
  32. data/lib/stripe_mock/errors/uninitialized_instance_error.rb +9 -0
  33. data/lib/stripe_mock/errors/unstarted_state_error.rb +9 -0
  34. data/lib/stripe_mock/errors/unsupported_request_error.rb +4 -0
  35. data/lib/stripe_mock/instance.rb +237 -0
  36. data/lib/stripe_mock/request_handlers/accounts.rb +86 -0
  37. data/lib/stripe_mock/request_handlers/balance.rb +17 -0
  38. data/lib/stripe_mock/request_handlers/balance_transactions.rb +37 -0
  39. data/lib/stripe_mock/request_handlers/cards.rb +35 -0
  40. data/lib/stripe_mock/request_handlers/charges.rb +177 -0
  41. data/lib/stripe_mock/request_handlers/country_spec.rb +22 -0
  42. data/lib/stripe_mock/request_handlers/coupons.rb +35 -0
  43. data/lib/stripe_mock/request_handlers/customers.rb +137 -0
  44. data/lib/stripe_mock/request_handlers/disputes.rb +35 -0
  45. data/lib/stripe_mock/request_handlers/ephemeral_key.rb +13 -0
  46. data/lib/stripe_mock/request_handlers/events.rb +21 -0
  47. data/lib/stripe_mock/request_handlers/external_accounts.rb +55 -0
  48. data/lib/stripe_mock/request_handlers/helpers/bank_account_helpers.rb +14 -0
  49. data/lib/stripe_mock/request_handlers/helpers/card_helpers.rb +127 -0
  50. data/lib/stripe_mock/request_handlers/helpers/charge_helpers.rb +16 -0
  51. data/lib/stripe_mock/request_handlers/helpers/coupon_helpers.rb +17 -0
  52. data/lib/stripe_mock/request_handlers/helpers/external_account_helpers.rb +49 -0
  53. data/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb +119 -0
  54. data/lib/stripe_mock/request_handlers/helpers/token_helpers.rb +44 -0
  55. data/lib/stripe_mock/request_handlers/invoice_items.rb +45 -0
  56. data/lib/stripe_mock/request_handlers/invoices.rb +177 -0
  57. data/lib/stripe_mock/request_handlers/orders.rb +80 -0
  58. data/lib/stripe_mock/request_handlers/payment_intents.rb +203 -0
  59. data/lib/stripe_mock/request_handlers/payment_methods.rb +112 -0
  60. data/lib/stripe_mock/request_handlers/payouts.rb +32 -0
  61. data/lib/stripe_mock/request_handlers/plans.rb +42 -0
  62. data/lib/stripe_mock/request_handlers/products.rb +43 -0
  63. data/lib/stripe_mock/request_handlers/recipients.rb +60 -0
  64. data/lib/stripe_mock/request_handlers/refunds.rb +91 -0
  65. data/lib/stripe_mock/request_handlers/sources.rb +55 -0
  66. data/lib/stripe_mock/request_handlers/subscription_items.rb +36 -0
  67. data/lib/stripe_mock/request_handlers/subscriptions.rb +296 -0
  68. data/lib/stripe_mock/request_handlers/tax_rates.rb +36 -0
  69. data/lib/stripe_mock/request_handlers/tokens.rb +75 -0
  70. data/lib/stripe_mock/request_handlers/transfers.rb +65 -0
  71. data/lib/stripe_mock/request_handlers/validators/param_validators.rb +32 -0
  72. data/lib/stripe_mock/server.rb +93 -0
  73. data/lib/stripe_mock/test_strategies/base.rb +81 -0
  74. data/lib/stripe_mock/test_strategies/live.rb +40 -0
  75. data/lib/stripe_mock/test_strategies/mock.rb +27 -0
  76. data/lib/stripe_mock/util.rb +44 -0
  77. data/lib/stripe_mock/version.rb +4 -0
  78. data/lib/stripe_mock/webhook_fixtures/account.application.deauthorized.json +12 -0
  79. data/lib/stripe_mock/webhook_fixtures/account.external_account.created.json +27 -0
  80. data/lib/stripe_mock/webhook_fixtures/account.external_account.deleted.json +27 -0
  81. data/lib/stripe_mock/webhook_fixtures/account.external_account.updated.json +27 -0
  82. data/lib/stripe_mock/webhook_fixtures/account.updated.json +26 -0
  83. data/lib/stripe_mock/webhook_fixtures/balance.available.json +25 -0
  84. data/lib/stripe_mock/webhook_fixtures/charge.dispute.closed.json +22 -0
  85. data/lib/stripe_mock/webhook_fixtures/charge.dispute.created.json +22 -0
  86. data/lib/stripe_mock/webhook_fixtures/charge.dispute.funds_reinstated.json +88 -0
  87. data/lib/stripe_mock/webhook_fixtures/charge.dispute.funds_withdrawn.json +88 -0
  88. data/lib/stripe_mock/webhook_fixtures/charge.dispute.updated.json +25 -0
  89. data/lib/stripe_mock/webhook_fixtures/charge.failed.json +56 -0
  90. data/lib/stripe_mock/webhook_fixtures/charge.refunded.json +69 -0
  91. data/lib/stripe_mock/webhook_fixtures/charge.succeeded.json +55 -0
  92. data/lib/stripe_mock/webhook_fixtures/charge.updated.json +58 -0
  93. data/lib/stripe_mock/webhook_fixtures/coupon.created.json +23 -0
  94. data/lib/stripe_mock/webhook_fixtures/coupon.deleted.json +23 -0
  95. data/lib/stripe_mock/webhook_fixtures/customer.created.json +54 -0
  96. data/lib/stripe_mock/webhook_fixtures/customer.deleted.json +42 -0
  97. data/lib/stripe_mock/webhook_fixtures/customer.discount.created.json +28 -0
  98. data/lib/stripe_mock/webhook_fixtures/customer.discount.deleted.json +28 -0
  99. data/lib/stripe_mock/webhook_fixtures/customer.discount.updated.json +43 -0
  100. data/lib/stripe_mock/webhook_fixtures/customer.source.created.json +32 -0
  101. data/lib/stripe_mock/webhook_fixtures/customer.source.deleted.json +32 -0
  102. data/lib/stripe_mock/webhook_fixtures/customer.source.updated.json +36 -0
  103. data/lib/stripe_mock/webhook_fixtures/customer.subscription.created.json +66 -0
  104. data/lib/stripe_mock/webhook_fixtures/customer.subscription.deleted.json +65 -0
  105. data/lib/stripe_mock/webhook_fixtures/customer.subscription.trial_will_end.json +65 -0
  106. data/lib/stripe_mock/webhook_fixtures/customer.subscription.updated.json +78 -0
  107. data/lib/stripe_mock/webhook_fixtures/customer.updated.json +57 -0
  108. data/lib/stripe_mock/webhook_fixtures/invoice.created.json +71 -0
  109. data/lib/stripe_mock/webhook_fixtures/invoice.payment_failed.json +105 -0
  110. data/lib/stripe_mock/webhook_fixtures/invoice.payment_succeeded.json +112 -0
  111. data/lib/stripe_mock/webhook_fixtures/invoice.updated.json +74 -0
  112. data/lib/stripe_mock/webhook_fixtures/invoiceitem.created.json +21 -0
  113. data/lib/stripe_mock/webhook_fixtures/invoiceitem.deleted.json +21 -0
  114. data/lib/stripe_mock/webhook_fixtures/invoiceitem.updated.json +24 -0
  115. data/lib/stripe_mock/webhook_fixtures/plan.created.json +20 -0
  116. data/lib/stripe_mock/webhook_fixtures/plan.deleted.json +20 -0
  117. data/lib/stripe_mock/webhook_fixtures/plan.updated.json +23 -0
  118. data/lib/stripe_mock/webhook_fixtures/transfer.created.json +89 -0
  119. data/lib/stripe_mock/webhook_fixtures/transfer.failed.json +89 -0
  120. data/lib/stripe_mock/webhook_fixtures/transfer.paid.json +89 -0
  121. data/lib/stripe_mock/webhook_fixtures/transfer.updated.json +92 -0
  122. data/lib/trollop.rb +782 -0
  123. data/spec/_dummy/webhooks/dummy.event.json +6 -0
  124. data/spec/api/instance_spec.rb +30 -0
  125. data/spec/fixtures/create_refund.yml +126 -0
  126. data/spec/fixtures/stripe_webhooks/account.updated.json +7 -0
  127. data/spec/fixtures/stripe_webhooks/custom.account.updated.json +5 -0
  128. data/spec/instance_spec.rb +100 -0
  129. data/spec/integration_examples/charge_token_examples.rb +51 -0
  130. data/spec/integration_examples/customer_card_examples.rb +42 -0
  131. data/spec/integration_examples/prepare_error_examples.rb +38 -0
  132. data/spec/list_spec.rb +140 -0
  133. data/spec/readme_spec.rb +75 -0
  134. data/spec/server_spec.rb +139 -0
  135. data/spec/shared_stripe_examples/account_examples.rb +96 -0
  136. data/spec/shared_stripe_examples/balance_examples.rb +11 -0
  137. data/spec/shared_stripe_examples/balance_transaction_examples.rb +63 -0
  138. data/spec/shared_stripe_examples/bank_examples.rb +229 -0
  139. data/spec/shared_stripe_examples/bank_token_examples.rb +59 -0
  140. data/spec/shared_stripe_examples/card_examples.rb +307 -0
  141. data/spec/shared_stripe_examples/card_token_examples.rb +185 -0
  142. data/spec/shared_stripe_examples/charge_examples.rb +510 -0
  143. data/spec/shared_stripe_examples/country_specs_examples.rb +18 -0
  144. data/spec/shared_stripe_examples/coupon_examples.rb +85 -0
  145. data/spec/shared_stripe_examples/customer_examples.rb +453 -0
  146. data/spec/shared_stripe_examples/dispute_examples.rb +98 -0
  147. data/spec/shared_stripe_examples/ephemeral_key_examples.rb +17 -0
  148. data/spec/shared_stripe_examples/error_mock_examples.rb +162 -0
  149. data/spec/shared_stripe_examples/external_account_examples.rb +170 -0
  150. data/spec/shared_stripe_examples/extra_features_examples.rb +36 -0
  151. data/spec/shared_stripe_examples/invoice_examples.rb +524 -0
  152. data/spec/shared_stripe_examples/invoice_item_examples.rb +69 -0
  153. data/spec/shared_stripe_examples/payment_intent_examples.rb +131 -0
  154. data/spec/shared_stripe_examples/payment_method_examples.rb +175 -0
  155. data/spec/shared_stripe_examples/payout_examples.rb +68 -0
  156. data/spec/shared_stripe_examples/plan_examples.rb +194 -0
  157. data/spec/shared_stripe_examples/product_example.rb +65 -0
  158. data/spec/shared_stripe_examples/recipient_examples.rb +118 -0
  159. data/spec/shared_stripe_examples/refund_examples.rb +472 -0
  160. data/spec/shared_stripe_examples/subscription_examples.rb +1148 -0
  161. data/spec/shared_stripe_examples/subscription_items_examples.rb +75 -0
  162. data/spec/shared_stripe_examples/tax_rate_examples.rb +42 -0
  163. data/spec/shared_stripe_examples/transfer_examples.rb +130 -0
  164. data/spec/shared_stripe_examples/validation_examples.rb +19 -0
  165. data/spec/shared_stripe_examples/webhook_event_examples.rb +261 -0
  166. data/spec/spec_helper.rb +58 -0
  167. data/spec/stripe_mock_spec.rb +123 -0
  168. data/spec/support/stripe_examples.rb +42 -0
  169. data/spec/util_spec.rb +121 -0
  170. data/stripe-ruby-mock.gemspec +27 -0
  171. metadata +344 -0
@@ -0,0 +1,35 @@
1
+ module StripeMock
2
+ module RequestHandlers
3
+ module Disputes
4
+
5
+ def Disputes.included(klass)
6
+ klass.add_handler 'get /v1/disputes/(.*)', :get_dispute
7
+ klass.add_handler 'post /v1/disputes/(.*)/close', :close_dispute
8
+ klass.add_handler 'post /v1/disputes/(.*)', :update_dispute
9
+ klass.add_handler 'get /v1/disputes', :list_disputes
10
+ end
11
+
12
+ def get_dispute(route, method_url, params, headers)
13
+ route =~ method_url
14
+ assert_existence :dispute, $1, disputes[$1]
15
+ end
16
+
17
+ def update_dispute(route, method_url, params, headers)
18
+ dispute = get_dispute(route, method_url, params, headers)
19
+ dispute.merge!(params)
20
+ dispute
21
+ end
22
+
23
+ def close_dispute(route, method_url, params, headers)
24
+ dispute = get_dispute(route, method_url, params, headers)
25
+ dispute.merge!({:status => 'lost'})
26
+ dispute
27
+ end
28
+
29
+ def list_disputes(route, method_url, params, headers)
30
+ Data.mock_list_object(disputes.values, params)
31
+ end
32
+
33
+ end
34
+ end
35
+ 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,21 @@
1
+ module StripeMock
2
+ module RequestHandlers
3
+ module Events
4
+
5
+ def Events.included(klass)
6
+ klass.add_handler 'get /v1/events/(.*)', :retrieve_event
7
+ klass.add_handler 'get /v1/events', :list_events
8
+ end
9
+
10
+ def retrieve_event(route, method_url, params, headers)
11
+ route =~ method_url
12
+ assert_existence :event, $1, events[$1]
13
+ end
14
+
15
+ def list_events(route, method_url, params, headers)
16
+ Data.mock_list_object(events.values, params)
17
+ end
18
+
19
+ end
20
+ end
21
+ 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
@@ -0,0 +1,14 @@
1
+ module StripeMock
2
+ module RequestHandlers
3
+ module Helpers
4
+
5
+ def verify_bank_account(object, bank_account_id, class_name='Customer')
6
+ bank_accounts = object[:external_accounts] || object[:bank_accounts] || object[:sources]
7
+ bank_account = bank_accounts[:data].find{|acc| acc[:id] == bank_account_id }
8
+ return if bank_account.nil?
9
+ bank_account['status'] = 'verified'
10
+ bank_account
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,127 @@
1
+ module StripeMock
2
+ module RequestHandlers
3
+ module Helpers
4
+
5
+ def get_card(object, card_id, class_name='Customer')
6
+ cards = object[:cards] || object[:sources] || object[:external_accounts]
7
+ card = cards[:data].find{|cc| cc[:id] == card_id }
8
+ if card.nil?
9
+ if class_name == 'Recipient'
10
+ msg = "#{class_name} #{object[:id]} does not have a card with ID #{card_id}"
11
+ raise Stripe::InvalidRequestError.new(msg, 'card', http_status: 404)
12
+ else
13
+ msg = "There is no source with ID #{card_id}"
14
+ raise Stripe::InvalidRequestError.new(msg, 'id', http_status: 404)
15
+ end
16
+ end
17
+ card
18
+ end
19
+
20
+ def add_source_to_object(type, source, object, replace_current=false)
21
+ source[type] = object[:id]
22
+ sources = object[:sources]
23
+
24
+ if replace_current && sources[:data]
25
+ sources[:data].delete_if {|source| source[:id] == object[:default_source]}
26
+ object[:default_source] = source[:id]
27
+ sources[:data] = [source]
28
+ else
29
+ sources[:total_count] = (sources[:total_count] || 0) + 1
30
+ (sources[:data] ||= []) << source
31
+ end
32
+ object[:default_source] = source[:id] if object[:default_source].nil?
33
+
34
+ source
35
+ end
36
+
37
+ def add_card_to_object(type, card, object, replace_current=false)
38
+ card[type] = object[:id]
39
+ cards_or_sources = object[:cards] || object[:sources] || object[:external_accounts]
40
+
41
+ is_customer = object.has_key?(:sources)
42
+
43
+ if replace_current && cards_or_sources[:data]
44
+ cards_or_sources[:data].delete_if {|card| card[:id] == object[:default_card]}
45
+ object[:default_card] = card[:id] unless is_customer
46
+ object[:default_source] = card[:id] if is_customer
47
+ cards_or_sources[:data] = [card]
48
+ else
49
+ cards_or_sources[:total_count] = (cards_or_sources[:total_count] || 0) + 1
50
+ (cards_or_sources[:data] ||= []) << card
51
+ end
52
+
53
+ object[:default_card] = card[:id] if !is_customer && object[:default_card].nil?
54
+ object[:default_source] = card[:id] if is_customer && object[:default_source].nil?
55
+
56
+ card
57
+ end
58
+
59
+ def retrieve_object_cards(type, type_id, objects)
60
+ resource = assert_existence type, type_id, objects[type_id]
61
+ cards = resource[:cards] || resource[:sources] || resource[:external_accounts]
62
+
63
+ Data.mock_list_object(cards[:data])
64
+ end
65
+
66
+ def delete_card_from(type, type_id, card_id, objects)
67
+ resource = assert_existence type, type_id, objects[type_id]
68
+
69
+ assert_existence :card, card_id, get_card(resource, card_id)
70
+
71
+ card = { id: card_id, deleted: true }
72
+ cards_or_sources = resource[:cards] || resource[:sources] || resource[:external_accounts]
73
+ cards_or_sources[:data].reject!{|cc|
74
+ cc[:id] == card[:id]
75
+ }
76
+
77
+ is_customer = resource.has_key?(:sources)
78
+ new_default = cards_or_sources[:data].count > 0 ? cards_or_sources[:data].first[:id] : nil
79
+ resource[:default_card] = new_default unless is_customer
80
+ resource[:sources][:total_count] = cards_or_sources[:data].count if is_customer
81
+ resource[:default_source] = new_default if is_customer
82
+ card
83
+ end
84
+
85
+ def add_source_to(type, type_id, params, objects)
86
+ resource = assert_existence type, type_id, objects[type_id]
87
+
88
+ source =
89
+ if params[:card]
90
+ card_from_params(params[:card])
91
+ elsif params[:bank_account]
92
+ get_bank_by_token(params[:bank_account])
93
+ else
94
+ begin
95
+ get_card_by_token(params[:source])
96
+ rescue Stripe::InvalidRequestError
97
+ get_bank_by_token(params[:source])
98
+ end
99
+ end
100
+ source[:metadata].merge!(params[:metadata]) if params[:metadata]
101
+ add_source_to_object(type, source, resource)
102
+ end
103
+
104
+ def add_card_to(type, type_id, params, objects)
105
+ resource = assert_existence type, type_id, objects[type_id]
106
+
107
+ card = card_from_params(params[:card] || params[:source] || params[:external_accounts])
108
+ add_card_to_object(type, card, resource)
109
+ end
110
+
111
+ def validate_card(card)
112
+ [:exp_month, :exp_year].each do |field|
113
+ card[field] = card[field].to_i
114
+ end
115
+ card
116
+ end
117
+
118
+ def card_from_params(attrs_or_token)
119
+ if attrs_or_token.is_a? Hash
120
+ attrs_or_token = generate_card_token(attrs_or_token)
121
+ end
122
+ card = get_card_by_token(attrs_or_token)
123
+ validate_card(card)
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,16 @@
1
+ module StripeMock
2
+ module RequestHandlers
3
+ module Helpers
4
+
5
+ def add_refund_to_charge(refund, charge)
6
+ refunds = charge[:refunds]
7
+ refunds[:data] << refund
8
+ refunds[:total_count] = refunds[:data].count
9
+
10
+ charge[:amount_refunded] = refunds[:data].reduce(0) {|sum, r| sum + r[:amount].to_i }
11
+ charge[:refunded] = true
12
+ end
13
+
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ module StripeMock
2
+ module RequestHandlers
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
11
+
12
+ object[:discount] = Stripe::Discount.construct_from(discount_attrs)
13
+ object
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,49 @@
1
+ module StripeMock
2
+ module RequestHandlers
3
+ module Helpers
4
+
5
+ def add_external_account_to(type, type_id, params, objects)
6
+ resource = assert_existence type, type_id, objects[type_id]
7
+
8
+ source =
9
+ if params[:card]
10
+ card_from_params(params[:card])
11
+ elsif params[:bank_account]
12
+ bank_from_params(params[:bank_account])
13
+ else
14
+ begin
15
+ get_card_by_token(params[:external_account])
16
+ rescue Stripe::InvalidRequestError
17
+ bank_from_params(params[:external_account])
18
+ end
19
+ end
20
+ add_external_account_to_object(type, source, resource)
21
+ end
22
+
23
+ def add_external_account_to_object(type, source, object, replace_current=false)
24
+ source[type] = object[:id]
25
+ accounts = object[:external_accounts]
26
+
27
+ if replace_current && accounts[:data]
28
+ accounts[:data].delete_if {|source| source[:id] == object[:default_source]}
29
+ object[:default_source] = source[:id]
30
+ accounts[:data] = [source]
31
+ else
32
+ accounts[:total_count] = (accounts[:total_count] || 0) + 1
33
+ (accounts[:data] ||= []) << source
34
+ end
35
+ object[:default_source] = source[:id] if object[:default_source].nil?
36
+
37
+ source
38
+ end
39
+
40
+ def bank_from_params(attrs_or_token)
41
+ if attrs_or_token.is_a? Hash
42
+ attrs_or_token = generate_bank_token(attrs_or_token)
43
+ end
44
+ get_bank_by_token(attrs_or_token)
45
+ end
46
+
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,119 @@
1
+ module StripeMock
2
+ module RequestHandlers
3
+ module Helpers
4
+
5
+ def get_customer_subscription(customer, sub_id)
6
+ customer[:subscriptions][:data].find{|sub| sub[:id] == sub_id }
7
+ end
8
+
9
+ def resolve_subscription_changes(subscription, plans, customer, options = {})
10
+ subscription.merge!(custom_subscription_params(plans, customer, options))
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
22
+ subscription
23
+ end
24
+
25
+ def custom_subscription_params(plans, cus, options = {})
26
+ verify_trial_end(options[:trial_end]) if options[:trial_end]
27
+
28
+ plan = plans.first if plans.size == 1
29
+
30
+ now = Time.now.utc.to_i
31
+ created_time = options[:created] || now
32
+ start_time = options[:current_period_start] || now
33
+ params = { customer: cus[:id], current_period_start: start_time, created: created_time }
34
+ params.merge!({ :plan => (plans.size == 1 ? plans.first : nil) })
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
+
44
+ # TODO: Implement coupon logic
45
+
46
+ if (((plan && plan[:trial_period_days]) || 0) == 0 && options[:trial_end].nil?) || options[:trial_end] == "now"
47
+ end_time = options[:billing_cycle_anchor] || get_ending_time(start_time, plan)
48
+ params.merge!({status: 'active', current_period_end: end_time, trial_start: nil, trial_end: nil, billing_cycle_anchor: options[:billing_cycle_anchor]})
49
+ else
50
+ end_time = options[:trial_end] || (Time.now.utc.to_i + plan[:trial_period_days]*86400)
51
+ params.merge!({status: 'trialing', current_period_end: end_time, trial_start: start_time, trial_end: end_time, billing_cycle_anchor: nil})
52
+ end
53
+
54
+ params
55
+ end
56
+
57
+ def add_subscription_to_customer(cus, sub)
58
+ if sub[:trial_end].nil? || sub[:trial_end] == "now"
59
+ id = new_id('ch')
60
+ charges[id] = Data.mock_charge(
61
+ :id => id,
62
+ :customer => cus[:id],
63
+ :amount => (sub[:plan] ? sub[:plan][:amount] : total_items_amount(sub[:items][:data]))
64
+ )
65
+ end
66
+
67
+ if cus[:currency].nil?
68
+ cus[:currency] = sub[:items][:data][0][:plan][:currency]
69
+ elsif cus[:currency] != sub[:items][:data][0][:plan][:currency]
70
+ raise Stripe::InvalidRequestError.new( "Can't combine currencies on a single customer. This customer has had a subscription, coupon, or invoice item with currency #{cus[:currency]}", 'currency', http_status: 400)
71
+ end
72
+ cus[:subscriptions][:total_count] = (cus[:subscriptions][:total_count] || 0) + 1
73
+ cus[:subscriptions][:data].unshift sub
74
+ end
75
+
76
+ def delete_subscription_from_customer(cus, subscription)
77
+ cus[:subscriptions][:data].reject!{|sub|
78
+ sub[:id] == subscription[:id]
79
+ }
80
+ cus[:subscriptions][:total_count] -=1
81
+ end
82
+
83
+ # `intervals` is set to 1 when calculating current_period_end from current_period_start & plan
84
+ # `intervals` is set to 2 when calculating Stripe::Invoice.upcoming end from current_period_start & plan
85
+ def get_ending_time(start_time, plan, intervals = 1)
86
+ return start_time unless plan
87
+
88
+ case plan[:interval]
89
+ when "week"
90
+ start_time + (604800 * (plan[:interval_count] || 1) * intervals)
91
+ when "month"
92
+ (Time.at(start_time).to_datetime >> ((plan[:interval_count] || 1) * intervals)).to_time.to_i
93
+ when "year"
94
+ (Time.at(start_time).to_datetime >> (12 * intervals)).to_time.to_i # max period is 1 year
95
+ else
96
+ start_time
97
+ end
98
+ end
99
+
100
+ def verify_trial_end(trial_end)
101
+ if trial_end != "now"
102
+ if !trial_end.is_a? Integer
103
+ raise Stripe::InvalidRequestError.new('Invalid timestamp: must be an integer', nil, http_status: 400)
104
+ elsif trial_end < Time.now.utc.to_i
105
+ raise Stripe::InvalidRequestError.new('Invalid timestamp: must be an integer Unix timestamp in the future', nil, http_status: 400)
106
+ elsif trial_end > Time.now.utc.to_i + 31557600*5 # five years
107
+ raise Stripe::InvalidRequestError.new('Invalid timestamp: can be no more than five years in the future', nil, http_status: 400)
108
+ end
109
+ end
110
+ end
111
+
112
+ def total_items_amount(items)
113
+ total = 0
114
+ items.each { |i| total += (i[:quantity] || 1) * i[:plan][:amount] }
115
+ total
116
+ end
117
+ end
118
+ end
119
+ end