braintree 4.29.0 → 4.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/lib/braintree/address_gateway.rb +5 -0
  3. data/lib/braintree/apple_pay_card.rb +1 -0
  4. data/lib/braintree/bank_account_instant_verification_gateway.rb +38 -0
  5. data/lib/braintree/bank_account_instant_verification_jwt.rb +23 -0
  6. data/lib/braintree/bank_account_instant_verification_jwt_request.rb +21 -0
  7. data/lib/braintree/dispute.rb +1 -0
  8. data/lib/braintree/error_codes.rb +2 -0
  9. data/lib/braintree/gateway.rb +4 -0
  10. data/lib/braintree/payment_method_gateway.rb +6 -0
  11. data/lib/braintree/successful_result.rb +1 -0
  12. data/lib/braintree/transaction/apple_pay_details.rb +1 -0
  13. data/lib/braintree/transaction.rb +3 -0
  14. data/lib/braintree/transaction_gateway.rb +29 -0
  15. data/lib/braintree/us_bank_account_verification.rb +3 -1
  16. data/lib/braintree/version.rb +1 -1
  17. data/lib/braintree/webhook_notification.rb +1 -0
  18. data/lib/braintree/webhook_testing_gateway.rb +16 -0
  19. data/lib/braintree.rb +3 -0
  20. data/spec/integration/braintree/bank_account_instant_verification_spec.rb +191 -0
  21. data/spec/integration/braintree/client_api/spec_helper.rb +81 -0
  22. data/spec/integration/braintree/dispute_spec.rb +13 -2
  23. data/spec/integration/braintree/payment_method_spec.rb +3 -0
  24. data/spec/integration/braintree/transaction_search_spec.rb +26 -24
  25. data/spec/integration/braintree/transaction_spec.rb +43 -3
  26. data/spec/integration/braintree/transaction_transfer_type_spec.rb +301 -0
  27. data/spec/spec_helper.rb +7 -0
  28. data/spec/unit/braintree/apple_pay_card_spec.rb +2 -0
  29. data/spec/unit/braintree/bank_account_instant_verification_gateway_spec.rb +98 -0
  30. data/spec/unit/braintree/bank_account_instant_verification_jwt_request_spec.rb +71 -0
  31. data/spec/unit/braintree/dispute_spec.rb +6 -0
  32. data/spec/unit/braintree/transaction_ach_mandate_spec.rb +82 -0
  33. data/spec/unit/braintree/transaction_gateway_spec.rb +25 -0
  34. data/spec/unit/braintree/transaction_spec.rb +18 -0
  35. data/spec/unit/braintree/webhook_notification_spec.rb +17 -0
  36. metadata +11 -4
  37. data/lib/ssl/securetrust_ca.crt +0 -44
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c1ad6cd9b340121a0247feb50ad4b05ed7b64508f6362a7712caf94d5eaf0841
4
- data.tar.gz: 42204c6e19efec3a657ef9dfa801efbe38d33869f452aa4f16db9806d165c727
3
+ metadata.gz: 0a1505b44cec85c55cc5237d0fa3d57165dfa809589893774fdecfc972147940
4
+ data.tar.gz: e92b0f87a02744e0df5db4173aa6a2e237dd5b7817932232e380a160d7df1450
5
5
  SHA512:
6
- metadata.gz: 960910f8cefb89b65b9463988235e95c1375ecb2401e04df8368fdd63412b9cec5d370832fe92c9d22932bdbe35001e91af6c97bfd37e288b49181cf4fb02c77
7
- data.tar.gz: 4ad2f1cb6ad7ba10cec9efe4ab695177e235b7cf1f95d8527a1a13d72533adc12807c7b8019aca7145b8ac6beb74cfa284b134ae8c266451eea1eff0187d2d03
6
+ metadata.gz: 2d5c303f50bd453a719fc0586593fcf9307fc8a1be02c5307487178bbf368d7e1467bf08ceda4e0b96c421a46f1d43d1f9c17af35206efcb22b5c3d0f0da9094
7
+ data.tar.gz: 287dadfccf55558b99069ffd42458b67085a68aabeb2d6575765e8f96c281c97158228bf99254a802d84b50e41fc90dc28b06d4c5984fb06e2563e28c108c286
@@ -84,6 +84,11 @@ module Braintree
84
84
  def self._update_signature
