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 +1 -0
- data/lib/braintree/descriptor.rb +1 -1
- data/lib/braintree/dispute.rb +3 -0
- data/lib/braintree/dispute/transaction_details.rb +14 -0
- data/lib/braintree/error_codes.rb +1 -0
- data/lib/braintree/payment_method.rb +4 -0
- data/lib/braintree/payment_method_gateway.rb +46 -6
- data/lib/braintree/paypal_account.rb +2 -1
- data/lib/braintree/subscription_gateway.rb +2 -2
- data/lib/braintree/transaction_gateway.rb +1 -1
- data/lib/braintree/version.rb +2 -2
- data/lib/braintree/webhook_notification.rb +7 -1
- data/lib/braintree/webhook_testing_gateway.rb +63 -0
- data/spec/httpsd.pid +1 -0
- data/spec/integration/braintree/client_api/client_token_spec.rb +1 -1
- data/spec/integration/braintree/payment_method_spec.rb +586 -0
- data/spec/integration/braintree/paypal_account_spec.rb +30 -0
- data/spec/integration/braintree/subscription_spec.rb +7 -3
- data/spec/integration/braintree/transaction_spec.rb +8 -2
- data/spec/unit/braintree/webhook_notification_spec.rb +47 -0
- metadata +134 -132
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"
|
data/lib/braintree/descriptor.rb
CHANGED
data/lib/braintree/dispute.rb
CHANGED
@@ -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
|
@@ -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
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
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
|
]
|
data/lib/braintree/version.rb
CHANGED
@@ -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 ==
|
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
|