braintree 2.83.0 → 2.84.0

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