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,44 @@
1
+ module StripeMock
2
+ module RequestHandlers
3
+ module Helpers
4
+
5
+ def generate_bank_token(bank_params = {})
6
+ token = new_id 'btok'
7
+ bank_params[:id] = new_id 'bank_account'
8
+ @bank_tokens[token] = Data.mock_bank_account bank_params
9
+ token
10
+ end
11
+
12
+ def generate_card_token(card_params = {})
13
+ token = new_id 'tok'
14
+ card_params[:id] = new_id 'cc'
15
+ @card_tokens[token] = Data.mock_card symbolize_names(card_params)
16
+ token
17
+ end
18
+
19
+ def get_bank_by_token(token)
20
+ if token.nil? || @bank_tokens[token].nil?
21
+ Data.mock_bank_account
22
+ else
23
+ @bank_tokens.delete(token)
24
+ end
25
+ end
26
+
27
+ def get_card_by_token(token)
28
+ if token.nil? || @card_tokens[token].nil?
29
+ # TODO: Make this strict
30
+ msg = "Invalid token id: #{token}"
31
+ raise Stripe::InvalidRequestError.new(msg, 'tok', http_status: 404)
32
+ else
33
+ @card_tokens.delete(token)
34
+ end
35
+ end
36
+
37
+ def get_card_or_bank_by_token(token)
38
+ token_id = token['id'] || token
39
+ @card_tokens[token_id] || @bank_tokens[token_id] || raise(Stripe::InvalidRequestError.new("Invalid token id: #{token_id}", 'tok', http_status: 404))
40
+ end
41
+
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,45 @@
1
+ module StripeMock
2
+ module RequestHandlers
3
+ module InvoiceItems
4
+
5
+ def InvoiceItems.included(klass)
6
+ klass.add_handler 'post /v1/invoiceitems', :new_invoice_item
7
+ klass.add_handler 'post /v1/invoiceitems/(.*)', :update_invoice_item
8
+ klass.add_handler 'get /v1/invoiceitems/(.*)', :get_invoice_item
9
+ klass.add_handler 'get /v1/invoiceitems', :list_invoice_items
10
+ klass.add_handler 'delete /v1/invoiceitems/(.*)', :delete_invoice_item
11
+ end
12
+
13
+ def new_invoice_item(route, method_url, params, headers)
14
+ params[:id] ||= new_id('ii')
15
+ invoice_items[params[:id]] = Data.mock_invoice_item(params)
16
+ end
17
+
18
+ def update_invoice_item(route, method_url, params, headers)
19
+ route =~ method_url
20
+ list_item = assert_existence :list_item, $1, invoice_items[$1]
21
+ list_item.merge!(params)
22
+ end
23
+
24
+ def delete_invoice_item(route, method_url, params, headers)
25
+ route =~ method_url
26
+ assert_existence :list_item, $1, invoice_items[$1]
27
+
28
+ invoice_items[$1] = {
29
+ id: invoice_items[$1][:id],
30
+ deleted: true
31
+ }
32
+ end
33
+
34
+ def list_invoice_items(route, method_url, params, headers)
35
+ Data.mock_list_object(invoice_items.values, params)
36
+ end
37
+
38
+ def get_invoice_item(route, method_url, params, headers)
39
+ route =~ method_url
40
+ assert_existence :invoice_item, $1, invoice_items[$1]
41
+ end
42
+
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,177 @@
1
+ module StripeMock
2
+ module RequestHandlers
3
+ module Invoices
4
+
5
+ def Invoices.included(klass)
6
+ klass.add_handler 'post /v1/invoices', :new_invoice
7
+ klass.add_handler 'get /v1/invoices/upcoming', :upcoming_invoice
8
+ klass.add_handler 'get /v1/invoices/(.*)/lines', :get_invoice_line_items
9
+ klass.add_handler 'get /v1/invoices/(.*)', :get_invoice
10
+ klass.add_handler 'get /v1/invoices', :list_invoices
11
+ klass.add_handler 'post /v1/invoices/(.*)/pay', :pay_invoice
12
+ klass.add_handler 'post /v1/invoices/(.*)', :update_invoice
13
+ end
14
+
15
+ def new_invoice(route, method_url, params, headers)
16
+ id = new_id('in')
17
+ invoice_item = Data.mock_line_item()
18
+ invoices[id] = Data.mock_invoice([invoice_item], params.merge(:id => id))
19
+ end
20
+
21
+ def update_invoice(route, method_url, params, headers)
22
+ route =~ method_url
23
+ params.delete(:lines) if params[:lines]
24
+ assert_existence :invoice, $1, invoices[$1]
25
+ invoices[$1].merge!(params)
26
+ end
27
+
28
+ def list_invoices(route, method_url, params, headers)
29
+ params[:offset] ||= 0
30
+ params[:limit] ||= 10
31
+
32
+ result = invoices.clone
33
+
34
+ if params[:customer]
35
+ result.delete_if { |k,v| v[:customer] != params[:customer] }
36
+ end
37
+
38
+ Data.mock_list_object(result.values, params)
39
+ end
40
+
41
+ def get_invoice(route, method_url, params, headers)
42
+ route =~ method_url
43
+ assert_existence :invoice, $1, invoices[$1]
44
+ end
45
+
46
+ def get_invoice_line_items(route, method_url, params, headers)
47
+ route =~ method_url
48
+ assert_existence :invoice, $1, invoices[$1]
49
+ invoices[$1][:lines]
50
+ end
51
+
52
+ def pay_invoice(route, method_url, params, headers)
53
+ route =~ method_url
54
+ assert_existence :invoice, $1, invoices[$1]
55
+ charge = invoice_charge(invoices[$1])
56
+ invoices[$1].merge!(:paid => true, :attempted => true, :charge => charge[:id])
57
+ end
58
+
59
+ def upcoming_invoice(route, method_url, params, headers)
60
+ route =~ method_url
61
+ raise Stripe::InvalidRequestError.new('Missing required param: customer', nil, http_status: 400) if params[:customer].nil?
62
+ raise Stripe::InvalidRequestError.new('When previewing changes to a subscription, you must specify either `subscription` or `subscription_items`', nil, http_status: 400) if !params[:subscription_proration_date].nil? && params[:subscription].nil? && params[:subscription_plan].nil?
63
+ raise Stripe::InvalidRequestError.new('Cannot specify proration date without specifying a subscription', nil, http_status: 400) if !params[:subscription_proration_date].nil? && params[:subscription].nil?
64
+
65
+ customer = customers[params[:customer]]
66
+ assert_existence :customer, params[:customer], customer
67
+
68
+ raise Stripe::InvalidRequestError.new("No upcoming invoices for customer: #{customer[:id]}", nil, http_status: 404) if customer[:subscriptions][:data].length == 0
69
+
70
+ subscription =
71
+ if params[:subscription]
72
+ customer[:subscriptions][:data].select{|s|s[:id] == params[:subscription]}.first
73
+ else
74
+ customer[:subscriptions][:data].min_by { |sub| sub[:current_period_end] }
75
+ end
76
+
77
+ if params[:subscription_proration_date] && !((subscription[:current_period_start]..subscription[:current_period_end]) === params[:subscription_proration_date])
78
+ raise Stripe::InvalidRequestError.new('Cannot specify proration date outside of current subscription period', nil, http_status: 400)
79
+ end
80
+
81
+ prorating = false
82
+ subscription_proration_date = nil
83
+ subscription_plan_id = params[:subscription_plan] || subscription[:plan][:id]
84
+ subscription_quantity = params[:subscription_quantity] || subscription[:quantity]
85
+ if subscription_plan_id != subscription[:plan][:id] || subscription_quantity != subscription[:quantity]
86
+ prorating = true
87
+ invoice_date = Time.now.to_i
88
+ subscription_plan = assert_existence :plan, subscription_plan_id, plans[subscription_plan_id.to_s]
89
+ preview_subscription = Data.mock_subscription
90
+ preview_subscription = resolve_subscription_changes(preview_subscription, [subscription_plan], customer, { trial_end: params[:subscription_trial_end] })
91
+ preview_subscription[:id] = subscription[:id]
92
+ preview_subscription[:quantity] = subscription_quantity
93
+ subscription_proration_date = params[:subscription_proration_date] || Time.now
94
+ else
95
+ preview_subscription = subscription
96
+ invoice_date = subscription[:current_period_end]
97
+ end
98
+
99
+ invoice_lines = []
100
+
101
+ if prorating
102
+ unused_amount = subscription[:plan][:amount] * subscription[:quantity] * (subscription[:current_period_end] - subscription_proration_date.to_i) / (subscription[:current_period_end] - subscription[:current_period_start])
103
+ invoice_lines << Data.mock_line_item(
104
+ id: new_id('ii'),
105
+ amount: -unused_amount,
106
+ description: 'Unused time',
107
+ plan: subscription[:plan],
108
+ period: {
109
+ start: subscription_proration_date.to_i,
110
+ end: subscription[:current_period_end]
111
+ },
112
+ quantity: subscription[:quantity],
113
+ proration: true
114
+ )
115
+
116
+ preview_plan = assert_existence :plan, params[:subscription_plan], plans[params[:subscription_plan]]
117
+ if preview_plan[:interval] == subscription[:plan][:interval] && preview_plan[:interval_count] == subscription[:plan][:interval_count] && params[:subscription_trial_end].nil?
118
+ remaining_amount = preview_plan[:amount] * subscription_quantity * (subscription[:current_period_end] - subscription_proration_date.to_i) / (subscription[:current_period_end] - subscription[:current_period_start])
119
+ invoice_lines << Data.mock_line_item(
120
+ id: new_id('ii'),
121
+ amount: remaining_amount,
122
+ description: 'Remaining time',
123
+ plan: preview_plan,
124
+ period: {
125
+ start: subscription_proration_date.to_i,
126
+ end: subscription[:current_period_end]
127
+ },
128
+ quantity: subscription_quantity,
129
+ proration: true
130
+ )
131
+ end
132
+ end
133
+
134
+ subscription_line = get_mock_subscription_line_item(preview_subscription)
135
+ invoice_lines << subscription_line
136
+
137
+ Data.mock_invoice(invoice_lines,
138
+ id: new_id('in'),
139
+ customer: customer[:id],
140
+ discount: customer[:discount],
141
+ created: invoice_date,
142
+ starting_balance: customer[:account_balance],
143
+ subscription: preview_subscription[:id],
144
+ period_start: prorating ? invoice_date : preview_subscription[:current_period_start],
145
+ period_end: prorating ? invoice_date : preview_subscription[:current_period_end],
146
+ next_payment_attempt: preview_subscription[:current_period_end] + 3600 )
147
+ end
148
+
149
+ private
150
+
151
+ def get_mock_subscription_line_item(subscription)
152
+ Data.mock_line_item(
153
+ id: subscription[:id],
154
+ type: "subscription",
155
+ plan: subscription[:plan],
156
+ amount: subscription[:status] == 'trialing' ? 0 : subscription[:plan][:amount] * subscription[:quantity],
157
+ discountable: true,
158
+ quantity: subscription[:quantity],
159
+ period: {
160
+ start: subscription[:current_period_end],
161
+ end: get_ending_time(subscription[:current_period_start], subscription[:plan], 2)
162
+ })
163
+ end
164
+
165
+ ## charge the customer on the invoice, if one does not exist, create
166
+ #anonymous charge
167
+ def invoice_charge(invoice)
168
+ begin
169
+ new_charge(nil, nil, {customer: invoice[:customer]["id"], amount: invoice[:amount_due], currency: StripeMock.default_currency}, nil)
170
+ rescue Stripe::InvalidRequestError
171
+ new_charge(nil, nil, {source: generate_card_token, amount: invoice[:amount_due], currency: StripeMock.default_currency}, nil)
172
+ end
173
+ end
174
+
175
+ end
176
+ end
177
+ end
@@ -0,0 +1,80 @@
1
+ module StripeMock
2
+ module RequestHandlers
3
+ module Orders
4
+
5
+ def Orders.included(klass)
6
+ klass.add_handler 'post /v1/orders', :new_order
7
+ klass.add_handler 'post /v1/orders/(.*)/pay', :pay_order
8
+ klass.add_handler 'post /v1/orders/(.*)', :update_order
9
+ klass.add_handler 'get /v1/orders/(.*)', :get_order
10
+ klass.add_handler 'get /v1/orders', :list_orders
11
+ end
12
+
13
+ def new_order(route, method_url, params, headers)
14
+ params[:id] ||= new_id('or')
15
+ order_items = []
16
+
17
+ unless params[:currency].to_s.size == 3
18
+ raise Stripe::InvalidRequestError.new('You must supply a currency', nil, http_status: 400)
19
+ end
20
+
21
+ if params[:items]
22
+ unless params[:items].is_a? Array
23
+ raise Stripe::InvalidRequestError.new('You must supply a list of items', nil, http_status: 400)
24
+ end
25
+
26
+ unless params[:items].first.is_a? Hash
27
+ raise Stripe::InvalidRequestError.new('You must supply an item', nil, http_status: 400)
28
+ end
29
+ end
30
+
31
+ orders[ params[:id] ] = Data.mock_order(order_items, params)
32
+
33
+ orders[ params[:id] ]
34
+ end
35
+
36
+ def update_order(route, method_url, params, headers)
37
+ route =~ method_url
38
+ order = assert_existence :order, $1, orders[$1]
39
+
40
+ if params[:metadata]
41
+ if params[:metadata].empty?
42
+ order[:metadata] = {}
43
+ else
44
+ order[:metadata].merge(params[:metadata])
45
+ end
46
+ end
47
+
48
+ if %w(created paid canceled fulfilled returned).include? params[:status]
49
+ order[:status] = params[:status]
50
+ end
51
+ order
52
+ end
53
+
54
+ def get_order(route, method_url, params, headers)
55
+ route =~ method_url
56
+ assert_existence :order, $1, orders[$1]
57
+ end
58
+
59
+ def pay_order(route, method_url, params, headers)
60
+ route =~ method_url
61
+ order = assert_existence :order, $1, orders[$1]
62
+
63
+ if params[:source].blank? && params[:customer].blank?
64
+ raise Stripe::InvalidRequestError.new('You must supply a source or customer', nil, http_status: 400)
65
+ end
66
+
67
+ charge_id = new_id('ch')
68
+ charges[charge_id] = Data.mock_charge(id: charge_id)
69
+ order[:charge] = charge_id
70
+ order[:status] = "paid"
71
+ order
72
+ end
73
+
74
+ def list_orders(route, method_url, params, headers)
75
+ Data.mock_list_object(orders.values, params)
76
+ end
77
+
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,203 @@
1
+ module StripeMock
2
+ module RequestHandlers
3
+ module PaymentIntents
4
+ FAILED_TRANSACTION_AMOUNT = 3178
5
+ REQUIRES_ACTION_AMOUNT = 3184
6
+ REQUIRES_CAPTURE_AMOUNT = 3169
7
+
8
+ ALLOWED_PARAMS = [:description, :metadata, :receipt_email, :shipping, :destination, :payment_method, :payment_method_types, :setup_future_usage, :transfer_data, :amount, :currency]
9
+
10
+ def PaymentIntents.included(klass)
11
+ klass.add_handler 'post /v1/payment_intents', :new_payment_intent
12
+ klass.add_handler 'get /v1/payment_intents', :get_payment_intents
13
+ klass.add_handler 'get /v1/payment_intents/(.*)', :get_payment_intent
14
+ klass.add_handler 'post /v1/payment_intents/(.*)/confirm', :confirm_payment_intent
15
+ klass.add_handler 'post /v1/payment_intents/(.*)/capture', :capture_payment_intent
16
+ klass.add_handler 'post /v1/payment_intents/(.*)/cancel', :cancel_payment_intent
17
+ klass.add_handler 'post /v1/payment_intents/(.*)', :update_payment_intent
18
+ end
19
+
20
+ def new_payment_intent(route, method_url, params, headers)
21
+ id = new_id('pi')
22
+
23
+ ensure_payment_intent_required_params(params)
24
+ status = case params[:amount]
25
+ when REQUIRES_ACTION_AMOUNT then 'requires_action'
26
+ when FAILED_TRANSACTION_AMOUNT then 'requires_payment_method'
27
+ when REQUIRES_CAPTURE_AMOUNT then 'requires_capture'
28
+ else
29
+ 'succeeded'
30
+ end
31
+ last_payment_error = params[:amount] == FAILED_TRANSACTION_AMOUNT ? last_payment_error_generator(code: 'card_declined', decline_code: 'insufficient_funds', message: 'Not enough funds.') : nil
32
+ next_action = params[:amount] == REQUIRES_ACTION_AMOUNT ? { type: "use_stripe_sdk" } : nil
33
+ payment_intents[id] = Data.mock_payment_intent(
34
+ params.merge(
35
+ id: id,
36
+ status: status,
37
+ last_payment_error: last_payment_error,
38
+ next_action: next_action,
39
+ charges: status == 'succeeded' ? charges_data(params[:amount], params[:currency]) : empty_charges
40
+ )
41
+ )
42
+
43
+ payment_intents[id].clone
44
+ end
45
+
46
+ def update_payment_intent(route, method_url, params, headers)
47
+ route =~ method_url
48
+ id = $1
49
+
50
+ payment_intent = assert_existence :payment_intent, id, payment_intents[id]
51
+ payment_intents[id] = Util.rmerge(payment_intent, params.select{ |k,v| ALLOWED_PARAMS.include?(k)})
52
+ end
53
+
54
+ def get_payment_intents(route, method_url, params, headers)
55
+ params[:offset] ||= 0
56
+ params[:limit] ||= 10
57
+
58
+ clone = payment_intents.clone
59
+
60
+ if params[:customer]
61
+ clone.delete_if { |k,v| v[:customer] != params[:customer] }
62
+ end
63
+
64
+ Data.mock_list_object(clone.values, params)
65
+ end
66
+
67
+ def get_payment_intent(route, method_url, params, headers)
68
+ route =~ method_url
69
+ payment_intent_id = $1 || params[:payment_intent]
70
+ payment_intent = assert_existence :payment_intent, payment_intent_id, payment_intents[payment_intent_id]
71
+
72
+ payment_intent = payment_intent.clone
73
+ payment_intent
74
+ end
75
+
76
+ def capture_payment_intent(route, method_url, params, headers)
77
+ route =~ method_url
78
+ payment_intent = assert_existence :payment_intent, $1, payment_intents[$1]
79
+
80
+ payment_intent[:status] = payment_intent[:amount] == FAILED_TRANSACTION_AMOUNT ? 'requires_payment_method' : 'succeeded'
81
+ payment_intent[:last_payment_error] = payment_intent[:status] == 'requires_payment_method' ? last_payment_error_generator(code: 'card_declined', decline_code: 'do_not_honor', message: 'Your card has been declined. Please contact your bank for more information.') : nil
82
+ payment_intent
83
+ end
84
+
85
+ def confirm_payment_intent(route, method_url, params, headers)
86
+ route =~ method_url
87
+ payment_intent = assert_existence :payment_intent, $1, payment_intents[$1]
88
+
89
+ payment_intent[:status] = 'succeeded'
90
+ payment_intent[:charges] = charges_data(payment_intent[:amount], payment_intent[:currency])
91
+ payment_intent
92
+ end
93
+
94
+ def cancel_payment_intent(route, method_url, params, headers)
95
+ route =~ method_url
96
+ payment_intent = assert_existence :payment_intent, $1, payment_intents[$1]
97
+
98
+ payment_intent[:status] = 'canceled'
99
+ payment_intent
100
+ end
101
+
102
+ private
103
+
104
+ def ensure_payment_intent_required_params(params)
105
+ if params[:amount].nil?
106
+ require_param(:amount)
107
+ elsif params[:currency].nil?
108
+ require_param(:currency)
109
+ elsif non_integer_charge_amount?(params)
110
+ raise Stripe::InvalidRequestError.new("Invalid integer: #{params[:amount]}", 'amount', http_status: 400)
111
+ elsif non_positive_charge_amount?(params)
112
+ raise Stripe::InvalidRequestError.new('Invalid positive integer', 'amount', http_status: 400)
113
+ end
114
+ end
115
+
116
+ def non_integer_charge_amount?(params)
117
+ params[:amount] && !params[:amount].is_a?(Integer)
118
+ end
119
+
120
+ def non_positive_charge_amount?(params)
121
+ params[:amount] && params[:amount] < 1
122
+ end
123
+
124
+ def charges_data(amount, currency)
125
+ payment_intent_charge_id = new_id('ch')
126
+ payment_intent_charge = Data.mock_charge(
127
+ id: payment_intent_charge_id,
128
+ amount: amount,
129
+ currency: currency
130
+ )
131
+ charges[payment_intent_charge_id] = payment_intent_charge
132
+ {
133
+ :data => [payment_intent_charge],
134
+ :object => 'list',
135
+ has_more: false,
136
+ total_count: 1,
137
+ url: "/v1/charges?payment_intent=pi_1EwXFB2eZvKYlo2CggNnFBo8"
138
+ }
139
+ end
140
+
141
+ def empty_charges()
142
+ {
143
+ object: "list",
144
+ data: [],
145
+ has_more: false,
146
+ total_count: 0,
147
+ url: "/v1/charges?payment_intent=pi_1EwXFB2eZvKYlo2CggNnFBo8"
148
+ }
149
+ end
150
+
151
+ def last_payment_error_generator(code:, message:, decline_code:)
152
+ {
153
+ code: code,
154
+ doc_url: "https://stripe.com/docs/error-codes/payment-intent-authentication-failure",
155
+ message: message,
156
+ decline_code: decline_code,
157
+ payment_method: {
158
+ id: "pm_1EwXFA2eZvKYlo2C0tlY091l",
159
+ object: "payment_method",
160
+ billing_details: {
161
+ address: {
162
+ city: nil,
163
+ country: nil,
164
+ line1: nil,
165
+ line2: nil,
166
+ postal_code: nil,
167
+ state: nil
168
+ },
169
+ email: nil,
170
+ name: "seller_08072019090000",
171
+ phone: nil
172
+ },
173
+ card: {
174
+ brand: "visa",
175
+ checks: {
176
+ address_line1_check: nil,
177
+ address_postal_code_check: nil,
178
+ cvc_check: "unchecked"
179
+ },
180
+ country: "US",
181
+ exp_month: 12,
182
+ exp_year: 2021,
183
+ fingerprint: "LQBhEmJnItuj3mxf",
184
+ funding: "credit",
185
+ generated_from: nil,
186
+ last4: "1629",
187
+ three_d_secure_usage: {
188
+ supported: true
189
+ },
190
+ wallet: nil
191
+ },
192
+ created: 1563208900,
193
+ customer: nil,
194
+ livemode: false,
195
+ metadata: {},
196
+ type: "card"
197
+ },
198
+ type: "invalid_request_error"
199
+ }
200
+ end
201
+ end
202
+ end
203
+ end