stripe-ruby-mock 3.0.1 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/rspec_tests.yml +38 -0
  3. data/.rspec +2 -1
  4. data/CHANGELOG.md +62 -15
  5. data/Gemfile +1 -0
  6. data/README.md +5 -3
  7. data/lib/stripe_mock/api/client.rb +1 -1
  8. data/lib/stripe_mock/api/instance.rb +1 -1
  9. data/lib/stripe_mock/api/webhooks.rb +66 -25
  10. data/lib/stripe_mock/client.rb +2 -1
  11. data/lib/stripe_mock/data/list.rb +31 -6
  12. data/lib/stripe_mock/data.rb +201 -30
  13. data/lib/stripe_mock/instance.rb +12 -3
  14. data/lib/stripe_mock/request_handlers/account_links.rb +15 -0
  15. data/lib/stripe_mock/request_handlers/accounts.rb +17 -6
  16. data/lib/stripe_mock/request_handlers/charges.rb +11 -4
  17. data/lib/stripe_mock/request_handlers/checkout_session.rb +179 -0
  18. data/lib/stripe_mock/request_handlers/customers.rb +22 -13
  19. data/lib/stripe_mock/request_handlers/ephemeral_key.rb +1 -1
  20. data/lib/stripe_mock/request_handlers/events.rb +30 -3
  21. data/lib/stripe_mock/request_handlers/express_login_links.rb +15 -0
  22. data/lib/stripe_mock/request_handlers/helpers/coupon_helpers.rb +1 -0
  23. data/lib/stripe_mock/request_handlers/helpers/subscription_helpers.rb +36 -12
  24. data/lib/stripe_mock/request_handlers/invoices.rb +10 -4
  25. data/lib/stripe_mock/request_handlers/payment_intents.rb +13 -2
  26. data/lib/stripe_mock/request_handlers/payment_methods.rb +11 -4
  27. data/lib/stripe_mock/request_handlers/prices.rb +62 -0
  28. data/lib/stripe_mock/request_handlers/promotion_codes.rb +43 -0
  29. data/lib/stripe_mock/request_handlers/refunds.rb +13 -2
  30. data/lib/stripe_mock/request_handlers/setup_intents.rb +16 -9
  31. data/lib/stripe_mock/request_handlers/sources.rb +12 -6
  32. data/lib/stripe_mock/request_handlers/subscriptions.rb +120 -21
  33. data/lib/stripe_mock/request_handlers/tokens.rb +6 -4
  34. data/lib/stripe_mock/request_handlers/transfers.rb +12 -1
  35. data/lib/stripe_mock/request_handlers/validators/param_validators.rb +33 -4
  36. data/lib/stripe_mock/server.rb +2 -2
  37. data/lib/stripe_mock/test_strategies/base.rb +62 -10
  38. data/lib/stripe_mock/version.rb +1 -1
  39. data/lib/stripe_mock/webhook_fixtures/account.updated.json +1 -1
  40. data/lib/stripe_mock/webhook_fixtures/balance.available.json +27 -15
  41. data/lib/stripe_mock/webhook_fixtures/charge.captured.json +143 -0
  42. data/lib/stripe_mock/webhook_fixtures/charge.dispute.created.json +63 -16
  43. data/lib/stripe_mock/webhook_fixtures/charge.failed.json +49 -120
  44. data/lib/stripe_mock/webhook_fixtures/charge.refund.updated.json +35 -0
  45. data/lib/stripe_mock/webhook_fixtures/charge.refunded.json +145 -50
  46. data/lib/stripe_mock/webhook_fixtures/charge.succeeded.json +114 -43
  47. data/lib/stripe_mock/webhook_fixtures/checkout.session.completed.json +79 -0
  48. data/lib/stripe_mock/webhook_fixtures/checkout.session.completed.payment_mode.json +53 -0
  49. data/lib/stripe_mock/webhook_fixtures/checkout.session.completed.setup_mode.json +45 -0
  50. data/lib/stripe_mock/webhook_fixtures/customer.created.json +37 -46
  51. data/lib/stripe_mock/webhook_fixtures/customer.deleted.json +36 -32
  52. data/lib/stripe_mock/webhook_fixtures/customer.source.created.json +31 -22
  53. data/lib/stripe_mock/webhook_fixtures/customer.source.updated.json +36 -25
  54. data/lib/stripe_mock/webhook_fixtures/customer.subscription.created.json +135 -47
  55. data/lib/stripe_mock/webhook_fixtures/customer.subscription.deleted.json +134 -45
  56. data/lib/stripe_mock/webhook_fixtures/customer.subscription.updated.json +135 -56
  57. data/lib/stripe_mock/webhook_fixtures/customer.updated.json +38 -47
  58. data/lib/stripe_mock/webhook_fixtures/invoice.created.json +176 -49
  59. data/lib/stripe_mock/webhook_fixtures/invoice.finalized.json +171 -0
  60. data/lib/stripe_mock/webhook_fixtures/invoice.paid.json +171 -0
  61. data/lib/stripe_mock/webhook_fixtures/invoice.payment_action_required.json +171 -0
  62. data/lib/stripe_mock/webhook_fixtures/invoice.payment_failed.json +149 -83
  63. data/lib/stripe_mock/webhook_fixtures/invoice.payment_succeeded.json +149 -90
  64. data/lib/stripe_mock/webhook_fixtures/invoice.upcoming.json +70 -0
  65. data/lib/stripe_mock/webhook_fixtures/invoice.updated.json +178 -50
  66. data/lib/stripe_mock/webhook_fixtures/invoiceitem.created.json +87 -13
  67. data/lib/stripe_mock/webhook_fixtures/invoiceitem.updated.json +88 -14
  68. data/lib/stripe_mock/webhook_fixtures/mandate.updated.json +34 -0
  69. data/lib/stripe_mock/webhook_fixtures/payment_intent.amount_capturable_updated.json +170 -0
  70. data/lib/stripe_mock/webhook_fixtures/payment_intent.canceled.json +73 -0
  71. data/lib/stripe_mock/webhook_fixtures/payment_intent.created.json +86 -0
  72. data/lib/stripe_mock/webhook_fixtures/payment_intent.payment_failed.json +225 -0
  73. data/lib/stripe_mock/webhook_fixtures/payment_intent.processing.json +162 -0
  74. data/lib/stripe_mock/webhook_fixtures/payment_intent.requires_action.json +191 -0
  75. data/lib/stripe_mock/webhook_fixtures/payment_intent.succeeded.json +196 -0
  76. data/lib/stripe_mock/webhook_fixtures/payment_link.created.json +47 -0
  77. data/lib/stripe_mock/webhook_fixtures/payment_link.updated.json +50 -0
  78. data/lib/stripe_mock/webhook_fixtures/payment_method.attached.json +63 -0
  79. data/lib/stripe_mock/webhook_fixtures/payment_method.detached.json +62 -0
  80. data/lib/stripe_mock/webhook_fixtures/payout.created.json +40 -0
  81. data/lib/stripe_mock/webhook_fixtures/payout.paid.json +40 -0
  82. data/lib/stripe_mock/webhook_fixtures/payout.updated.json +46 -0
  83. data/lib/stripe_mock/webhook_fixtures/plan.created.json +30 -13
  84. data/lib/stripe_mock/webhook_fixtures/plan.deleted.json +30 -13
  85. data/lib/stripe_mock/webhook_fixtures/plan.updated.json +34 -14
  86. data/lib/stripe_mock/webhook_fixtures/price.created.json +42 -0
  87. data/lib/stripe_mock/webhook_fixtures/price.deleted.json +42 -0
  88. data/lib/stripe_mock/webhook_fixtures/price.updated.json +48 -0
  89. data/lib/stripe_mock/webhook_fixtures/product.created.json +19 -13
  90. data/lib/stripe_mock/webhook_fixtures/product.deleted.json +20 -14
  91. data/lib/stripe_mock/webhook_fixtures/product.updated.json +24 -15
  92. data/lib/stripe_mock/webhook_fixtures/quote.accepted.json +92 -0
  93. data/lib/stripe_mock/webhook_fixtures/quote.canceled.json +92 -0
  94. data/lib/stripe_mock/webhook_fixtures/quote.created.json +92 -0
  95. data/lib/stripe_mock/webhook_fixtures/quote.finalized.json +92 -0
  96. data/lib/stripe_mock/webhook_fixtures/setup_intent.canceled.json +46 -0
  97. data/lib/stripe_mock/webhook_fixtures/setup_intent.created.json +51 -0
  98. data/lib/stripe_mock/webhook_fixtures/setup_intent.setup_failed.json +100 -0
  99. data/lib/stripe_mock/webhook_fixtures/setup_intent.succeeded.json +46 -0
  100. data/lib/stripe_mock/webhook_fixtures/subscription_schedule.canceled.json +119 -0
  101. data/lib/stripe_mock/webhook_fixtures/subscription_schedule.created.json +114 -0
  102. data/lib/stripe_mock/webhook_fixtures/subscription_schedule.released.json +111 -0
  103. data/lib/stripe_mock/webhook_fixtures/subscription_schedule.updated.json +125 -0
  104. data/lib/stripe_mock/webhook_fixtures/tax_rate.created.json +32 -0
  105. data/lib/stripe_mock/webhook_fixtures/tax_rate.updated.json +37 -0
  106. data/lib/stripe_mock.rb +7 -1
  107. data/spec/instance_spec.rb +7 -7
  108. data/spec/integration_examples/completing_checkout_sessions_example.rb +37 -0
  109. data/spec/list_spec.rb +23 -0
  110. data/spec/readme_spec.rb +1 -1
  111. data/spec/server_spec.rb +4 -2
  112. data/spec/shared_stripe_examples/account_examples.rb +9 -1
  113. data/spec/shared_stripe_examples/account_link_examples.rb +16 -0
  114. data/spec/shared_stripe_examples/balance_examples.rb +6 -0
  115. data/spec/shared_stripe_examples/card_token_examples.rb +17 -21
  116. data/spec/shared_stripe_examples/checkout_session_examples.rb +99 -0
  117. data/spec/shared_stripe_examples/customer_examples.rb +11 -13
  118. data/spec/shared_stripe_examples/express_login_link_examples.rb +12 -0
  119. data/spec/shared_stripe_examples/invoice_examples.rb +29 -8
  120. data/spec/shared_stripe_examples/payment_intent_examples.rb +74 -0
  121. data/spec/shared_stripe_examples/payment_method_examples.rb +336 -67
  122. data/spec/shared_stripe_examples/price_examples.rb +223 -0
  123. data/spec/shared_stripe_examples/product_examples.rb +1 -9
  124. data/spec/shared_stripe_examples/promotion_code_examples.rb +68 -0
  125. data/spec/shared_stripe_examples/refund_examples.rb +13 -0
  126. data/spec/shared_stripe_examples/setup_intent_examples.rb +17 -0
  127. data/spec/shared_stripe_examples/subscription_examples.rb +327 -9
  128. data/spec/shared_stripe_examples/transfer_examples.rb +10 -1
  129. data/spec/shared_stripe_examples/webhook_event_examples.rb +51 -5
  130. data/spec/spec_helper.rb +4 -0
  131. data/spec/stripe_mock_spec.rb +2 -2
  132. data/spec/support/stripe_examples.rb +8 -2
  133. data/stripe-ruby-mock.gemspec +7 -2
  134. metadata +72 -15
  135. data/.travis.yml +0 -28
  136. data/lib/stripe_mock/request_handlers/checkout.rb +0 -15
  137. data/spec/shared_stripe_examples/checkout_examples.rb +0 -19
