braintree 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. data/lib/braintree.rb +36 -46
  2. data/lib/braintree/address.rb +14 -14
  3. data/lib/braintree/base_module.rb +3 -3
  4. data/lib/braintree/configuration.rb +16 -16
  5. data/lib/braintree/credit_card.rb +39 -30
  6. data/lib/braintree/credit_card_verification.rb +4 -3
  7. data/lib/braintree/customer.rb +22 -22
  8. data/lib/braintree/digest.rb +2 -8
  9. data/lib/braintree/error_codes.rb +16 -3
  10. data/lib/braintree/error_result.rb +5 -5
  11. data/lib/braintree/exceptions.rb +58 -0
  12. data/lib/braintree/http.rb +7 -7
  13. data/lib/braintree/paged_collection.rb +14 -14
  14. data/lib/braintree/ssl_expiration_check.rb +5 -1
  15. data/lib/braintree/subscription.rb +110 -0
  16. data/lib/braintree/successful_result.rb +3 -3
  17. data/lib/braintree/test/credit_card_numbers.rb +1 -1
  18. data/lib/braintree/test/transaction_amounts.rb +18 -0
  19. data/lib/braintree/transaction.rb +52 -25
  20. data/lib/braintree/transaction/address_details.rb +2 -2
  21. data/lib/braintree/transaction/credit_card_details.rb +12 -4
  22. data/lib/braintree/transaction/customer_details.rb +9 -1
  23. data/lib/braintree/transaction/status_details.rb +1 -1
  24. data/lib/braintree/transparent_redirect.rb +15 -15
  25. data/lib/braintree/util.rb +7 -7
  26. data/lib/braintree/validation_error.rb +1 -1
  27. data/lib/braintree/validation_error_collection.rb +6 -6
  28. data/lib/braintree/version.rb +3 -3
  29. data/lib/braintree/xml/generator.rb +2 -2
  30. data/lib/braintree/xml/libxml.rb +1 -1
  31. data/lib/braintree/xml/parser.rb +1 -1
  32. data/spec/integration/braintree/address_spec.rb +12 -12
  33. data/spec/integration/braintree/credit_card_spec.rb +189 -37
  34. data/spec/integration/braintree/customer_spec.rb +35 -35
  35. data/spec/integration/braintree/http_spec.rb +3 -3
  36. data/spec/integration/braintree/subscription_spec.rb +362 -0
  37. data/spec/integration/braintree/transaction_spec.rb +130 -58
  38. data/spec/integration/spec_helper.rb +1 -1
  39. data/spec/spec_helper.rb +4 -4
  40. data/spec/unit/braintree/address_spec.rb +6 -6
  41. data/spec/unit/braintree/base_module_spec.rb +18 -0
  42. data/spec/unit/braintree/configuration_spec.rb +10 -10
  43. data/spec/unit/braintree/credit_card_spec.rb +15 -15
  44. data/spec/unit/braintree/credit_card_verification_spec.rb +4 -2
  45. data/spec/unit/braintree/customer_spec.rb +8 -8
  46. data/spec/unit/braintree/digest_spec.rb +4 -0
  47. data/spec/unit/braintree/http_spec.rb +2 -2
  48. data/spec/unit/braintree/paged_collection_spec.rb +12 -12
  49. data/spec/unit/braintree/ssl_expiration_check_spec.rb +18 -9
  50. data/spec/unit/braintree/transaction/credit_card_details_spec.rb +15 -0
  51. data/spec/unit/braintree/transaction/customer_details_spec.rb +19 -0
  52. data/spec/unit/braintree/transaction_spec.rb +14 -14
  53. data/spec/unit/braintree/transparent_redirect_spec.rb +11 -11
  54. data/spec/unit/braintree/util_spec.rb +8 -8
  55. data/spec/unit/braintree/validation_error_collection_spec.rb +6 -6
  56. data/spec/unit/braintree/validation_error_spec.rb +2 -2
  57. data/spec/unit/braintree/xml/libxml_spec.rb +5 -5
  58. data/spec/unit/braintree/xml_spec.rb +16 -16
  59. data/spec/unit/braintree_spec.rb +11 -0
  60. metadata +8 -2
