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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 771c4e4adbc92b37176d94b8f91fb1f2bef740ba
4
- data.tar.gz: 86c21735586588ea3aabc958d3eb93dda6dbc7ec
3
+ metadata.gz: a15c09639e0a93df5b15cc5a45646c0fb189460e
4
+ data.tar.gz: b8cdf0692aba8099229f66e1906e691dd40075aa
5
5
  SHA512:
6
- metadata.gz: 0c747fed9cf7824d59fdb1be3ec1b399beabd69d54d08101c1c46d96792fe400e4d7b8c1aa6a816c981d58d902a3fdf46c6edff21bd280a07615799384a4a82f
7
- data.tar.gz: 68b2ab17e0f504f783bd8c5a20893ec89bee491a29cac16a824d8cdb45e48f7a2a34e307b153feb7fe58662ee661d2732c4d838391cf23497ea20fe0a90393a5
6
+ metadata.gz: 9d70a5de8ae26c090632d00c1013d5ca63933fee41e40561b2e713b511a1c1347b21485b1d1c4eb8ee6837b6f4ec9a9f8e857a7bfa0785ac2182b8190180bfbb
7
+ data.tar.gz: 542f7a6f6c8743269e0337c1fc0b4fabef97003fe4d945c077ab4e16fe908bf7c21996dcb8c25ade7a4975a273a51972621d8d997e35a86f00dcb5287ae63d89
data/.travis.yml CHANGED
@@ -17,7 +17,7 @@ script: "bundle exec rspec && bundle exec rspec -t live"
17
17
 
18
18
  env:
19
19
  global:
20
- - IS_TRAVIS=true STRIPE_TEST_SECRET_KEY_A=sk_test_sXdhUWu3NhrB7r1tkK0zZfMW STRIPE_TEST_SECRET_KEY_B=sk_test_uPfIX9ziFNloXwtSdDPJTdnh STRIPE_TEST_SECRET_KEY_C=sk_test_waSy1TaP2RNEpoz9t2pFCysm STRIPE_TEST_SECRET_KEY_D=sk_test_Z1mQZNehRFmI3EN9mHnMafnq
20
+ - IS_TRAVIS=true STRIPE_TEST_SECRET_KEY_A=sk_test_Ut2MSlZANdT3iDALdGhyLymy STRIPE_TEST_SECRET_KEY_B=sk_test_JXtzss9tHOG1ofIyEZgoUP4Q STRIPE_TEST_SECRET_KEY_C=sk_test_ZR5nVz9p3ivsqVa7mYB0sFep STRIPE_TEST_SECRET_KEY_D=sk_test_ZR5nVz9p3ivsqVa7mYB0sFep
21
21
 
22
22
  notifications:
23
23
  webhooks:
data/README.md CHANGED
@@ -12,7 +12,7 @@ This gem has unexpectedly grown in popularity and I've gotten pretty busy, so I'
12
12
 
13
13
  In your gemfile:
14
14
 
15
- gem 'stripe-ruby-mock', '~> 2.5.4', :require => 'stripe_mock'
15
+ gem 'stripe-ruby-mock', '~> 2.5.8', :require => 'stripe_mock'
16
16
 
17
17
  ## Features
18
18
 
data/lib/stripe_mock.rb CHANGED
@@ -67,8 +67,12 @@ require 'stripe_mock/request_handlers/refunds.rb'
67
67
  require 'stripe_mock/request_handlers/transfers.rb'
68
68
  require 'stripe_mock/request_handlers/payouts.rb'
69
69
  require 'stripe_mock/request_handlers/subscriptions.rb'
70
+ require 'stripe_mock/request_handlers/subscription_items.rb'
70
71
  require 'stripe_mock/request_handlers/tokens.rb'
71
72
  require 'stripe_mock/request_handlers/country_spec.rb'
73
+ require 'stripe_mock/request_handlers/ephemeral_key.rb'
74
+ require 'stripe_mock/request_handlers/products.rb'
75
+ require 'stripe_mock/request_handlers/tax_rates.rb'
72
76
  require 'stripe_mock/instance'
73
77
 
74
78
  require 'stripe_mock/test_strategies/base.rb'
@@ -50,6 +50,8 @@ module StripeMock
50
50
  'charge.dispute.created',
