braintree 2.30.0 → 2.30.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +1 -1
  3. data/lib/braintree.rb +5 -1
  4. data/lib/braintree/client_token.rb +18 -0
  5. data/lib/braintree/client_token_gateway.rb +30 -0
  6. data/lib/braintree/configuration.rb +29 -0
  7. data/lib/braintree/credit_card.rb +4 -0
  8. data/lib/braintree/credit_card_gateway.rb +9 -1
  9. data/lib/braintree/customer.rb +4 -0
  10. data/lib/braintree/gateway.rb +4 -0
  11. data/lib/braintree/http.rb +13 -1
  12. data/lib/braintree/sha256_digest.rb +13 -0
  13. data/lib/braintree/signature_service.rb +19 -0
  14. data/lib/braintree/subscription_gateway.rb +2 -0
  15. data/lib/braintree/successful_result.rb +4 -6
  16. data/lib/braintree/transaction_gateway.rb +1 -1
  17. data/lib/braintree/transparent_redirect_gateway.rb +3 -8
  18. data/lib/braintree/version.rb +1 -1
  19. data/lib/braintree/webhook_notification_gateway.rb +11 -5
  20. data/lib/braintree/webhook_testing_gateway.rb +13 -13
  21. data/spec/httpsd.pid +1 -1
  22. data/spec/integration/braintree/client_api/client_token_spec.rb +143 -0
  23. data/spec/integration/braintree/client_api/spec_helper.rb +80 -0
  24. data/spec/integration/braintree/credit_card_spec.rb +99 -0
  25. data/spec/integration/braintree/customer_spec.rb +24 -0
  26. data/spec/integration/braintree/subscription_spec.rb +40 -1
  27. data/spec/integration/braintree/transaction_search_spec.rb +2 -2
  28. data/spec/integration/braintree/transaction_spec.rb +27 -5
  29. data/spec/unit/braintree/client_token_spec.rb +37 -0
  30. data/spec/unit/braintree/configuration_spec.rb +30 -0
  31. data/spec/unit/braintree/credit_card_spec.rb +2 -0
  32. data/spec/unit/braintree/customer_spec.rb +2 -0
  33. data/spec/unit/braintree/digest_spec.rb +14 -0
  34. data/spec/unit/braintree/http_spec.rb +19 -0
  35. data/spec/unit/braintree/sha256_digest_spec.rb +11 -0
  36. data/spec/unit/braintree/signature_service_spec.rb +23 -0
  37. data/spec/unit/braintree/successful_result_spec.rb +7 -7
  38. data/spec/unit/braintree/transparent_redirect_spec.rb +8 -1
  39. data/spec/unit/braintree/webhook_notification_spec.rb +58 -4
  40. metadata +126 -121
@@ -0,0 +1,80 @@
1
+ require 'json'
2
+
3
+ def nonce_for_new_credit_card(options)
4
+ client_token_options = options.delete(:client_token_options) || {}
5
+ client_token = Braintree::ClientToken.generate(client_token_options)
6
+ client = ClientApiHttp.new(Braintree::Configuration.instantiate,
7
+ :authorization_fingerprint => JSON.parse(client_token)["authorizationFingerprint"],
8
+ :shared_customer_identifier => "fake_identifier",
9
+ :shared_customer_identifier_type => "testing"
10
+ )
11
+
12
+ response = client.add_card(options)
13
+ body = JSON.parse(response.body)
14
+
15
+ if body["errors"] != nil
16
+ raise body["errors"].inspect
17
+ end
18
+
19
+ body["nonce"]
20
+ end
21
+
22
+ class ClientApiHttp
23
+ attr_reader :config, :options
24
+
25
+ def initialize(config, options)
26
+ @config = config
27
+ @options = options
28
+ end
29
+
30
+ def get(path)
31
+ _http_do(Net::HTTP::Get, path)
32
+ end
33
+
34
+ def post(path, params)
35
+ _http_do(Net::HTTP::Post, path, params.to_json)
36
+ end
37
+
38
+ def fingerprint=(fingerprint)
39
+ @options[:authorization_fingerprint] = fingerprint
40
+ end
41
+
42
+ def _http_do(http_verb, path, body = nil)
43
+ connection = Net::HTTP.new(@config.server, @config.port)
44
+ connection.read_timeout = 60
45
+ if @config.ssl?
46
+ connection.use_ssl = true
47
+ connection.verify_mode = OpenSSL::SSL::VERIFY_PEER
48
+ connection.ca_file = @config.ca_file
49
+ connection.verify_callback = proc { |preverify_ok, ssl_context| _verify_ssl_certificate(preverify_ok, ssl_context) }
50
+ end
51
+ connection.start do |http|
52
+ request = http_verb.new(path)
53
+ request["X-ApiVersion"] = @config.api_version
54
+ request["Content-Type"] = "application/json"
55
+ request.body = body if body
56
+ http.request(request)
57
+ end
58
+ rescue OpenSSL::SSL::SSLError
59
+ raise Braintree::SSLCertificateError
60
+ end
61
+
62
+ def get_cards
63
+ encoded_fingerprint = Braintree::Util.url_encode(@options[:authorization_fingerprint])
64
+ url = "/merchants/#{@config.merchant_id}/client_api/nonces.json?"
65
+ url += "authorizationFingerprint=#{encoded_fingerprint}"
66
+ url += "&sharedCustomerIdentifier=#{@options[:shared_customer_identifier]}"
67
+ url += "&sharedCustomerIdentifierType=#{@options[:shared_customer_identifier_type]}"
68
+
69
+ get(url)
70
+ end
71
+
72
+ def add_card(params)
73
+ fingerprint = @options[:authorization_fingerprint]
74
+ params[:authorizationFingerprint] = fingerprint
75
+ params[:sharedCustomerIdentifier] = @options[:shared_customer_identifier]
76
+ params[:sharedCustomerIdentifierType] = @options[:shared_customer_identifier_type]
77
+
78
+ post("/merchants/#{@config.merchant_id}/client_api/nonces.json", params)
79
+ end
80
+ end
@@ -1,4 +1,5 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
2
+ require File.expand_path(File.dirname(__FILE__) + "/client_api/spec_helper")
2
3
 
