braintree 2.24.0 → 2.25.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- if response[:transaction]
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
- if response[:transaction]
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
- if response[:transaction]
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
- if response[:transaction]
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:
@@ -1,7 +1,7 @@
1
1
  module Braintree
2
2
  module Version
3
3
  Major = 2
4
- Minor = 24
4
+ Minor = 25
5
5
  Tiny = 0
6
6
 
7
7
  String = "#{Major}.#{Minor}.#{Tiny}"
@@ -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, id)
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
- #{_subscription_sample_xml(id)}
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
- XML_FORMATTING_NAMES = {
17
- "BigDecimal" => "bigdecimal",
18
- "Symbol" => "symbol"
19
- }.merge(XML_TYPE_NAMES)
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 },
@@ -1 +1 @@
1
- 28977
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
- elsif RUBY_VERSION =~ /^1.9/
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