braintree 2.32.1 → 2.33.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.
data/lib/braintree.rb CHANGED
@@ -42,6 +42,7 @@ require "braintree/digest"
42
42
  require "braintree/discount"
43
43
  require "braintree/discount_gateway"
44
44
  require "braintree/dispute"
45
+ require "braintree/dispute/transaction_details"
45
46
  require "braintree/error_codes"
46
47
  require "braintree/error_result"
47
48
  require "braintree/errors"
@@ -2,7 +2,7 @@ module Braintree
2
2
  class Descriptor # :nodoc:
3
3
  include BaseModule
4
4
 
5
- attr_reader :name, :phone
5
+ attr_reader :name, :phone, :url
6
6
 
7
7
  def initialize(attributes)
8
8
  set_instance_variables_from_hash attributes unless attributes.nil?
@@ -8,6 +8,8 @@ module Braintree
8
8
  attr_reader :status
9
9
  attr_reader :reason
10
10
  attr_reader :currency_iso_code
11
+ attr_reader :id
12
+ attr_reader :transaction_details
11
13
 
12
14
  module Status
13
15
  Open = "open"
@@ -40,6 +42,7 @@ module Braintree
40
42
  @received_date = Date.parse(received_date)
41
43
  @reply_by_date = Date.parse(reply_by_date) unless reply_by_date.nil?
42
44
  @amount = Util.to_big_decimal(amount)
45
+ @transaction_details = TransactionDetails.new(@transaction)
43
46
  end
44
47
  end
45
48
  end
@@ -0,0 +1,14 @@
1
+ module Braintree
2
+ class Dispute
3
+ class TransactionDetails # :nodoc:
4
+ include BaseModule
5
+
6
+ attr_reader :amount, :id
7
+
8
+ def initialize(attributes)
9
+ set_instance_variables_from_hash attributes unless attributes.nil?
10
+ @amount = Util.to_big_decimal(amount)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -107,6 +107,7 @@ module Braintree
107
107
  DynamicDescriptorsDisabled = "92203"
108
108
  InternationalNameFormatIsInvalid = "92204"
109
109
  InternationalPhoneFormatIsInvalid = "92205"
110
+ UrlFormatIsInvalid = "92206"
110
111
  end
111
112
 
112
113
  module PayPalAccount
@@ -10,6 +10,10 @@ module Braintree
10
10
  Configuration.gateway.payment_method.find(token)
11
11
  end
12
12
 
13
+ def self.update(token, attributes)
14
+ Configuration.gateway.payment_method.update(token, attributes)
15
+ end
16
+
13
17
  def self.delete(token)
14
18
  Configuration.gateway.payment_method.delete(token)
15
19
  end
@@ -45,14 +45,54 @@ module Braintree
45
45
  raise NotFoundError, "payment method with token #{token.inspect} not found"
46
46
  end
47
47
 
48
+ def update(token, attributes)
49
+ Util.verify_keys(PaymentMethodGateway._update_signature, attributes)
50
+ _do_update(:put, "/payment_methods/any/#{token}", :payment_method => attributes)
51
+ end
52
+
53
+ def _do_update(http_verb, url, params) # :nodoc:
54
+ response = @config.http.send http_verb, url, params
55
+ if response[:credit_card]
56
+ SuccessfulResult.new(:payment_method => CreditCard._new(@gateway, response[:credit_card]))
57
+ elsif response[:paypal_account]
58
+ SuccessfulResult.new(:payment_method => PayPalAccount._new(@gateway, response[:paypal_account]))
59
+ elsif response[:api_error_response]
60
+ ErrorResult.new(@gateway, response[:api_error_response])
61
+ else
62
+ raise UnexpectedError, "expected :payment_method or :api_error_response"
63
+ end
64
+ end
65
+
48
66
  def self._create_signature # :nodoc:
