braintree 2.45.0 → 2.46.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 (52) hide show
  1. data/lib/braintree.rb +5 -0
  2. data/lib/braintree/add_on_gateway.rb +2 -1
  3. data/lib/braintree/address_gateway.rb +5 -4
  4. data/lib/braintree/client_token_gateway.rb +2 -1
  5. data/lib/braintree/configuration.rb +59 -7
  6. data/lib/braintree/credentials_parser.rb +40 -0
  7. data/lib/braintree/credit_card_gateway.rb +12 -11
  8. data/lib/braintree/credit_card_verification_gateway.rb +4 -3
  9. data/lib/braintree/customer_gateway.rb +12 -11
  10. data/lib/braintree/discount_gateway.rb +2 -1
  11. data/lib/braintree/error_codes.rb +9 -0
  12. data/lib/braintree/europe_bank_account_gateway.rb +2 -1
  13. data/lib/braintree/exceptions.rb +1 -5
  14. data/lib/braintree/gateway.rb +8 -0
  15. data/lib/braintree/http.rb +8 -2
  16. data/lib/braintree/merchant.rb +19 -0
  17. data/lib/braintree/merchant_account_gateway.rb +6 -5
  18. data/lib/braintree/merchant_gateway.rb +27 -0
  19. data/lib/braintree/oauth_credentials.rb +19 -0
  20. data/lib/braintree/oauth_gateway.rb +68 -0
  21. data/lib/braintree/payment_method_gateway.rb +7 -6
  22. data/lib/braintree/payment_method_nonce_gateway.rb +3 -2
  23. data/lib/braintree/paypal_account_gateway.rb +5 -4
  24. data/lib/braintree/plan_gateway.rb +2 -1
  25. data/lib/braintree/settlement_batch_summary_gateway.rb +2 -1
  26. data/lib/braintree/subscription_gateway.rb +8 -7
  27. data/lib/braintree/successful_result.rb +1 -1
  28. data/lib/braintree/testing_gateway.rb +5 -4
  29. data/lib/braintree/transaction.rb +1 -0
  30. data/lib/braintree/transaction_gateway.rb +12 -11
  31. data/lib/braintree/transaction_search.rb +3 -0
  32. data/lib/braintree/transparent_redirect_gateway.rb +1 -0
  33. data/lib/braintree/version.rb +1 -1
  34. data/lib/braintree/webhook_notification_gateway.rb +1 -0
  35. data/lib/braintree/webhook_testing_gateway.rb +1 -0
  36. data/spec/httpsd.pid +1 -1
  37. data/spec/integration/braintree/add_on_spec.rb +12 -0
  38. data/spec/integration/braintree/customer_spec.rb +31 -0
  39. data/spec/integration/braintree/http_spec.rb +10 -6
  40. data/spec/integration/braintree/merchant_account_spec.rb +0 -7
  41. data/spec/integration/braintree/merchant_spec.rb +55 -0
  42. data/spec/integration/braintree/oauth_spec.rb +191 -0
  43. data/spec/integration/braintree/plan_spec.rb +2 -1
  44. data/spec/integration/braintree/transaction_search_spec.rb +63 -0
  45. data/spec/integration/braintree/transaction_spec.rb +73 -4
  46. data/spec/integration/spec_helper.rb +1 -1
  47. data/spec/oauth_test_helper.rb +17 -0
  48. data/spec/spec_helper.rb +8 -4
  49. data/spec/unit/braintree/configuration_spec.rb +23 -0
  50. data/spec/unit/braintree/credentials_parser_spec.rb +81 -0
  51. metadata +161 -148
  52. checksums.yaml +0 -15
@@ -3,10 +3,11 @@ module Braintree
3
3
  def initialize(gateway)
4
4
  @gateway = gateway
5
5
  @config = gateway.config
6
+ @config.assert_has_access_token_or_keys
6
7
  end
7
8
 
8
9
  def all
9
- response = @config.http.get "/discounts"
10
+ response = @config.http.get("#{@config.base_merchant_path}/discounts")
10
11
  attributes_collection = response[:discounts]
