braintree 4.30.0 → 4.32.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.
- checksums.yaml +4 -4
- data/lib/braintree/address_gateway.rb +5 -0
- data/lib/braintree/apple_pay_card.rb +1 -0
- data/lib/braintree/bank_account_instant_verification_gateway.rb +38 -0
- data/lib/braintree/bank_account_instant_verification_jwt.rb +23 -0
- data/lib/braintree/bank_account_instant_verification_jwt_request.rb +21 -0
- data/lib/braintree/error_codes.rb +12 -0
- data/lib/braintree/gateway.rb +4 -0
- data/lib/braintree/payment_method_gateway.rb +6 -0
- data/lib/braintree/successful_result.rb +1 -0
- data/lib/braintree/transaction/apple_pay_details.rb +2 -0
- data/lib/braintree/transaction/credit_card_details.rb +2 -0
- data/lib/braintree/transaction/google_pay_details.rb +1 -0
- data/lib/braintree/transaction.rb +1 -0
- data/lib/braintree/transaction_gateway.rb +25 -1
- data/lib/braintree/us_bank_account_verification.rb +3 -1
- data/lib/braintree/version.rb +1 -1
- data/lib/braintree.rb +3 -0
- data/spec/integration/braintree/bank_account_instant_verification_spec.rb +191 -0
- data/spec/integration/braintree/client_api/spec_helper.rb +81 -0
- data/spec/integration/braintree/credit_card_verification_spec.rb +7 -0
- data/spec/integration/braintree/payment_method_spec.rb +3 -0
- data/spec/integration/braintree/payment_method_us_bank_account_spec.rb +0 -3
- data/spec/integration/braintree/transaction_search_spec.rb +26 -24
- data/spec/integration/braintree/transaction_spec.rb +117 -3
- data/spec/integration/braintree/transaction_transfer_type_spec.rb +255 -0
- data/spec/integration/braintree/transaction_us_bank_account_spec.rb +0 -1
- data/spec/integration/braintree/us_bank_account_spec.rb +0 -3
- data/spec/spec_helper.rb +7 -0
- data/spec/unit/braintree/apple_pay_card_spec.rb +2 -0
- data/spec/unit/braintree/bank_account_instant_verification_gateway_spec.rb +98 -0
- data/spec/unit/braintree/bank_account_instant_verification_jwt_request_spec.rb +71 -0
- data/spec/unit/braintree/credit_card_verification_spec.rb +17 -0
- data/spec/unit/braintree/transaction/apple_pay_details_spec.rb +9 -0
- data/spec/unit/braintree/transaction/credit_card_details_spec.rb +10 -1
- data/spec/unit/braintree/transaction/google_pay_details_spec.rb +9 -0
- data/spec/unit/braintree/transaction_ach_mandate_spec.rb +82 -0
- data/spec/unit/braintree/transaction_gateway_spec.rb +21 -1
- data/spec/unit/braintree/transaction_spec.rb +48 -0
- metadata +13 -7
- data/lib/ssl/securetrust_ca.crt +0 -44
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b982c20d1a239e4a623a802fb0c0d6ce66a5be501e767c22a33a7b06a3c09a59
|
|
4
|
+
data.tar.gz: 9d515e47f8d7e5908b21f7ed5ccd98b02f2ce712298082fd67cbc3d88d7fd13d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1459fb667fdbb3d3f52feb2082394f347dbe0c3347eef68c87cbc31a8c915fcc96ff8623bbd98f8766e2391801423514fa2db4a4c90f511ea9d2c1c3778f9b3d
|
|
7
|
+
data.tar.gz: 36af58c2cc626136cfaa8bc1f28d2acb8fa47e9e747e0e9eb641acabcbf4e0afadf34556e5467928c4e091a66279b505f952a1fb9e5476e19531f0c81b91a59d
|
|
@@ -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
|
|
|
@@ -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
|
|
@@ -395,6 +395,7 @@ module Braintree
|
|
|
395
395
|
PaymentMethodNonceUnknown = "91565"
|
|
396
396
|
PaymentMethodTokenCardTypeIsNotAccepted = "91517"
|
|
397
397
|
PaymentMethodTokenIsInvalid = "91518"
|
|
398
|
+
ProcessingMerchantCategoryCodeIsInvalid = "915265"
|
|
398
399
|
ProcessorAuthorizationCodeCannotBeSet = "91519"
|
|
399
400
|
ProcessorAuthorizationCodeIsInvalid = "81520"
|
|
400
401
|
ProcessorDoesNotSupportAuths = "915104"
|
|
@@ -459,6 +460,17 @@ module Braintree
|
|
|
459
460
|
TransactionIsNotEligibleForAdjustment = "915219"
|
|
460
461
|
TransactionMustBeInStateAuthorized = "915218"
|
|
461
462
|
TransactionSourceIsInvalid = "915133"
|
|
463
|
+
TransactionTransferDetailsAreMandatory = "97510"
|
|
464
|
+
TransactionTransferDetailsAreNotApplicable = "97511"
|
|
465
|
+
TransactionTransferReceiverAccountReferenceNumberIsNotValid = "97509"
|
|
466
|
+
TransactionTransferReceiverFirstNameIsNotValid = "97507"
|
|
467
|
+
TransactionTransferReceiverLastNameIsNotValid = "97508"
|
|
468
|
+
TransactionTransferReceiverTaxIdIsNotValid = "97506"
|
|
469
|
+
TransactionTransferSenderAccountReferenceNumberIsNotValid = "97505"
|
|
470
|
+
TransactionTransferSenderFirstNameIsNotValid = "97503"
|
|
471
|
+
TransactionTransferSenderLastNameIsNotValid = "97504"
|
|
472
|
+
TransactionTransferSenderTaxIdIsNotValid = "97502"
|
|
473
|
+
TransactionTransferTypeIsInvalid = "97501"
|
|
462
474
|
TypeIsInvalid = "91523"
|
|
463
475
|
TypeIsRequired = "91524"
|
|
464
476
|
UnsupportedVoiceAuthorization = "91539"
|
data/lib/braintree/gateway.rb
CHANGED
|
@@ -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,9 +18,11 @@ 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
|
|
25
|
+
attr_reader :payment_account_reference
|
|
24
26
|
attr_reader :payment_instrument_name
|
|
25
27
|
attr_reader :payroll
|
|
26
28
|
attr_reader :prepaid
|
|
@@ -21,6 +21,7 @@ module Braintree
|
|
|
21
21
|
attr_reader :image_url
|
|
22
22
|
attr_reader :issuing_bank
|
|
23
23
|
attr_reader :last_4
|
|
24
|
+
attr_reader :payment_account_reference
|
|
24
25
|
attr_reader :payroll
|
|
25
26
|
attr_reader :prepaid
|
|
26
27
|
attr_reader :prepaid_reloadable
|
|
@@ -55,6 +56,7 @@ module Braintree
|
|
|
55
56
|
:image_url,
|
|
56
57
|
:issuing_bank,
|
|
57
58
|
:last_4,
|
|
59
|
+
:payment_account_reference,
|
|
58
60
|
:payroll,
|
|
59
61
|
:prepaid,
|
|
60
62
|
:prepaid_reloadable,
|
|
@@ -196,7 +196,7 @@ module Braintree
|
|
|
196
196
|
[
|
|
197
197
|
:amount, :billing_address_id, :channel, :currency_iso_code, :customer_id, :device_data,
|
|
198
198
|
:discount_amount, :exchange_rate_quote_id, :foreign_retailer,
|
|
199
|
-
:merchant_account_id, :order_id, :payment_method_nonce, :payment_method_token,
|
|
199
|
+
:merchant_account_id, :order_id, :payment_method_nonce, :payment_method_token, :processing_merchant_category_code,
|
|
200
200
|
:product_sku, :purchase_order_number, :service_fee_amount, :shared_billing_address_id,
|
|
201
201
|
:shared_customer_id, :shared_payment_method_nonce, :shared_payment_method_token,
|
|
202
202
|
:shared_shipping_address_id, :shipping_address_id, :shipping_amount, :shipping_tax_amount,
|
|
@@ -283,6 +283,30 @@ module Braintree
|
|
|
283
283
|
{
|
|
284
284
|
:transfer => [
|
|
285
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,
|
|
286
310
|
]
|
|
287
311
|
},
|
|
288
312
|
]
|
|
@@ -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
|
data/lib/braintree/version.rb
CHANGED
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)
|
|
@@ -257,6 +257,13 @@ describe Braintree::CreditCardVerification, "search" do
|
|
|
257
257
|
Braintree::CreditCardVerification.find("invalid-id")
|
|
258
258
|
end.to raise_error(Braintree::NotFoundError, 'verification with id "invalid-id" not found')
|
|
259
259
|
end
|
|
260
|
+
|
|
261
|
+
it "finds a verification and returns a payment_account_reference if the verification has one" do
|
|
262
|
+
verification = Braintree::CreditCardVerification.find("threedsecuredverification")
|
|
263
|
+
|
|
264
|
+
expect(verification.credit_card).not_to be_nil
|
|
265
|
+
expect(verification.credit_card).to have_key(:payment_account_reference)
|
|
266
|
+
end
|
|
260
267
|
end
|
|
261
268
|
|
|
262
269
|
describe "self.search" 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
|
|
@@ -32,7 +32,6 @@ describe Braintree::PaymentMethod do
|
|
|
32
32
|
expect(us_bank_account.account_holder_name).to eq("John Doe")
|
|
33
33
|
expect(us_bank_account.bank_name).to match(/CHASE/)
|
|
34
34
|
expect(us_bank_account.default).to eq(true)
|
|
35
|
-
expect(us_bank_account.ach_mandate.text).to eq("cl mandate text")
|
|
36
35
|
expect(us_bank_account.ach_mandate.accepted_at).to be_a Time
|
|
37
36
|
|
|
38
37
|
expect(us_bank_account.verifications.count).to eq(0)
|
|
@@ -100,7 +99,6 @@ describe Braintree::PaymentMethod do
|
|
|
100
99
|
expect(us_bank_account.account_holder_name).to eq("Dan Schulman")
|
|
101
100
|
expect(us_bank_account.bank_name).to match(/Wells Fargo/)
|
|
102
101
|
expect(us_bank_account.default).to eq(true)
|
|
103
|
-
expect(us_bank_account.ach_mandate.text).to eq("example mandate text")
|
|
104
102
|
expect(us_bank_account.ach_mandate.accepted_at).to be_a Time
|
|
105
103
|
|
|
106
104
|
expect(us_bank_account.verifications.count).to eq(1)
|
|
@@ -135,7 +133,6 @@ describe Braintree::PaymentMethod do
|
|
|
135
133
|
expect(us_bank_account.account_holder_name).to eq("John Doe")
|
|
136
134
|
expect(us_bank_account.bank_name).to match(/CHASE/)
|
|
137
135
|
expect(us_bank_account.default).to eq(true)
|
|
138
|
-
expect(us_bank_account.ach_mandate.text).to eq("cl mandate text")
|
|
139
136
|
expect(us_bank_account.ach_mandate.accepted_at).to be_a Time
|
|
140
137
|
|
|
141
138
|
expect(us_bank_account.verifications.count).to eq(1)
|