@@ -101,6 +101,27 @@ module StripeMock
101
101
  }.merge(params)
102
102
  end
103
103
 
104
+ def self.mock_account_link(params = {})
105
+ now = Time.now.to_i
106
+ {
107
+ object: 'account_link',
108
+ created: now,
109
+ expires_at: now + 300,
110
+ url: 'https://connect.stripe.com/setup/c/iB0ph1cPnRLY',
111
+ data: {}
112
+ }.merge(params)
113
+ end
114
+
115
+ def self.mock_express_login_link(params = {})
116
+ now = Time.now.to_i
117
+ {
118
+ object: 'login_link',
119
+ created: now,
120
+ url: 'https://connect.stripe.com/express/Ln7FfnNpUcCU',
121
+ data: {}
122
+ }.merge(params)
123
+ end
124
+
104
125
  def self.mock_tax_rate(params)
105
126
  {
106
127
  id: 'test_cus_default',
@@ -119,7 +140,7 @@ module StripeMock
119
140
 
120
141
  def self.mock_customer(sources, params)
121
142
  cus_id = params[:id] || "test_cus_default"
122
- currency = params[:currency] || StripeMock.default_currency
143
+ currency = params[:currency]
123
144
  sources.each {|source| source[:customer] = cus_id}
124
145
  {
125
146
  email: 'stripe_mock@example.com',
@@ -142,12 +163,14 @@ module StripeMock
142
163
  sources: {
143
164
  object: "list",
144
165
  total_count: sources.size,
166
+ has_more: false,
145
167
  url: "/v1/customers/#{cus_id}/sources",
146
168
  data: sources
147
169
  },
148
170
  subscriptions: {
149
171
  object: "list",
150
172
  total_count: 0,
173
+ has_more: false,
151
174
  url: "/v1/customers/#{cus_id}/subscriptions",
152
175
  data: []
153
176
  },
@@ -166,11 +189,34 @@ module StripeMock
166
189
  paid: true,
167
190
  amount: 0,
168
191
  application_fee: nil,
192
+ application_fee_amount: nil,
169
193
  currency: currency,
170
194
  destination: nil,
171
195
  fraud_details: {},
196
+ payment_method_details: {
197
+ card: {
198
+ brand: "visa",
199
+ checks: {
200
+ address_line1_check: nil,
201
+ address_postal_code_check: nil,
202
+ cvc_check: "pass"
203
+ },
204
+ country: "US",
205
+ exp_month: 12,
206
+ exp_year: 2013,
207
+ fingerprint: "3TQGpK9JoY1GgXPw",
208
+ funding: "credit",
209
+ installments: nil,
210
+ last4: "4242",
211
+ network: "visa",
212
+ three_d_secure: nil,
213
+ wallet: nil
214
+ },
215
+ type: "card"
216
+ },
172
217
  receipt_email: nil,
173
218
  receipt_number: nil,
219
+ receipt_url: nil,
174
220
  refunded: false,
175
221
  shipping: {},
176
222
  statement_descriptor: "Charge #{charge_id}",
@@ -231,7 +277,8 @@ module StripeMock
231
277
  charge: "ch_4fWhYjzQ23UFWT",
232
278
  receipt_number: nil,
233
279
  status: "succeeded",
234
- reason: "requested_by_customer"
280
+ reason: "requested_by_customer",
281
+ receipt_url: nil
235
282
  }.merge(params)
236
283
  end
237
284
 
@@ -339,8 +386,9 @@ module StripeMock
339
386
  },