11
12
  attributes_collection.map do |attributes|
12
13
  Discount._new(attributes)
@@ -381,6 +381,7 @@ module Braintree
381
381
  EmailFormatIsInvalid = "93602"
382
382
  EmailIsRequired = "83601"
383
383
  InconsistentCountry = "93612"
384
+ PaymentMethodsAreInvalid = "93613"
384
385
  end
385
386
 
386
387
  module MerchantAccount
@@ -528,6 +529,14 @@ module Braintree
528
529
  OptionsNotAllowedWithoutCustomer = "93207"
529
530
  SignatureRevoked = "93203"
530
531
  end
532
+
533
+ module OAuth
534
+ InvalidGrant = "93801";
535
+ InvalidCredentials = "93802";
536
+ InvalidScope = "93803";
537
+ InvalidRequest = "93804";
538
+ UnsupportedGrantType = "93805";
539
+ end
531
540
  end
532
541
  end
533
542
 
@@ -3,11 +3,12 @@ module Braintree
3
3
  def initialize(gateway)
4
4
  @gateway = gateway
5
5
  @config = gateway.config
6
+ @config.assert_has_access_token_or_keys
6
7
  end
7
8
 
8
9
  def find(token)
9
10
  raise ArgumentError if token.nil? || token.to_s.strip == ""
10
- response = @config.http.get "/payment_methods/europe_bank_account/#{token}"
11
+ response = @config.http.get("#{@config.base_merchant_path}/payment_methods/europe_bank_account/#{token}")
11
12
  EuropeBankAccount._new(@gateway, response[:europe_bank_account])
12
13
  rescue NotFoundError
13
14
  raise NotFoundError, "payment method with token #{token.inspect} not found"
@@ -6,11 +6,7 @@ module Braintree # :nodoc:
6
6
 
7
7
  class AuthorizationError < BraintreeError; end
8
8
 
9
- class ConfigurationError < BraintreeError
10
- def initialize(setting, message) # :nodoc:
11
- super "Braintree::Configuration.#{setting} #{message}"
12
- end
13
- end
9
+ class ConfigurationError < BraintreeError; end
14
10
 
15
11
  class DownForMaintenanceError < BraintreeError; end
16
12
 
@@ -36,6 +36,14 @@ module Braintree
36
36
  DiscountGateway.new(self)
37
37
  end
38
38
 
39
+ def merchant
40
+ MerchantGateway.new(self)
41
+ end
42
+
43
+ def oauth
44
+ OAuthGateway.new(self)
45
+ end
46
+
39
47
  def plan
40
48
  PlanGateway.new(self)
41
49
  end
@@ -69,12 +69,18 @@ module Braintree
69
69
  connection.verify_callback = proc { |preverify_ok, ssl_context| _verify_ssl_certificate(preverify_ok, ssl_context) }
70
70
  end
71
71
  connection.start do |http|
72
- request = http_verb.new("#{@config.base_merchant_path}#{path}")
72
+ request = http_verb.new(path)
73
73
  request["Accept"] = "application/xml"
74
74
  request["User-Agent"] = @config.user_agent
75
75
  request["Accept-Encoding"] = "gzip"
76
76
  request["X-ApiVersion"] = @config.api_version
77
- request.basic_auth @config.public_key, @config.private_key
77
+ if @config.client_credentials?
78
+ request.basic_auth @config.client_id, @config.client_secret
79
+ elsif @config.access_token
80
+ request["Authorization"] = "Bearer #{@config.access_token}"
81
+ else
82
+ request.basic_auth @config.public_key, @config.private_key
83
+ end
78
84
  @config.logger.debug "[Braintree] [#{_current_time}] #{request.method} #{path}"
79
85
  if body
80
86
  request["Content-Type"] = "application/xml"