51
51
  'charge.dispute.updated',
52
52
  'charge.dispute.closed',
53
+ 'charge.dispute.funds_reinstated',
54
+ 'charge.dispute.funds_withdrawn',
53
55
  'customer.source.created',
54
56
  'customer.source.deleted',
55
57
  'customer.source.updated',
@@ -101,6 +101,22 @@ module StripeMock
101
101
  }.merge(params)
102
102
  end
103
103
 
104
+ def self.mock_tax_rate(params)
105
+ {
106
+ id: 'test_cus_default',
107
+ object: 'tax_rate',
108
+ active: true,
109
+ created: 1559079603,
110
+ description: nil,
111
+ display_name: 'VAT',
112
+ inclusive: false,
113
+ jurisdiction: 'EU',
114
+ livemode: false,
115
+ metadata: {},
116
+ percentage: 21.0
117
+ }.merge(params)
118
+ end
119
+
104
120
  def self.mock_customer(sources, params)
105
121
  cus_id = params[:id] || "test_cus_default"
106
122
  currency = params[:currency] || StripeMock.default_currency
@@ -111,6 +127,7 @@ module StripeMock
111
127
  object: "customer",
112
128
  created: 1372126710,
113
129
  id: cus_id,
130
+ name: nil,
114
131
  livemode: false,
115
132
  delinquent: false,
116
133
  discount: nil,
@@ -243,7 +260,8 @@ module StripeMock
243
260
  cvc_check: nil,
244
261
  address_line1_check: nil,
245
262
  address_zip_check: nil,
246
- tokenization_method: nil
263
+ tokenization_method: nil,
264
+ metadata: {}
247
265
  }, params)
248
266
  end
249
267
 
@@ -261,7 +279,8 @@ module StripeMock
261
279
  status: 'new',
262
280
  account_holder_name: 'John Doe',
263
281
  account_holder_type: 'individual',
264
- fingerprint: "aBcFinGerPrINt123"
282
+ fingerprint: "aBcFinGerPrINt123",
283
+ metadata: {}
265
284
  }.merge(params)
266
285
  end
267
286
 
@@ -289,6 +308,7 @@ module StripeMock
289
308
  current_period_start: 1308595038,
290
309
  current_period_end: 1308681468,
291
310
  status: 'trialing',
