braintree 2.83.0 → 2.84.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/lib/braintree/address.rb +12 -12
  3. data/lib/braintree/address/country_names.rb +4 -1
  4. data/lib/braintree/address_gateway.rb +10 -0
  5. data/lib/braintree/client_token.rb +2 -2
  6. data/lib/braintree/client_token_gateway.rb +2 -0
  7. data/lib/braintree/credit_card.rb +20 -20
  8. data/lib/braintree/credit_card_gateway.rb +18 -0
  9. data/lib/braintree/credit_card_verification.rb +2 -2
  10. data/lib/braintree/customer.rb +18 -18
  11. data/lib/braintree/customer_gateway.rb +14 -2
  12. data/lib/braintree/dispute.rb +12 -12
  13. data/lib/braintree/dispute/history_event.rb +2 -0
  14. data/lib/braintree/dispute_search.rb +3 -0
  15. data/lib/braintree/document_upload.rb +6 -2
  16. data/lib/braintree/document_upload_gateway.rb +6 -0
  17. data/lib/braintree/ideal_payment.rb +3 -3
  18. data/lib/braintree/ideal_payment_gateway.rb +0 -1
  19. data/lib/braintree/merchant_account.rb +14 -6
  20. data/lib/braintree/merchant_account_gateway.rb +10 -0
  21. data/lib/braintree/payment_method.rb +20 -12
  22. data/lib/braintree/payment_method_gateway.rb +10 -0
  23. data/lib/braintree/payment_method_nonce.rb +8 -4
  24. data/lib/braintree/payment_method_nonce_gateway.rb +6 -0
  25. data/lib/braintree/settlement_batch_summary_gateway.rb +2 -0
  26. data/lib/braintree/subscription.rb +18 -14
  27. data/lib/braintree/subscription_gateway.rb +18 -0
  28. data/lib/braintree/transaction.rb +54 -54
  29. data/lib/braintree/transaction_gateway.rb +44 -1
  30. data/lib/braintree/transparent_redirect.rb +12 -12
  31. data/lib/braintree/us_bank_account.rb +5 -5
  32. data/lib/braintree/us_bank_account_gateway.rb +0 -1
  33. data/lib/braintree/version.rb +1 -1
  34. data/lib/braintree/webhook_notification.rb +4 -4
  35. data/lib/braintree/webhook_testing.rb +2 -2
  36. data/spec/httpsd.pid +1 -0
  37. data/spec/integration/braintree/client_api/spec_helper.rb +0 -15
  38. data/spec/integration/braintree/customer_spec.rb +73 -0
  39. data/spec/integration/braintree/dispute_search_spec.rb +69 -0
  40. data/spec/integration/braintree/document_upload_spec.rb +13 -0
  41. data/spec/integration/braintree/merchant_account_spec.rb +22 -0
  42. data/spec/integration/braintree/payment_method_nonce_spec.rb +29 -0
  43. data/spec/integration/braintree/payment_method_spec.rb +63 -93
  44. data/spec/integration/braintree/subscription_spec.rb +13 -0
  45. data/spec/integration/braintree/transaction_search_spec.rb +0 -41
  46. data/spec/integration/braintree/transaction_spec.rb +12 -1
  47. data/spec/unit/braintree/dispute_search_spec.rb +3 -0
  48. data/spec/unit/braintree/transaction_spec.rb +0 -1
  49. metadata +4 -3
@@ -1,5 +1,7 @@
1
1
  module Braintree
2
2
  class TransactionGateway # :nodoc:
3
+ include BaseModule
4
+
3
5
  def initialize(gateway)
4
6
  @gateway = gateway
5
7
  @config = gateway.config
@@ -17,12 +19,20 @@ module Braintree
17
19
  _handle_transaction_response(response)
18
20
  end
19
21
 
22
+ def cancel_release!(*args)
23
+ return_object_or_raise(:transaction) { cancel_release(*args) }
24
+ end
25
+
20
26
  def hold_in_escrow(transaction_id)