85
85
  _create_signature
86
86
  end
87
+
88
+ def self._address_attributes
89
+ [:street_address, :extended_address, :locality, :region, :postal_code, :country_code_alpha2,
90
+ {:international_phone => [:country_code, :national_number]}]
91
+ end
87
92
  end
88
93
  end
89
94
 
@@ -29,6 +29,7 @@ module Braintree
29
29
  attr_reader :expired
30
30
  attr_reader :healthcare
31
31
  attr_reader :image_url
32
+ attr_reader :is_device_token
32
33
  attr_reader :issuing_bank
33
34
  attr_reader :last_4
34
35
  attr_reader :merchant_token_identifier
@@ -0,0 +1,38 @@
1
+ module Braintree
2
+ class BankAccountInstantVerificationGateway
3
+
4
+ CREATE_JWT_MUTATION =
5
+ "mutation CreateBankAccountInstantVerificationJwt($input: CreateBankAccountInstantVerificationJwtInput!) { " +
6
+ " createBankAccountInstantVerificationJwt(input: $input) {" +
7
+ " jwt" +
8
+ " }" +
9
+ "}"
10
+
11
+ def initialize(gateway)
12
+ @gateway = gateway
13
+ @config = gateway.config
14
+ end
15
+
16
+ def create_jwt(request)
17
+ variables = request.to_graphql_variables
18
+ response = @gateway.graphql_client.query(CREATE_JWT_MUTATION, variables)
19
+ errors = Braintree::GraphQLClient.get_validation_errors(response)
20
+
21
+ if errors
22
+ ErrorResult.new(@gateway, {errors: errors})
23
+ else
24
+ data = response.dig(:data, :createBankAccountInstantVerificationJwt)
25
+
26
+ if data.nil?
27
+ raise UnexpectedError, "expected :createBankAccountInstantVerificationJwt"
28
+ end
29
+
30
+ jwt_attrs = {
31
+ :jwt => data[:jwt]
32
+ }
33
+
34
+ SuccessfulResult.new(:bank_account_instant_verification_jwt => BankAccountInstantVerificationJwt._new(jwt_attrs))
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,23 @@
1
+ module Braintree
2
+ class BankAccountInstantVerificationJwt
3
+ include BaseModule
4
+
5
+ attr_reader :jwt
6
+
7
+ def initialize(attributes)
8
+ set_instance_variables_from_hash(attributes)
9
+ end
10
+
11
+ def self._new(*args)
12
+ self.new(*args)
13
+ end
14
+
15
+ def inspect
16
+ attr_order = [:jwt]
17
+ formatted_attrs = attr_order.map do |attr|
18
+ "#{attr}: #{send(attr).inspect}"
19
+ end
20
+ "#<#{self.class} #{formatted_attrs.join(', ')}>"
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,21 @@
1
+ module Braintree
2
+ class BankAccountInstantVerificationJwtRequest
3
+ include BaseModule
4
+
5
+ attr_accessor :business_name, :return_url, :cancel_url
6
+
7
+ def initialize(attributes = {})
8
+ set_instance_variables_from_hash(attributes)
9
+ end
10
+
11
+ def to_graphql_variables
12
+ variables = {:input => {}}
13
+
14
+ variables[:input][:businessName] = business_name if business_name
15
+ variables[:input][:returnUrl] = return_url if return_url
16
+ variables[:input][:cancelUrl] = cancel_url if cancel_url
17
+
18
+ variables
19
+ end
20
+ end
21
+ end
@@ -32,6 +32,7 @@ module Braintree
32
32
  attr_reader :reply_by_date
33
33
  attr_reader :status
34
34
  attr_reader :status_history
35
+ attr_reader :remaining_file_evidence_storage
35
36
  attr_reader :transaction
36
37
  attr_reader :transaction_details
37
38
  attr_reader :updated_at
@@ -459,6 +459,8 @@ module Braintree
459
459
  TransactionIsNotEligibleForAdjustment = "915219"
460
460
  TransactionMustBeInStateAuthorized = "915218"
461
461
  TransactionSourceIsInvalid = "915133"
462
+ TransactionTransferDetailsAreMandatory = "97510"
463
+ TransactionTransferTypeIsInvalid = "97501"
462
464
  TypeIsInvalid = "91523"