311
+ trial_from_plan: false,
292
312
  plan: {
293
313
  interval: 'month',
294
314
  amount: 7500,
@@ -332,7 +352,10 @@ module StripeMock
332
352
  lines << Data.mock_line_item() if lines.empty?
333
353
  invoice = {
334
354
  id: 'in_test_invoice',
335
- date: 1349738950,
355
+ status: 'open',
356
+ invoice_pdf: 'pdf_url',
357
+ hosted_invoice_url: 'hosted_invoice_url',
358
+ created: 1349738950,
336
359
  period_end: 1349738950,
337
360
  period_start: 1349738950,
338
361
  lines: {
@@ -353,12 +376,13 @@ module StripeMock
353
376
  paid: false,
354
377
  receipt_number: nil,
355
378
  statement_descriptor: nil,
356
- tax: nil,
379
+ tax: 10,
357
380
  tax_percent: nil,
358
381
  webhooks_delivered_at: 1349825350,
359
382
  livemode: false,
360
383
  attempt_count: 0,
361
- amount_due: nil,
384
+ amount_due: 100,
385
+ amount_paid: 0,
362
386
  currency: currency,
363
387
  starting_balance: 0,
364
388
  ending_balance: nil,
@@ -393,6 +417,11 @@ module StripeMock
393
417
  start: 1349738920,
394
418
  end: 1349738920
395
419
  },
420
+ tax_amounts: [
421
+ {
422
+ amount: 10
423
+ }
424
+ ],
396
425
  quantity: nil,
397
426
  subscription: nil,
398
427
  plan: nil,
@@ -406,7 +435,7 @@ module StripeMock
406
435
  {
407
436
  id: "test_ii",
408
437
  object: "invoiceitem",
409
- date: 1349738920,
438
+ created: 1349738920,
410
439
  amount: 1099,
411
440
  livemode: false,
412
441
  proration: false,
@@ -507,6 +536,21 @@ module StripeMock
507
536
  }.merge(params)
508
537
  end
509
538
 
539
+ def self.mock_product(params = {})
540
+ {
541
+ id: "default_test_prod",
542
+ object: "product",
543
+ active: true,
544
+ created: 1556896214,
545
+ livemode: false,
546
+ metadata: {},
547
+ name: "Default Test Product",
548
+ statement_descriptor: "PRODUCT",
549
+ type: "service",
550
+ updated: 1556918200,
551
+ }.merge(params)
552
+ end
553
+
510
554
  def self.mock_recipient(cards, params={})
511
555
  rp_id = params[:id] || "test_rp_default"
512
556
  cards.each {|card| card[:recipient] = rp_id}
@@ -594,31 +638,29 @@ module StripeMock
594
638
  currency = params[:currency] || StripeMock.default_currency
595
639
  id = params[:id] || 'tr_test_transfer'
596
640
  {
597
- :status => 'pending',
598
641
  :amount => 100,
599
- :account => {
600
- :object => 'bank_account',
601
- :country => 'US',
602
- :bank_name => 'STRIPE TEST BANK',
603
- :last4 => '6789'
604
- },
605
- :recipient => 'test_recipient',
606
- :fee => 0,
607
- :fee_details => [],
642
+ :amount_reversed => 0,
643
+ :balance_transaction => "txn_2dyYXXP90MN26R",
608
644
  :id => id,
609
645
  :livemode => false,
610
646
  :metadata => {},
611
647
  :currency => currency,
612
648
  :object => "transfer",
613
- :date => 1304114826,
649
+ :created => 1304114826,
614
650
  :description => "Transfer description",
615
651
  :reversed => false,
616
652
  :reversals => {
617
653
  :object => "list",
654
+ :data => [],
618
655
  :total_count => 0,
619
656
  :has_more => false,
620
657
  :url => "/v1/transfers/#{id}/reversals"
621
658
  },
659
+ :destination => "acct_164wxjKbnvuxQXGu",
660
+ :destination_payment => "py_164xRvKbnvuxQXGuVFV2pZo1",
661
+ :source_transaction => "ch_164xRv2eZvKYlo2Clu1sIJWB",
662
+ :source_type => "card",
663
+ :transfer_group => "group_ch_164xRv2eZvKYlo2Clu1sIJWB",
622
664
  }.merge(params)
623
665
  end
624
666
 
@@ -1027,5 +1069,24 @@ module StripeMock
1027
1069
  quantity: 2
1028
1070
  }.merge(params)
1029
1071
  end
1072
+
1073
+ def self.mock_ephemeral_key(**params)
1074
+ created = Time.now.to_i
1075
+ expires = created + 34_000
1076
+ {
1077
+ id: "ephkey_default",
1078
+ object: "ephemeral_key",
1079
+ associated_objects: [
1080
+ {
1081
+ id: params[:customer],
1082
+ type: "customer"
1083
+ }
1084
+ ],
1085
+ created: created,
1086
+ expires: expires,
1087
+ livemode: false,
1088
+ secret: "ek_test_default"
1089
+ }
1090
+ end
1030
1091
  end
1031
1092
  end
@@ -1,12 +1,13 @@
1
1
  module StripeMock
2
2
  module Data
3
3
  class List
4
- attr_reader :data, :limit, :offset, :starting_after
4
+ attr_reader :data, :limit, :offset, :starting_after, :ending_before
5
5
 
6
6
  def initialize(data, options = {})
7
7
  @data = Array(data.clone)
8
8
  @limit = [[options[:limit] || 10, 100].min, 1].max # restrict @limit to 1..100
9
9
  @starting_after = options[:starting_after]
10
+ @ending_before = options[:ending_before]
10
11
  if @data.first.is_a?(Hash) && @data.first[:created]
11
12
  @data.sort_by! { |x| x[:created] }
12
13
  @data.reverse!
@@ -46,9 +47,13 @@ module StripeMock
46
47
  private
47
48
 
48
49
  def offset
49
- if starting_after
50
+ case
51
+ when starting_after
50
52
  index = data.index { |datum| datum[:id] == starting_after }
51
53
  (index || raise("No such object id: #{starting_after}")) + 1
54
+ when ending_before
55
+ index = data.index { |datum| datum[:id] == ending_before }
56
+ (index || raise("No such object id: #{ending_before}")) - 1
52
57
  else
53
58
  0
54
59
  end
@@ -28,6 +28,7 @@ module StripeMock
28
28
  include StripeMock::RequestHandlers::Cards
29
29
  include StripeMock::RequestHandlers::Sources
30
30
  include StripeMock::RequestHandlers::Subscriptions # must be before Customers
31
+ include StripeMock::RequestHandlers::SubscriptionItems
31
32
  include StripeMock::RequestHandlers::Customers
32
33
  include StripeMock::RequestHandlers::Coupons
33
34
  include StripeMock::RequestHandlers::Disputes
@@ -36,16 +37,20 @@ module StripeMock
36
37
  include StripeMock::RequestHandlers::InvoiceItems
37
38
  include StripeMock::RequestHandlers::Orders
38
39
  include StripeMock::RequestHandlers::Plans
40
+ include StripeMock::RequestHandlers::Products
39
41
  include StripeMock::RequestHandlers::Refunds
40
42
  include StripeMock::RequestHandlers::Recipients
41
43
  include StripeMock::RequestHandlers::Transfers
42
44
  include StripeMock::RequestHandlers::Tokens
43
45
  include StripeMock::RequestHandlers::CountrySpec
44
46
  include StripeMock::RequestHandlers::Payouts
47
+ include StripeMock::RequestHandlers::EphemeralKey
48
+ include StripeMock::RequestHandlers::TaxRates
45
49
 
46
50
  attr_reader :accounts, :balance, :balance_transactions, :bank_tokens, :charges, :coupons, :customers,
47
51
  :disputes, :events, :invoices, :invoice_items, :orders, :plans, :recipients,
48
- :refunds, :transfers, :payouts, :subscriptions, :country_spec, :subscriptions_items
52
+ :refunds, :transfers, :payouts, :subscriptions, :country_spec, :subscriptions_items,
53
+ :products, :tax_rates
49
54
 
50
55
  attr_accessor :error_queue, :debug, :conversion_rate, :account_balance
51
56
 
@@ -64,13 +69,15 @@ module StripeMock
64
69
  @invoice_items = {}
65
70
  @orders = {}
66
71
  @plans = {}
72
+ @products = {}
67
73
  @recipients = {}
68
74
  @refunds = {}
69
75
  @transfers = {}
70
76
  @payouts = {}
71
77
  @subscriptions = {}
72
- @subscriptions_items = []
78
+ @subscriptions_items = {}
73
79
  @country_spec = {}
80
+ @tax_rates = {}
74
81
 
75
82
  @debug = false
76
83
  @error_queue = ErrorQueue.new
@@ -174,7 +181,8 @@ module StripeMock
174
181
  amount = params[:amount]
175
182
  unless amount.nil?
176
183
  # Fee calculation
177
- params[:fee] ||= (30 + (amount.abs * 0.029).ceil) * (amount > 0 ? 1 : -1)
184
+ calculate_fees(params) unless params[:fee]
185
+ params[:net] = amount - params[:fee]
178
186
  params[:amount] = amount * @conversion_rate
179
187
  end
180
188
  @balance_transactions[id] = Data.mock_balance_transaction(params.merge(id: id))
@@ -195,5 +203,31 @@ module StripeMock
195
203
  response = Struct.new(:data)
196
204
  response.new(hash)
197
205
  end
206
+
207
+ def calculate_fees(params)
208
+ application_fee = params[:application_fee] || 0
209
+ params[:fee] = processing_fee(params[:amount]) + application_fee
210
+ params[:fee_details] = [
211
+ {
212
+ amount: processing_fee(params[:amount]),
213
+ application: nil,
214
+ currency: params[:currency] || StripeMock.default_currency,
215
+ description: "Stripe processing fees",
216
+ type: "stripe_fee"
217
+ }
218
+ ]
219
+ if application_fee
220
+ params[:fee_details] << {
221
+ amount: application_fee,
222
+ currency: params[:currency] || StripeMock.default_currency,
223
+ description: "Application fee",
224
+ type: "application_fee"
225
+ }
226
+ end
227
+ end
228
+
229
+ def processing_fee(amount)
230
+ (30 + (amount.abs * 0.029).ceil) * (amount > 0 ? 1 : -1)
231
+ end
198
232
  end
199
233
  end
@@ -1,6 +1,7 @@
1
1
  module StripeMock
2
2
  module RequestHandlers
3
3
  module Accounts
4
+ VALID_START_YEAR = 2009
4
5
 
5
6
  def Accounts.included(klass)
6
7
  klass.add_handler 'post /v1/accounts', :new_account
@@ -30,6 +31,8 @@ module StripeMock
30
31
  account.merge!(params)
31
32
  if blank_value?(params[:tos_acceptance], :date)
32
33
  raise Stripe::InvalidRequestError.new("Invalid integer: ", "tos_acceptance[date]", http_status: 400)
34
+ elsif params[:tos_acceptance] && params[:tos_acceptance][:date]
35
+ validate_acceptance_date(params[:tos_acceptance][:date])
33
36
  end
34
37
  account
35
38
  end
@@ -65,6 +68,19 @@ module StripeMock
65
68
  end
66
69
  false
67
70
  end
71
+
72
+ def validate_acceptance_date(unix_date)
73
+ unix_now = Time.now.strftime("%s").to_i
74
+ formatted_date = Time.at(unix_date)
75
+
76
+ return if formatted_date.year >= VALID_START_YEAR && unix_now >= unix_date
77
+
78
+ raise Stripe::InvalidRequestError.new(
79
+ "ToS acceptance date is not valid. Dates are expected to be integers, measured in seconds, not in the future, and after 2009",
80
+ "tos_acceptance[date]",
81
+ http_status: 400
82
+ )
83
+ end
68
84
  end
69
85
  end
70
86
  end
@@ -13,9 +13,12 @@ module StripeMock
13
13
  end
14
14
 
15
15
  def new_charge(route, method_url, params, headers)
16
- if params[:idempotency_key] && charges.any?
17
- original_charge = charges.values.find { |c| c[:idempotency_key] == params[:idempotency_key]}
18
- return charges[original_charge[:id]] if original_charge
16
+ if headers && headers[:idempotency_key]
17
+ params[:idempotency_key] = headers[:idempotency_key]
18
+ if charges.any?
19
+ original_charge = charges.values.find { |c| c[:idempotency_key] == headers[:idempotency_key]}
20
+ return charges[original_charge[:id]] if original_charge
21
+ end
19
22
  end
20
23
 
21
24
  id = new_id('ch')
@@ -41,7 +44,7 @@ module StripeMock
41
44
  end
42
45
 
43
46
  ensure_required_params(params)
44
- bal_trans_params = { amount: params[:amount], source: id }
47
+ bal_trans_params = { amount: params[:amount], source: id, application_fee: params[:application_fee] }
45
48
 
46
49
  balance_transaction_id = new_balance_transaction('txn', bal_trans_params)
47
50
 
@@ -155,10 +158,6 @@ module StripeMock
155
158
  params[:amount] && params[:amount] < 1
156
159
  end
157
160
 
158
- def require_param(param)
159
- raise Stripe::InvalidRequestError.new("Missing required param: #{param}", param.to_s, http_status: 400)
160
- end
161
-
162
161
  def allowed_params(params)
163
162
  allowed = [:description, :metadata, :receipt_email, :fraud_details, :shipping, :destination]
164
163
 
@@ -51,7 +51,7 @@ module StripeMock
51
51
  coupon = coupons[ params[:coupon] ]
52
52
  assert_existence :coupon, params[:coupon], coupon
53
53
 
54
- add_coupon_to_customer(customers[params[:id]], coupon)
54
+ add_coupon_to_object(customers[params[:id]], coupon)
55
55
  end
56
56
 
57
57
  customers[ params[:id] ]
@@ -90,7 +90,7 @@ module StripeMock
90
90
  coupon = coupons[ params[:coupon] ]
91
91
  assert_existence :coupon, params[:coupon], coupon
92
92
 
93
- add_coupon_to_customer(cus, coupon)
93
+ add_coupon_to_object(cus, coupon)
94
94
  end
95
95
 
96
96
  cus