@@ -0,0 +1,19 @@
1
+ module Braintree
2
+ class Merchant
3
+ include BaseModule # :nodoc:
4
+
5
+ attr_reader :id, :email, :company_name, :country_code_alpha2, :country_code_alpha3, :country_code_numeric, :country_name
6
+
7
+ def initialize(attributes) # :nodoc:
8
+ set_instance_variables_from_hash(attributes)
9
+ end
10
+
11
+ class << self
12
+ protected :new
13
+ end
14
+
15
+ def self._new(*args) # :nodoc:
16
+ self.new *args
17
+ end
18
+ end
19
+ end
@@ -3,6 +3,7 @@ module Braintree
3
3
  def initialize(gateway)
4
4
  @gateway = gateway
5
5
  @config = gateway.config
6
+ @config.assert_has_access_token_or_keys
6
7
  end
7
8
 
8
9
  def create(attributes)
@@ -13,7 +14,7 @@ module Braintree
13
14
 
14
15
  def find(merchant_account_id)
15
16
  raise ArgumentError if merchant_account_id.nil? || merchant_account_id.to_s.strip == ""
16
- response = @config.http.get "/merchant_accounts/#{merchant_account_id}"
17
+ response = @config.http.get("#{@config.base_merchant_path}/merchant_accounts/#{merchant_account_id}")
17
18
  MerchantAccount._new(@gateway, response[:merchant_account])
18
19
  rescue NotFoundError
19
20
  raise NotFoundError, "Merchant account with id #{merchant_account_id} not found"
@@ -24,8 +25,8 @@ module Braintree
24
25
  _do_update "/merchant_accounts/#{merchant_account_id}/update_via_api", :merchant_account => attributes
25
26
  end
26
27
 
27
- def _do_create(url, params=nil) # :nodoc:
28
- response = @config.http.post url, params
28
+ def _do_create(path, params=nil) # :nodoc:
29
+ response = @config.http.post("#{@config.base_merchant_path}#{path}", params)
29
30
  if response[:api_error_response]
30
31
  ErrorResult.new(@gateway, response[:api_error_response])
31
32
  else
@@ -33,8 +34,8 @@ module Braintree
33
34
  end
34
35
  end
35
36
 
36
- def _do_update(url, params=nil) # :nodoc:
37
- response = @config.http.put(url, params)
37
+ def _do_update(path, params=nil) # :nodoc:
38
+ response = @config.http.put("#{@config.base_merchant_path}#{path}", params)
38
39
  if response[:api_error_response]
39
40
  ErrorResult.new(@gateway, response[:api_error_response])
40
41
  else