463
465
  TypeIsRequired = "91524"
464
466
  UnsupportedVoiceAuthorization = "91539"
@@ -26,6 +26,10 @@ module Braintree
26
26
  ApplePayGateway.new(self)
27
27
  end
28
28
 
29
+ def bank_account_instant_verification
30
+ BankAccountInstantVerificationGateway.new(self)
31
+ end
32
+
29
33
  def client_token
30
34
  ClientTokenGateway.new(self)
31
35
  end
@@ -201,6 +201,12 @@ module Braintree
201
201
  :ds_transaction_id,
202
202
  ]
203
203
  }
204
+ signature << {
205
+ :us_bank_account => [
206
+ :ach_mandate_text,
207
+ :ach_mandate_accepted_at,
208
+ ]
209
+ }
204
210
 
205
211
  case type
206
212
  when :create
@@ -23,6 +23,7 @@ module Braintree
23
23
  attr_reader :subscription
24
24
  attr_reader :supported_networks
25
25
  attr_reader :transaction
26
+ attr_reader :bank_account_instant_verification_jwt
26
27
  attr_reader :us_bank_account_verification
27
28
  attr_reader :session_id
28
29
  attr_reader :customer_recommendations
@@ -18,6 +18,7 @@ module Braintree
18
18
  attr_reader :global_id
19
19
  attr_reader :healthcare
20
20
  attr_reader :image_url
21
+ attr_reader :is_device_token
21
22
  attr_reader :issuing_bank
22
23
  attr_reader :last_4
23
24
  attr_reader :merchant_token_identifier
@@ -82,6 +82,8 @@ module Braintree
82
82
  end
83
83
  end
84
84
 
85
+ attr_reader :account_funding_transaction
86
+ attr_reader :ach_reject_reason
85
87
  attr_reader :ach_return_code
86
88
  attr_reader :ach_return_responses
87
89
  attr_reader :acquirer_reference_number
@@ -171,6 +173,7 @@ module Braintree
171
173
  attr_reader :tax_exempt
172
174
  attr_reader :three_d_secure_info
173
175
  attr_reader :type
176
+ attr_reader :upcoming_retry_date
174
177
  attr_reader :updated_at
175
178
  attr_reader :us_bank_account_details
176
179
  attr_reader :venmo_account_details
@@ -280,6 +280,35 @@ module Braintree
280
280
  :ds_transaction_id,
281
281
  ]
282
282
  },
283
+ {
284
+ :transfer => [
285
+ :type,
286
+ {
287
+ :sender => [
288
+ :first_name,
289
+ :last_name,
290
+ :account_reference_number,
291
+ :tax_id,
292
+ {:address => AddressGateway._address_attributes}
293
+ ]
294
+ },
295
+ {
296
+ :receiver => [
297
+ :first_name,
298
+ :last_name,
299
+ :account_reference_number,
300
+ :tax_id,
301
+ {:address => AddressGateway._address_attributes}
302
+ ]
303
+ },
304
+ ]
305
+ },
306
+ {
307
+ :us_bank_account => [
308
+ :ach_mandate_text,
309
+ :ach_mandate_accepted_at,
310
+ ]
311
+ },
283
312
  ]
284
313
  end
285
314
 
@@ -15,11 +15,12 @@ module Braintree
15
15
 
16
16
  module VerificationMethod
17
17
  IndependentCheck = "independent_check"
18
+ InstantVerificationAccountValidation = "instant_verification_account_validation"
18
19
  NetworkCheck = "network_check"
19
20
  TokenizedCheck = "tokenized_check"
20
21
  MicroTransfers = "micro_transfers"
21
22
 
22
- All = [IndependentCheck, NetworkCheck, TokenizedCheck, MicroTransfers]
23
+ All = [IndependentCheck, InstantVerificationAccountValidation, NetworkCheck, TokenizedCheck, MicroTransfers]
23
24
  end
24
25
 
25
26
  module VerificationAddOns
@@ -42,6 +43,7 @@ module Braintree
42
43
 
43
44
  def initialize(attributes)
44
45
  set_instance_variables_from_hash(attributes)
46
+ @us_bank_account = UsBankAccount._new(nil, attributes[:us_bank_account]) if attributes[:us_bank_account]
45
47
  end
