braintree 2.24.0 → 2.25.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|