21
27
  raise ArgumentError, "transaction_id is invalid" unless transaction_id =~ /\A[0-9a-z]+\z/
22
28
  response = @config.http.put("#{@config.base_merchant_path}/transactions/#{transaction_id}/hold_in_escrow")
23
29
  _handle_transaction_response(response)
24
30
  end
25
31
 
32
+ def hold_in_escrow!(*args)
33
+ return_object_or_raise(:transaction) { hold_in_escrow(*args) }
34
+ end
35
+
26
36
  def _handle_transaction_response(response)
27
37
  if response[:transaction]
28
38
  SuccessfulResult.new(:transaction => Transaction._new(@gateway, response[:transaction]))
@@ -38,6 +48,10 @@ module Braintree
38
48
  _do_create "/transactions/#{transaction_id}/clone", :transaction_clone => attributes
39
49
  end
40
50
 
51
+ def clone_transaction!(*args)
52
+ return_object_or_raise(:transaction) { clone_transaction(*args) }
53
+ end
54
+
41
55
  # Deprecated
42
56
  def create_from_transparent_redirect(query_string)
43
57
  params = @gateway.transparent_redirect.parse_and_validate_query_string query_string
@@ -53,6 +67,10 @@ module Braintree
53
67
  create(attributes.merge(:type => 'credit'))
54
68
  end
55
69
 
70
+ def credit!(*args)
71
+ return_object_or_raise(:transaction) { credit(*args) }
72
+ end
73
+
56
74
  def find(id)
57
75
  raise ArgumentError if id.nil? || id.strip.to_s == ""
58
76
  response = @config.http.get("#{@config.base_merchant_path}/transactions/#{id}")
@@ -73,6 +91,10 @@ module Braintree
73
91
  _handle_transaction_response(response)
74
92
  end
75
93
 
94
+ def refund!(*args)
95
+ return_object_or_raise(:transaction) { refund(*args) }
96
+ end
97
+
76
98
  def retry_subscription_charge(subscription_id, amount=nil, submit_for_settlement=false)
77
99
  attributes = {
78
100
  :amount => amount,
@@ -89,6 +111,10 @@ module Braintree
89
111
  create(attributes.merge(:type => 'sale'))
90
112
  end
91
113
 
114
+ def sale!(*args)
115
+ return_object_or_raise(:transaction) { sale(*args) }
116
+ end
117
+
92
118
  def search(&block)
93
119
  search = TransactionSearch.new
94
120
  block.call(search) if block
@@ -108,6 +134,10 @@ module Braintree
108
134
  _handle_transaction_response(response)
109
135
  end
110
136
 
137
+ def release_from_escrow!(*args)
138
+ return_object_or_raise(:transaction) { release_from_escrow(*args) }
139
+ end
140
+
111
141
  def submit_for_settlement(transaction_id, amount = nil, options = {})
112
142
  raise ArgumentError, "transaction_id is invalid" unless transaction_id =~ /\A[0-9a-z]+\z/
113
143
  Util.verify_keys(TransactionGateway._submit_for_settlement_signature, options)
@@ -116,6 +146,10 @@ module Braintree
116
146
  _handle_transaction_response(response)
117
147
  end
118
148
 
149
+ def submit_for_settlement!(*args)
150
+ return_object_or_raise(:transaction) { submit_for_settlement(*args) }
151
+ end
152
+
119
153
  def update_details(transaction_id, options = {})
120
154
  raise ArgumentError, "transaction_id is invalid" unless transaction_id =~ /\A[0-9a-z]+\z/
121
155
  Util.verify_keys(TransactionGateway._update_details_signature, options)
@@ -131,11 +165,18 @@ module Braintree
131
165
  _handle_transaction_response(response)
132
166
  end
133
167
 
168
+ def submit_for_partial_settlement!(*args)
169
+ return_object_or_raise(:transaction) { submit_for_partial_settlement(*args) }
170
+ end
171
+
134
172
  def void(transaction_id)
135
173
  response = @config.http.put("#{@config.base_merchant_path}/transactions/#{transaction_id}/void")
136
174
  _handle_transaction_response(response)
137
175
  end
138
176
 
177
+ def void!(*args)
178
+ return_object_or_raise(:transaction) { void(*args) }
179
+ end
139
180
 
140
181
  def self._clone_signature # :nodoc:
141
182
  [:amount, :channel, {:options => [:submit_for_settlement]}]
@@ -180,7 +221,9 @@ module Braintree
180
221
  :skip_cvv,
181
222
  {:paypal => [:custom_field, :payee_email, :description, {:supplementary_data => :_any_key_}]},
182
223
  {:three_d_secure => [:required]},
183
- {:amex_rewards => [:request_id, :points, :currency_amount, :currency_iso_code]}]
224
+ {:amex_rewards => [:request_id, :points, :currency_amount, :currency_iso_code]},
225
+ {:venmo => [:profile_id]}
226
+ ]
184
227
  },