46
48
 
47
49
  def inspect
@@ -1,7 +1,7 @@
1
1
  module Braintree
2
2
  module Version
3
3
  Major = 4
4
- Minor = 29
4
+ Minor = 31
5
5
  Tiny = 0
6
6
 
7
7
  String = "#{Major}.#{Minor}.#{Tiny}"
@@ -57,6 +57,7 @@ module Braintree
57
57
  SubscriptionWentPastDue = "subscription_went_past_due"
58
58
 
59
59
  TransactionDisbursed = "transaction_disbursed"
60
+ TransactionRetried = "transaction_retried"
60
61
  TransactionReviewed = "transaction_reviewed"
61
62
  TransactionSettlementDeclined = "transaction_settlement_declined"
62
63
  TransactionSettled = "transaction_settled"
@@ -60,6 +60,8 @@ module Braintree
60
60
  _oauth_access_revoked_sample_xml(id)
61
61
  when Braintree::WebhookNotification::Kind::TransactionDisbursed
62
62
  _transaction_disbursed_sample_xml(id)
63
+ when Braintree::WebhookNotification::Kind::TransactionRetried
64
+ _transaction_retried_sample_xml(id)
63
65
  when Braintree::WebhookNotification::Kind::TransactionReviewed
64
66
  _transaction_reviewed_sample_xml(id)
65
67
  when Braintree::WebhookNotification::Kind::TransactionSettled
@@ -269,6 +271,20 @@ module Braintree
269
271
  XML
270
272
  end
271
273
 
274
+ def _transaction_retried_sample_xml(id)
275
+
276
+ <<-XML
277
+ <transaction>
278
+ <id>#{id}</id>
279
+ <amount>100</amount>
280
+ <status>submitted_for_settlement</status>
281
+ <type>sale</type>
282
+ <currency-iso-code>USD</currency-iso-code>
283
+ <retried-transaction-id>original_txn_id</retried-transaction-id>
284
+ </transaction>
285
+ XML
286
+ end
287
+
272
288
  def _transaction_reviewed_sample_xml(id)
273
289
 
274
290
  <<-XML
data/lib/braintree.rb CHANGED
@@ -35,6 +35,9 @@ require "braintree/apple_pay_card"
35
35
  require "braintree/apple_pay_gateway"
36
36
  require "braintree/apple_pay_options"
37
37
  require "braintree/authorization_adjustment"
38
+ require "braintree/bank_account_instant_verification_gateway"
39
+ require "braintree/bank_account_instant_verification_jwt"
40
+ require "braintree/bank_account_instant_verification_jwt_request"
38
41
  require "braintree/bin_data"
39
42
  require "braintree/client_token"
40
43
  require "braintree/client_token_gateway"