340
387
  cancel_at_period_end: false,
341
388
  canceled_at: nil,
389
+ collection_method: 'charge_automatically',
342
390
  ended_at: nil,
343
- start: 1308595038,
391
+ start_date: 1308595038,
344
392
  object: 'subscription',
345
393
  trial_start: 1308595038,
346
394
  trial_end: 1308681468,
@@ -350,8 +398,10 @@ module StripeMock
350
398
  discount: nil,
351
399
  metadata: {},
352
400
  default_tax_rates: nil,
401
+ default_payment_method: nil,
353
402
  pending_invoice_item_interval: nil,
354
- next_pending_invoice_item_invoice: nil
403
+ next_pending_invoice_item_invoice: nil,
404
+ latest_invoice: nil
355
405
  }, params)
356
406
  end
357
407
 
@@ -371,6 +421,7 @@ module StripeMock
371
421
  lines: {
372
422
  object: "list",
373
423
  total_count: lines.count,
424
+ has_more: false,
374
425
  url: "/v1/invoices/#{in_id}/lines",
375
426
  data: lines
376
427
  },
@@ -399,7 +450,8 @@ module StripeMock
399
450
  next_payment_attempt: 1349825350,
400
451
  charge: nil,
401
452
  discount: nil,
402
- subscription: nil
453
+ subscription: nil,
454
+ number: "6C41730-0001"
403
455
  }.merge(params)