185
228
  {:custom_fields => :_any_key_},
186
229
  {:descriptor => [:name, :phone, :url]},
@@ -8,28 +8,28 @@ module Braintree
8
8
  CreateTransaction = "create_transaction"
9
9
  end
10
10
 
11
- def self.confirm(query_string)
12
- Configuration.gateway.transparent_redirect.confirm(query_string)
11
+ def self.confirm(*args)
12
+ Configuration.gateway.transparent_redirect.confirm(*args)
13
13
  end
14
14
 
15
- def self.create_credit_card_data(params)
16
- Configuration.gateway.transparent_redirect.create_credit_card_data(params)
15
+ def self.create_credit_card_data(*args)
16
+ Configuration.gateway.transparent_redirect.create_credit_card_data(*args)
17
17
  end
18
18
 
19
- def self.create_customer_data(params)
20
- Configuration.gateway.transparent_redirect.create_customer_data(params)
19
+ def self.create_customer_data(*args)
20
+ Configuration.gateway.transparent_redirect.create_customer_data(*args)
21
21
  end
22
22
 
23
- def self.transaction_data(params)
24
- Configuration.gateway.transparent_redirect.transaction_data(params)
23
+ def self.transaction_data(*args)
24
+ Configuration.gateway.transparent_redirect.transaction_data(*args)
25
25
  end
26
26
 
27
- def self.update_credit_card_data(params)
28
- Configuration.gateway.transparent_redirect.update_credit_card_data(params)
27
+ def self.update_credit_card_data(*args)
28
+ Configuration.gateway.transparent_redirect.update_credit_card_data(*args)
29
29
  end
30
30
 
31
- def self.update_customer_data(params)
32
- Configuration.gateway.transparent_redirect.update_customer_data(params)
31
+ def self.update_customer_data(*args)
32
+ Configuration.gateway.transparent_redirect.update_customer_data(*args)
33
33
  end
34
34
 
35
35
  # Returns the URL to which Transparent Redirect Requests should be posted
@@ -30,16 +30,16 @@ module Braintree
30
30
  self.new *args
31
31
  end
32
32
 
33
- def self.find(token)
34
- Configuration.gateway.us_bank_account.find(token)
33
+ def self.find(*args)
34
+ Configuration.gateway.us_bank_account.find(*args)
35
35
  end
36
36
 
37
37
  def self.sale(token, transaction_attributes)
38
38
  Configuration.gateway.transaction.sale(transaction_attributes.merge(
39
- :payment_method_token => token,
40
- :options => { :submit_for_settlement => true }
41
- )
39
+ :payment_method_token => token,
40
+ :options => { :submit_for_settlement => true }
42
41
  )
42
+ )
43
43
  end
44
44
 
45
45
  def self.sale!(token, transaction_attributes)
@@ -13,6 +13,5 @@ module Braintree
13
13
  rescue NotFoundError
14
14
  raise NotFoundError, "payment method with token #{token.inspect} not found"
15
15
  end
16
-
17
16
  end
18
17
  end
@@ -1,7 +1,7 @@
1
1
  module Braintree