@@ -0,0 +1,27 @@
1
+ module Braintree
2
+ class MerchantGateway
3
+ def initialize(gateway)
4
+ @gateway = gateway
5
+ @config = gateway.config
6
+ end
7
+
8
+ def create(params)
9
+ _create_merchant(params)
10
+ end
11
+
12
+ def _create_merchant(params)
13
+ response = @config.http.post("/merchants/create_via_api", :merchant => params)
14
+
15
+ if response.has_key?(:response) && response[:response][:merchant]
16
+ Braintree::SuccessfulResult.new(
17
+ :merchant => Merchant._new(response[:response][:merchant]),
18
+ :credentials => OAuthCredentials._new(response[:response][:credentials])
19
+ )
20
+ elsif response[:api_error_response]
21
+ ErrorResult.new(@gateway, response[:api_error_response])
22
+ else
23
+ raise "expected :merchant or :api_error_response"
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,19 @@
1
+ module Braintree
2
+ class OAuthCredentials
3
+ include BaseModule # :nodoc:
4
+
5
+ attr_reader :access_token, :refresh_token, :expires_at, :token_type
6
+
7
+ def initialize(attributes) # :nodoc:
8
+ set_instance_variables_from_hash(attributes)
9
+ end
10
+
11
+ class << self
12
+ protected :new
13
+ end
14
+
15
+ def self._new(*args) # :nodoc:
16
+ self.new *args
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,68 @@
1
+ module Braintree
2
+ class OAuthGateway
3
+ def initialize(gateway)
4
+ @gateway = gateway
5
+ @config = gateway.config
6
+ @config.assert_has_client_credentials
7
+ end
8
+
9
+ def create_token_from_code(params)
10
+ params[:grant_type] = "authorization_code"
11
+ _create_token(params)
12
+ end
13
+
14
+ def create_token_from_refresh_token(params)
15
+ params[:grant_type] = "refresh_token"
16
+ _create_token(params)
17
+ end
18
+
19
+ def _create_token(params)
20
+ response = @config.http.post("/oauth/access_tokens", {
21
+ :credentials => params,
22
+ })
23
+ if response[:credentials]
24
+ Braintree::SuccessfulResult.new(
25
+ :credentials => OAuthCredentials._new(response[:credentials])
26
+ )
27
+ elsif response[:api_error_response]
28
+ ErrorResult.new(@gateway, response[:api_error_response])
29
+ else
30
+ raise "expected :credentials or :api_error_response"
31
+ end
32
+ end
33
+
34
+ def connect_url(params)
35
+ params[:client_id] = @config.client_id
36
+ user_params = _sub_query(params, :user)
37
+ business_params = _sub_query(params, :business)
38
+ payment_methods = _sub_array_query(params, :payment_methods)
39
+ query = params.to_a.
40
+ concat(user_params).
41
+ concat(business_params).
42
+ concat(payment_methods)
43
+
44
+ query_string = query.map { |k, v| "#{URI.escape(k.to_s)}=#{URI.escape(v.to_s)}" }.join("&")
45
+ _sign_url("#{@config.base_url}/oauth/connect?#{CGI.escape(query_string)}")
46
+ end
47
+
48
+ def _sub_query(params, root)
49
+ sub_params = params.delete(root) || {}
50
+ sub_params.map do |key, value|
51
+ ["#{root}[#{key}]", value]
52
+ end
53
+ end
54
+
55
+ def _sub_array_query(params, root)
56
+ sub_params = params.delete(root) || []
57
+ sub_params.map do |value|
58
+ ["#{root}[]", value]
59
+ end
60
+ end
61
+
62
+ def _sign_url(url)
63
+ key_digest = OpenSSL::Digest::SHA256.digest(@config.client_secret)
64
+ signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, key_digest, url)
65
+ "#{url}&signature=#{signature}&algorithm=SHA256"
66
+ end
67
+ end
68
+ end
@@ -3,6 +3,7 @@ module Braintree
3
3
  def initialize(gateway)
4
4
  @gateway = gateway
5
5
  @config = gateway.config
6
+ @config.assert_has_access_token_or_keys
6
7
  end
7
8
 
8
9
  def create(attributes)
@@ -10,8 +11,8 @@ module Braintree
10
11
  _do_create("/payment_methods", :payment_method => attributes)
11
12
  end
12
13
 
13
- def _do_create(url, params=nil) # :nodoc:
14
- response = @config.http.post url, params
14
+ def _do_create(path, params=nil) # :nodoc:
15
+ response = @config.http.post("#{@config.base_merchant_path}#{path}", params)
15
16
  if response[:credit_card]
16
17
  SuccessfulResult.new(:payment_method => CreditCard._new(@gateway, response[:credit_card]))
17
18
  elsif response[:paypal_account]
@@ -34,12 +35,12 @@ module Braintree
34
35
  end
35
36
 
36
37
  def delete(token)
37
- @config.http.delete("/payment_methods/any/#{token}")
38
+ @config.http.delete("#{@config.base_merchant_path}/payment_methods/any/#{token}")
38
39
  end
39
40
 
40
41
  def find(token)
41
42
  raise ArgumentError if token.nil? || token.to_s.strip == ""
42
- response = @config.http.get "/payment_methods/any/#{token}"
43
+ response = @config.http.get("#{@config.base_merchant_path}/payment_methods/any/#{token}")
43
44
  if response.has_key?(:credit_card)
44
45
  CreditCard._new(@gateway, response[:credit_card])
45
46
  elsif response.has_key?(:paypal_account)
@@ -64,8 +65,8 @@ module Braintree
64
65
  _do_update(:put, "/payment_methods/any/#{token}", :payment_method => attributes)