49
- options = [:make_default]
50
- [
51
- :customer_id,
52
- :payment_method_nonce,
53
- :token,
54
- {:options => options}
67
+ _signature(:create)
68
+ end
69
+
70
+ def self._update_signature # :nodoc:
71
+ _signature(:update)
72
+ end
73
+
74
+ def self._signature(type) # :nodoc:
75
+ billing_address_params = AddressGateway._shared_signature
76
+ options = [:make_default, :verification_merchant_account_id, :verify_card, :venmo_sdk_session]
77
+ signature = [
78
+ :billing_address_id, :cardholder_name, :cvv, :device_session_id, :expiration_date,
79
+ :expiration_month, :expiration_year, :number, :token, :venmo_sdk_payment_method_code,
80
+ :device_data, :fraud_merchant_id, :payment_method_nonce,
81
+ {:options => options},
82
+ {:billing_address => billing_address_params}
55
83
  ]
84
+
85
+ case type
86
+ when :create
87
+ options << :fail_on_duplicate_payment_method
88
+ signature << :customer_id
89
+ when :update
90
+ billing_address_params << {:options => [:update_existing]}
91
+ else
92
+ raise ArgumentError
93
+ end
94
+
95
+ return signature
56
96
  end
57
97
  end
58
98
  end
@@ -2,11 +2,12 @@ module Braintree
2
2
  class PayPalAccount
3
3
  include BaseModule
4
4
 
5
- attr_reader :email, :token, :image_url, :created_at, :updated_at
5
+ attr_reader :email, :token, :image_url, :created_at, :updated_at, :subscriptions
6
6
 
7
7
  def initialize(gateway, attributes) # :nodoc:
8
8
  @gateway = gateway
9
9
  set_instance_variables_from_hash(attributes)
10
+ @subscriptions = (@subscriptions || []).map { |subscription_hash| Subscription._new(@gateway, subscription_hash) }
10
11
  end
11
12
 
12
13
  class << self
@@ -67,7 +67,7 @@ module Braintree
67
67
  :trial_duration_unit,
68
68
  :trial_period,
69
69
  {:options => [:do_not_inherit_add_ons_or_discounts, :start_immediately]},
70
- {:descriptor => [:name, :phone]}
70
+ {:descriptor => [:name, :phone, :url]}
71
71
  ] + _add_on_discount_signature
72
72
  end
73
73
 
@@ -86,7 +86,7 @@ module Braintree
86
86
  :replace_all_add_ons_and_discounts,
87
87
  :revert_subscription_on_proration_failure
88
88
  ]},
89
- {:descriptor => [:name, :phone]}
89
+ {:descriptor => [:name, :phone, :url]}
90
90
  ] + _add_on_discount_signature
91
91
  end
92
92
 
@@ -129,7 +129,7 @@ module Braintree
129
129
  },
130
130
  {:options => [:hold_in_escrow, :store_in_vault, :store_in_vault_on_success, :submit_for_settlement, :add_billing_address_to_payment_method, :store_shipping_address_in_vault, :venmo_sdk_session]},
131
131
  {:custom_fields => :_any_key_},
132
- {:descriptor => [:name, :phone]},
132
+ {:descriptor => [:name, :phone, :url]},
133
133
  {:paypal_account => [:email, :token, :paypal_data]},
134
134
  {:industry => [:industry_type, {:data => [:folio_number, :check_in_date, :check_out_date]}]}
135
135
  ]
@@ -1,8 +1,8 @@
1
1
  module Braintree
2
2
  module Version
3
3
  Major = 2
4
- Minor = 32
5
- Tiny = 1
4
+ Minor = 33
5
+ Tiny = 0
6
6
 
7
7
  String = "#{Major}.#{Minor}.#{Tiny}"
8
8
  end
@@ -7,6 +7,11 @@ module Braintree
7
7
  module Kind
8
8
  Disbursement = "disbursement"
9
9
  DisbursementException = "disbursement_exception"
10
+
11
+ DisputeOpened = "dispute_opened"
12
+ DisputeLost = "dispute_lost"
13
+ DisputeWon = "dispute_won"
14
+
10
15
  SubscriptionCanceled = "subscription_canceled"
11
16
  SubscriptionChargedSuccessfully = "subscription_charged_successfully"
12
17
  SubscriptionChargedUnsuccessfully = "subscription_charged_unsuccessfully"
@@ -23,7 +28,7 @@ module Braintree
23
28
  PartnerMerchantDeclined = "partner_merchant_declined"
24
29
  end
25
30
 
26
- attr_reader :subscription, :kind, :timestamp, :transaction, :partner_merchant, :disbursement
31
+ attr_reader :subscription, :kind, :timestamp, :transaction, :partner_merchant, :disbursement, :dispute
27
32
 
28
33
  def self.parse(signature, payload)
29
34
  Configuration.gateway.webhook_notification.parse(signature, payload)
@@ -42,6 +47,7 @@ module Braintree
42
47
  @subscription = Subscription._new(gateway, @subject[:subscription]) if @subject.has_key?(:subscription)
43
48
  @transaction = Transaction._new(gateway, @subject[:transaction]) if @subject.has_key?(:transaction)
44
49
  @disbursement = Disbursement._new(gateway, @subject[:disbursement]) if @subject.has_key?(:disbursement)
50
+ @dispute = Dispute._new(@subject[:dispute]) if @subject.has_key?(:dispute)
45
51
  end
46
52
 
47
53
  def merchant_account
@@ -26,6 +26,12 @@ module Braintree
26
26
 
27
27
  def _subject_sample_xml(kind, id)
28
28
  case kind
29
+ when Braintree::WebhookNotification::Kind::DisputeOpened
30
+ _dispute_opened_sample_xml(id)
31
+ when Braintree::WebhookNotification::Kind::DisputeLost
32
+ _dispute_lost_sample_xml(id)
33
+ when Braintree::WebhookNotification::Kind::DisputeWon
34
+ _dispute_won_sample_xml(id)
29
35
  when Braintree::WebhookNotification::Kind::PartnerMerchantConnected
