braintree 2.45.0 → 2.46.0

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