3
4
  describe Braintree::CreditCard do
4
5
  describe "self.create" do
@@ -452,6 +453,30 @@ describe Braintree::CreditCard do
452
453
  end
453
454
  end
454
455
  end
456
+
457
+ context "client API" do
458
+ it "adds credit card to an existing customer using a payment method nonce" do
459
+ nonce = nonce_for_new_credit_card(
460
+ :credit_card => {
461
+ :number => "4111111111111111",
462
+ :expiration_month => "11",
463
+ :expiration_year => "2099",
464
+ },
465
+ :share => true
466
+ )
467
+ customer = Braintree::Customer.create!
468
+ result = Braintree::CreditCard.create(
469
+ :customer_id => customer.id,
470
+ :payment_method_nonce => nonce
471
+ )
472
+
473
+ result.success?.should == true
474
+ credit_card = result.credit_card
475
+ credit_card.bin.should == "411111"
476
+ credit_card.last_4.should == "1111"
477
+ credit_card.expiration_date.should == "11/2099"
478
+ end
479
+ end
455
480
  end
456
481
 
457
482
  describe "self.create!" do
@@ -1138,6 +1163,80 @@ describe Braintree::CreditCard do
1138
1163
  end
1139
1164
  end
1140
1165
 
1166
+ describe "self.from_nonce" do
1167
+ it "finds the payment method with the given nonce" do
1168
+ customer = Braintree::Customer.create!
1169
+ nonce = nonce_for_new_credit_card(
1170
+ :credit_card => {
1171
+ :number => "4111111111111111",
1172
+ :expiration_month => "11",
1173
+ :expiration_year => "2099",
1174
+ },
1175
+ :client_token_options => {:customer_id => customer.id}
1176
+ )
1177
+
1178
+ credit_card = Braintree::CreditCard.from_nonce(nonce)
1179
+ customer = Braintree::Customer.find(customer.id)
1180
+ credit_card.should == customer.credit_cards.first
1181
+ end
1182
+
1183
+ it "does not find a payment method for an unlocked nonce that points to a shared credit card" do
1184
+ nonce = nonce_for_new_credit_card(
1185
+ :credit_card => {
1186
+ :number => "4111111111111111",
1187
+ :expiration_month => "11",
1188
+ :expiration_year => "2099",
1189
+ }
1190
+ )
1191
+ expect do
1192
+ Braintree::CreditCard.from_nonce(nonce)
1193
+ end.to raise_error(Braintree::NotFoundError)
1194
+ end
1195
+
1196
+ it "does not find the payment method for a locked nonce" do
1197
+ client_token = Braintree::ClientToken.generate
1198
+ client = ClientApiHttp.new(Braintree::Configuration.instantiate,
1199
+ :authorization_fingerprint => JSON.parse(client_token)["authorizationFingerprint"],
1200
+ :shared_customer_identifier => "fake_identifier",
1201
+ :shared_customer_identifier_type => "testing"
1202
+ )
1203
+
1204
+ client.add_card(
1205
+ :credit_card => {
1206
+ :number => "4111111111111111",
1207
+ :expiration_month => "11",
1208
+ :expiration_year => "2099",
1209
+ },
1210
+ :share => true
1211
+ )
1212
+
1213
+ response = client.get_cards
1214
+ body = JSON.parse(response.body)
1215
+ nonce = body["creditCards"].first["nonce"]
1216
+
1217
+ expect do
1218
+ Braintree::CreditCard.from_nonce(nonce)
1219
+ end.to raise_error(Braintree::NotFoundError, /locked/)
1220
+ end
1221
+
1222
+ it "does not find the payment method for a consumednonce" do
1223
+ customer = Braintree::Customer.create!
1224
+ nonce = nonce_for_new_credit_card(
1225
+ :credit_card => {
1226
+ :number => "4111111111111111",
1227
+ :expiration_month => "11",
1228
+ :expiration_year => "2099",
1229
+ },
1230
+ :client_token_options => {:customer_id => customer.id}
1231
+ )
1232
+
1233
+ Braintree::CreditCard.from_nonce(nonce)
1234
+ expect do
1235
+ Braintree::CreditCard.from_nonce(nonce)
1236
+ end.to raise_error(Braintree::NotFoundError, /consumed/)
1237
+ end
1238
+ end
1239
+
1141
1240
  describe "self.sale" do