30
36
  _partner_merchant_connected_sample_xml(id)
31
37
  when Braintree::WebhookNotification::Kind::PartnerMerchantDisconnected
@@ -149,6 +155,63 @@ module Braintree
149
155
  XML
150
156
  end
151
157
 
158
+ def _dispute_opened_sample_xml(id)
159
+
160
+ <<-XML
161
+ <dispute>
162
+ <amount>250.00</amount>
163
+ <currency-iso-code>USD</currency-iso-code>
164
+ <received-date type="date">2014-03-01</received-date>
165
+ <reply-by-date type="date">2014-03-21</reply-by-date>
166
+ <status>open</status>
167
+ <reason>fraud</reason>
168
+ <id>#{id}</id>
169
+ <transaction>
170
+ <id>#{id}</id>
171
+ <amount>250.00</amount>
172
+ </transaction>
173
+ </dispute>
174
+ XML
175
+ end
176
+
177
+ def _dispute_lost_sample_xml(id)
178
+
179
+ <<-XML
180
+ <dispute>
181
+ <amount>250.00</amount>
182
+ <currency-iso-code>USD</currency-iso-code>
183
+ <received-date type="date">2014-03-01</received-date>
184
+ <reply-by-date type="date">2014-03-21</reply-by-date>
185
+ <status>lost</status>
186
+ <reason>fraud</reason>
187
+ <id>#{id}</id>
188
+ <transaction>
189
+ <id>#{id}</id>
190
+ <amount>250.00</amount>
191
+ </transaction>
192
+ </dispute>
193
+ XML
194
+ end
195
+
196
+ def _dispute_won_sample_xml(id)
197
+
198
+ <<-XML
199
+ <dispute>
200
+ <amount>250.00</amount>
201
+ <currency-iso-code>USD</currency-iso-code>
202
+ <received-date type="date">2014-03-01</received-date>
203
+ <reply-by-date type="date">2014-03-21</reply-by-date>
204
+ <status>won</status>
205
+ <reason>fraud</reason>
206
+ <id>#{id}</id>
207
+ <transaction>
208
+ <id>#{id}</id>
209
+ <amount>250.00</amount>
210
+ </transaction>
211
+ </dispute>
212
+ XML
213
+ end
214
+
152
215
  def _disbursement_exception_sample_xml(id)
153
216
 
154
217
  <<-XML
data/spec/httpsd.pid ADDED
@@ -0,0 +1 @@
1
+ 2708
@@ -38,7 +38,7 @@ describe Braintree::ClientToken do
38
38
  config = Braintree::Configuration.instantiate
39
39
  client_token_string = Braintree::ClientToken.generate
40
40
  client_token = decode_client_token(client_token_string)
41
- client_token["version"].should == "2"
41
+ client_token["version"].should == 2
42
42
  end
43
43
  end
44
44
 
@@ -116,6 +116,220 @@ describe Braintree::PaymentMethod do
116
116
  payment_method.token.should == second_token
117
117
  end
118
118
 