@@ -0,0 +1,191 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
2
+ require File.expand_path(File.dirname(__FILE__) + "/client_api/spec_helper")
3
+
4
+ describe Braintree::BankAccountInstantVerificationGateway do
5
+ before do
6
+ @gateway = Braintree::Gateway.new(
7
+ :environment => :development,
8
+ :merchant_id => "integration2_merchant_id",
9
+ :public_key => "integration2_public_key",
10
+ :private_key => "integration2_private_key",
11
+ )
12
+
13
+ @us_bank_gateway = Braintree::Gateway.new(
14
+ :environment => :development,
15
+ :merchant_id => "integration_merchant_id",
16
+ :public_key => "integration_public_key",
17
+ :private_key => "integration_private_key",
18
+ )
19
+ end
20
+
21
+ describe "create_jwt" do
22
+ it "creates a jwt with valid request" do
23
+ request = Braintree::BankAccountInstantVerificationJwtRequest.new(
24
+ :business_name => "15Ladders",
25
+ :return_url => "https://example.com/success",
26
+ :cancel_url => "https://example.com/cancel",
27
+ )
28
+
29
+ result = @gateway.bank_account_instant_verification.create_jwt(request)
30
+
31
+ unless result.success?
32
+ puts "DEBUG: Result failed!"
33
+ puts "DEBUG: Errors: #{result.errors.inspect}" if result.respond_to?(:errors)
34
+ end
35
+
36
+ expect(result.success?).to eq(true)
37
+ expect(result.bank_account_instant_verification_jwt).to have_attributes(
38
+ jwt: a_string_matching(/.+/),
39
+ )
40
+ end
41
+
42
+ it "fails with invalid business name" do
43
+ request = Braintree::BankAccountInstantVerificationJwtRequest.new(
44
+ :business_name => "", # Empty business name should cause validation error
45
+ :return_url => "https://example.com/return",
46
+ :cancel_url => "https://example.com/cancel",
47
+ )
48
+
49
+ result = @gateway.bank_account_instant_verification.create_jwt(request)
50
+
51
+ expect(result.success?).to eq(false)
52
+ expect(result.errors).not_to be_nil
53
+ end
54
+
55
+ it "fails with invalid URLs" do
56
+ request = Braintree::BankAccountInstantVerificationJwtRequest.new(
57
+ :business_name => "15Ladders",
58
+ :return_url => "not-a-valid-url",
59
+ :cancel_url => "also-not-valid",
60
+ )
61
+
62
+ result = @us_bank_gateway.bank_account_instant_verification.create_jwt(request)
63
+
64
+ expect(result.success?).to eq(false)
65
+ expect(result.errors).not_to be_nil
66
+ end
67
+ end
68
+
69
+ describe "charge US bank with ACH mandate" do
70
+ it "creates transaction directly with nonce and provides ACH mandate at transaction time (instant verification)" do
71
+ nonce = generate_us_bank_account_nonce_via_open_banking
72
+
73
+ mandate_accepted_at = Time.now - 300 # 5 minutes ago
74
+
75
+ # Create transaction directly with nonce and provide ACH mandate at transaction time (instant verification)
76
+ transaction_request = {
77
+ :amount => "12.34",
78
+ :payment_method_nonce => nonce,
79
+ :merchant_account_id => SpecHelper::UsBankMerchantAccountId, # could it be?
80
+ :us_bank_account => {
81
+ :ach_mandate_text => "I authorize this transaction and future debits",
82
+ :ach_mandate_accepted_at => mandate_accepted_at
83
+ },
84
+ :options => {
85
+ :submit_for_settlement => true
86
+ }
87
+ }
88
+
89
+ transaction_result = @us_bank_gateway.transaction.sale(transaction_request)
90
+
91
+ expect(transaction_result.success?).to eq(true), "Expected transaction success but got failure with validation errors (see console output)"
92
+ transaction = transaction_result.transaction
93
+
94
+ expected_transaction = {
95
+ id: a_string_matching(/.+/),
96
+ amount: BigDecimal("12.34"),
97
+ us_bank_account_details: have_attributes(
98
+ ach_mandate: have_attributes(
99
+ text: "I authorize this transaction and future debits",
100
+ accepted_at: be_a(Time),
101
+ ),
102
+ account_holder_name: "Dan Schulman",
103
+ last_4: "1234",
104
+ routing_number: "021000021",
105
+ account_type: "checking",
106
+ )
107
+ }
108
+
109
+ expect(transaction).to have_attributes(expected_transaction)
110
+ end
111
+ end
112
+
113
+ describe "Open Finance flow with INSTANT_VERIFICATION_ACCOUNT_VALIDATION" do
114
+ it "tokenizes bank account via Open Finance API, vaults with and charges" do
115
+
116
+ nonce = generate_us_bank_account_nonce_via_open_banking
117
+
118
+ customer_result = @us_bank_gateway.customer.create({})
119
+ expect(customer_result.success?).to eq(true)
120
+ customer = customer_result.customer
121
+
122
+ mandate_accepted_at = Time.now - 300
123
+
124
+ payment_method_request = {
125
+ :customer_id => customer.id,
126
+ :payment_method_nonce => nonce,
127
+ :us_bank_account => {
128
+ :ach_mandate_text => "I authorize this transaction and future debits",
129
+ :ach_mandate_accepted_at => mandate_accepted_at
130
+ },
131
+ :options => {
132
+ :verification_merchant_account_id => SpecHelper::UsBankMerchantAccountId,
133
+ :us_bank_account_verification_method => Braintree::UsBankAccountVerification::VerificationMethod::InstantVerificationAccountValidation
134
+ }
135
+ }
136
+
137
+ payment_method_result = @us_bank_gateway.payment_method.create(payment_method_request)
138
+ expect(payment_method_result.success?).to eq(true), "Expected payment method creation success but got failure with validation errors"
139
+
140
+ us_bank_account = payment_method_result.payment_method
141
+
142
+ expected_us_bank_account = {
143
+ verifications: a_collection_containing_exactly(
144
+ have_attributes(
145
+ verification_method: Braintree::UsBankAccountVerification::VerificationMethod::InstantVerificationAccountValidation,
146
+ status: "verified",
147
+ ),
148
+ ),
149
+ ach_mandate: have_attributes(
150
+ text: "I authorize this transaction and future debits",
151
+ accepted_at: be_a(Time),
152
+ )
153
+ }
154
+
155
+ expect(us_bank_account).to have_attributes(expected_us_bank_account)
156
+
157
+ verification = us_bank_account.verifications.first
158
+ expect(verification.verification_method).to eq(Braintree::UsBankAccountVerification::VerificationMethod::InstantVerificationAccountValidation)
159
+
160
+ transaction_request = {
161
+ :amount => "12.34",
162
+ :payment_method_token => us_bank_account.token,
163
+ :merchant_account_id => SpecHelper::UsBankMerchantAccountId,
164
+ :options => {
165
+ :submit_for_settlement => true
166
+ }
167
+ }
168
+
169
+ transaction_result = @us_bank_gateway.transaction.sale(transaction_request)
170
+ expect(transaction_result.success?).to eq(true), "Expected transaction success but got failure"
171
+ transaction = transaction_result.transaction
172
+
173
+ expected_transaction = {
174
+ id: a_string_matching(/.+/),
175
+ amount: BigDecimal("12.34"),
176
+ us_bank_account_details: have_attributes(
177
+ token: us_bank_account.token,
178
+ ach_mandate: have_attributes(
179
+ text: "I authorize this transaction and future debits",
180
+ accepted_at: be_a(Time),
181
+ ),
182
+ last_4: "1234",
183
+ routing_number: "021000021",
184
+ account_type: "checking",
185
+ )
186
+ }
187
+
188
+ expect(transaction).to have_attributes(expected_transaction)
189
+ end
190
+ end
191
+ end
@@ -147,6 +147,87 @@ def generate_invalid_us_bank_account_nonce
147
147
  nonce + "_xxx"