1142
1241
  it "creates a sale transaction using the credit card, returning a result object" do
1143
1242
  customer = Braintree::Customer.create!(
@@ -1,5 +1,6 @@
1
1
  # encoding: utf-8
2
2
  require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
3
+ require File.expand_path(File.dirname(__FILE__) + "/client_api/spec_helper")
3
4
 
4
5
  describe Braintree::Customer do
5
6
  describe "self.all" do
@@ -379,6 +380,29 @@ describe Braintree::Customer do
379
380
  result.customer.credit_cards.first.venmo_sdk?.should == true
380
381
  end
381
382
  end
383
+
384
+ context "client API" do
385
+ it "can create a customer with a payment method nonce" do
386
+ nonce = nonce_for_new_credit_card(
387
+ :credit_card => {
388
+ :number => "4111111111111111",
389
+ :expiration_month => "11",
390
+ :expiration_year => "2099",
391
+ },
392
+ :share => true
393
+ )
394
+
395
+ result = Braintree::Customer.create(
396
+ :credit_card => {
397
+ :payment_method_nonce => nonce
398
+ }
399
+ )
400
+
401
+ result.success?.should == true
402
+ result.customer.credit_cards.first.bin.should == "411111"
403
+ result.customer.credit_cards.first.last_4.should == "1111"
404
+ end
405
+ end
382
406
  end
383
407
 
384
408
  describe "self.create!" do
@@ -1,4 +1,5 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
2
+ require File.expand_path(File.dirname(__FILE__) + "/client_api/spec_helper")
2
3
 
3
4
  describe Braintree::Subscription do
4
5
 
@@ -63,6 +64,28 @@ describe Braintree::Subscription do
63
64
  result.subscription.id.should == new_id
64
65
  end
65
66
 
67
+ it "creates a subscription when given a payment_method_nonce" do
68
+ nonce = nonce_for_new_credit_card(
69
+ :credit_card => {
70
+ :number => Braintree::Test::CreditCardNumbers::Visa,
71
+ :expiration_month => "11",
72
+ :expiration_year => "2099",
73
+ },
74
+ :client_token_options => {
75
+ :customer_id => @credit_card.customer_id
76
+ }
77
+ )
78
+ result = Braintree::Subscription.create(
79
+ :payment_method_nonce => nonce,
80
+ :plan_id => SpecHelper::TriallessPlan[:id]
81
+ )
82
+
83
+ result.success?.should == true
84
+ transaction = result.subscription.transactions[0]
85
+ transaction.credit_card_details.bin.should == Braintree::Test::CreditCardNumbers::Visa[0, 6]
86
+ transaction.credit_card_details.last_4.should == Braintree::Test::CreditCardNumbers::Visa[-4, 4]
87
+ end
88
+
66
89
  context "billing_day_of_month" do
67
90
  it "inherits from the plan if not provided" do
68
91
  result = Braintree::Subscription.create(
@@ -653,7 +676,7 @@ describe Braintree::Subscription do
653
676
  result.subscription.merchant_account_id.should == SpecHelper::NonDefaultMerchantAccountId
654
677
  end
655
678
 
656
- it "allows changing the payment_method_token" do
679
+ it "allows changing the payment method by payment_method_token" do
657
680
  new_credit_card = Braintree::CreditCard.create!(
658
681
  :customer_id => @credit_card.customer_id,
659
682
  :number => Braintree::Test::CreditCardNumbers::Visa,
@@ -667,6 +690,22 @@ describe Braintree::Subscription do
667
690
  result.subscription.payment_method_token.should == new_credit_card.token
668
691
  end
669
692
 
693
+ it "allows changing the payment_method by payment_method_nonce" do
694
+ nonce = nonce_for_new_credit_card(
695
+ :credit_card => {
696
+ :number => Braintree::Test::CreditCardNumbers::MasterCard,
697
+ :expiration_date => "05/2010"
698
+ },
699
+ :client_token_options => {
700
+ :customer_id => @credit_card.customer_id,
701
+ }
702
+ )
703
+
704
+ result = Braintree::Subscription.update(@subscription.id, :payment_method_nonce => nonce)
705
+ result.subscription.transactions[0].credit_card_details.token.should == @credit_card.token
706
+ result.subscription.payment_method_token.should_not == @credit_card.token
707
+ end
708
+
670
709
  it "allows chaning the descriptors" do
671
710
  result = Braintree::Subscription.update(@subscription.id,
672
711
  :descriptor => {
@@ -696,7 +696,7 @@ describe Braintree::Transaction, "search" do
696
696
  context "dispute_date" do
697
697
  it "searches on dispute_date in UTC" do
698
698
  disputed_time = Time.parse("2014-03-01 00:00:00 UTC")
699
- transaction_id = "disputedtransaction"
699
+ transaction_id = "2disputetransaction"
700
700
 
701
701
  collection = Braintree::Transaction.search do |search|
702
702
  search.id.is transaction_id
@@ -746,7 +746,7 @@ describe Braintree::Transaction, "search" do
746
746
 
747
747
  it "searches on dispute_date in local time" do
748
748
  now = Time.parse("2014-03-01 18:00:00 CST")
749
- transaction_id = "disputedtransaction"
749
+ transaction_id = "2disputetransaction"
750
750
 
751
751
  collection = Braintree::Transaction.search do |search|
752
752
  search.id.is transaction_id
@@ -1,4 +1,5 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
2
+ require File.expand_path(File.dirname(__FILE__) + "/client_api/spec_helper")
2
3
 
3
4
  describe Braintree::Transaction do
4
5
  describe "self.clone_transaction" do
@@ -1053,6 +1054,27 @@ describe Braintree::Transaction do
1053
1054
  result.transaction.credit_card_details.venmo_sdk?.should == true
1054
1055
  end
1055
1056
  end
1057
+
1058
+ context "client API" do
1059
+ it "can create a transaction with a nonce" do
1060
+ nonce = nonce_for_new_credit_card(
1061
+ :credit_card => {
1062
+ :number => "4111111111111111",
1063
+ :expiration_month => "11",
1064
+ :expiration_year => "2099",
1065
+ },
1066
+ :share => true
1067
+ )
1068
+
1069
+ result = Braintree::Transaction.create(
1070
+ :type => "sale",
1071
+ :amount => Braintree::Test::TransactionAmounts::Authorize,
1072
+ :payment_method_nonce => nonce
1073
+ )
1074
+
1075
+ result.success?.should == true
1076
+ end
1077
+ end
1056
1078
  end
1057
1079
 
1058
1080
  describe "self.create!" do
@@ -1207,7 +1229,7 @@ describe Braintree::Transaction do
1207
1229
  :customer => {
1208
1230
  :first_name => "Dan",
1209
1231
  :last_name => "Smith",
1210
- :company => "Braintree Payment Solutions",
1232
+ :company => "Braintree",
1211
1233
  :email => "dan@example.com",
1212
1234
  :phone => "419-555-1234",
1213
1235
  :fax => "419-555-1235",
@@ -1259,7 +1281,7 @@ describe Braintree::Transaction do
1259
1281
  transaction.cvv_response_code.should == "M"
1260
1282
  transaction.customer_details.first_name.should == "Dan"
1261
1283
  transaction.customer_details.last_name.should == "Smith"
1262
- transaction.customer_details.company.should == "Braintree Payment Solutions"
1284
+ transaction.customer_details.company.should == "Braintree"
1263
1285
  transaction.customer_details.email.should == "dan@example.com"
1264
1286
  transaction.customer_details.phone.should == "419-555-1234"
1265
1287
  transaction.customer_details.fax.should == "419-555-1235"
@@ -1917,7 +1939,7 @@ describe Braintree::Transaction do
1917
1939
  :customer => {
1918
1940
  :first_name => "Dan",
1919
1941
  :last_name => "Smith",
1920
- :company => "Braintree Payment Solutions",
1942
+ :company => "Braintree",
1921
1943
  :email => "dan@example.com",
1922
1944
  :phone => "419-555-1234",
1923
1945
  :fax => "419-555-1235",
@@ -1972,7 +1994,7 @@ describe Braintree::Transaction do
1972
1994
  transaction.cvv_response_code.should == "M"
1973
1995
  transaction.customer_details.first_name.should == "Dan"
1974
1996
  transaction.customer_details.last_name.should == "Smith"
1975
- transaction.customer_details.company.should == "Braintree Payment Solutions"
1997
+ transaction.customer_details.company.should == "Braintree"
1976
1998
  transaction.customer_details.email.should == "dan@example.com"
1977
1999
  transaction.customer_details.phone.should == "419-555-1234"
1978
2000
  transaction.customer_details.fax.should == "419-555-1235"
@@ -2079,7 +2101,7 @@ describe Braintree::Transaction do
2079
2101
  it "includes disputes on found transactions" do
2080
2102
  found_transaction = Braintree::Transaction.find("disputedtransaction")
2081
2103
 
2082
- found_transaction.disputes.count.should == 2
2104
+ found_transaction.disputes.count.should == 1
2083
2105
 
2084
2106
  dispute = found_transaction.disputes.first
2085
2107
  dispute.received_date.should == Date.new(2014, 3, 1)
@@ -0,0 +1,37 @@
1
+
2
+ require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
3
+
4
+ module Braintree
5
+ describe ClientToken do
6
+ describe "self.generate" do
7
+ it "delegates to ClientTokenGateway#generate" do
8
+ options = {:foo => :bar}
9
+ client_token_gateway = double(:client_token_gateway)
10
+ client_token_gateway.should_receive(:generate).with(options).once
11
+ ClientTokenGateway.stub(:new).and_return(client_token_gateway)
12
+ ClientToken.generate(options)
13
+ end
14
+
15
+ it "can't overwrite public_key, or created_at" do
16
+ expect {
17
+ client_token = Braintree::ClientToken.generate(
18
+ :public_key => "bad_key",
19
+ :created_at => "bad_time"
20
+ )
21
+ }.to raise_error(ArgumentError, /created_at, public_key/)
22
+ end
23
+ end
24
+
25
+ context "adding credit_card options with no customer ID" do
26
+ %w(verify_card fail_on_duplicate_payment_method make_default).each do |option_name|
27
+ it "raises an ArgumentError if #{option_name} is present" do
28
+ expect do
29
+ Braintree::ClientToken.generate(
30
+ option_name.to_sym => true
31
+ )
32
+ end.to raise_error(ArgumentError, /#{option_name}/)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -235,6 +235,28 @@ describe Braintree::Configuration do
235
235
  end
236
236
  end
237
237
 
238
+ describe "auth_url" do
239
+ it "is http://auth.venmo.dev for development" do
240
+ Braintree::Configuration.environment = :development
241
+ Braintree::Configuration.instantiate.auth_url.should == "http://auth.venmo.dev:9292"
242
+ end
243
+
244
+ it "is https://auth.venmo.com for production" do
245
+ Braintree::Configuration.environment = :production
246
+ Braintree::Configuration.instantiate.auth_url.should == "https://auth.venmo.com"
247
+ end
248
+
249
+ it "is https://auth.sandbox.venmo.com for sandbox" do
250
+ Braintree::Configuration.environment = :sandbox
251
+ Braintree::Configuration.instantiate.auth_url.should == "https://auth.venmo.sandbox.braintreegateway.com"
252
+ end
253
+
254
+ it "is https://auth.qa.venmo.com for qa" do
255
+ Braintree::Configuration.environment = :qa
256
+ Braintree::Configuration.instantiate.auth_url.should == "https://auth.venmo.qa2.braintreegateway.com"
257
+ end
258
+ end
259
+
238
260
  describe "ssl?" do
239
261
  it "returns false for development" do
240
262
  Braintree::Configuration.environment = :development
@@ -275,4 +297,12 @@ describe Braintree::Configuration do
275
297
  config.inspect.should_not include('secret_key')
276
298
  end
277
299
  end
300
+
301
+ describe "signature_service" do
302
+ it "has a signature service initialized with the private key" do
303
+ config = Braintree::Configuration.new(:private_key => "secret_key")
304
+
305
+ config.signature_service.key.should == "secret_key"
306
+ end
307
+ end
278
308
  end