2
2
  module Version
3
3
  Major = 2
4
- Minor = 83
4
+ Minor = 84
5
5
  Tiny = 0
6
6
 
7
7
  String = "#{Major}.#{Minor}.#{Tiny}"
@@ -56,12 +56,12 @@ module Braintree
56
56
  attr_reader :timestamp
57
57
  attr_reader :transaction
58
58
 
59
- def self.parse(signature, payload)
60
- Configuration.gateway.webhook_notification.parse(signature, payload)
59
+ def self.parse(*args)
60
+ Configuration.gateway.webhook_notification.parse(*args)
61
61
  end
62
62
 
63
- def self.verify(challenge)
64
- Configuration.gateway.webhook_notification.verify(challenge)
63
+ def self.verify(*args)
64
+ Configuration.gateway.webhook_notification.verify(*args)
65
65
  end
66
66
 
67
67
  def initialize(gateway, attributes) # :nodoc:
@@ -1,7 +1,7 @@
1
1
  module Braintree
2
2
  class WebhookTesting # :nodoc:
3
- def self.sample_notification(kind, id, source_merchant_id=nil)
4
- Configuration.gateway.webhook_testing.sample_notification(kind, id, source_merchant_id)
3
+ def self.sample_notification(*args)
4
+ Configuration.gateway.webhook_testing.sample_notification(*args)
5
5
  end
6
6
  end
7
7
  end
@@ -0,0 +1 @@
1
+ 1590
@@ -243,19 +243,4 @@ class ClientApiHttp
243
243
 
244
244
  post("/merchants/#{config.merchant_id}/client_api/v1/payment_methods/paypal_accounts", params)
245
245
  end
246
-
247
- def create_europe_bank_account_nonce(params)
248
- foo = {
249
- :authorization_fingerprint => @options[:authorization_fingerprint],
250
- :shared_customer_identifier => "fake_identifier",
251
- :shared_customer_identifier_type => "testing",
252
-
253
- :sepa_mandate => params
254
- }
255
-
256
- response = post("/merchants/#{config.merchant_id}/client_api/v1/sepa_mandates", foo)
257
-
258
- mrn = JSON.parse(response.body)['europeBankAccounts'][0]['sepaMandates'][0]['mandateReferenceNumber']
259
- JSON.parse(response.body)['europeBankAccounts'][0]['nonce']
260
- end
261
246
  end
@@ -823,6 +823,79 @@ describe Braintree::Customer do
823
823
  found_customer.credit_cards.first.subscriptions.first.price.should == BigDecimal.new("1.00")
824
824
  end
825
825
 