148
148
  end
149
149
 
150
+ def generate_us_bank_account_nonce_via_open_banking
151
+
152
+ config = Braintree::Configuration.new(
153
+ :environment => :development,
154
+ :merchant_id => "integration_merchant_id",
155
+ :public_key => "integration_public_key",
156
+ :private_key => "integration_private_key",
157
+ )
158
+
159
+ request_body = {
160
+ :account_details => {
161
+ :account_number => "567891234",
162
+ :account_type => "CHECKING",
163
+ :classification => "PERSONAL",
164
+ :tokenized_account => true,
165
+ :last_4 => "1234"
166
+ },
167
+ :institution_details => {
168
+ :bank_id => {
169
+ :bank_code => "021000021",
170
+ :country_code => "US"
171
+ }
172
+ },
173
+ :account_holders => [
174
+ {
175
+ :ownership => "PRIMARY",
176
+ :full_name => {
177
+ :name => "Dan Schulman"
178
+ },
179
+ :name => {
180
+ :given_name => "Dan",
181
+ :surname => "Schulman",
182
+ :full_name => "Dan Schulman"
183
+ }
184
+ }
185
+ ]
186
+ }
187
+
188
+ graphql_base_url = config.graphql_base_url
189
+ atmosphere_base_url = graphql_base_url.gsub("/graphql", "")
190
+ url = "#{atmosphere_base_url}/v1/open-finance/tokenize-bank-account-details"
191
+
192
+ uri = URI.parse(url)
193
+ connection = Net::HTTP.new(uri.host, uri.port)
194
+
195
+ if uri.scheme == "https"
196
+ connection.use_ssl = true
197
+ connection.verify_mode = OpenSSL::SSL::VERIFY_PEER
198
+ connection.ca_file = config.ca_file if config.ca_file
199
+ end
200
+
201
+ response = connection.start do |http|
202
+ request = Net::HTTP::Post.new(uri.path)
203
+ request["Content-Type"] = "application/json"
204
+ request["Accept"] = "application/json"
205
+ request["Braintree-Version"] = "2019-01-01"
206
+ request["User-Agent"] = "Braintree Ruby Library #{Braintree::Version::String}"
207
+ request["X-ApiVersion"] = config.api_version
208
+
209
+ # Basic auth like Node.js
210
+ auth_string = "#{config.public_key}:#{config.private_key}"
211
+ request["Authorization"] = "Basic #{Base64.strict_encode64(auth_string)}"
212
+
213
+ request.body = request_body.to_json
214
+
215
+ http.request(request)
216
+ end
217
+
218
+ if response.code.to_i != 200
219
+ raise "HTTP #{response.code}: #{response.body}"
220
+ end
221
+
222
+ result = JSON.parse(response.body)
223
+
224
+ unless result["tenant_token"]
225
+ raise "Open Banking tokenization failed: #{result.inspect}"
226
+ end
227
+
228
+ result["tenant_token"]
229
+ end
230
+
150
231
  def _cosmos_post(token, url, payload)