@@ -6,7 +6,7 @@ describe Braintree::Customer do
6
6
  first_page = Braintree::Customer.all
7
7
  first_page.current_page_number.should == 1
8
8
  end
9
-
9
+
10
10
  it "can get the next_page" do
11
11
  first_page = Braintree::Customer.all
12
12
  first_page.current_page_number.should == 1
@@ -55,12 +55,12 @@ describe Braintree::Customer do
55
55
  result.customer.created_at.between?(Time.now - 10, Time.now).should == true
56
56
  result.customer.updated_at.between?(Time.now - 10, Time.now).should == true
57
57
  end
58
-
58
+
59
59
  it "can create without any attributes" do
60
60
  result = Braintree::Customer.create
61
61
  result.success?.should == true
62
62
  end
63
-
63
+
64
64
  it "returns an error response if invalid" do
65
65
  result = Braintree::Customer.create(
66
66
  :email => "@invalid.com"
@@ -100,7 +100,7 @@ describe Braintree::Customer do
100
100
  result.success?.should == false
101
101
  result.credit_card_verification.status.should == "processor_declined"
102
102
  end
103
-
103
+
104
104
  it "can create a customer, payment method, and billing address at the same time" do
105
105
  result = Braintree::Customer.create(
106
106
  :first_name => "Mike",
@@ -133,7 +133,7 @@ describe Braintree::Customer do
133
133
  result.customer.addresses[0].postal_code.should == "60622"
134
134
  result.customer.addresses[0].country_name.should == "United States of America"
135
135
  end
136
-
136
+
137
137
  it "stores custom fields when valid" do
138
138
  result = Braintree::Customer.create(
139
139
  :first_name => "Bill",
@@ -174,7 +174,7 @@ describe Braintree::Customer do
174
174
  result.errors.for(:customer).on(:custom_fields)[0].message.should == "Custom field is invalid: spouse_name."
175
175
  end
176
176
  end
177
-
177
+
178
178
  describe "self.create!" do
179
179
  it "returns the customer if successful" do
180
180
  customer = Braintree::Customer.create!(
@@ -185,19 +185,19 @@ describe Braintree::Customer do
185
185
  customer.first_name.should == "Jim"
186
186
  customer.last_name.should == "Smith"
187
187
  end
188
-
188
+
189
189
  it "can create without any attributes" do
190
190
  customer = Braintree::Customer.create!
191
191
  customer.id.should =~ /\d+/
192
192
  end
193
-
193
+
194
194
  it "raises an exception if not successful" do
195
195
  expect do
196
196
  Braintree::Customer.create!(:email => "@foo.com")
197
197
  end.to raise_error(Braintree::ValidationsFailed)
198
198
  end
199
199
  end
200
-
200
+
201
201
  describe "self.credit" do
202
202
  it "creates a credit transaction for given customer id, returning a result object" do
203
203
  customer = Braintree::Customer.create!(
@@ -208,7 +208,7 @@ describe Braintree::Customer do
208
208
  )
209
209
  result = Braintree::Customer.credit(customer.id, :amount => "100.00")
210
210
  result.success?.should == true
211
- result.transaction.amount.should == "100.00"
211
+ result.transaction.amount.should == BigDecimal.new("100.00")
212
212
  result.transaction.type.should == "credit"
213
213
  result.transaction.customer_details.id.should == customer.id
214
214
  result.transaction.credit_card_details.token.should == customer.credit_cards[0].token
@@ -227,7 +227,7 @@ describe Braintree::Customer do
227
227
  }
228
228
  )
229
229
  transaction = Braintree::Customer.credit!(customer.id, :amount => "100.00")
230
- transaction.amount.should == "100.00"
230
+ transaction.amount.should == BigDecimal.new("100.00")
231
231
  transaction.type.should == "credit"
232
232
  transaction.customer_details.id.should == customer.id
233
233
  transaction.credit_card_details.token.should == customer.credit_cards[0].token
@@ -236,7 +236,7 @@ describe Braintree::Customer do
236
236
  transaction.credit_card_details.expiration_date.should == "05/2010"
237
237
  end
238
238
  end
239
-
239
+
240
240
  describe "self.sale" do
241
241
  it "creates a sale transaction for given customer id, returning a result object" do
242
242
  customer = Braintree::Customer.create!(
@@ -247,7 +247,7 @@ describe Braintree::Customer do
247
247
  )
248
248
  result = Braintree::Customer.sale(customer.id, :amount => "100.00")
249
249
  result.success?.should == true
250
- result.transaction.amount.should == "100.00"
250
+ result.transaction.amount.should == BigDecimal.new("100.00")
251
251
  result.transaction.type.should == "sale"
252
252
  result.transaction.customer_details.id.should == customer.id
253
253
  result.transaction.credit_card_details.token.should == customer.credit_cards[0].token
@@ -266,7 +266,7 @@ describe Braintree::Customer do
266
266
  }
267
267
  )