826
+ context "when given an association filter id" do
827
+ it "filters out all filterable associations" do
828
+ customer = Braintree::Customer.create(
829
+ :custom_fields => {
830
+ :store_me => "custom value"
831
+ }
832
+ ).customer
833
+ credit_card = Braintree::CreditCard.create(
834
+ :customer_id => customer.id,
835
+ :number => Braintree::Test::CreditCardNumbers::Visa,
836
+ :expiration_date => "05/2012",
837
+ :billing_address => {
838
+ :street_address => "1 E Main St",
839
+ :locality => "Chicago",
840
+ :region => "Illinois",
841
+ :postal_code => "60622",
842
+ :country_name => "United States of America"
843
+ }
844
+ ).credit_card
845
+
846
+ subscription = Braintree::Subscription.create(
847
+ :payment_method_token => credit_card.token,
848
+ :plan_id => "integration_trialless_plan",
849
+ :price => "1.00"
850
+ ).subscription
851
+
852
+ found_customer = Braintree::Customer.find(customer.id, {
853
+ :association_filter_id => "customernoassociations"
854
+ })
855
+ found_customer.credit_cards.length.should == 0
856
+ found_customer.payment_methods.length.should == 0
857
+ found_customer.addresses.length.should == 0
858
+ found_customer.custom_fields.should == {}
859
+ end
860
+
861
+ it "filters out nested filterable associations" do
862
+ customer = Braintree::Customer.create(
863
+ :custom_fields => {
864
+ :store_me => "custom value"
865
+ }
866
+ ).customer
867
+ credit_card = Braintree::CreditCard.create(
868
+ :customer_id => customer.id,
869
+ :number => Braintree::Test::CreditCardNumbers::Visa,
870
+ :expiration_date => "05/2012",
871
+ :billing_address => {
872
+ :street_address => "1 E Main St",
873
+ :locality => "Chicago",
874
+ :region => "Illinois",
875
+ :postal_code => "60622",
876
+ :country_name => "United States of America"
877
+ }
878
+ ).credit_card
879
+
880
+ subscription = Braintree::Subscription.create(
881
+ :payment_method_token => credit_card.token,
882
+ :plan_id => "integration_trialless_plan",
883
+ :price => "1.00"
884
+ ).subscription
885
+
886
+ found_customer = Braintree::Customer.find(customer.id, {
887
+ :association_filter_id => "customertoplevelassociations"
888
+ })
889
+
890
+ found_customer.credit_cards.length.should == 1
891
+ found_customer.credit_cards.first.subscriptions.length.should == 0
892
+ found_customer.payment_methods.length.should == 1
893
+ found_customer.payment_methods.first.subscriptions.length.should == 0
894
+ found_customer.addresses.length.should == 1
895
+ found_customer.custom_fields.length.should == 1
896
+ end
897
+ end
898
+
826
899
  it "returns associated ApplePayCards" do