404
456
  if invoice[:discount]
405
457
  invoice[:total] = [0, invoice[:subtotal] - invoice[:discount][:coupon][:amount_off]].max if invoice[:discount][:coupon][:amount_off]
@@ -554,6 +606,34 @@ module StripeMock
554
606
  }.merge(params)
555
607
  end
556
608
 
609
+ def self.mock_price(params={})
610
+ currency = params[:currency] || StripeMock.default_currency
611
+ {
612
+ id: "mock_price_123",
613
+ object: "price",
614
+ active: true,
615
+ billing_scheme: "per_unit",
616
+ created: 1593044959,
617
+ currency: currency,
618
+ livemode: false,
619
+ lookup_key: nil,
620
+ metadata: {},
621
+ nickname: 'My Mock Price',
622
+ product: "mock_prod_NONEXIST", # override this with your own existing product id
623
+ recurring: {
624
+ aggregate_usage: nil,
625
+ interval: "month",
626
+ interval_count: 1,
627
+ usage_type: "licensed"
628
+ },
629
+ tiers_mode: nil,
630
+ transform_quantity: nil,
631
+ type: "recurring",
632
+ unit_amount: 2000,
633
+ unit_amount_decimal: "2000"
634
+ }.merge(params)
635
+ end
636
+
557
637
  def self.mock_product(params={})
558
638
  {
559
639
  id: "mock_prod_abc123",
@@ -578,6 +658,44 @@ module StripeMock
578
658
  }.merge(params)
579
659
  end
580
660
 
661
+ def self.mock_promotion_code(params={})
662
+ {
663
+ id: "mock_promo_abc123",
664
+ object: "promotion_code",
665
+ active: true,
666
+ code: "TESTCODE",
667
+ coupon: {
668
+ id: "mock_coupon_abc123",
669
+ object: "coupon",
670
+ amount_off: nil,
671
+ created: 1665773498,
672
+ currency: "usd",
673
+ duration: "repeating",
674
+ duration_in_months: 1,
675
+ livemode: false,
676
+ max_redemptions: nil,
677
+ metadata: {},
678
+ name: "Mock Coupon",
679
+ percent_off: 10.0,
680
+ redeem_by: nil,
681
+ times_redeemed: 0,
682
+ valid: true
683
+ },
684
+ created: 1665773499,
685
+ customer: nil,
686
+ expires_at: nil,
687
+ livemode: false,
688
+ max_redemptions: nil,
689
+ metadata: {},
690
+ restrictions: {
691
+ first_time_transaction: false,
692
+ minimum_amount: nil,
693
+ minimum_amount_currency: nil
694
+ },
695
+ times_redeemed: 0
696
+ }.merge(params)
697
+ end
698
+
581
699
  def self.mock_recipient(cards, params={})
582
700
  rp_id = params[:id] || "test_rp_default"
583
701
  cards.each {|card| card[:recipient] = rp_id}
@@ -601,6 +719,7 @@ module StripeMock
601
719
  object: "list",
602
720
  url: "/v1/recipients/#{rp_id}/cards",
603
721
  data: cards,
722
+ has_more: false,
604
723
  total_count: cards.count
605
724
  },
606
725
  default_card: nil
@@ -1008,6 +1127,16 @@ module StripeMock
1008
1127
  bitcoin_receiver: 1545182
1009
1128
  }
1010
1129
  }],