268
268
  transaction = Braintree::Customer.sale!(customer.id, :amount => "100.00")
269
- transaction.amount.should == "100.00"
269
+ transaction.amount.should == BigDecimal.new("100.00")
270
270
  transaction.type.should == "sale"
271
271
  transaction.customer_details.id.should == customer.id
272
272
  transaction.credit_card_details.token.should == customer.credit_cards[0].token
@@ -305,7 +305,7 @@ describe Braintree::Customer do
305
305
  :amount => "100.00"
306
306
  )
307
307
  result.success?.should == true
308
- result.transaction.amount.should == "100.00"
308
+ result.transaction.amount.should == BigDecimal.new("100.00")
309
309
  result.transaction.type.should == "sale"
310
310
  result.transaction.customer_details.id.should == customer.id
311
311
  result.transaction.credit_card_details.token.should == customer.credit_cards[0].token
@@ -314,7 +314,7 @@ describe Braintree::Customer do
314
314
  result.transaction.credit_card_details.expiration_date.should == "05/2010"
315
315
  end
316
316
  end
317
-
317
+
318
318
  describe "sale!" do
319
319
  it "returns the created sale tranaction if valid" do
320
320
  customer = Braintree::Customer.create!(
@@ -324,7 +324,7 @@ describe Braintree::Customer do
324
324
  }
325
325
  )
326
326
  transaction = customer.sale!(:amount => "100.00")
327
- transaction.amount.should == "100.00"
327
+ transaction.amount.should == BigDecimal.new("100.00")
328
328
  transaction.type.should == "sale"
329
329
  transaction.customer_details.id.should == customer.id
330
330
  transaction.credit_card_details.token.should == customer.credit_cards[0].token
@@ -349,7 +349,7 @@ describe Braintree::Customer do
349
349
  collection[0].should == transaction
350
350
  end
351
351
  end
352
-
352
+
353
353
  describe "credit" do
354
354
  it "creates a credit transaction using the customer, returning a result object" do
355
355
  customer = Braintree::Customer.create!(
@@ -362,7 +362,7 @@ describe Braintree::Customer do
362
362
  :amount => "100.00"
363
363
  )
364
364
  result.success?.should == true
365
- result.transaction.amount.should == "100.00"
365
+ result.transaction.amount.should == BigDecimal.new("100.00")
366
366
  result.transaction.type.should == "credit"
367
367
  result.transaction.customer_details.id.should == customer.id
368
368
  result.transaction.credit_card_details.token.should == customer.credit_cards[0].token
@@ -371,7 +371,7 @@ describe Braintree::Customer do
371
371
  result.transaction.credit_card_details.expiration_date.should == "05/2010"
372
372
  end
373
373
  end
374
-
374
+
375
375
  describe "credit!" do
376
376
  it "returns the created credit tranaction if valid" do
377
377
  customer = Braintree::Customer.create!(
@@ -381,7 +381,7 @@ describe Braintree::Customer do
381
381
  }
382
382
  )
383
383
  transaction = customer.credit!(:amount => "100.00")
384
- transaction.amount.should == "100.00"
384
+ transaction.amount.should == BigDecimal.new("100.00")
385
385
  transaction.type.should == "credit"
386
386
  transaction.customer_details.id.should == customer.id
387
387
  transaction.credit_card_details.token.should == customer.credit_cards[0].token
@@ -416,7 +416,7 @@ describe Braintree::Customer do
416
416
  customer.fax.should == "614.555.5656"