119
+ it "respects verify_card and verification_merchant_account_id when included outside of the nonce" do
120
+ nonce = nonce_for_new_payment_method(
121
+ :credit_card => {
122
+ :number => "4000111111111115",
123
+ :expiration_month => "11",
124
+ :expiration_year => "2099",
125
+ }
126
+ )
127
+ customer = Braintree::Customer.create!
128
+ result = Braintree::PaymentMethod.create(
129
+ :payment_method_nonce => nonce,
130
+ :customer_id => customer.id,
131
+ :options => {
132
+ :verify_card => true,
133
+ :verification_merchant_account_id => SpecHelper::NonDefaultMerchantAccountId
134
+ }
135
+ )
136
+
137
+ result.should_not be_success
138
+ result.credit_card_verification.status.should == Braintree::Transaction::Status::ProcessorDeclined
139
+ result.credit_card_verification.processor_response_code.should == "2000"
140
+ result.credit_card_verification.processor_response_text.should == "Do Not Honor"
141
+ result.credit_card_verification.merchant_account_id.should == SpecHelper::NonDefaultMerchantAccountId
142
+ end
143
+
144
+ it "respects fail_on_duplicate_payment_method when included outside of the nonce" do
145
+ customer = Braintree::Customer.create!
146
+ result = Braintree::CreditCard.create(
147
+ :customer_id => customer.id,
148
+ :number => Braintree::Test::CreditCardNumbers::Visa,
149
+ :expiration_date => "05/2012"
150
+ )
151
+ result.should be_success
152
+
153
+ nonce = nonce_for_new_payment_method(
154
+ :credit_card => {
155
+ :number => Braintree::Test::CreditCardNumbers::Visa,
156
+ :expiration_date => "05/2012"
157
+ }
158
+ )
159
+ result = Braintree::PaymentMethod.create(
160
+ :payment_method_nonce => nonce,
161
+ :customer_id => customer.id,
162
+ :options => {
163
+ :fail_on_duplicate_payment_method => true
164
+ }
165
+ )
166
+
167
+ result.should_not be_success
168
+ result.errors.first.code.should == "81724"
169
+ end
170
+
171
+ it "allows passing the billing address outside of the nonce" do
172
+ config = Braintree::Configuration.instantiate
173
+ customer = Braintree::Customer.create.customer
174
+ raw_client_token = Braintree::ClientToken.generate(:customer_id => customer.id)
175
+ client_token = decode_client_token(raw_client_token)
176
+ authorization_fingerprint = client_token["authorizationFingerprint"]
177
+ http = ClientApiHttp.new(
178
+ config,
179
+ :authorization_fingerprint => authorization_fingerprint,
180
+ :shared_customer_identifier => "fake_identifier",
181
+ :shared_customer_identifier_type => "testing"
182
+ )
183
+
184
+ response = http.create_credit_card(
185
+ :number => "4111111111111111",
186
+ :expirationMonth => "12",
187
+ :expirationYear => "2020",
188
+ :options => {:validate => false}
189
+ )
190
+ response.code.should == "202"
191
+
192
+ nonce = JSON.parse(response.body)["creditCards"].first["nonce"]
193
+ result = Braintree::PaymentMethod.create(
194
+ :payment_method_nonce => nonce,
195
+ :customer_id => customer.id,
196
+ :billing_address => {
197
+ :street_address => "123 Abc Way"
198
+ }
199
+ )
200
+
201
+ result.should be_success
202
+ result.payment_method.should be_a(Braintree::CreditCard)
203
+ token = result.payment_method.token
204
+
205
+ found_credit_card = Braintree::CreditCard.find(token)
206
+ found_credit_card.should_not be_nil
207
+ found_credit_card.billing_address.street_address.should == "123 Abc Way"
208
+ end
209
+
210
+ it "allows passing a billing address id outside of the nonce" do
211
+ config = Braintree::Configuration.instantiate
212
+ customer = Braintree::Customer.create.customer
213
+ raw_client_token = Braintree::ClientToken.generate(:customer_id => customer.id)
214
+ client_token = decode_client_token(raw_client_token)
215
+ authorization_fingerprint = client_token["authorizationFingerprint"]
216
+ http = ClientApiHttp.new(
217
+ config,
218
+ :authorization_fingerprint => authorization_fingerprint,
219
+ :shared_customer_identifier => "fake_identifier",
220
+ :shared_customer_identifier_type => "testing"
221
+ )
222
+
223
+ response = http.create_credit_card(
224
+ :number => "4111111111111111",
225
+ :expirationMonth => "12",
226
+ :expirationYear => "2020",
227
+ :options => {:validate => false}
228
+ )
229
+ response.code.should == "202"
230
+
231
+ nonce = JSON.parse(response.body)["creditCards"].first["nonce"]
232
+
233
+ address = Braintree::Address.create!(:customer_id => customer.id, :first_name => "Bobby", :last_name => "Tables")
234
+ result = Braintree::PaymentMethod.create(
235
+ :payment_method_nonce => nonce,
236
+ :customer_id => customer.id,
237
+ :billing_address_id => address.id
238
+ )
239
+
240
+ result.should be_success
241
+ result.payment_method.should be_a(Braintree::CreditCard)
242
+ token = result.payment_method.token
243
+
244
+ found_credit_card = Braintree::CreditCard.find(token)
245
+ found_credit_card.should_not be_nil
246
+ found_credit_card.billing_address.first_name.should == "Bobby"
247
+ found_credit_card.billing_address.last_name.should == "Tables"
248
+ end
249
+
250
+ it "overrides the billing address in the nonce" do
251
+ config = Braintree::Configuration.instantiate
252
+ customer = Braintree::Customer.create.customer
253
+ raw_client_token = Braintree::ClientToken.generate(:customer_id => customer.id)
254
+ client_token = decode_client_token(raw_client_token)
255
+ authorization_fingerprint = client_token["authorizationFingerprint"]
256
+ http = ClientApiHttp.new(
257
+ config,
258
+ :authorization_fingerprint => authorization_fingerprint,
259
+ :shared_customer_identifier => "fake_identifier",
260
+ :shared_customer_identifier_type => "testing"
261
+ )
262
+
263
+ response = http.create_credit_card(
264
+ :number => "4111111111111111",
265
+ :expirationMonth => "12",
266
+ :expirationYear => "2020",
267
+ :options => {:validate => false},
268
+ :billing_address => {
269
+ :street_address => "456 Xyz Way"
270
+ }
271
+ )
272
+ response.code.should == "202"
273
+
274
+ nonce = JSON.parse(response.body)["creditCards"].first["nonce"]
275
+ result = Braintree::PaymentMethod.create(
276
+ :payment_method_nonce => nonce,
277
+ :customer_id => customer.id,
278
+ :billing_address => {
279
+ :street_address => "123 Abc Way"
280
+ }
281
+ )
282
+
283
+ result.should be_success
284
+ result.payment_method.should be_a(Braintree::CreditCard)
285
+ token = result.payment_method.token
286
+
287
+ found_credit_card = Braintree::CreditCard.find(token)
288
+ found_credit_card.should_not be_nil
289
+ found_credit_card.billing_address.street_address.should == "123 Abc Way"
290
+ end
291
+
292
+ it "does not override the billing address for a vaulted credit card" do
293
+ config = Braintree::Configuration.instantiate
294
+ customer = Braintree::Customer.create.customer
295
+ raw_client_token = Braintree::ClientToken.generate(:customer_id => customer.id)
296
+ client_token = decode_client_token(raw_client_token)
297
+ authorization_fingerprint = client_token["authorizationFingerprint"]
298
+ http = ClientApiHttp.new(
299
+ config,
300
+ :authorization_fingerprint => authorization_fingerprint,
301
+ :shared_customer_identifier => "fake_identifier",
302
+ :shared_customer_identifier_type => "testing"
303
+ )
304
+
305
+ response = http.create_credit_card(
306
+ :number => 4111111111111111,
307
+ :expirationMonth => 12,
308
+ :expirationYear => 2020,
309
+ :billing_address => {
310
+ :street_address => "456 Xyz Way"
311
+ }
312
+ )
313
+ response.code.should == "201"
314
+
315
+ nonce = JSON.parse(response.body)["creditCards"].first["nonce"]
316
+ result = Braintree::PaymentMethod.create(
317
+ :payment_method_nonce => nonce,
318
+ :customer_id => customer.id,
319
+ :billing_address => {
320
+ :street_address => "123 Abc Way"
321
+ }
322
+ )
323
+
324
+ result.should be_success
325
+ result.payment_method.should be_a(Braintree::CreditCard)
326
+ token = result.payment_method.token
327
+
328
+ found_credit_card = Braintree::CreditCard.find(token)
329
+ found_credit_card.should_not be_nil
330
+ found_credit_card.billing_address.street_address.should == "456 Xyz Way"
331
+ end
332
+
119
333
  context "paypal" do