65
66
  end
66
67
 
67
- def _do_update(http_verb, url, params) # :nodoc:
68
- response = @config.http.send http_verb, url, params
68
+ def _do_update(http_verb, path, params) # :nodoc:
69
+ response = @config.http.send(http_verb, "#{@config.base_merchant_path}#{path}", params)
69
70
  if response[:credit_card]
70
71
  SuccessfulResult.new(:payment_method => CreditCard._new(@gateway, response[:credit_card]))
71
72
  elsif response[:paypal_account]
@@ -3,16 +3,17 @@ module Braintree
3
3
  def initialize(gateway)
4
4
  @gateway = gateway
5
5
  @config = gateway.config
6
+ @config.assert_has_access_token_or_keys
6
7
  end
7
8
 
8
9
  def create(payment_method_token)
9
- response = @config.http.post "/payment_methods/#{payment_method_token}/nonces"
10
+ response = @config.http.post("#{@config.base_merchant_path}/payment_methods/#{payment_method_token}/nonces")
10
11
  payment_method_nonce = PaymentMethodNonce._new(@gateway, response.fetch(:payment_method_nonce))
11
12
  SuccessfulResult.new(:payment_method_nonce => payment_method_nonce)
12
13
  end
13
14
 
14
15
  def find(payment_method_nonce)
15
- response = @config.http.get "/payment_method_nonces/#{payment_method_nonce}"
16
+ response = @config.http.get("#{@config.base_merchant_path}/payment_method_nonces/#{payment_method_nonce}")
16
17
  payment_method_nonce = PaymentMethodNonce._new(@gateway, response.fetch(:payment_method_nonce))
17
18
  SuccessfulResult.new(:payment_method_nonce => payment_method_nonce)
18
19
  end
@@ -3,11 +3,12 @@ module Braintree
3
3
  def initialize(gateway)
4
4
  @gateway = gateway
5
5
  @config = gateway.config
6
+ @config.assert_has_access_token_or_keys
6
7
  end
7
8
 
8
9
  def find(token)
9
10
  raise ArgumentError if token.nil? || token.to_s.strip == ""
10
- response = @config.http.get "/payment_methods/paypal_account/#{token}"
11
+ response = @config.http.get("#{@config.base_merchant_path}/payment_methods/paypal_account/#{token}")
11
12
  PayPalAccount._new(@gateway, response[:paypal_account])
12
13
  rescue NotFoundError
13
14
  raise NotFoundError, "payment method with token #{token.inspect} not found"
@@ -19,11 +20,11 @@ module Braintree
19
20
  end
20
21
 
21
22
  def delete(token)
22
- @config.http.delete("/payment_methods/paypal_account/#{token}")
23
+ @config.http.delete("#{@config.base_merchant_path}/payment_methods/paypal_account/#{token}")
23
24
  end
24
25
 
25
- def _do_update(http_verb, url, params) # :nodoc:
26
- response = @config.http.send http_verb, url, params
26
+ def _do_update(http_verb, path, params) # :nodoc:
27
+ response = @config.http.send(http_verb, "#{@config.base_merchant_path}#{path}", params)
27
28
  if response[:paypal_account]
28
29
  SuccessfulResult.new(:paypal_account => PayPalAccount._new(@gateway, response[:paypal_account]))
29
30
  elsif response[:api_error_response]
@@ -3,10 +3,11 @@ module Braintree
3
3
  def initialize(gateway)
4
4
  @gateway = gateway
5
5
  @config = gateway.config
6
+ @config.assert_has_access_token_or_keys
6
7
  end
7
8
 
8
9
  def all
9
- response = @config.http.get "/plans"
10
+ response = @config.http.get("#{@config.base_merchant_path}/plans")
10
11
  attributes_collection = response[:plans] || []
11
12
  attributes_collection.map do |attributes|
12
13
  Plan._new(@gateway, attributes)
@@ -3,11 +3,12 @@ module Braintree
3
3
  def initialize(gateway)
4
4
  @gateway = gateway
5
5
  @config = gateway.config