417
417
  customer.website.should == "www.johndoe.com"
418
418
  end
419
-
419
+
420
420
  it "can pass any attribute through tr_data" do
421
421
  customer_id = "customer_#{rand(1_000_000)}"
422
422
  tr_data_params = {
@@ -445,7 +445,7 @@ describe Braintree::Customer do
445
445
  customer.website.should == "www.johndoe.com"
446
446
  end
447
447
  end
448
-
448
+
449
449
  describe "delete" do
450
450
  it "deletes the customer" do
451
451
  result = Braintree::Customer.create(
@@ -461,7 +461,7 @@ describe Braintree::Customer do
461
461
  end.to raise_error(Braintree::NotFoundError)
462
462
  end
463
463
  end
464
-
464
+
465
465
 
466
466
  describe "self.find" do
467
467
  it "finds the customer with the given id" do
@@ -483,7 +483,7 @@ describe Braintree::Customer do
483
483
  end.to raise_error(Braintree::NotFoundError, 'customer with id "invalid-id" not found')
484
484
  end
485
485
  end
486
-
486
+
487
487
  describe "self.update" do
488
488
  it "updates the customer with the given id if successful" do
489
489
  customer = Braintree::Customer.create!(
@@ -515,8 +515,8 @@ describe Braintree::Customer do
515
515
  result.errors.for(:customer).on(:email)[0].message.should == "Email is an invalid format."
516
516
  end
517
517
  end
518
-
519
- describe "self.update!" do
518
+
519
+ describe "self.update!" do
520
520
  it "returns the updated customer if successful" do
521
521
  customer = Braintree::Customer.create!(
522
522
  :first_name => "Joe",
@@ -531,7 +531,7 @@ describe Braintree::Customer do
531
531
  updated_customer.last_name.should == "Super Cool"
532
532
  updated_customer.updated_at.between?(Time.now - 5, Time.now).should == true
533
533
  end
534
-
534
+
535
535
  it "raises an error if unsuccessful" do
536
536
  customer = Braintree::Customer.create!(:email => "valid@email.com")
537
537
  expect do
@@ -539,7 +539,7 @@ describe Braintree::Customer do
539
539
  end.to raise_error(Braintree::ValidationsFailed)
540
540
  end
541
541
  end
542
-
542
+
543
543
  describe "update" do
544
544
  it "updates the customer" do
545
545
  customer = Braintree::Customer.create!(
@@ -556,7 +556,7 @@ describe Braintree::Customer do
556
556
  updated_customer.first_name.should == "Mr. Joe"
557
557
  updated_customer.last_name.should == "Super Cool"
558
558
  end
559
-
559
+
560
560
  it "returns an error response if invalid" do
561
561
  customer = Braintree::Customer.create!(
562
562
  :email => "valid@email.com"
@@ -568,8 +568,8 @@ describe Braintree::Customer do
568
568
  result.errors.for(:customer).on(:email)[0].message.should == "Email is an invalid format."
569
569
  end
570
570
  end
571
-
572
- describe "update!" do
571
+
572
+ describe "update!" do
573
573
  it "returns the customer and updates the customer if successful" do
574
574
  customer = Braintree::Customer.create!(
575
575
  :first_name => "Joe",
@@ -583,7 +583,7 @@ describe Braintree::Customer do
583
583
  customer.last_name.should == "Super Cool"
584
584
  customer.updated_at.between?(Time.now - 5, Time.now).should == true
585
585
  end
586
-
586
+
587
587
  it "raises an error if unsuccessful" do
588
588
  customer = Braintree::Customer.create!(
589
589
  :email => "valid@email.com"
@@ -687,7 +687,7 @@ describe Braintree::Customer do
687
687
  request.body = Braintree::Util.hash_to_query_string(params)
688
688
  response = http.request(request)
689
689
  end
690
- query_string = response["Location"].split("?", 2).last
690
+ query_string = response["Location"].split("?", 2).last
691
691
  query_string
692
692
  end
693
693
 
@@ -703,6 +703,6 @@ describe Braintree::Customer do
703
703
  request.body = Braintree::Util.hash_to_query_string({ :tr_data => tr_data }.merge(regular_params))
704
704
  response = http.request(request)
705
705
  end
706
- query_string = response["Location"].split("?", 2).last
706
+ query_string = response["Location"].split("?", 2).last
707
707
  end
708
708
  end
@@ -20,7 +20,7 @@ describe Braintree::Http do
20
20
  end.to raise_error(Braintree::AuthorizationError)
21
21
  end
22
22
  end
23
-
23
+
24
24
  describe "self._http_do" do
25
25
  it "logs one line of info to the logger" do
26
26
  begin
@@ -124,7 +124,7 @@ describe Braintree::Http do
124
124
  end
125
125
  end
126
126
 
127
- xit "Patrick: waiting on a production box - accepts the certificate on the production server" do
127
+ it "accepts the certificate on the production server" do
128
128
  begin
129
129
  original_env = Braintree::Configuration.environment
130
130
  Braintree::Configuration.environment = :production
@@ -143,7 +143,7 @@ describe Braintree::Http do
143
143
  Braintree::Configuration.stub(:base_merchant_path).and_return("/")
144
144
  original_ca_file = Braintree::Configuration.ca_file
145
145
  Braintree::Configuration.stub(:ca_file).and_return(nil)
146
-
146
+
147
147
  expect { Braintree::Http._http_do(Net::HTTP::Get, "/login") }.to raise_error(Braintree::SSLCertificateError)
148
148
  ensure
149
149
  Braintree::Configuration.environment = original_env
@@ -0,0 +1,362 @@
1
+ require File.dirname(__FILE__) + "/../spec_helper"
2
+
3
+ describe Braintree::Subscription do
4
+ before(:each) do
5
+ @credit_card = Braintree::Customer.create!(
6
+ :credit_card => {
7
+ :number => Braintree::Test::CreditCardNumbers::Visa,
8
+ :expiration_date => "05/2010"
9
+ }
10
+ ).credit_cards[0]
11
+ end
12
+
13
+ describe "self.create" do
14
+ it "is successful with a miniumum of params" do
15
+ result = Braintree::Subscription.create(
16
+ :payment_method_token => @credit_card.token,
17
+ :plan_id => Braintree::Test::Plans::TriallessPlan[:id]
18
+ )
19
+
20
+ date_format = /^\d{4}\D\d{1,2}\D\d{1,2}$/
21
+ result.success?.should == true
22
+ result.subscription.id.should =~ /^\w{6}$/
23
+ result.subscription.status.should == Braintree::Subscription::Status::Active
24
+ result.subscription.plan_id.should == "integration_trialless_plan"
25
+
26
+ result.subscription.first_billing_date.should match(date_format)
27
+ result.subscription.next_billing_date.should match(date_format)
28
+ result.subscription.billing_period_start_date.should match(date_format)
29
+ result.subscription.billing_period_end_date.should match(date_format)
30
+
31
+ result.subscription.failure_count.should == 0
32
+ result.subscription.payment_method_token.should == @credit_card.token
33
+ end
34
+
35
+ it "can set the id" do
36
+ new_id = rand(36**9).to_s(36)
37
+ result = Braintree::Subscription.create(
38
+ :payment_method_token => @credit_card.token,
39
+ :plan_id => Braintree::Test::Plans::TriallessPlan[:id],
40
+ :id => new_id
41
+ )
42
+
43
+ date_format = /^\d{4}\D\d{1,2}\D\d{1,2}$/
44
+ result.success?.should == true
45
+ result.subscription.id.should == new_id
46
+ end
47
+
48
+ context "trial period" do
49
+ context "defaults to the plan's trial period settings" do
50
+ it "with no trial" do
51
+ result = Braintree::Subscription.create(
52
+ :payment_method_token => @credit_card.token,
53
+ :plan_id => Braintree::Test::Plans::TriallessPlan[:id]
54
+ )
55
+
56
+ result.subscription.trial_period.should == false
57
+ result.subscription.trial_duration.should == nil
58
+ result.subscription.trial_duration_unit.should == nil
59
+ end
60
+
61
+ it "with a trial" do
62
+ result = Braintree::Subscription.create(
63
+ :payment_method_token => @credit_card.token,
64
+ :plan_id => Braintree::Test::Plans::TrialPlan[:id]
65
+ )
66
+
67
+ result.subscription.trial_period.should == true
68
+ result.subscription.trial_duration.should == 2
69
+ result.subscription.trial_duration_unit.should == Braintree::Subscription::TrialDurationUnit::Day
70
+ end
71
+
72
+ it "can alter the trial period params" do
73
+ result = Braintree::Subscription.create(
74
+ :payment_method_token => @credit_card.token,
75
+ :plan_id => Braintree::Test::Plans::TrialPlan[:id],
76
+ :trial_duration => 5,
77
+ :trial_duration_unit => Braintree::Subscription::TrialDurationUnit::Month
78
+ )
79
+
80
+ result.subscription.trial_period.should == true
81
+ result.subscription.trial_duration.should == 5
82
+ result.subscription.trial_duration_unit.should == Braintree::Subscription::TrialDurationUnit::Month
83
+ end
84
+
85
+ it "can override the trial_period param" do
86
+ result = Braintree::Subscription.create(
87
+ :payment_method_token => @credit_card.token,
88
+ :plan_id => Braintree::Test::Plans::TrialPlan[:id],
89
+ :trial_period => false
90
+ )
91
+
92
+ result.subscription.trial_period.should == false
93
+ end
94
+
95
+ it "creates a transaction if no trial period" do
96
+ result = Braintree::Subscription.create(
97
+ :payment_method_token => @credit_card.token,
98
+ :plan_id => Braintree::Test::Plans::TriallessPlan[:id]
99
+ )
100
+
101
+ result.subscription.transactions.size.should == 1
102
+ result.subscription.transactions.first.should be_a(Braintree::Transaction)
103
+ result.subscription.transactions.first.amount.should == BigDecimal.new("12.34")
104
+ result.subscription.transactions.first.type.should == Braintree::Transaction::Type::Sale
105
+ end
106
+
107
+ it "doesn't create a transaction if there's a trial period" do
108
+ result = Braintree::Subscription.create(
109
+ :payment_method_token => @credit_card.token,
110
+ :plan_id => Braintree::Test::Plans::TrialPlan[:id]
111
+ )
112
+
113
+ result.subscription.transactions.size.should == 0
114
+ end
115
+ end
116
+
117
+ context "price" do
118
+ it "defaults to the plan's price" do
119
+ result = Braintree::Subscription.create(
120
+ :payment_method_token => @credit_card.token,
121
+ :plan_id => Braintree::Test::Plans::TrialPlan[:id]
122
+ )
123
+
124
+ result.subscription.price.should == BigDecimal.new("43.21")
125
+ end
126
+
127
+ it "can be overridden" do
128
+ result = Braintree::Subscription.create(
129
+ :payment_method_token => @credit_card.token,
130
+ :plan_id => Braintree::Test::Plans::TrialPlan[:id],
131
+ :price => 98.76
132
+ )
133
+
134
+ result.subscription.price.should == BigDecimal.new("98.76")
135
+ end
136
+ end
137
+ end
138
+
139
+ context "validation errors" do
140
+ it "has validation errors on id" do
141
+ result = Braintree::Subscription.create(
142
+ :payment_method_token => @credit_card.token,
143
+ :plan_id => Braintree::Test::Plans::TrialPlan[:id],
144
+ :id => "invalid token"
145
+ )
146
+ result.success?.should == false
147
+ result.errors.for(:subscription).on(:id)[0].message.should == "ID is invalid (use only letters, numbers, '-', and '_')."
148
+ end
149
+
150
+ it "has validation errors on duplicate id" do
151
+ duplicate_token = "duplicate_token_#{rand(36**8).to_s(36)}"
152
+ result = Braintree::Subscription.create(
153
+ :payment_method_token => @credit_card.token,
154
+ :plan_id => Braintree::Test::Plans::TrialPlan[:id],
155
+ :id => duplicate_token
156
+ )
157
+ result.success?.should == true
158
+
159
+ result = Braintree::Subscription.create(
160
+ :payment_method_token => @credit_card.token,
161
+ :plan_id => Braintree::Test::Plans::TrialPlan[:id],
162
+ :id => duplicate_token
163
+ )
164
+ result.success?.should == false
165
+ result.errors.for(:subscription).on(:id)[0].message.should == "ID has already been taken."
166
+ end
167
+
168
+ it "trial duration required" do
169
+ result = Braintree::Subscription.create(
170
+ :payment_method_token => @credit_card.token,
171
+ :plan_id => Braintree::Test::Plans::TrialPlan[:id],
172
+ :trial_period => true,
173
+ :trial_duration => nil
174
+ )
175
+ result.success?.should == false
176
+ result.errors.for(:subscription)[0].message.should == "Trial Duration is required."
177
+ end
178
+
179
+ it "trial duration unit required" do
180
+ result = Braintree::Subscription.create(
181
+ :payment_method_token => @credit_card.token,
182
+ :plan_id => Braintree::Test::Plans::TrialPlan[:id],
183
+ :trial_period => true,
184
+ :trial_duration_unit => nil
185
+ )
186
+ result.success?.should == false
187
+ result.errors.for(:subscription)[0].message.should == "Trial Duration Unit is invalid."
188
+ end
189
+
190
+ end
191
+ end
192
+
193
+ describe "self.find" do
194
+ it "finds a subscription" do
195
+ result = Braintree::Subscription.create(
196
+ :payment_method_token => @credit_card.token,
197
+ :plan_id => Braintree::Test::Plans::TriallessPlan[:id]
198
+ )
199
+ result.success?.should == true
200
+
201
+ Braintree::Subscription.find(result.subscription.id).should == result.subscription
202
+ end
203
+
204
+ it "raises Braintree::NotFoundError if it cannot find" do
205
+ expect {
206
+ Braintree::Subscription.find('noSuchSubscription')
207
+ }.to raise_error(Braintree::NotFoundError, 'subscription with id "noSuchSubscription" not found')
208
+ end
209
+ end
210
+
211
+ describe "self.update" do
212
+ before(:each) do
213
+ @subscription = Braintree::Subscription.create(
214
+ :payment_method_token => @credit_card.token,
215
+ :price => 54.32,
216
+ :plan_id => Braintree::Test::Plans::TriallessPlan[:id]
217
+ ).subscription
218
+ end
219
+
220
+ context "when successful" do
221
+ it "returns a success response with the updated subscription if valid" do
222
+ new_id = rand(36**9).to_s(36)
223
+ result = Braintree::Subscription.update(@subscription.id,
224
+ :id => new_id,
225
+ :price => 9999.88,
226
+ :plan_id => Braintree::Test::Plans::TrialPlan[:id]
227
+ )
228
+
229
+ result.success?.should == true
230
+ result.subscription.id.should =~ /#{new_id}/
231
+ result.subscription.plan_id.should == Braintree::Test::Plans::TrialPlan[:id]
232
+ result.subscription.price.should == BigDecimal.new("9999.88")
233
+ end
234
+
235
+ it "prorates if there is a charge (because merchant has proration option enabled in control panel)" do
236
+ result = Braintree::Subscription.update(@subscription.id,
237
+ :price => @subscription.price.to_f + 1
238
+ )
239
+
240
+ result.success?.should == true
241
+ result.subscription.price.to_f.should == @subscription.price.to_f + 1
242
+ result.subscription.transactions.size.should ==
243
+ @subscription.transactions.size + 1
244
+ end
245
+
246
+ it "doesn't prorate if price decreases" do
247
+ result = Braintree::Subscription.update(@subscription.id,
248
+ :price => @subscription.price.to_f - 1
249
+ )
250
+
251
+ result.success?.should == true
252
+ result.subscription.price.to_f.should == @subscription.price.to_f - 1
253
+ result.subscription.transactions.size.should ==
254
+ @subscription.transactions.size
255
+ end
256
+ end
257
+
258
+ context "when unsuccessful" do
259
+ before(:each) do
260
+ @subscription = Braintree::Subscription.create(
261
+ :payment_method_token => @credit_card.token,
262
+ :plan_id => Braintree::Test::Plans::TrialPlan[:id]
263
+ ).subscription
264
+ end
265
+
266
+ it "raises NotFoundError if the subscription can't be found" do
267
+ expect {
268
+ Braintree::Subscription.update(rand(36**9).to_s(36),
269
+ :price => 58.20
270
+ )
271
+ }.to raise_error(Braintree::NotFoundError)
272
+ end
273
+
274
+ it "has validation errors on id" do
275
+ result = Braintree::Subscription.update(@subscription.id, :id => "invalid token")
276
+ result.success?.should == false
277
+ result.errors.for(:subscription).on(:id)[0].code.should == Braintree::ErrorCodes::Subscription::TokenFormatIsInvalid
278
+ end
279
+
280
+ it "has a price" do
281
+ result = Braintree::Subscription.update(@subscription.id, :price => "")
282
+ result.success?.should == false
283
+ result.errors.for(:subscription).on(:price)[0].code.should == Braintree::ErrorCodes::Subscription::PriceCannotBeBlank
284
+ end
285
+
286
+ it "has a properly formatted price" do
287
+ result = Braintree::Subscription.update(@subscription.id, :price => "9.2.1 apples")
288
+ result.success?.should == false
289
+ result.errors.for(:subscription).on(:price)[0].code.should == Braintree::ErrorCodes::Subscription::PriceFormatIsInvalid
290
+ end
291
+
292
+ it "has validation errors on duplicate id" do
293
+ duplicate_id = "new_id_#{rand(36**6).to_s(36)}"
294
+ duplicate = Braintree::Subscription.create(
295
+ :payment_method_token => @credit_card.token,
296
+ :plan_id => Braintree::Test::Plans::TrialPlan[:id],
297
+ :id => duplicate_id
298
+ )
299
+ result = Braintree::Subscription.update(
300
+ @subscription.id,
301
+ :id => duplicate_id
302
+ )
303
+ result.success?.should == false
304
+ result.errors.for(:subscription).on(:id)[0].code.should == Braintree::ErrorCodes::Subscription::IdIsInUse
305
+ end
306
+
307
+ it "cannot update a canceled subscription" do
308
+ subscription = Braintree::Subscription.create(
309
+ :payment_method_token => @credit_card.token,
310
+ :price => 54.32,
311
+ :plan_id => Braintree::Test::Plans::TriallessPlan[:id]
312
+ ).subscription
313
+
314
+ result = Braintree::Subscription.cancel(subscription.id)
315
+ result.success?.should == true
316
+
317
+ result = Braintree::Subscription.update(subscription.id,
318
+ :price => 123.45
319
+ )
320
+ result.success?.should == false
321
+ result.errors.for(:subscription)[0].code.should == Braintree::ErrorCodes::Subscription::CannotEditCanceledSubscription
322
+ end
323
+ end
324
+ end
325
+
326
+
327
+ describe "self.cancel" do
328
+ it "returns a success response with the updated subscription if valid" do
329
+ subscription = Braintree::Subscription.create(
330
+ :payment_method_token => @credit_card.token,
331
+ :price => 54.32,
332
+ :plan_id => Braintree::Test::Plans::TriallessPlan[:id]
333
+ ).subscription
334
+
335
+ result = Braintree::Subscription.cancel(subscription.id)
336
+ result.success?.should == true
337
+ result.subscription.status.should == Braintree::Subscription::Status::Canceled
338
+ end
339
+
340
+ it "returns a validation error if record not found" do
341
+ expect {
342
+ r = Braintree::Subscription.cancel('noSuchSubscription')
343
+ }.to raise_error(Braintree::NotFoundError, 'subscription with id "noSuchSubscription" not found')
344
+ end
345
+
346
+ it "cannot be canceled if already canceld" do
347
+ subscription = Braintree::Subscription.create(
348
+ :payment_method_token => @credit_card.token,
349
+ :price => 54.32,
350
+ :plan_id => Braintree::Test::Plans::TriallessPlan[:id]
351
+ ).subscription
352
+
353
+ result = Braintree::Subscription.cancel(subscription.id)
354
+ result.success?.should == true
355
+ result.subscription.status.should == Braintree::Subscription::Status::Canceled
356
+
357
+ result = Braintree::Subscription.cancel(subscription.id)
358
+ result.success?.should == false
359
+ result.errors.for(:subscription)[0].code.should == "81905"
360
+ end
361
+ end
362
+ end