braintree 2.32.1 → 2.33.0

Sign up to get free protection for your applications and to get access to all the features.
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