151
232
  uri = URI::parse(url)
152
233
  connection = Net::HTTP.new(uri.host, uri.port)
@@ -32,8 +32,8 @@ describe Braintree::Dispute do
32
32
 
33
33
  expect(result.success?).to eq(true)
34
34
 
35
- refreshed_dispute = Braintree::Dispute.find(dispute.id)
36
- expect(refreshed_dispute.status).to eq(Braintree::Dispute::Status::Accepted)
35
+ updated_dispute = Braintree::Dispute.find(dispute.id)
36
+ expect(updated_dispute.status).to eq(Braintree::Dispute::Status::Accepted)
37
37
 
38
38
  dispute_from_transaction = Braintree::Transaction.find(dispute.transaction.id).disputes[0]
39
39
  expect(dispute_from_transaction.status).to eq(Braintree::Dispute::Status::Accepted)
@@ -99,6 +99,17 @@ describe Braintree::Dispute do
99
99
  expect(result.evidence.category).to eq("GENERAL")
100
100
  expect(result.evidence.url).to include("bt_logo.png")
101
101
  end
102
+
103
+ it "reflects the updated remaining_file_evidence_storage" do
104
+ initial_storage = dispute.remaining_file_evidence_storage
105
+ expect(initial_storage).not_to be_nil
106
+
107
+ Braintree::Dispute.add_file_evidence(dispute.id, document_upload.id)
108
+
109
+ refreshed_dispute = Braintree::Dispute.find(dispute.id)
110
+ updated_storage = refreshed_dispute.remaining_file_evidence_storage
111
+ expect(updated_storage).to be < initial_storage
112
+ end
102
113
  end
103
114
 
104
115
  describe "self.add_text_evidence" do
@@ -108,6 +108,7 @@ describe Braintree::PaymentMethod do
108
108
  expect(apple_pay_card.payroll).not_to be_nil
109
109
  expect(apple_pay_card.prepaid).not_to be_nil
110
110
  expect(apple_pay_card.product_id).not_to be_nil
111
+ expect(apple_pay_card.is_device_token).to eq(true)
111
112
  end
112
113
 
113
114
  it "creates a payment method from a fake apple pay mpan nonce" do
@@ -143,6 +144,7 @@ describe Braintree::PaymentMethod do
143
144
  expect(apple_pay_card.prepaid).not_to be_nil
144
145
  expect(apple_pay_card.product_id).not_to be_nil
145
146
  expect(apple_pay_card.merchant_token_identifier).not_to be_nil
147
+ expect(apple_pay_card.is_device_token).not_to be_nil
146
148
  expect(apple_pay_card.source_card_last4).not_to be_nil
147
149
  end
148
150
 
@@ -1254,6 +1256,7 @@ describe Braintree::PaymentMethod do
1254
1256
  expect(apple_pay_card.expiration_year.to_i).to be > 0
1255
1257
  expect(apple_pay_card.source_description).to eq("Visa 2006")
1256
1258
  expect(apple_pay_card.customer_id).to eq(customer.id)
1259
+ expect(apple_pay_card.is_device_token).to eq(false)
1257
1260
  apple_pay_card.merchant_token_identifier == "DNITHE302308980427388297"
1258
1261
  apple_pay_card.source_card_last4 == "2006"
1259
1262
  end