6
+ @config.assert_has_access_token_or_keys
6
7
  end
7
8
 
8
9
  def generate(criteria)
9
10
  Util.verify_keys(_signature, criteria)
10
- response = @config.http.post "/settlement_batch_summary", :settlement_batch_summary => criteria
11
+ response = @config.http.post("#{@config.base_merchant_path}/settlement_batch_summary", :settlement_batch_summary => criteria)
11
12
  if response[:settlement_batch_summary]
12
13
  SuccessfulResult.new(:settlement_batch_summary => SettlementBatchSummary._new(@gateway, response[:settlement_batch_summary]))
13
14
  elsif response[:api_error_response]
@@ -3,10 +3,11 @@ module Braintree
3
3
  def initialize(gateway)
4
4
  @gateway = gateway
5
5
  @config = gateway.config
6
+ @config.assert_has_access_token_or_keys
6
7
  end
7
8
 
8
9
  def cancel(subscription_id)
9
- response = @config.http.put "/subscriptions/#{subscription_id}/cancel"
10
+ response = @config.http.put("#{@config.base_merchant_path}/subscriptions/#{subscription_id}/cancel")
10
11
  if response[:subscription]
11
12
  SuccessfulResult.new(:subscription => Subscription._new(@gateway, response[:subscription]))
12
13
  elsif response[:api_error_response]
@@ -25,7 +26,7 @@ module Braintree
25
26
 
26
27
  def find(id)
27
28
  raise ArgumentError if id.nil? || id.to_s.strip == ""
28
- response = @config.http.get "/subscriptions/#{id}"
29
+ response = @config.http.get("#{@config.base_merchant_path}/subscriptions/#{id}")
29
30
  Subscription._new(@gateway, response[:subscription])
30
31
  rescue NotFoundError
31
32
  raise NotFoundError, "subscription with id #{id.inspect} not found"
@@ -35,13 +36,13 @@ module Braintree
35
36
  search = SubscriptionSearch.new
36
37
  block.call(search) if block
37
38
 
38
- response = @config.http.post "/subscriptions/advanced_search_ids", {:search => search.to_hash}
39
+ response = @config.http.post("#{@config.base_merchant_path}/subscriptions/advanced_search_ids", {:search => search.to_hash})
39
40
  ResourceCollection.new(response) { |ids| _fetch_subscriptions(search, ids) }
40
41
  end
41
42
 
42
43
  def update(subscription_id, attributes)
43
44
  Util.verify_keys(SubscriptionGateway._update_signature, attributes)
44
- response = @config.http.put "/subscriptions/#{subscription_id}", :subscription => attributes
45
+ response = @config.http.put("#{@config.base_merchant_path}/subscriptions/#{subscription_id}", :subscription => attributes)
45
46
  if response[:subscription]
46
47
  SuccessfulResult.new(:subscription => Subscription._new(@gateway, response[:subscription]))
47
48
  elsif response[:api_error_response]
@@ -109,8 +110,8 @@ module Braintree
109
110
  ]
110
111
  end
111
112
 
112
- def _do_create(url, params) # :nodoc:
113
- response = @config.http.post url, params
113
+ def _do_create(path, params) # :nodoc:
114
+ response = @config.http.post("#{@config.base_merchant_path}#{path}", params)
114
115
  if response[:subscription]
115
116
  SuccessfulResult.new(:subscription => Subscription._new(@gateway, response[:subscription]))
116
117
  elsif response[:api_error_response]
@@ -122,7 +123,7 @@ module Braintree
122
123
 
123
124
  def _fetch_subscriptions(search, ids) # :nodoc:
124
125
  search.ids.in ids
125
- response = @config.http.post "/subscriptions/advanced_search", {:search => search.to_hash}
126
+ response = @config.http.post("#{@config.base_merchant_path}/subscriptions/advanced_search", {:search => search.to_hash})
126
127
  attributes = response[:subscriptions]
127
128
  Util.extract_attribute_as_array(attributes, :subscription).map { |attrs| Subscription._new(@gateway, attrs) }
128
129
  end