braintree 2.24.0 → 2.25.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 +3 -0
- data/lib/braintree/error_codes.rb +99 -45
- data/lib/braintree/error_result.rb +2 -1
- data/lib/braintree/gateway.rb +4 -0
- data/lib/braintree/merchant_account.rb +38 -0
- data/lib/braintree/merchant_account_gateway.rb +33 -0
- data/lib/braintree/test/merchant_account.rb +7 -0
- data/lib/braintree/transaction.rb +35 -1
- data/lib/braintree/transaction/credit_card_details.rb +4 -0
- data/lib/braintree/transaction_gateway.rb +34 -30
- data/lib/braintree/version.rb +1 -1
- data/lib/braintree/webhook_notification.rb +22 -1
- data/lib/braintree/webhook_testing_gateway.rb +86 -2
- data/lib/braintree/xml/generator.rb +4 -4
- data/spec/httpsd.pid +1 -1
- data/spec/integration/braintree/customer_spec.rb +17 -3
- data/spec/integration/braintree/merchant_account_spec.rb +72 -0
- data/spec/integration/braintree/transaction_spec.rb +389 -0
- data/spec/spec_helper.rb +155 -161
- data/spec/unit/braintree/merchant_account_spec.rb +23 -0
- data/spec/unit/braintree/webhook_notification_spec.rb +68 -1
- metadata +105 -100
@@ -10,6 +10,28 @@ module Braintree
|
|
10
10
|
_do_create "/transactions", :transaction => attributes
|
11
11
|
end
|
12
12
|
|
13
|
+
def cancel_release(transaction_id)
|
14
|
+
raise ArgumentError, "transaction_id is invalid" unless transaction_id =~ /\A[0-9a-z]+\z/
|
15
|
+
response = @config.http.put "/transactions/#{transaction_id}/cancel_release"
|
16
|
+
_handle_transaction_response(response)
|
17
|
+
end
|
18
|
+
|
19
|
+
def hold_in_escrow(transaction_id)
|
20
|
+
raise ArgumentError, "transaction_id is invalid" unless transaction_id =~ /\A[0-9a-z]+\z/
|
21
|
+
response = @config.http.put "/transactions/#{transaction_id}/hold_in_escrow"
|
22
|
+
_handle_transaction_response(response)
|
23
|
+
end
|
24
|
+
|
25
|
+
def _handle_transaction_response(response)
|
26
|
+
if response[:transaction]
|
27
|
+
SuccessfulResult.new(:transaction => Transaction._new(@gateway, response[:transaction]))
|
28
|
+
elsif response[:api_error_response]
|
29
|
+
ErrorResult.new(@gateway, response[:api_error_response])
|
30
|
+
else
|
31
|
+
raise UnexpectedError, "expected :transaction or :response"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
13
35
|
def clone_transaction(transaction_id, attributes)
|
14
36
|
Util.verify_keys(TransactionGateway._clone_signature, attributes)
|
15
37
|
_do_create "/transactions/#{transaction_id}/clone", :transaction_clone => attributes
|
@@ -40,13 +62,7 @@ module Braintree
|
|
40
62
|
|
41
63
|
def refund(transaction_id, amount = nil)
|
42
64
|
response = @config.http.post "/transactions/#{transaction_id}/refund", :transaction => {:amount => amount}
|
43
|
-
|
44
|
-
SuccessfulResult.new(:transaction => Transaction._new(@gateway, response[:transaction]))
|
45
|
-
elsif response[:api_error_response]
|
46
|
-
ErrorResult.new(@gateway, response[:api_error_response])
|
47
|
-
else
|
48
|
-
raise UnexpectedError, "expected :transaction or :api_error_response"
|
49
|
-
end
|
65
|
+
_handle_transaction_response(response)
|
50
66
|
end
|
51
67
|
|
52
68
|
def retry_subscription_charge(subscription_id, amount=nil)
|
@@ -70,27 +86,21 @@ module Braintree
|
|
70
86
|
ResourceCollection.new(response) { |ids| _fetch_transactions(search, ids) }
|
71
87
|
end
|
72
88
|
|
89
|
+
def release_from_escrow(transaction_id)
|
90
|
+
raise ArgumentError, "transaction_id is invalid" unless transaction_id =~ /\A[0-9a-z]+\z/
|
91
|
+
response = @config.http.put "/transactions/#{transaction_id}/release_from_escrow"
|
92
|
+
_handle_transaction_response(response)
|
93
|
+
end
|
94
|
+
|
73
95
|
def submit_for_settlement(transaction_id, amount = nil)
|
74
96
|
raise ArgumentError, "transaction_id is invalid" unless transaction_id =~ /\A[0-9a-z]+\z/
|
75
97
|
response = @config.http.put "/transactions/#{transaction_id}/submit_for_settlement", :transaction => {:amount => amount}
|
76
|
-
|
77
|
-
SuccessfulResult.new(:transaction => Transaction._new(@gateway, response[:transaction]))
|
78
|
-
elsif response[:api_error_response]
|
79
|
-
ErrorResult.new(@gateway, response[:api_error_response])
|
80
|
-
else
|
81
|
-
raise UnexpectedError, "expected :transaction or :response"
|
82
|
-
end
|
98
|
+
_handle_transaction_response(response)
|
83
99
|
end
|
84
100
|
|
85
101
|
def void(transaction_id)
|
86
102
|
response = @config.http.put "/transactions/#{transaction_id}/void"
|
87
|
-
|
88
|
-
SuccessfulResult.new(:transaction => Transaction._new(@gateway, response[:transaction]))
|
89
|
-
elsif response[:api_error_response]
|
90
|
-
ErrorResult.new(@gateway, response[:api_error_response])
|
91
|
-
else
|
92
|
-
raise UnexpectedError, "expected :transaction or :api_error_response"
|
93
|
-
end
|
103
|
+
_handle_transaction_response(response)
|
94
104
|
end
|
95
105
|
|
96
106
|
def self._clone_signature # :nodoc:
|
@@ -101,7 +111,7 @@ module Braintree
|
|
101
111
|
[
|
102
112
|
:amount, :customer_id, :merchant_account_id, :order_id, :channel, :payment_method_token,
|
103
113
|
:purchase_order_number, :recurring, :shipping_address_id, :type, :tax_amount, :tax_exempt,
|
104
|
-
:venmo_sdk_payment_method_code, :device_session_id, :device_data,
|
114
|
+
:venmo_sdk_payment_method_code, :device_session_id, :service_fee_amount, :device_data,
|
105
115
|
{:credit_card => [:token, :cardholder_name, :cvv, :expiration_date, :expiration_month, :expiration_year, :number]},
|
106
116
|
{:customer => [:id, :company, :email, :fax, :first_name, :last_name, :phone, :website]},
|
107
117
|
{
|
@@ -110,7 +120,7 @@ module Braintree
|
|
110
120
|
{
|
111
121
|
:shipping => AddressGateway._shared_signature
|
112
122
|
},
|
113
|
-
{:options => [: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]},
|
123
|
+
{: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]},
|
114
124
|
{:custom_fields => :_any_key_},
|
115
125
|
{:descriptor => [:name, :phone]}
|
116
126
|
]
|
@@ -118,13 +128,7 @@ module Braintree
|
|
118
128
|
|
119
129
|
def _do_create(url, params=nil) # :nodoc:
|
120
130
|
response = @config.http.post url, params
|
121
|
-
|
122
|
-
SuccessfulResult.new(:transaction => Transaction._new(@gateway, response[:transaction]))
|
123
|
-
elsif response[:api_error_response]
|
124
|
-
ErrorResult.new(@gateway, response[:api_error_response])
|
125
|
-
else
|
126
|
-
raise UnexpectedError, "expected :transaction or :api_error_response"
|
127
|
-
end
|
131
|
+
_handle_transaction_response(response)
|
128
132
|
end
|
129
133
|
|
130
134
|
def _fetch_transactions(search, ids) # :nodoc:
|
data/lib/braintree/version.rb
CHANGED
@@ -10,9 +10,14 @@ module Braintree
|
|
10
10
|
SubscriptionTrialEnded = "subscription_trial_ended"
|
11
11
|
SubscriptionWentActive = "subscription_went_active"
|
12
12
|
SubscriptionWentPastDue = "subscription_went_past_due"
|
13
|
+
|
14
|
+
SubMerchantAccountApproved = "sub_merchant_account_approved"
|
15
|
+
SubMerchantAccountDeclined = "sub_merchant_account_declined"
|
16
|
+
TransactionDisbursed = "transaction_disbursed"
|
17
|
+
PartnerUserCreated = "partner_user_created"
|
13
18
|
end
|
14
19
|
|
15
|
-
attr_reader :subscription, :kind, :timestamp
|
20
|
+
attr_reader :subscription, :kind, :timestamp, :partner_credentials, :transaction
|
16
21
|
|
17
22
|
def self.parse(signature, payload)
|
18
23
|
Configuration.gateway.webhook_notification.parse(signature, payload)
|
@@ -25,7 +30,23 @@ module Braintree
|
|
25
30
|
def initialize(gateway, attributes) # :nodoc:
|
26
31
|
@gateway = gateway
|
27
32
|
set_instance_variables_from_hash(attributes)
|
33
|
+
@error_result = ErrorResult.new(gateway, @subject[:api_error_response]) if @subject.has_key?(:api_error_response)
|
34
|
+
@merchant_account = MerchantAccount._new(gateway, @subject[:merchant_account]) if @subject.has_key?(:merchant_account)
|
35
|
+
@partner_credentials = OpenStruct.new(@subject[:partner_credentials]) if @subject.has_key?(:partner_credentials)
|
28
36
|
@subscription = Subscription._new(gateway, @subject[:subscription]) if @subject.has_key?(:subscription)
|
37
|
+
@transaction = Transaction._new(gateway, @subject[:transaction]) if @subject.has_key?(:transaction)
|
38
|
+
end
|
39
|
+
|
40
|
+
def merchant_account
|
41
|
+
@error_result.nil? ? @merchant_account : @error_result.merchant_account
|
42
|
+
end
|
43
|
+
|
44
|
+
def errors
|
45
|
+
@error_result.errors if @error_result
|
46
|
+
end
|
47
|
+
|
48
|
+
def message
|
49
|
+
@error_result.message if @error_result
|
29
50
|
end
|
30
51
|
|
31
52
|
class << self
|
@@ -12,19 +12,35 @@ module Braintree
|
|
12
12
|
return signature_string, payload
|
13
13
|
end
|
14
14
|
|
15
|
-
def _sample_xml(kind,
|
15
|
+
def _sample_xml(kind, data)
|
16
16
|
<<-XML
|
17
17
|
<notification>
|
18
18
|
<timestamp type="datetime">#{Time.now.utc.iso8601}</timestamp>
|
19
19
|
<kind>#{kind}</kind>
|
20
20
|
<subject>
|
21
|
-
#{
|
21
|
+
#{_subject_sample_xml(kind, data)}
|
22
22
|
</subject>
|
23
23
|
</notification>
|
24
24
|
XML
|
25
25
|
end
|
26
26
|
|
27
|
+
def _subject_sample_xml(kind, id)
|
28
|
+
case kind
|
29
|
+
when Braintree::WebhookNotification::Kind::PartnerUserCreated
|
30
|
+
_partner_credentials_sample_xml(id)
|
31
|
+
when Braintree::WebhookNotification::Kind::SubMerchantAccountApproved
|
32
|
+
_merchant_account_approved_sample_xml(id)
|
33
|
+
when Braintree::WebhookNotification::Kind::SubMerchantAccountDeclined
|
34
|
+
_merchant_account_declined_sample_xml(id)
|
35
|
+
when Braintree::WebhookNotification::Kind::TransactionDisbursed
|
36
|
+
_transaction_disbursed_sample_xml(id)
|
37
|
+
else
|
38
|
+
_subscription_sample_xml(id)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
27
42
|
def _subscription_sample_xml(id)
|
43
|
+
|
28
44
|
<<-XML
|
29
45
|
<subscription>
|
30
46
|
<id>#{id}</id>
|
@@ -37,5 +53,73 @@ module Braintree
|
|
37
53
|
</subscription>
|
38
54
|
XML
|
39
55
|
end
|
56
|
+
|
57
|
+
def _partner_credentials_sample_xml(data)
|
58
|
+
|
59
|
+
<<-XML
|
60
|
+
<partner_credentials>
|
61
|
+
<merchant_public_id>public_id</merchant_public_id>
|
62
|
+
<public_key>public_key</public_key>
|
63
|
+
<private_key>private_key</private_key>
|
64
|
+
<partner_user_id>abc123</partner_user_id>
|
65
|
+
</partner_credentials>
|
66
|
+
XML
|
67
|
+
end
|
68
|
+
|
69
|
+
def _merchant_account_approved_sample_xml(id)
|
70
|
+
|
71
|
+
<<-XML
|
72
|
+
<merchant_account>
|
73
|
+
<id>#{id}</id>
|
74
|
+
<master_merchant_account>
|
75
|
+
<id>master_ma_for_#{id}</id>
|
76
|
+
<status>active</status>
|
77
|
+
</master_merchant_account>
|
78
|
+
<status>active</status>
|
79
|
+
</merchant_account>
|
80
|
+
XML
|
81
|
+
end
|
82
|
+
|
83
|
+
def _merchant_account_declined_sample_xml(id)
|
84
|
+
|
85
|
+
<<-XML
|
86
|
+
<api-error-response>
|
87
|
+
<message>Credit score is too low</message>
|
88
|
+
<errors>
|
89
|
+
<errors type="array"/>
|
90
|
+
<merchant-account>
|
91
|
+
<errors type="array">
|
92
|
+
<error>
|
93
|
+
<code>82621</code>
|
94
|
+
<message>Credit score is too low</message>
|
95
|
+
<attribute type="symbol">base</attribute>
|
96
|
+
</error>
|
97
|
+
</errors>
|
98
|
+
</merchant-account>
|
99
|
+
</errors>
|
100
|
+
<merchant-account>
|
101
|
+
<id>#{id}</id>
|
102
|
+
<status>suspended</status>
|
103
|
+
<master-merchant-account>
|
104
|
+
<id>master_ma_for_#{id}</id>
|
105
|
+
<status>suspended</status>
|
106
|
+
</master-merchant-account>
|
107
|
+
</merchant-account>
|
108
|
+
</api-error-response>
|
109
|
+
XML
|
110
|
+
end
|
111
|
+
|
112
|
+
def _transaction_disbursed_sample_xml(id)
|
113
|
+
|
114
|
+
<<-XML
|
115
|
+
<transaction>
|
116
|
+
<id>#{id}</id>
|
117
|
+
<amount>100</amount>
|
118
|
+
<disbursement-details>
|
119
|
+
<disbursement-date type="datetime">2013-07-09T18:23:29Z</disbursement-date>
|
120
|
+
</disbursement-details>
|
121
|
+
</transaction>
|
122
|
+
XML
|
123
|
+
end
|
40
124
|
end
|
41
125
|
end
|
@@ -13,10 +13,10 @@ module Braintree
|
|
13
13
|
"Time" => "datetime",
|
14
14
|
}
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
XML_FORMATTING_NAMES = {
|
17
|
+
"BigDecimal" => "bigdecimal",
|
18
|
+
"Symbol" => "symbol"
|
19
|
+
}.merge(XML_TYPE_NAMES)
|
20
20
|
|
21
21
|
XML_FORMATTING = {
|
22
22
|
"symbol" => Proc.new { |symbol| symbol.to_s },
|
data/spec/httpsd.pid
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
4742
|
@@ -84,7 +84,7 @@ describe Braintree::Customer do
|
|
84
84
|
found_customer = Braintree::Customer.find(result.customer.id)
|
85
85
|
found_customer.first_name.should == first_name
|
86
86
|
found_customer.last_name.should == last_name
|
87
|
-
|
87
|
+
else
|
88
88
|
result.customer.first_name.should == "José"
|
89
89
|
result.customer.first_name.bytes.map {|b| b.to_s(8)}.should == ["112", "157", "163", "303", "251"]
|
90
90
|
result.customer.last_name.should == "Muñoz"
|
@@ -95,8 +95,6 @@ describe Braintree::Customer do
|
|
95
95
|
found_customer.first_name.bytes.map {|b| b.to_s(8)}.should == ["112", "157", "163", "303", "251"]
|
96
96
|
found_customer.last_name.should == "Muñoz"
|
97
97
|
found_customer.last_name.bytes.map {|b| b.to_s(8)}.should == ["115", "165", "303", "261", "157", "172"]
|
98
|
-
else
|
99
|
-
raise "unknown ruby version: #{RUBY_VERSION.inspect}"
|
100
98
|
end
|
101
99
|
end
|
102
100
|
|
@@ -363,6 +361,22 @@ describe Braintree::Customer do
|
|
363
361
|
result.customer.credit_cards.first.bin.should == "400934"
|
364
362
|
result.customer.credit_cards.first.last_4.should == "1881"
|
365
363
|
end
|
364
|
+
|
365
|
+
it "can create a customer with a venmo sdk session" do
|
366
|
+
result = Braintree::Customer.create(
|
367
|
+
:first_name => "Steve",
|
368
|
+
:last_name => "Hamlin",
|
369
|
+
:credit_card => {
|
370
|
+
:number => Braintree::Test::CreditCardNumbers::MasterCard,
|
371
|
+
:expiration_date => "05/2010",
|
372
|
+
:options => {
|
373
|
+
:venmo_sdk_session => Braintree::Test::VenmoSDK::Session
|
374
|
+
}
|
375
|
+
}
|
376
|
+
)
|
377
|
+
result.success?.should == true
|
378
|
+
result.customer.credit_cards.first.venmo_sdk?.should == true
|
379
|
+
end
|
366
380
|
end
|
367
381
|
end
|
368
382
|
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper")
|
2
|
+
|
3
|
+
VALID_APPLICATION_PARAMS = {
|
4
|
+
:applicant_details => {
|
5
|
+
:first_name => "Joe",
|
6
|
+
:last_name => "Bloggs",
|
7
|
+
:email => "joe@bloggs.com",
|
8
|
+
:phone => "312-555-1234",
|
9
|
+
:address => {
|
10
|
+
:street_address => "123 Credibility St.",
|
11
|
+
:postal_code => "60606",
|
12
|
+
:locality => "Chicago",
|
13
|
+
:region => "IL",
|
14
|
+
},
|
15
|
+
:date_of_birth => "10/9/1980",
|
16
|
+
:ssn => "123-00-1234",
|
17
|
+
:routing_number => "1234567890",
|
18
|
+
:account_number => "43759348798"
|
19
|
+
},
|
20
|
+
:tos_accepted => true,
|
21
|
+
:master_merchant_account_id => "sandbox_master_merchant_account"
|
22
|
+
}
|
23
|
+
|
24
|
+
describe Braintree::MerchantAccount do
|
25
|
+
describe "create" do
|
26
|
+
it "doesn't require an id" do
|
27
|
+
result = Braintree::MerchantAccount.create(VALID_APPLICATION_PARAMS)
|
28
|
+
|
29
|
+
result.should be_success
|
30
|
+
result.merchant_account.status.should == Braintree::MerchantAccount::Status::Pending
|
31
|
+
result.merchant_account.master_merchant_account.id.should == "sandbox_master_merchant_account"
|
32
|
+
end
|
33
|
+
|
34
|
+
it "allows an id to be passed" do
|
35
|
+
random_number = rand(10000)
|
36
|
+
sub_merchant_account_id = "sub_merchant_account_id#{random_number}"
|
37
|
+
result = Braintree::MerchantAccount.create(
|
38
|
+
VALID_APPLICATION_PARAMS.merge(
|
39
|
+
:id => sub_merchant_account_id
|
40
|
+
)
|
41
|
+
)
|
42
|
+
|
43
|
+
result.should be_success
|
44
|
+
result.merchant_account.status.should == Braintree::MerchantAccount::Status::Pending
|
45
|
+
result.merchant_account.id.should == sub_merchant_account_id
|
46
|
+
result.merchant_account.master_merchant_account.id.should == "sandbox_master_merchant_account"
|
47
|
+
end
|
48
|
+
|
49
|
+
it "handles unsuccessful results" do
|
50
|
+
result = Braintree::MerchantAccount.create({})
|
51
|
+
result.should_not be_success
|
52
|
+
result.errors.for(:merchant_account).on(:master_merchant_account_id).first.code.should == Braintree::ErrorCodes::MerchantAccount::MasterMerchantAccountIdIsRequired
|
53
|
+
end
|
54
|
+
|
55
|
+
it "requires all fields" do
|
56
|
+
result = Braintree::MerchantAccount.create(
|
57
|
+
:master_merchant_account_id => "sandbox_master_merchant_account"
|
58
|
+
)
|
59
|
+
result.should_not be_success
|
60
|
+
result.errors.for(:merchant_account).for(:applicant_details).on(:first_name).first.code.should == Braintree::ErrorCodes::MerchantAccount::ApplicantDetails::FirstNameIsRequired
|
61
|
+
end
|
62
|
+
|
63
|
+
it "accepts tax_id and business_name fields" do
|
64
|
+
params = VALID_APPLICATION_PARAMS.clone
|
65
|
+
params[:applicant_details][:company_name] = "Test Company"
|
66
|
+
params[:applicant_details][:tax_id] = "123456789"
|
67
|
+
result = Braintree::MerchantAccount.create(params)
|
68
|
+
result.should be_success
|
69
|
+
result.merchant_account.status.should == Braintree::MerchantAccount::Status::Pending
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -887,6 +887,108 @@ describe Braintree::Transaction do
|
|
887
887
|
end
|
888
888
|
end
|
889
889
|
|
890
|
+
context "service fees" do
|
891
|
+
it "allows specifying service fees" do
|
892
|
+
result = Braintree::Transaction.create(
|
893
|
+
:type => "sale",
|
894
|
+
:amount => Braintree::Test::TransactionAmounts::Authorize,
|
895
|
+
:merchant_account_id => SpecHelper::NonDefaultSubMerchantAccountId,
|
896
|
+
:credit_card => {
|
897
|
+
:number => Braintree::Test::CreditCardNumbers::Visa,
|
898
|
+
:expiration_date => "12/12",
|
899
|
+
},
|
900
|
+
:service_fee_amount => "1.00"
|
901
|
+
)
|
902
|
+
result.success?.should == true
|
903
|
+
result.transaction.service_fee_amount.should == BigDecimal.new("1.00")
|
904
|
+
end
|
905
|
+
|
906
|
+
it "raises an error if transaction merchant account is a master" do
|
907
|
+
result = Braintree::Transaction.create(
|
908
|
+
:type => "sale",
|
909
|
+
:amount => Braintree::Test::TransactionAmounts::Authorize,
|
910
|
+
:merchant_account_id => SpecHelper::NonDefaultMerchantAccountId,
|
911
|
+
:credit_card => {
|
912
|
+
:number => Braintree::Test::CreditCardNumbers::Visa,
|
913
|
+
:expiration_date => "12/12",
|
914
|
+
},
|
915
|
+
:service_fee_amount => "1.00"
|
916
|
+
)
|
917
|
+
result.success?.should == false
|
918
|
+
expected_error_code = Braintree::ErrorCodes::Transaction::ServiceFeeAmountNotAllowedOnMasterMerchantAccount
|
919
|
+
result.errors.for(:transaction).on(:service_fee_amount)[0].code.should == expected_error_code
|
920
|
+
end
|
921
|
+
|
922
|
+
it "raises an error if no service fee is present on a sub merchant account transaction" do
|
923
|
+
result = Braintree::Transaction.create(
|
924
|
+
:type => "sale",
|
925
|
+
:amount => Braintree::Test::TransactionAmounts::Authorize,
|
926
|
+
:merchant_account_id => SpecHelper::NonDefaultSubMerchantAccountId,
|
927
|
+
:credit_card => {
|
928
|
+
:number => Braintree::Test::CreditCardNumbers::Visa,
|
929
|
+
:expiration_date => "12/12",
|
930
|
+
}
|
931
|
+
)
|
932
|
+
result.success?.should == false
|
933
|
+
expected_error_code = Braintree::ErrorCodes::Transaction::SubMerchantAccountRequiresServiceFeeAmount
|
934
|
+
result.errors.for(:transaction).on(:merchant_account_id)[0].code.should == expected_error_code
|
935
|
+
end
|
936
|
+
|
937
|
+
it "raises an error if service fee amount is negative" do
|
938
|
+
result = Braintree::Transaction.create(
|
939
|
+
:merchant_account_id => SpecHelper::NonDefaultSubMerchantAccountId,
|
940
|
+
:service_fee_amount => "-1.00"
|
941
|
+
)
|
942
|
+
result.success?.should == false
|
943
|
+
result.errors.for(:transaction).on(:service_fee_amount)[0].code.should == Braintree::ErrorCodes::Transaction::ServiceFeeAmountCannotBeNegative
|
944
|
+
end
|
945
|
+
|
946
|
+
it "raises an error if service fee amount is invalid" do
|
947
|
+
result = Braintree::Transaction.create(
|
948
|
+
:merchant_account_id => SpecHelper::NonDefaultSubMerchantAccountId,
|
949
|
+
:service_fee_amount => "invalid amount"
|
950
|
+
)
|
951
|
+
result.success?.should == false
|
952
|
+
result.errors.for(:transaction).on(:service_fee_amount)[0].code.should == Braintree::ErrorCodes::Transaction::ServiceFeeAmountFormatIsInvalid
|
953
|
+
end
|
954
|
+
end
|
955
|
+
|
956
|
+
context "escrow" do
|
957
|
+
it "allows specifying transactions to be held for escrow" do
|
958
|
+
result = Braintree::Transaction.create(
|
959
|
+
:type => "sale",
|
960
|
+
:amount => Braintree::Test::TransactionAmounts::Authorize,
|
961
|
+
:merchant_account_id => SpecHelper::NonDefaultSubMerchantAccountId,
|
962
|
+
:credit_card => {
|
963
|
+
:number => Braintree::Test::CreditCardNumbers::Visa,
|
964
|
+
:expiration_date => "12/12",
|
965
|
+
},
|
966
|
+
:service_fee_amount => "10.00",
|
967
|
+
:options => {:hold_in_escrow => true}
|
968
|
+
)
|
969
|
+
|
970
|
+
result.success?.should == true
|
971
|
+
result.transaction.escrow_status.should == Braintree::Transaction::EscrowStatus::HoldPending
|
972
|
+
end
|
973
|
+
|
974
|
+
it "raises an error if transaction merchant account is a master" do
|
975
|
+
result = Braintree::Transaction.create(
|
976
|
+
:type => "sale",
|
977
|
+
:amount => Braintree::Test::TransactionAmounts::Authorize,
|
978
|
+
:merchant_account_id => SpecHelper::NonDefaultMerchantAccountId,
|
979
|
+
:credit_card => {
|
980
|
+
:number => Braintree::Test::CreditCardNumbers::Visa,
|
981
|
+
:expiration_date => "12/12",
|
982
|
+
},
|
983
|
+
:service_fee_amount => "1.00",
|
984
|
+
:options => {:hold_in_escrow => true}
|
985
|
+
)
|
986
|
+
result.success?.should == false
|
987
|
+
expected_error_code = Braintree::ErrorCodes::Transaction::CannotHoldInEscrow
|
988
|
+
result.errors.for(:transaction).on(:base)[0].code.should == expected_error_code
|
989
|
+
end
|
990
|
+
end
|
991
|
+
|
890
992
|
describe "venmo_sdk" do
|
891
993
|
it "can create a card with a venmo sdk payment method code" do
|
892
994
|
result = Braintree::Transaction.create(
|
@@ -898,6 +1000,22 @@ describe Braintree::Transaction do
|
|
898
1000
|
result.transaction.credit_card_details.bin.should == "400934"
|
899
1001
|
result.transaction.credit_card_details.last_4.should == "1881"
|
900
1002
|
end
|
1003
|
+
|
1004
|
+
it "can create a transaction with venmo sdk session" do
|
1005
|
+
result = Braintree::Transaction.create(
|
1006
|
+
:type => "sale",
|
1007
|
+
:amount => Braintree::Test::TransactionAmounts::Authorize,
|
1008
|
+
:credit_card => {
|
1009
|
+
:number => Braintree::Test::CreditCardNumbers::Visa,
|
1010
|
+
:expiration_date => "12/12",
|
1011
|
+
},
|
1012
|
+
:options => {
|
1013
|
+
:venmo_sdk_session => Braintree::Test::VenmoSDK::Session
|
1014
|
+
}
|
1015
|
+
)
|
1016
|
+
result.success?.should == true
|
1017
|
+
result.transaction.credit_card_details.venmo_sdk?.should == true
|
1018
|
+
end
|
901
1019
|
end
|
902
1020
|
end
|
903
1021
|
|
@@ -1441,6 +1559,24 @@ describe Braintree::Transaction do
|
|
1441
1559
|
result.success?.should == false
|
1442
1560
|
result.errors.for(:transaction).on(:base)[0].code.should == Braintree::ErrorCodes::Transaction::CannotSubmitForSettlement
|
1443
1561
|
end
|
1562
|
+
|
1563
|
+
context "service fees" do
|
1564
|
+
it "returns an error result if amount submitted for settlement is less than service fee amount" do
|
1565
|
+
transaction = Braintree::Transaction.create(
|
1566
|
+
:type => "sale",
|
1567
|
+
:amount => Braintree::Test::TransactionAmounts::Authorize,
|
1568
|
+
:merchant_account_id => SpecHelper::NonDefaultSubMerchantAccountId,
|
1569
|
+
:credit_card => {
|
1570
|
+
:number => Braintree::Test::CreditCardNumbers::Visa,
|
1571
|
+
:expiration_date => "06/2009"
|
1572
|
+
},
|
1573
|
+
:service_fee_amount => "1.00"
|
1574
|
+
).transaction
|
1575
|
+
result = Braintree::Transaction.submit_for_settlement(transaction.id, "0.01")
|
1576
|
+
result.success?.should == false
|
1577
|
+
result.errors.for(:transaction).on(:amount)[0].code.should == Braintree::ErrorCodes::Transaction::SettlementAmountIsLessThanServiceFeeAmount
|
1578
|
+
end
|
1579
|
+
end
|
1444
1580
|
end
|
1445
1581
|
|
1446
1582
|
describe "self.submit_for_settlement!" do
|
@@ -1472,6 +1608,101 @@ describe Braintree::Transaction do
|
|
1472
1608
|
end
|
1473
1609
|
end
|
1474
1610
|
|
1611
|
+
describe "self.release_from_escrow" do
|
1612
|
+
it "returns the transaction if successful" do
|
1613
|
+
original_transaction = create_escrowed_transcation
|
1614
|
+
|
1615
|
+
result = Braintree::Transaction.release_from_escrow(original_transaction.id)
|
1616
|
+
result.transaction.escrow_status.should == Braintree::Transaction::EscrowStatus::ReleasePending
|
1617
|
+
end
|
1618
|
+
|
1619
|
+
it "returns an error result if escrow_status is not HeldForEscrow" do
|
1620
|
+
transaction = Braintree::Transaction.sale!(
|
1621
|
+
:amount => Braintree::Test::TransactionAmounts::Authorize,
|
1622
|
+
:merchant_account_id => SpecHelper::NonDefaultSubMerchantAccountId,
|
1623
|
+
:credit_card => {
|
1624
|
+
:number => Braintree::Test::CreditCardNumbers::Visa,
|
1625
|
+
:expiration_date => "05/2009"
|
1626
|
+
},
|
1627
|
+
:service_fee_amount => '1.00'
|
1628
|
+
)
|
1629
|
+
|
1630
|
+
transaction.escrow_status.should be_nil
|
1631
|
+
|
1632
|
+
result = Braintree::Transaction.release_from_escrow(transaction.id)
|
1633
|
+
result.errors.for(:transaction).on(:base)[0].code.should == Braintree::ErrorCodes::Transaction::CannotReleaseFromEscrow
|
1634
|
+
end
|
1635
|
+
end
|
1636
|
+
|
1637
|
+
describe "self.release_from_escrow!" do
|
1638
|
+
it "returns the transaction when successful" do
|
1639
|
+
original_transaction = create_escrowed_transcation
|
1640
|
+
|
1641
|
+
transaction = Braintree::Transaction.release_from_escrow!(original_transaction.id)
|
1642
|
+
transaction.escrow_status.should == Braintree::Transaction::EscrowStatus::ReleasePending
|
1643
|
+
end
|
1644
|
+
|
1645
|
+
it "raises an error when transaction is not successful" do
|
1646
|
+
transaction = Braintree::Transaction.sale!(
|
1647
|
+
:amount => Braintree::Test::TransactionAmounts::Authorize,
|
1648
|
+
:merchant_account_id => SpecHelper::NonDefaultSubMerchantAccountId,
|
1649
|
+
:credit_card => {
|
1650
|
+
:number => Braintree::Test::CreditCardNumbers::Visa,
|
1651
|
+
:expiration_date => "05/2009"
|
1652
|
+
},
|
1653
|
+
:service_fee_amount => '1.00'
|
1654
|
+
)
|
1655
|
+
|
1656
|
+
transaction.escrow_status.should be_nil
|
1657
|
+
|
1658
|
+
expect do
|
1659
|
+
Braintree::Transaction.release_from_escrow!(transaction.id)
|
1660
|
+
end.to raise_error(Braintree::ValidationsFailed)
|
1661
|
+
end
|
1662
|
+
end
|
1663
|
+
|
1664
|
+
describe "self.cancel_release" do
|
1665
|
+
it "returns the transaction if successful" do
|
1666
|
+
transaction = create_escrowed_transcation
|
1667
|
+
result = Braintree::Transaction.release_from_escrow(transaction.id)
|
1668
|
+
result.transaction.escrow_status.should == Braintree::Transaction::EscrowStatus::ReleasePending
|
1669
|
+
|
1670
|
+
result = Braintree::Transaction.cancel_release(transaction.id)
|
1671
|
+
|
1672
|
+
result.success?.should be_true
|
1673
|
+
result.transaction.escrow_status.should == Braintree::Transaction::EscrowStatus::Held
|
1674
|
+
end
|
1675
|
+
|
1676
|
+
it "returns an error result if escrow_status is not ReleasePending" do
|
1677
|
+
transaction = create_escrowed_transcation
|
1678
|
+
|
1679
|
+
result = Braintree::Transaction.cancel_release(transaction.id)
|
1680
|
+
|
1681
|
+
result.success?.should be_false
|
1682
|
+
result.errors.for(:transaction).on(:base)[0].code.should == Braintree::ErrorCodes::Transaction::CannotCancelRelease
|
1683
|
+
end
|
1684
|
+
end
|
1685
|
+
|
1686
|
+
describe "self.cancel_release!" do
|
1687
|
+
it "returns the transaction when release is cancelled" do
|
1688
|
+
transaction = create_escrowed_transcation
|
1689
|
+
result = Braintree::Transaction.release_from_escrow(transaction.id)
|
1690
|
+
result.transaction.escrow_status.should == Braintree::Transaction::EscrowStatus::ReleasePending
|
1691
|
+
|
1692
|
+
transaction = Braintree::Transaction.cancel_release!(transaction.id)
|
1693
|
+
|
1694
|
+
transaction.escrow_status.should == Braintree::Transaction::EscrowStatus::Held
|
1695
|
+
end
|
1696
|
+
|
1697
|
+
it "raises an error when release cannot be cancelled" do
|
1698
|
+
transaction = create_escrowed_transcation
|
1699
|
+
|
1700
|
+
expect {
|
1701
|
+
transaction = Braintree::Transaction.cancel_release!(transaction.id)
|
1702
|
+
}.to raise_error(Braintree::ValidationsFailed)
|
1703
|
+
end
|
1704
|
+
end
|
1705
|
+
|
1475
1706
|
describe "self.credit" do
|
1476
1707
|
it "returns a successful result with type=credit if successful" do
|
1477
1708
|
result = Braintree::Transaction.credit(
|
@@ -1530,6 +1761,22 @@ describe Braintree::Transaction do
|
|
1530
1761
|
result.success?.should == true
|
1531
1762
|
result.transaction.merchant_account_id.should == SpecHelper::DefaultMerchantAccountId
|
1532
1763
|
end
|
1764
|
+
|
1765
|
+
it "disallows service fee on a credit" do
|
1766
|
+
params = {
|
1767
|
+
:transaction => {
|
1768
|
+
:amount => Braintree::Test::TransactionAmounts::Authorize,
|
1769
|
+
:credit_card => {
|
1770
|
+
:number => Braintree::Test::CreditCardNumbers::Visa,
|
1771
|
+
:expiration_date => "05/2009"
|
1772
|
+
},
|
1773
|
+
:service_fee_amount => "1.00"
|
1774
|
+
}
|
1775
|
+
}
|
1776
|
+
result = Braintree::Transaction.credit(params[:transaction])
|
1777
|
+
result.success?.should == false
|
1778
|
+
result.errors.for(:transaction).on(:base).map(&:code).should include(Braintree::ErrorCodes::Transaction::ServiceFeeIsNotAllowedOnCredits)
|
1779
|
+
end
|
1533
1780
|
end
|
1534
1781
|
|
1535
1782
|
describe "self.credit!" do
|
@@ -1792,6 +2039,76 @@ describe Braintree::Transaction do
|
|
1792
2039
|
end
|
1793
2040
|
end
|
1794
2041
|
|
2042
|
+
describe "self.hold_in_escrow" do
|
2043
|
+
it "returns the transaction if successful" do
|
2044
|
+
result = Braintree::Transaction.create(
|
2045
|
+
:type => "sale",
|
2046
|
+
:amount => Braintree::Test::TransactionAmounts::Authorize,
|
2047
|
+
:merchant_account_id => SpecHelper::NonDefaultSubMerchantAccountId,
|
2048
|
+
:credit_card => {
|
2049
|
+
:number => Braintree::Test::CreditCardNumbers::Visa,
|
2050
|
+
:expiration_date => "12/12",
|
2051
|
+
},
|
2052
|
+
:service_fee_amount => "10.00"
|
2053
|
+
)
|
2054
|
+
|
2055
|
+
result.transaction.escrow_status.should be_nil
|
2056
|
+
result = Braintree::Transaction.hold_in_escrow(result.transaction.id)
|
2057
|
+
|
2058
|
+
result.success?.should be_true
|
2059
|
+
result.transaction.escrow_status.should == Braintree::Transaction::EscrowStatus::HoldPending
|
2060
|
+
end
|
2061
|
+
|
2062
|
+
it "returns an error result if the transaction cannot be held in escrow" do
|
2063
|
+
transaction = Braintree::Transaction.sale!(
|
2064
|
+
:amount => Braintree::Test::TransactionAmounts::Authorize,
|
2065
|
+
:merchant_account_id => SpecHelper::NonDefaultMerchantAccountId,
|
2066
|
+
:credit_card => {
|
2067
|
+
:number => Braintree::Test::CreditCardNumbers::Visa,
|
2068
|
+
:expiration_date => "05/2009"
|
2069
|
+
}
|
2070
|
+
)
|
2071
|
+
|
2072
|
+
result = Braintree::Transaction.hold_in_escrow(transaction.id)
|
2073
|
+
result.errors.for(:transaction).on(:base)[0].code.should == Braintree::ErrorCodes::Transaction::CannotHoldInEscrow
|
2074
|
+
end
|
2075
|
+
end
|
2076
|
+
|
2077
|
+
describe "self.hold_in_escrow!" do
|
2078
|
+
it "returns the transaction if successful" do
|
2079
|
+
result = Braintree::Transaction.create(
|
2080
|
+
:type => "sale",
|
2081
|
+
:amount => Braintree::Test::TransactionAmounts::Authorize,
|
2082
|
+
:merchant_account_id => SpecHelper::NonDefaultSubMerchantAccountId,
|
2083
|
+
:credit_card => {
|
2084
|
+
:number => Braintree::Test::CreditCardNumbers::Visa,
|
2085
|
+
:expiration_date => "12/12",
|
2086
|
+
},
|
2087
|
+
:service_fee_amount => "10.00"
|
2088
|
+
)
|
2089
|
+
|
2090
|
+
result.transaction.escrow_status.should be_nil
|
2091
|
+
transaction = Braintree::Transaction.hold_in_escrow!(result.transaction.id)
|
2092
|
+
|
2093
|
+
transaction.escrow_status.should == Braintree::Transaction::EscrowStatus::HoldPending
|
2094
|
+
end
|
2095
|
+
|
2096
|
+
it "raises an error if the transaction cannot be held in escrow" do
|
2097
|
+
transaction = Braintree::Transaction.sale!(
|
2098
|
+
:amount => Braintree::Test::TransactionAmounts::Authorize,
|
2099
|
+
:merchant_account_id => SpecHelper::NonDefaultMerchantAccountId,
|
2100
|
+
:credit_card => {
|
2101
|
+
:number => Braintree::Test::CreditCardNumbers::Visa,
|
2102
|
+
:expiration_date => "05/2009"
|
2103
|
+
}
|
2104
|
+
)
|
2105
|
+
|
2106
|
+
expect do
|
2107
|
+
Braintree::Transaction.hold_in_escrow!(transaction.id)
|
2108
|
+
end.to raise_error(Braintree::ValidationsFailed)
|
2109
|
+
end
|
2110
|
+
end
|
2111
|
+
|
1795
2112
|
describe "self.void" do
|
1796
2113
|
it "returns a successful result if successful" do
|
1797
2114
|
transaction = Braintree::Transaction.sale!(
|
@@ -2137,4 +2454,76 @@ describe Braintree::Transaction do
|
|
2137
2454
|
response = Braintree::Configuration.instantiate.http.put "/transactions/#{transaction.id}/settle"
|
2138
2455
|
Braintree::Transaction.find(transaction.id)
|
2139
2456
|
end
|
2457
|
+
|
2458
|
+
def create_escrowed_transcation
|
2459
|
+
transaction = Braintree::Transaction.sale!(
|
2460
|
+
:amount => Braintree::Test::TransactionAmounts::Authorize,
|
2461
|
+
:merchant_account_id => SpecHelper::NonDefaultSubMerchantAccountId,
|
2462
|
+
:credit_card => {
|
2463
|
+
:number => Braintree::Test::CreditCardNumbers::Visa,
|
2464
|
+
:expiration_date => "05/2009"
|
2465
|
+
},
|
2466
|
+
:service_fee_amount => '1.00',
|
2467
|
+
:options => { :hold_in_escrow => true }
|
2468
|
+
)
|
2469
|
+
|
2470
|
+
response = Braintree::Configuration.instantiate.http.put "/transactions/#{transaction.id}/settle"
|
2471
|
+
response = Braintree::Configuration.instantiate.http.put "/transactions/#{transaction.id}/escrow"
|
2472
|
+
Braintree::Transaction.find(transaction.id)
|
2473
|
+
end
|
2474
|
+
|
2475
|
+
context "venmo sdk" do
|
2476
|
+
describe "venmo_sdk_payment_method_code" do
|
2477
|
+
it "can create a transaction with venmo_sdk_payment_method_code" do
|
2478
|
+
result = Braintree::Transaction.sale(
|
2479
|
+
:amount => "10.00",
|
2480
|
+
:venmo_sdk_payment_method_code => Braintree::Test::VenmoSDK.generate_test_payment_method_code(Braintree::Test::CreditCardNumbers::Visa)
|
2481
|
+
)
|
2482
|
+
result.success?.should == true
|
2483
|
+
result.transaction.credit_card_details.venmo_sdk?.should == true
|
2484
|
+
end
|
2485
|
+
|
2486
|
+
it "errors when an invalid payment method code is passed" do
|
2487
|
+
result = Braintree::Transaction.sale(
|
2488
|
+
:amount => "10.00",
|
2489
|
+
:venmo_sdk_payment_method_code => Braintree::Test::VenmoSDK::InvalidPaymentMethodCode
|
2490
|
+
)
|
2491
|
+
result.success?.should == false
|
2492
|
+
result.message.should == "Invalid VenmoSDK payment method code"
|
2493
|
+
result.errors.first.code.should == "91727"
|
2494
|
+
end
|
2495
|
+
end
|
2496
|
+
|
2497
|
+
describe "venmo_sdk_session" do
|
2498
|
+
it "can create a transaction and vault a card when a venmo_sdk_session is present" do
|
2499
|
+
result = Braintree::Transaction.sale(
|
2500
|
+
:amount => "10.00",
|
2501
|
+
:credit_card => {
|
2502
|
+
:number => Braintree::Test::CreditCardNumbers::Visa,
|
2503
|
+
:expiration_date => "05/2009"
|
2504
|
+
},
|
2505
|
+
:options => {
|
2506
|
+
:venmo_sdk_session => Braintree::Test::VenmoSDK::Session
|
2507
|
+
}
|
2508
|
+
)
|
2509
|
+
result.success?.should == true
|
2510
|
+
result.transaction.credit_card_details.venmo_sdk?.should == true
|
2511
|
+
end
|
2512
|
+
|
2513
|
+
it "venmo_sdk boolean is false when an invalid session is passed" do
|
2514
|
+
result = Braintree::Transaction.sale(
|
2515
|
+
:amount => "10.00",
|
2516
|
+
:credit_card => {
|
2517
|
+
:number => Braintree::Test::CreditCardNumbers::Visa,
|
2518
|
+
:expiration_date => "05/2009"
|
2519
|
+
},
|
2520
|
+
:options => {
|
2521
|
+
:venmo_sdk_session => Braintree::Test::VenmoSDK::InvalidSession
|
2522
|
+
}
|
2523
|
+
)
|
2524
|
+
result.success?.should == true
|
2525
|
+
result.transaction.credit_card_details.venmo_sdk?.should == false
|
2526
|
+
end
|
2527
|
+
end
|
2528
|
+
end
|
2140
2529
|
end
|