braintree 1.0.1 → 1.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 (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