120
334
  it "creates a payment method from an unvalidated future paypal account nonce" do
121
335
  nonce = nonce_for_paypal_account(:consent_code => "PAYPAL_CONSENT_CODE")
@@ -146,6 +360,44 @@ describe Braintree::PaymentMethod do
146
360
  result.errors.first.code.should == "82902"
147
361
  end
148
362
 
363
+ it "ignores passed billing address params" do
364
+ nonce = nonce_for_paypal_account(:consent_code => "PAYPAL_CONSENT_CODE")
365
+ customer = Braintree::Customer.create.customer
366
+ result = Braintree::PaymentMethod.create(
367
+ :payment_method_nonce => nonce,
368
+ :customer_id => customer.id,
369
+ :billing_address => {
370
+ :street_address => "123 Abc Way"
371
+ }
372
+ )
373
+
374
+ result.should be_success
375
+ result.payment_method.should be_a(Braintree::PayPalAccount)
376
+ result.payment_method.image_url.should_not be_nil
377
+ token = result.payment_method.token
378
+
379
+ found_paypal_account = Braintree::PayPalAccount.find(token)
380
+ found_paypal_account.should_not be_nil
381
+ end
382
+
383
+ it "ignores passed billing address id" do
384
+ nonce = nonce_for_paypal_account(:consent_code => "PAYPAL_CONSENT_CODE")
385
+ customer = Braintree::Customer.create.customer
386
+ result = Braintree::PaymentMethod.create(
387
+ :payment_method_nonce => nonce,
388
+ :customer_id => customer.id,
389
+ :billing_address_id => "address_id"
390
+ )
391
+
392
+ result.should be_success
393
+ result.payment_method.should be_a(Braintree::PayPalAccount)
394
+ result.payment_method.image_url.should_not be_nil
395
+ token = result.payment_method.token
396
+
397
+ found_paypal_account = Braintree::PayPalAccount.find(token)
398
+ found_paypal_account.should_not be_nil
399
+ end
400
+
149
401
  it "returns appropriate validation errors" do
150
402
  customer = Braintree::Customer.create.customer
151
403
  nonce = nonce_for_paypal_account(:token => "PAYPAL_TOKEN")
@@ -159,6 +411,27 @@ describe Braintree::PaymentMethod do
159
411
  errors.should include("82901")