1130
+ instant_available: [
1131
+ {
1132
+ currency: "usd",
1133
+ amount: usd_balance,
1134
+ source_types: {
1135
+ card: 25907032203,
1136
+ bank_account: 108476658,
1137
+ bitcoin_receiver: 1545182
1138
+ }
1139
+ }],
1011
1140
  connect_reserved: [
1012
1141
  {
1013
1142
  currency: "usd",
@@ -1072,13 +1201,12 @@ module StripeMock
1072
1201
  end
1073
1202
 
1074
1203
  def self.mock_subscription_item(params = {})
1075
- iid = params[:id] || 'test_txn_default'
1204
+ id = params[:id] || 'test_si_default'
1076
1205
  {
1077
- id: iid,
1206
+ id: id,
1078
1207
  object: 'subscription_item',
1079
1208
  created: 1504716183,
1080
- metadata: {
1081
- },
1209
+ metadata: {},
1082
1210
  plan: {
1083
1211
  id: 'PER_USER_PLAN1',
1084
1212
  object: 'plan',
@@ -1167,29 +1295,72 @@ module StripeMock
1167
1295
  end
1168
1296
 
1169
1297
  def self.mock_payment_method(params = {})
1170
- payment_method_id = params[:id] || "pm_1ExEuFL2DI6wht39WNJgbybl"
1171
- {
1172
- id: payment_method_id,
1173
- object: "payment_method",
1174
- type: "card",
1175
- billing_details: {},
1176
- card: {
1177
- brand: "visa",
1178
- checks: { address_line1_check: nil, address_postal_code_check: nil, cvc_check: "pass" },
1179
- country: "FR",
1180
- exp_month: 2,
1181
- exp_year: 2022,
1182
- fingerprint: "Hr3Ly5z5IYxsokWA",
1183
- funding: "credit",
1184
- last4: "3155",
1185
- three_d_secure_usage: { supported: true }
1298
+ payment_method_id = params[:id] || 'pm_1ExEuFL2DI6wht39WNJgbybl'
1299
+
1300
+ type = params[:type].to_sym
1301
+ last4 = params.dig(:card, :number)
1302
+ data = {
1303
+ card: {
1304
+ brand: case last4&.to_s
1305
+ when /^4/, nil
1306
+ 'visa'
1307
+ when /^5[1-5]/
1308
+ 'mastercard'
1309
+ else
1310
+ 'unknown'
1311
+ end,
1312
+ checks: {
1313
+ address_line1_check: nil,
1314
+ address_postal_code_check: nil,
1315
+ cvc_check: 'pass'
1186
1316
  },
1187
- customer: params[:customer] || nil,
1188
- metadata: {
1189
- order_id: "123456789"
1190
- }
1317
+ country: 'FR',
1318
+ exp_month: params.dig(:card, :exp_month) || 2,
1319
+ exp_year: params.dig(:card, :exp_year) || 2022,
1320
+ fingerprint: 'Hr3Ly5z5IYxsokWA',
1321
+ funding: 'credit',
1322
+ generated_from: nil,
1323
+ last4: last4.nil? ? '3155' : last4.to_s[-4..],
1324
+ three_d_secure_usage: { supported: true },
1325
+ wallet: nil
1326
+ },
1327
+ ideal: {
1328
+ bank: 'ing',
1329
+ bic: 'INGBNL2A',
1330
+ iban_last4: '****',
1331
+ verified_name: 'JENNY ROSEN'
1332
+ },
1333
+ sepa_debit: {
1334
+ bank_code: '37040044',
1335
+ branch_code: '',
1336
+ country: 'DE',
1337
+ fingerprint: 'FD81kbVPe7M05BMj',
1338
+ last4: params.dig(:sepa_debit, :iban)&.[](-4..) || '3000'
1339
+ }
1340
+ }
1191
1341
 
1192
- }.merge(params)
1342
+ {
1343
+ id: payment_method_id,
1344
+ object: 'payment_method',
1345
+ type: params[:type],
1346
+ billing_details: {
1347
+ address: {
1348
+ city: 'New Orleans',
1349
+ country: 'US',
1350
+ line1: 'Bourbon Street 23',
1351
+ line2: nil,
1352
+ postal_code: '10000',
1353
+ state: nil
1354
+ },
1355
+ email: 'foo@bar.com',
1356
+ name: 'John Dolton',
1357
+ phone: nil
1358
+ },
1359
+ customer: params[:customer] || nil,
1360
+ metadata: {
1361
+ order_id: '123456789'
1362
+ }
1363
+ }.merge(params).merge(type => data[type])
1193
1364
  end
1194
1365
 
1195
1366
  def self.mock_setup_intent(params = {})
@@ -24,6 +24,8 @@ module StripeMock
24
24
  include StripeMock::RequestHandlers::PaymentMethods
25
25
  include StripeMock::RequestHandlers::SetupIntents
26
26
  include StripeMock::RequestHandlers::ExternalAccounts
27
+ include StripeMock::RequestHandlers::AccountLinks
28
+ include StripeMock::RequestHandlers::ExpressLoginLinks
27
29
  include StripeMock::RequestHandlers::Accounts
28
30
  include StripeMock::RequestHandlers::Balance
29
31
  include StripeMock::RequestHandlers::BalanceTransactions
@@ -40,7 +42,9 @@ module StripeMock
40
42
  include StripeMock::RequestHandlers::InvoiceItems
41
43
  include StripeMock::RequestHandlers::Orders
42
44
  include StripeMock::RequestHandlers::Plans
45
+ include StripeMock::RequestHandlers::Prices
43
46
  include StripeMock::RequestHandlers::Products
47
+ include StripeMock::RequestHandlers::PromotionCodes
44
48
  include StripeMock::RequestHandlers::Refunds
45
49
  include StripeMock::RequestHandlers::Recipients
46
50
  include StripeMock::RequestHandlers::Transfers
@@ -50,11 +54,13 @@ module StripeMock
50
54
  include StripeMock::RequestHandlers::EphemeralKey
51
55
  include StripeMock::RequestHandlers::TaxRates
52
56
  include StripeMock::RequestHandlers::Checkout
57
+ include StripeMock::RequestHandlers::Checkout::Session
53
58
 
54
59
  attr_reader :accounts, :balance, :balance_transactions, :bank_tokens, :charges, :coupons, :customers,
55
60
  :disputes, :events, :invoices, :invoice_items, :orders, :payment_intents, :payment_methods,
56
- :setup_intents, :plans, :recipients, :refunds, :transfers, :payouts, :subscriptions, :country_spec,
57
- :subscriptions_items, :products, :tax_rates, :checkout_sessions
61
+ :setup_intents, :plans, :prices, :promotion_codes, :recipients, :refunds, :transfers, :payouts,
62
+ :subscriptions, :country_spec, :subscriptions_items, :products, :tax_rates, :checkout_sessions,
63
+ :checkout_session_line_items
58
64
 
59
65
  attr_accessor :error_queue, :debug, :conversion_rate, :account_balance
60
66
 
@@ -64,7 +70,7 @@ module StripeMock
64
70
  @balance_transactions = Data.mock_balance_transactions(['txn_05RsQX2eZvKYlo2C0FRTGSSA','txn_15RsQX2eZvKYlo2C0ERTYUIA', 'txn_25RsQX2eZvKYlo2C0ZXCVBNM', 'txn_35RsQX2eZvKYlo2C0QAZXSWE', 'txn_45RsQX2eZvKYlo2C0EDCVFRT', 'txn_55RsQX2eZvKYlo2C0OIKLJUY', 'txn_65RsQX2eZvKYlo2C0ASDFGHJ', 'txn_75RsQX2eZvKYlo2C0EDCXSWQ', 'txn_85RsQX2eZvKYlo2C0UJMCDET', 'txn_95RsQX2eZvKYlo2C0EDFRYUI'])
65
71
  @bank_tokens = {}
66
72
  @card_tokens = {}
67
- @customers = {}
73
+ @customers = { Stripe.api_key => {} }
68
74
  @charges = {}
69
75
  @payment_intents = {}
70
76
  @payment_methods = {}
@@ -77,7 +83,9 @@ module StripeMock
77
83
  @orders = {}
78
84
  @payment_methods = {}
79
85
  @plans = {}
86
+ @prices = {}
80
87
  @products = {}
88
+ @promotion_codes = {}
81
89
  @recipients = {}
82
90
  @refunds = {}
83
91
  @transfers = {}
@@ -87,6 +95,7 @@ module StripeMock
87
95
  @country_spec = {}
88
96
  @tax_rates = {}
89
97
  @checkout_sessions = {}
98
+ @checkout_session_line_items = {}
90
99
 
91
100
  @debug = false
92
101
  @error_queue = ErrorQueue.new
@@ -0,0 +1,15 @@
1
+ module StripeMock
2
+ module RequestHandlers
3
+ module AccountLinks
4
+
5
+ def AccountLinks.included(klass)
6
+ klass.add_handler 'post /v1/account_links', :new_account_link
7
+ end
8
+
9
+ def new_account_link(route, method_url, params, headers)
10
+ route =~ method_url
11
+ Data.mock_account_link(params)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -4,12 +4,13 @@ module StripeMock
4
4
  VALID_START_YEAR = 2009
5
5
 
6
6
  def Accounts.included(klass)
7
- klass.add_handler 'post /v1/accounts', :new_account
8
- klass.add_handler 'get /v1/account', :get_account
9
- klass.add_handler 'get /v1/accounts/(.*)', :get_account
10
- klass.add_handler 'post /v1/accounts/(.*)', :update_account
11
- klass.add_handler 'get /v1/accounts', :list_accounts
12
- klass.add_handler 'post /oauth/deauthorize',:deauthorize
7
+ klass.add_handler 'post /v1/accounts', :new_account
8
+ klass.add_handler 'get /v1/account', :get_account
9
+ klass.add_handler 'get /v1/accounts/(.*)', :get_account
10
+ klass.add_handler 'post /v1/accounts/(.*)', :update_account
11
+ klass.add_handler 'get /v1/accounts', :list_accounts
12
+ klass.add_handler 'post /oauth/deauthorize', :deauthorize
13
+ klass.add_handler 'delete /v1/accounts/(.*)', :delete_account
13
14
  end
14
15
 
15
16
  def new_account(route, method_url, params, headers)
@@ -48,6 +49,16 @@ module StripeMock
48
49
  Stripe::StripeObject.construct_from(:stripe_user_id => params[:stripe_user_id])
49
50
  end
50
51
 
52
+ def delete_account(route, method_url, params, headers)
53
+ init_account
54
+ route =~ method_url
55
+ assert_existence :account, $1, accounts[$1]
56
+ accounts[$1] = {
57
+ id: accounts[$1][:id],
58
+ deleted: true
59
+ }
60
+ end
61
+
51
62
  private
52
63
 
53
64
  def init_account
@@ -12,7 +12,9 @@ module StripeMock
12
12
  klass.add_handler 'post /v1/charges/(.*)', :update_charge
13
13
  end
14
14
 
15
- def new_charge(route, method_url, params, headers)
15
+ def new_charge(route, method_url, params, headers = {})
16
+ stripe_account = headers && headers[:stripe_account] || Stripe.api_key
17
+
16
18
  if headers && headers[:idempotency_key]
17
19
  params[:idempotency_key] = headers[:idempotency_key]
18
20
  if charges.any?
@@ -29,7 +31,7 @@ module StripeMock
29
31
  # card id, not a token. in this case we'll find the card in the customer
30
32
  # object and return that.
31
33
  if params[:customer]
32
- params[:source] = get_card(customers[params[:customer]], params[:source])
34
+ params[:source] = get_card(customers[stripe_account][params[:customer]], params[:source])
33
35
  else
34
36
  params[:source] = get_card_or_bank_by_token(params[:source])
35
37
  end
@@ -37,7 +39,7 @@ module StripeMock
37
39
  raise Stripe::InvalidRequestError.new("Invalid token id: #{params[:source]}", 'card', http_status: 400)
38
40
  end
39
41
  elsif params[:customer]
40
- customer = customers[params[:customer]]
42
+ customer = customers[stripe_account][params[:customer]]
41
43
  if customer && customer[:default_source]
42
44
  params[:source] = get_card(customer, customer[:default_source])
43
45
  end
@@ -146,7 +148,7 @@ module StripeMock
146
148
  elsif non_positive_charge_amount?(params)
147
149
  raise Stripe::InvalidRequestError.new('Invalid positive integer', 'amount', http_status: 400)
148
150
  elsif params[:source].nil? && params[:customer].nil?
149
- raise Stripe::InvalidRequestError.new('Must provide source or customer.', http_status: nil)
151
+ raise Stripe::InvalidRequestError.new('Must provide source or customer.', nil, http_status: nil)
150
152
  end
151
153
  end
152
154
 
@@ -169,6 +171,11 @@ module StripeMock
169
171
  params[:refunds].has_key?(:data) && params[:refunds][:data].nil?)
170
172
  allowed << :refunds
171
173
  end
174
+ if params.has_key?(:payment_method_details) && (params[:payment_method_details].empty? ||
175
+ params[:payment_method_details].has_key?(:card) && (params[:payment_method_details][:card].empty? ||
176
+ params[:payment_method_details][:card].has_key?(:checks) && params[:payment_method_details][:card][:checks].empty?))
177
+ allowed << :payment_method_details
178
+ end
172
179
 
173
180
  allowed
174
181
  end
@@ -0,0 +1,179 @@
1
+ module StripeMock
2
+ module RequestHandlers
3
+ module Checkout
4
+ module Session
5
+ def Session.included(klass)
6
+ klass.add_handler 'post /v1/checkout/sessions', :new_session
7
+ klass.add_handler 'get /v1/checkout/sessions', :list_checkout_sessions
8
+ klass.add_handler 'get /v1/checkout/sessions/([^/]*)', :get_checkout_session
9
+ klass.add_handler 'get /v1/checkout/sessions/([^/]*)/line_items', :list_line_items
10
+ end
11
+
12
+ def new_session(route, method_url, params, headers)
13
+ id = params[:id] || new_id('cs')
14
+
15
+ [:cancel_url, :success_url].each do |p|
16
+ require_param(p) if params[p].nil? || params[p].empty?
17
+ end
18
+
19
+ line_items = nil
20
+ if params[:line_items]
21
+ line_items = params[:line_items].each_with_index.map do |line_item, i|
22
+ throw Stripe::InvalidRequestError("Quantity is required. Add `quantity` to `line_items[#{i}]`") unless line_item[:quantity]
23
+ unless line_item[:price] || line_item[:price_data] || (line_item[:amount] && line_item[:currency] && line_item[:name])
24
+ throw Stripe::InvalidRequestError("Price or amount and currency is required. Add `price`, `price_data`, or `amount`, `currency` and `name` to `line_items[#{i}]`")
25
+ end
26
+ {
27
+ id: new_id("li"),
28
+ price: if line_item[:price]
29
+ line_item[:price]
30
+ elsif line_item[:price_data]
31
+ new_price(nil, nil, line_item[:price_data], nil)[:id]
32
+ else
33
+ new_price(nil, nil, {
34
+ unit_amount: line_item[:amount],
35
+ currency: line_item[:currency],
36
+ product_data: {
37
+ name: line_item[:name]
38
+ }
39
+ }, nil)[:id]
40
+ end,
41
+ quantity: line_item[:quantity]
42
+ }
43
+ end
44
+ end
45
+
46
+ amount = nil
47
+ currency = nil
48
+ if line_items
49
+ amount = 0
50
+
51
+ line_items.each do |line_item|
52
+ price = prices[line_item[:price]]
53
+
54
+ if price.nil?
55
+ raise StripeMock::StripeMockError.new("Price not found for ID: #{line_item[:price]}")
56
+ end
57
+
58
+ amount += (price[:unit_amount] * line_item[:quantity])
59
+ end
60
+
61
+ currency = prices[line_items.first[:price]][:currency]
62
+ end
63
+
64
+ payment_status = "unpaid"
65
+ payment_intent = nil
66
+ setup_intent = nil
67
+ case params[:mode]
68
+ when nil, "payment"
69
+ params[:customer] ||= new_customer(nil, nil, {email: params[:customer_email]}, nil)[:id]
70
+ require_param(:line_items) if params[:line_items].nil? || params[:line_items].empty?
71
+ payment_intent = new_payment_intent(nil, nil, {
72
+ amount: amount,
73
+ currency: currency,
74
+ customer: params[:customer],
75
+ payment_method_options: params[:payment_method_options],
76
+ payment_method_types: params[:payment_method_types]
77
+ }.merge(params[:payment_intent_data] || {}), nil)[:id]
78
+ checkout_session_line_items[id] = line_items
79
+ when "setup"
80
+ if !params[:line_items].nil? && !params[:line_items].empty?
81
+ throw Stripe::InvalidRequestError.new("You cannot pass `line_items` in `setup` mode", :line_items, http_status: 400)
82
+ end
83
+ setup_intent = new_setup_intent(nil, nil, {
84
+ customer: params[:customer],
85
+ payment_method_options: params[:payment_method_options],
86
+ payment_method_types: params[:payment_method_types]
87
+ }.merge(params[:setup_intent_data] || {}), nil)[:id]
88
+ payment_status = "no_payment_required"
89
+ when "subscription"
90
+ params[:customer] ||= new_customer(nil, nil, {email: params[:customer_email]}, nil)[:id]
91
+ require_param(:line_items) if params[:line_items].nil? || params[:line_items].empty?
92
+ checkout_session_line_items[id] = line_items
93
+ else
94
+ throw Stripe::InvalidRequestError.new("Invalid mode: must be one of payment, setup, or subscription", :mode, http_status: 400)
95
+ end
96
+
97
+ checkout_sessions[id] = {
98
+ id: id,
99
+ object: "checkout.session",
100
+ allow_promotion_codes: nil,
101
+ amount_subtotal: amount,
102
+ amount_total: amount,
103
+ automatic_tax: {
104
+ enabled: false,
105
+ status: nil
106
+ },
107
+ billing_address_collection: nil,
108
+ cancel_url: params[:cancel_url],
109
+ client_reference_id: nil,
110
+ currency: currency,
111
+ customer: params[:customer],
112
+ customer_details: nil,
113
+ customer_email: params[:customer_email],
114
+ livemode: false,
115
+ locale: nil,
116
+ metadata: params[:metadata],
117
+ mode: params[:mode],
118
+ payment_intent: payment_intent,
119
+ payment_method_options: params[:payment_method_options],
120
+ payment_method_types: params[:payment_method_types],
121
+ payment_status: payment_status,
122
+ setup_intent: setup_intent,
123
+ shipping: nil,
124
+ shipping_address_collection: nil,
125
+ submit_type: nil,
126
+ subscription: nil,
127
+ success_url: params[:success_url],
128
+ total_details: nil,
129
+ url: URI.join(StripeMock.checkout_base, id).to_s
130
+ }
131
+ end
132
+
133
+ def list_checkout_sessions(route, method_url, params, headers)
134
+ Data.mock_list_object(checkout_sessions.values)
135
+ end
136
+
137
+ def get_checkout_session(route, method_url, params, headers)
138
+ route =~ method_url
139
+ checkout_session = assert_existence :checkout_session, $1, checkout_sessions[$1]
140
+
141
+ checkout_session = checkout_session.clone
142
+ if params[:expand]&.include?('setup_intent') && checkout_session[:setup_intent]
143
+ checkout_session[:setup_intent] = setup_intents[checkout_session[:setup_intent]]
144
+ end
145
+ checkout_session
146
+ end
147
+
148
+ def list_line_items(route, method_url, params, headers)
149
+ route =~ method_url
150
+ checkout_session = assert_existence :checkout_session, $1, checkout_sessions[$1]
151
+
152
+ case checkout_session[:mode]
153
+ when "payment", "subscription"
154
+ line_items = assert_existence :checkout_session_line_items, $1, checkout_session_line_items[$1]
155
+ line_items.map do |line_item|
156
+ price = prices[line_item[:price]].clone
157
+
158
+ if price.nil?
159
+ raise StripeMock::StripeMockError.new("Price not found for ID: #{line_item[:price]}")
160
+ end
161
+
162
+ {
163
+ id: line_item[:id],
164
+ object: "item",
165
+ amount_subtotal: price[:unit_amount] * line_item[:quantity],
166
+ amount_total: price[:unit_amount] * line_item[:quantity],
167
+ currency: price[:currency],
168
+ price: price.clone,
169
+ quantity: line_item[:quantity]
170
+ }
171
+ end
172
+ else
173
+ throw Stripe::InvalidRequestError("Only payment and subscription sessions have line items")
174
+ end
175
+ end
176
+ end
177
+ end
178
+ end
179
+ end