827
900
  result = Braintree::Customer.create(
828
901
  :payment_method_nonce => Braintree::Test::Nonce::ApplePayAmEx
@@ -2,6 +2,37 @@ require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
2
2
  require File.expand_path(File.dirname(__FILE__) + "/client_api/spec_helper")
3
3
 
4
4
  describe Braintree::Dispute, "search" do
5
+ let(:customer) do
6
+ result = Braintree::Customer.create(
7
+ :first_name => "Jen",
8
+ :last_name => "Smith",
9
+ :company => "Braintree",
10
+ :email => "jen@example.com",
11
+ :phone => "312.555.1234",
12
+ :fax => "614.555.5678",
13
+ :website => "www.example.com"
14
+ )
15
+
16
+ result.customer
17
+ end
18
+
19
+ let(:transaction) do
20
+ result = Braintree::Transaction.sale(
21
+ :amount => '10.00',
22
+ :credit_card => {
23
+ :expiration_date => '01/2020',
24
+ :number => Braintree::Test::CreditCardNumbers::Disputes::Chargeback
25
+ },
26
+ :customer_id => customer.id,
27
+ :merchant_account_id => "14LaddersLLC_instant",
28
+ :options => {
29
+ :submit_for_settlement => true
30
+ }
31
+ )
32
+
33
+ result.transaction
34
+ end
35
+
5
36
  context "advanced" do
6
37
  it "correctly returns a result with no matches" do
7
38
  collection = Braintree::Dispute.search do |search|
@@ -23,6 +54,18 @@ describe Braintree::Dispute, "search" do
23
54
  expect(dispute.status).to eq(Braintree::Dispute::Status::Open)
24
55
  end
25
56
 
57
+ it "correctly returns a single dispute by customer_id" do
58
+ collection = Braintree::Dispute.search do |search|
59
+ search.customer_id.is transaction.customer_details.id
60
+ end
61
+
62
+ expect(collection.disputes.count).to eq(1)
63
+ dispute = collection.disputes.first
64
+
65
+ expect(dispute.id).to eq(transaction.disputes.first.id)
66
+ expect(dispute.status).to eq(Braintree::Dispute::Status::Open)
67
+ end
68
+
26
69
  it "correctly returns disputes by multiple reasons" do
27
70
  collection = Braintree::Dispute.search do |search|
28
71
  search.reason.in [
@@ -35,6 +78,32 @@ describe Braintree::Dispute, "search" do
35
78
  dispute = collection.disputes.first
36
79
  end
37
80
 
81
+ it "correctly returns disputes by effective_date range" do
82
+ effective_date = transaction.disputes.first.status_history.first.effective_date
83
+
84
+ collection = Braintree::Dispute.search do |search|
85
+ search.effective_date.between(effective_date, Date.parse(effective_date).next_day.to_s)
86
+ end
87
+
88
+ expect(collection.disputes.count).to be >= 1
89
+
90
+ dispute_ids = collection.disputes.map { |d| d.id }
91
+ expect(dispute_ids).to include(transaction.disputes.first.id)
92
+ end
93
+
94
+ it "correctly returns disputes by disbursement_date range" do
95
+ disbursement_date = transaction.disputes.first.status_history.first.disbursement_date
96
+
97
+ collection = Braintree::Dispute.search do |search|
98
+ search.disbursement_date.between(disbursement_date, Date.parse(disbursement_date).next_day.to_s)
99
+ end
100
+
101
+ expect(collection.disputes.count).to be >= 1
102
+
103
+ dispute_ids = collection.disputes.map { |d| d.id }
104
+ expect(dispute_ids).to include(transaction.disputes.first.id)
105
+ end
106
+
38
107
  it "correctly returns disputes by received_date range" do
39
108
  collection = Braintree::Dispute.search do |search|
40
109
  search.received_date.between("03/03/2014", "03/05/2014")
@@ -52,4 +52,17 @@ describe Braintree::DocumentUploadGateway do
52
52
  end.to raise_error(ArgumentError, "invalid keys: invalid_key")
53
53
  end
54
54
  end
55
+
56
+ describe "create!" do
57
+ it "returns successful with valid request" do
58
+ file = File.new("#{File.dirname(__FILE__)}/../../fixtures/files/bt_logo.png", "r")
59
+ document_upload = Braintree::DocumentUpload.create!({:kind => Braintree::DocumentUpload::Kind::EvidenceDocument, :file => file})
60
+
61
+ document_upload.id.should_not be_nil
62
+ document_upload.content_type.should == "image/png"
63
+ document_upload.kind.should == Braintree::DocumentUpload::Kind::EvidenceDocument
64
+ document_upload.name.should == "bt_logo.png"
65
+ document_upload.size.should == 2443
66
+ end
67
+ end
55
68
  end
@@ -217,6 +217,15 @@ describe Braintree::MerchantAccount do
217
217
  end
218
218
  end
219
219
 
220
+ describe "create!" do
221
+ it "creates a merchant account with the new parameters and doesn't require an id" do
222
+ merchant_account = Braintree::MerchantAccount.create!(VALID_APPLICATION_PARAMS)
223
+
224
+ merchant_account.status.should == Braintree::MerchantAccount::Status::Pending
225
+ merchant_account.master_merchant_account.id.should == "sandbox_master_merchant_account"
226
+ end
227
+ end
228
+
220
229
  describe "create_for_currency" do
221
230
  it "creates a new merchant account for currency" do
222
231
  result = SpecHelper::create_merchant
@@ -579,4 +588,17 @@ describe Braintree::MerchantAccount do
579
588
  result.errors.for(:merchant_account).for(:funding).on(:mobile_phone).map(&:code).should == [Braintree::ErrorCodes::MerchantAccount::Funding::MobilePhoneIsRequired]
580
589
  end
581
590
  end
591
+
592
+ describe "update!" do
593
+ it "updates the Merchant Account info" do
594
+ params = VALID_APPLICATION_PARAMS.clone
595
+ params.delete(:tos_accepted)
596
+ params.delete(:master_merchant_account_id)
597
+ params[:individual][:first_name] = "John"
598
+ params[:individual][:last_name] = "Doe"
599
+ merchant_account = Braintree::MerchantAccount.update!("sandbox_sub_merchant_account", params)
600
+ merchant_account.individual_details.first_name.should == "John"
601
+ merchant_account.individual_details.last_name.should == "Doe"
602
+ end
603
+ end
582
604
  end