160
412
  errors.should include("82902")
161
413
  end
414
+
415
+ it "doesn't return an error if credit card options are present for a paypal nonce" do
416
+ customer = Braintree::Customer.create!
417
+ original_token = "paypal-account-#{Time.now.to_i}"
418
+ nonce = nonce_for_paypal_account(
419
+ :consent_code => "consent-code",
420
+ :token => original_token
421
+ )
422
+
423
+ result = Braintree::PaymentMethod.create(
424
+ :payment_method_nonce => nonce,
425
+ :customer_id => customer.id,
426
+ :options => {
427
+ :verify_card => true,
428
+ :fail_on_duplicate_payment_method => true,
429
+ :verification_merchant_account_id => "not_a_real_merchant_account_id"
430
+ }
431
+ )
432
+
433
+ result.should be_success
434
+ end
162
435
  end
163
436
 
164
437
  context "SEPA" do
@@ -312,4 +585,317 @@ describe Braintree::PaymentMethod do
312
585
  end.to raise_error(Braintree::NotFoundError, "payment method with token \"#{token}\" not found")
313
586
  end
314
587
  end
588
+
589
+ describe "self.update" do
590
+ context "credit cards" do
591
+ it "updates the credit card" do
592
+ customer = Braintree::Customer.create!
593
+ credit_card = Braintree::CreditCard.create!(
594
+ :cardholder_name => "Original Holder",
595
+ :customer_id => customer.id,
596
+ :cvv => "123",
597
+ :number => Braintree::Test::CreditCardNumbers::Visa,
598
+ :expiration_date => "05/2012"
599
+ )
600
+ update_result = Braintree::PaymentMethod.update(credit_card.token,
601
+ :cardholder_name => "New Holder",
602
+ :cvv => "456",
603
+ :number => Braintree::Test::CreditCardNumbers::MasterCard,
604
+ :expiration_date => "06/2013"
605
+ )
606
+ update_result.success?.should == true
607
+ update_result.payment_method.should == credit_card
608
+ updated_credit_card = update_result.payment_method
609
+ updated_credit_card.cardholder_name.should == "New Holder"
610
+ updated_credit_card.bin.should == Braintree::Test::CreditCardNumbers::MasterCard[0, 6]
611
+ updated_credit_card.last_4.should == Braintree::Test::CreditCardNumbers::MasterCard[-4..-1]
612
+ updated_credit_card.expiration_date.should == "06/2013"
613
+ end
614
+
615
+ context "billing address" do
616
+ it "creates a new billing address by default" do
617
+ customer = Braintree::Customer.create!
618
+ credit_card = Braintree::CreditCard.create!(
619
+ :customer_id => customer.id,
620
+ :number => Braintree::Test::CreditCardNumbers::Visa,
621
+ :expiration_date => "05/2012",
622
+ :billing_address => {
623
+ :street_address => "123 Nigeria Ave"
624
+ }
625
+ )
626
+ update_result = Braintree::PaymentMethod.update(credit_card.token,
627
+ :billing_address => {
628
+ :region => "IL"
629
+ }
630
+ )
631
+ update_result.success?.should == true
632
+ updated_credit_card = update_result.payment_method
633
+ updated_credit_card.billing_address.region.should == "IL"
634
+ updated_credit_card.billing_address.street_address.should == nil
635
+ updated_credit_card.billing_address.id.should_not == credit_card.billing_address.id
636
+ end
637
+
638
+ it "updates the billing address if option is specified" do
639
+ customer = Braintree::Customer.create!
640
+ credit_card = Braintree::CreditCard.create!(
641
+ :customer_id => customer.id,
642
+ :number => Braintree::Test::CreditCardNumbers::Visa,
643
+ :expiration_date => "05/2012",
644
+ :billing_address => {
645
+ :street_address => "123 Nigeria Ave"
646
+ }
647
+ )
648
+ update_result = Braintree::PaymentMethod.update(credit_card.token,
649
+ :billing_address => {
650
+ :region => "IL",
651
+ :options => {:update_existing => true}
652
+ }
653
+ )
654
+ update_result.success?.should == true
655
+ updated_credit_card = update_result.payment_method
656
+ updated_credit_card.billing_address.region.should == "IL"
657
+ updated_credit_card.billing_address.street_address.should == "123 Nigeria Ave"
658
+ updated_credit_card.billing_address.id.should == credit_card.billing_address.id
659
+ end
660
+
661
+ it "updates the country via codes" do
662
+ customer = Braintree::Customer.create!
663
+ credit_card = Braintree::CreditCard.create!(
664
+ :customer_id => customer.id,
665
+ :number => Braintree::Test::CreditCardNumbers::Visa,
666
+ :expiration_date => "05/2012",
667
+ :billing_address => {
668
+ :street_address => "123 Nigeria Ave"
669
+ }
670
+ )
671
+ update_result = Braintree::PaymentMethod.update(credit_card.token,
672
+ :billing_address => {
673
+ :country_name => "American Samoa",
674
+ :country_code_alpha2 => "AS",
675
+ :country_code_alpha3 => "ASM",
676
+ :country_code_numeric => "016",
677
+ :options => {:update_existing => true}
678
+ }
679
+ )
680
+ update_result.success?.should == true
681
+ updated_credit_card = update_result.payment_method
682
+ updated_credit_card.billing_address.country_name.should == "American Samoa"
683
+ updated_credit_card.billing_address.country_code_alpha2.should == "AS"
684
+ updated_credit_card.billing_address.country_code_alpha3.should == "ASM"
685
+ updated_credit_card.billing_address.country_code_numeric.should == "016"
686
+ end
687
+ end
688
+
689
+ it "can pass expiration_month and expiration_year" do
690
+ customer = Braintree::Customer.create!
691
+ credit_card = Braintree::CreditCard.create!(
692
+ :customer_id => customer.id,
693
+ :number => Braintree::Test::CreditCardNumbers::Visa,
694
+ :expiration_date => "05/2012"
695
+ )
696
+ update_result = Braintree::PaymentMethod.update(credit_card.token,
697
+ :number => Braintree::Test::CreditCardNumbers::MasterCard,
698
+ :expiration_month => "07",
699
+ :expiration_year => "2011"
700
+ )
701
+ update_result.success?.should == true
702
+ update_result.payment_method.should == credit_card
703
+ update_result.payment_method.expiration_month.should == "07"
704
+ update_result.payment_method.expiration_year.should == "2011"
705
+ update_result.payment_method.expiration_date.should == "07/2011"
706
+ end
707
+
708
+ it "verifies the update if options[verify_card]=true" do
709
+ customer = Braintree::Customer.create!
710
+ credit_card = Braintree::CreditCard.create!(
711
+ :cardholder_name => "Original Holder",
712
+ :customer_id => customer.id,
713
+ :cvv => "123",
714
+ :number => Braintree::Test::CreditCardNumbers::Visa,
715
+ :expiration_date => "05/2012"
716
+ )
717
+ update_result = Braintree::PaymentMethod.update(credit_card.token,
718
+ :cardholder_name => "New Holder",
719
+ :cvv => "456",
720
+ :number => Braintree::Test::CreditCardNumbers::FailsSandboxVerification::MasterCard,
721
+ :expiration_date => "06/2013",
722
+ :options => {:verify_card => true}
723
+ )
724
+ update_result.success?.should == false
725
+ update_result.credit_card_verification.status.should == Braintree::Transaction::Status::ProcessorDeclined
726
+ update_result.credit_card_verification.gateway_rejection_reason.should be_nil
727
+ end
728
+
729
+ it "can update the billing address" do
730
+ customer = Braintree::Customer.create!
731
+ credit_card = Braintree::CreditCard.create!(
732
+ :cardholder_name => "Original Holder",
733
+ :customer_id => customer.id,
734
+ :cvv => "123",
735
+ :number => Braintree::Test::CreditCardNumbers::Visa,
736
+ :expiration_date => "05/2012",
737
+ :billing_address => {
738
+ :first_name => "Old First Name",
739
+ :last_name => "Old Last Name",
740
+ :company => "Old Company",
741
+ :street_address => "123 Old St",
742
+ :extended_address => "Apt Old",
743
+ :locality => "Old City",
744
+ :region => "Old State",
745
+ :postal_code => "12345",
746
+ :country_name => "Canada"
747
+ }
748
+ )
749
+ result = Braintree::PaymentMethod.update(credit_card.token,
750
+ :options => {:verify_card => false},
751
+ :billing_address => {
752
+ :first_name => "New First Name",
753
+ :last_name => "New Last Name",
754
+ :company => "New Company",
755
+ :street_address => "123 New St",
756
+ :extended_address => "Apt New",
757
+ :locality => "New City",
758
+ :region => "New State",
759
+ :postal_code => "56789",
760
+ :country_name => "United States of America"
761
+ }
762
+ )
763
+ result.success?.should == true
764
+ address = result.payment_method.billing_address
765
+ address.first_name.should == "New First Name"
766
+ address.last_name.should == "New Last Name"
767
+ address.company.should == "New Company"
768
+ address.street_address.should == "123 New St"
769
+ address.extended_address.should == "Apt New"
770
+ address.locality.should == "New City"
771
+ address.region.should == "New State"
772
+ address.postal_code.should == "56789"
773
+ address.country_name.should == "United States of America"
774
+ end
775
+
776
+ it "returns an error response if invalid" do
777
+ customer = Braintree::Customer.create!
778
+ credit_card = Braintree::CreditCard.create!(
779
+ :cardholder_name => "Original Holder",
780
+ :customer_id => customer.id,
781
+ :number => Braintree::Test::CreditCardNumbers::Visa,
782
+ :expiration_date => "05/2012"
783
+ )
784
+ update_result = Braintree::PaymentMethod.update(credit_card.token,
785
+ :cardholder_name => "New Holder",
786
+ :number => "invalid",
787
+ :expiration_date => "05/2014"
788
+ )
789
+ update_result.success?.should == false
790
+ update_result.errors.for(:credit_card).on(:number)[0].message.should == "Credit card number must be 12-19 digits."
791
+ end
792
+
793
+ it "can update the default" do
794
+ customer = Braintree::Customer.create!
795
+ card1 = Braintree::CreditCard.create(
796
+ :customer_id => customer.id,
797
+ :number => Braintree::Test::CreditCardNumbers::Visa,
798
+ :expiration_date => "05/2009"
799
+ ).credit_card
800
+ card2 = Braintree::CreditCard.create(
801
+ :customer_id => customer.id,
802
+ :number => Braintree::Test::CreditCardNumbers::Visa,
803
+ :expiration_date => "05/2009"
804
+ ).credit_card
805
+
806
+ card1.should be_default
807
+ card2.should_not be_default
808
+
809
+ Braintree::PaymentMethod.update(card2.token, :options => {:make_default => true})
810
+
811
+ Braintree::CreditCard.find(card1.token).should_not be_default
812
+ Braintree::CreditCard.find(card2.token).should be_default
813
+ end
814
+ end
815
+
816
+ context "paypal accounts" do
817
+ it "updates a paypal account's token" do
818
+ customer = Braintree::Customer.create!
819
+ original_token = "paypal-account-#{Time.now.to_i}"
820
+ nonce = nonce_for_paypal_account(
821
+ :consent_code => "consent-code",
822
+ :token => original_token
823
+ )
824
+ original_result = Braintree::PaymentMethod.create(
825
+ :payment_method_nonce => nonce,
826
+ :customer_id => customer.id
827
+ )
828
+
829
+ updated_token = "UPDATED_TOKEN-" + rand(36**3).to_s(36)
830
+ updated_result = Braintree::PaymentMethod.update(
831
+ original_token,
832
+ :token => updated_token
833
+ )
834
+
835
+ updated_paypal_account = Braintree::PayPalAccount.find(updated_token)
836
+ updated_paypal_account.email.should == original_result.payment_method.email
837
+
838
+ expect do
839
+ Braintree::PayPalAccount.find(original_token)
840
+ end.to raise_error(Braintree::NotFoundError, "payment method with token \"#{original_token}\" not found")
841
+ end
842
+
843
+ it "can make a paypal account the default payment method" do
844
+ customer = Braintree::Customer.create!
845
+ result = Braintree::CreditCard.create(
846
+ :customer_id => customer.id,
847
+ :number => Braintree::Test::CreditCardNumbers::Visa,
848
+ :expiration_date => "05/2009",
849
+ :options => {:make_default => true}
850
+ )
851
+ result.should be_success
852
+
853
+ nonce = nonce_for_paypal_account(:consent_code => "consent-code")
854
+ original_token = Braintree::PaymentMethod.create(
855
+ :payment_method_nonce => nonce,
856
+ :customer_id => customer.id
857
+ ).payment_method.token
858
+
859
+ updated_result = Braintree::PaymentMethod.update(
860
+ original_token,
861
+ :options => {:make_default => true}
862
+ )
863
+
864
+ updated_paypal_account = Braintree::PayPalAccount.find(original_token)
865
+ updated_paypal_account.should be_default
866
+ end
867
+
868
+ it "returns an error if a token for account is used to attempt an update" do
869
+ customer = Braintree::Customer.create!
870
+ first_token = "paypal-account-#{rand(36**3).to_s(36)}"
871
+ second_token = "paypal-account-#{rand(36**3).to_s(36)}"
872
+
873
+ first_nonce = nonce_for_paypal_account(
874
+ :consent_code => "consent-code",
875
+ :token => first_token
876
+ )
877
+ first_result = Braintree::PaymentMethod.create(
878
+ :payment_method_nonce => first_nonce,
879
+ :customer_id => customer.id
880
+ )
881
+
882
+ second_nonce = nonce_for_paypal_account(
883
+ :consent_code => "consent-code",
884
+ :token => second_token
885
+ )
886
+ second_result = Braintree::PaymentMethod.create(
887
+ :payment_method_nonce => second_nonce,
888
+ :customer_id => customer.id
889
+ )
890
+
891
+ updated_result = Braintree::PaymentMethod.update(
892
+ first_token,
893
+ :token => second_token
894
+ )
895
+
896
+ updated_result.should_not be_success
897
+ updated_result.errors.first.code.should == "92906"
898
+ end
899
+ end
900
+ end
315
901
  end