braintree 2.90.0 → 2.91.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/braintree.gemspec +5 -0
  3. data/lib/braintree/configuration.rb +39 -1
  4. data/lib/braintree/credit_card.rb +1 -0
  5. data/lib/braintree/error_codes.rb +8 -0
  6. data/lib/braintree/graphql_client.rb +28 -0
  7. data/lib/braintree/http.rb +21 -9
  8. data/lib/braintree/local_payment_completed.rb +20 -0
  9. data/lib/braintree/test/credit_card.rb +2 -0
  10. data/lib/braintree/transaction.rb +8 -0
  11. data/lib/braintree/transaction_gateway.rb +4 -0
  12. data/lib/braintree/util.rb +44 -3
  13. data/lib/braintree/validation_error.rb +10 -2
  14. data/lib/braintree/validation_error_collection.rb +2 -1
  15. data/lib/braintree/version.rb +1 -1
  16. data/lib/braintree/webhook_notification.rb +5 -1
  17. data/lib/braintree/webhook_testing_gateway.rb +34 -3
  18. data/lib/braintree.rb +3 -1
  19. data/spec/integration/braintree/credit_card_spec.rb +14 -0
  20. data/spec/integration/braintree/dispute_spec.rb +3 -0
  21. data/spec/integration/braintree/graphql_client_spec.rb +74 -0
  22. data/spec/integration/braintree/http_spec.rb +1 -1
  23. data/spec/integration/braintree/transaction_search_spec.rb +19 -0
  24. data/spec/integration/braintree/transaction_spec.rb +203 -2
  25. data/spec/spec_helper.rb +1 -0
  26. data/spec/unit/braintree/configuration_spec.rb +37 -0
  27. data/spec/unit/braintree/http_spec.rb +65 -0
  28. data/spec/unit/braintree/local_payment_completed_spec.rb +24 -0
  29. data/spec/unit/braintree/transaction_spec.rb +8 -0
  30. data/spec/unit/braintree/util_spec.rb +109 -0
  31. data/spec/unit/braintree/validation_error_collection_spec.rb +335 -132
  32. data/spec/unit/braintree/webhook_notification_spec.rb +31 -0
  33. metadata +10 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4ded1b6a4f547ae738fdb43c3762fcc51515f8276833bcede2828f5b51a6720a
4
- data.tar.gz: be2edc1892560c721da8cef1b116b49229b2f790c5e0bf4b8f668625e0074ada
3
+ metadata.gz: 703d5a600446c3eccc5d310b70091319b9d426c2db3826851bbcd55fed478ecc
4
+ data.tar.gz: d98833b2c0937b365e18817b5bf5c0335695282e4aad32ecf08537fc5f36e3b8
5
5
  SHA512:
6
- metadata.gz: 8daaea6f26713cea097e60bcb39b68f912638c385f40e57fced65d548554c6ad73c78cee736a3c30f96915a10922c7425281c5a0b99f875672c40482df9672ca
7
- data.tar.gz: c13b688529c17c8c5b5a82ff95e67bbc47058381f29e386f1b5a9da11a84d51528454d4de85a0f2f6afff419e7288e304cb71f041325339356e372fc028ac464
6
+ metadata.gz: 83411d93215207167d8aa5f3f52a2af58b0744f502c0ec64989dee900f87b415861563f11809e9f9f428c09ed63b7a4f47afb6a419708cfc78d5a3ae7ae3c847
7
+ data.tar.gz: bd72dec425b6f7164c5ca5f2560aacc5a62e3db20ea3b01afee70683fe964f888fd4e77f35139d7034e8dbc083a5bd15f5138844d01569b21614f675b64aed77
data/braintree.gemspec CHANGED
@@ -14,5 +14,10 @@ Gem::Specification.new do |s|
14
14
  s.has_rdoc = false
15
15
  s.files = Dir.glob ["README.rdoc", "LICENSE", "lib/**/*.{rb,crt}", "spec/**/*", "*.gemspec"]
16
16
  s.add_dependency "builder", ">= 2.0.0"
17
+ s.metadata = {
18
+ "bug_tracker_uri" => "https://github.com/braintree/braintree_ruby/issues",
19
+ "changelog_uri" => "https://github.com/braintree/braintree_ruby/blob/master/CHANGELOG.md",
20
+ "source_code_uri" => "https://github.com/braintree/braintree_ruby",
21
+ }
17
22
  end
18
23
 
@@ -1,7 +1,8 @@
1
1
  module Braintree
2
2
  class Configuration
3
- API_VERSION = "4" # :nodoc:
3
+ API_VERSION = "5" # :nodoc:
4
4
  DEFAULT_ENDPOINT = "api" # :nodoc:
5
+ GRAPHQL_API_VERSION = "2018-09-10" # :nodoc:
5
6
 
6
7
  READABLE_ATTRIBUTES = [
7
8
  :merchant_id,
@@ -44,6 +45,7 @@ module Braintree
44
45
  end
45
46
  attr_reader *READABLE_ATTRIBUTES
46
47
  attr_reader *NON_REQUIRED_READABLE_ATTRIBUTES
48
+ attr_writer *WRITABLE_ATTRIBUTES
47
49
 
48
50
  def self.expectant_reader(*attributes) # :nodoc:
49
51
  attributes.each do |attribute|
@@ -113,6 +115,8 @@ module Braintree
113
115
  instance_variable_set "@#{attr}", options[attr]
114
116
  end
115
117
 
118
+ @environment = @environment.to_sym if @environment
119
+
116
120
  _check_for_mixed_credentials(options)
117
121
 
118
122
  parser = Braintree::CredentialsParser.new
@@ -158,6 +162,10 @@ module Braintree
158
162
  API_VERSION
159
163
  end
160
164
 
165
+ def graphql_api_version # :nodoc:
166
+ GRAPHQL_API_VERSION
167
+ end
168
+
161
169
  def base_merchant_path # :nodoc:
162
170
  "/merchants/#{merchant_id}"
163
171
  end
@@ -166,6 +174,10 @@ module Braintree
166
174
  "#{protocol}://#{server}:#{port}"
167
175
  end
168
176
 
177
+ def graphql_base_url
178
+ "#{protocol}://#{graphql_server}:#{graphql_port}/graphql"
179
+ end
180
+
169
181
  def base_merchant_url # :nodoc:
170
182
  "#{base_url}#{base_merchant_path}"
171
183
  end
@@ -182,6 +194,10 @@ module Braintree
182
194
  Http.new(self)
183
195
  end
184
196
 
197
+ def graphql_client
198
+ GraphQLClient.new(self)
199
+ end
200
+
185
201
  def logger
186
202
  @logger ||= self.class._default_logger
187
203
  end
@@ -195,6 +211,15 @@ module Braintree
195
211
  end
196
212
  end
197
213
 
214
+ def graphql_port # :nodoc:
215
+ case @environment
216
+ when :development, :integration
217
+ ENV['GRAPHQL_PORT'] || 8080
218
+ when :production, :qa, :sandbox
219
+ 443
220
+ end
221
+ end
222
+
198
223
  def protocol # :nodoc:
199
224
  ssl? ? "https" : "http"
200
225
  end
@@ -220,6 +245,19 @@ module Braintree
220
245
  end
221
246
  end
222
247
 
248
+ def graphql_server # :nodoc:
249
+ case @environment
250
+ when :development, :integration
251
+ ENV['GRAPHQL_HOST'] || "graphql.bt.local"
252
+ when :production
253
+ "payments.braintree-api.com"
254
+ when :qa
255
+ "payments-qa.dev.braintree-api.com"
256
+ when :sandbox
257
+ "payments.sandbox.braintree-api.com"
258
+ end
259
+ end
260
+
223
261
  def auth_url
224
262
  case @environment
225
263
  when :development, :integration
@@ -9,6 +9,7 @@ module Braintree
9
9
  ChinaUnionPay = "China UnionPay"
10
10
  DinersClubInternational = "Diners Club"
11
11
  Discover = "Discover"
12
+ Elo = "Elo"
12
13
  JCB = "JCB"
13
14
  Laser = "Laser"
14
15
  UK_Maestro = "UK Maestro"
@@ -328,6 +328,7 @@ module Braintree
328
328
  FinalAuthSubmitForSettlementForDifferentAmount = "95601"
329
329
  HasAlreadyBeenRefunded = "91512"
330
330
  IdealPaymentNotComplete = "815141"
331
+ PaymentInstrumentWithExternalVaultIsInvalid = "915176"
331
332
  TooManyLineItems = "915157"
332
333
  LineItemsExpected = "915158"
333
334
  DiscountAmountFormatIsInvalid = "915159"
@@ -439,6 +440,13 @@ module Braintree
439
440
  LodgingCheckOutDateIsInvalid = "93413"
440
441
  end
441
442
  end
443
+
444
+ module ExternalVault
445
+ StatusIsInvalid = "915175"
446
+ CardTypeIsInvalid = "915178"
447
+ StatusWithPreviousNetworkTransactionIdIsInvalid = "915177"
448
+ PreviousNetworkTransactionIdIsInvalid = "915179"
449
+ end
442
450
  end
443
451
 
444
452
  module TransactionLineItem
@@ -0,0 +1,28 @@
1
+ module Braintree
2
+ class GraphQLClient < Http # :nodoc:
3
+
4
+ def initialize(config)
5
+ @config = config
6
+ @graphql_headers = {
7
+ 'Accept' => 'application/json',
8
+ 'Braintree-Version' => @config.graphql_api_version,
9
+ 'Content-Type' => 'application/json'
10
+ }
11
+ end
12
+
13
+ def query(definition, variables = {}, operationName = nil)
14
+ graphql_connection = _setup_connection(@config.graphql_server, @config.graphql_port)
15
+
16
+ request = {}
17
+ request['query'] = definition
18
+ request['operationName'] = operationName if operationName
19
+ request['variables'] = variables
20
+
21
+ response = _http_do Net::HTTP::Post, @config.graphql_base_url, request.to_json, nil, graphql_connection, @graphql_headers
22
+ data = JSON.parse(response.body, :symbolize_names => true)
23
+ Util.raise_exception_for_graphql_error(data)
24
+
25
+ data
26
+ end
27
+ end
28
+ end
@@ -67,19 +67,34 @@ module Braintree
67
67
  end
68
68
  end
69
69
 
70
- def _http_do(http_verb, path, body = nil, file = nil)
70
+ def _setup_connection(server = @config.server, port = @config.port)
71
71
  if @config.proxy_address
72
72
  connection = Net::HTTP.new(
73
- @config.server,
74
- @config.port,
73
+ server,
74
+ port,
75
75
  @config.proxy_address,
76
76
  @config.proxy_port,
77
77
  @config.proxy_user,
78
78
  @config.proxy_pass
79
79
  )
80
80
  else
81
- connection = Net::HTTP.new(@config.server, @config.port)
81
+ connection = Net::HTTP.new(server, port)
82
82
  end
83
+ end
84
+
85
+ def _compose_headers(header_overrides = {})
86
+ headers = {}
87
+ headers["Accept"] = "application/xml"
88
+ headers["User-Agent"] = @config.user_agent
89
+ headers["Accept-Encoding"] = "gzip"
90
+ headers["X-ApiVersion"] = @config.api_version
91
+ headers["Content-Type"] = "application/xml"
92
+
93
+ headers.merge(header_overrides)
94
+ end
95
+
96
+ def _http_do(http_verb, path, body = nil, file = nil, connection = nil, header_overrides = {})
97
+ connection ||= _setup_connection
83
98
 
84
99
  connection.open_timeout = @config.http_open_timeout
85
100
  connection.read_timeout = @config.http_read_timeout
@@ -90,12 +105,10 @@ module Braintree
90
105
  connection.ca_file = @config.ca_file
91
106
  connection.verify_callback = proc { |preverify_ok, ssl_context| _verify_ssl_certificate(preverify_ok, ssl_context) }
92
107
  end
108
+
93
109
  connection.start do |http|
94
110
  request = http_verb.new(path)
95
- request["Accept"] = "application/xml"
96
- request["User-Agent"] = @config.user_agent
97
- request["Accept-Encoding"] = "gzip"
98
- request["X-ApiVersion"] = @config.api_version
111
+ _compose_headers(header_overrides).each { |header, value| request[header] = value }
99
112
  if @config.client_credentials?
100
113
  request.basic_auth @config.client_id, @config.client_secret
101
114
  elsif @config.access_token
@@ -117,7 +130,6 @@ module Braintree
117
130
  request.body = form_params.collect {|p| "--" + boundary + "#{LINE_FEED}" + p}.join("") + "--" + boundary + "--"
118
131
  @config.logger.debug _format_and_sanitize_body_for_log(_build_xml(body))
119
132
  else
120
- request["Content-Type"] = "application/xml"
121
133
  request.body = body
122
134
  @config.logger.debug _format_and_sanitize_body_for_log(body)
123
135
  end
@@ -0,0 +1,20 @@
1
+ module Braintree
2
+ class LocalPaymentCompleted
3
+ include BaseModule
4
+
5
+ attr_reader :payment_id
6
+ attr_reader :payer_id
7
+
8
+ def initialize(attributes) # :nodoc:
9
+ set_instance_variables_from_hash(attributes)
10
+ end
11
+
12
+ class << self
13
+ protected :new
14
+ end
15
+
16
+ def self._new(*args) # :nodoc:
17
+ self.new *args
18
+ end
19
+ end
20
+ end
@@ -21,6 +21,7 @@ module Braintree
21
21
  CarteBlanches = %w[30569309025904] # :nodoc:
22
22
  DinersClubs = %w[38520000023237] # :nodoc:
23
23
 
24
+ Discover = "6011111111111117"
24
25
  Discovers = %w[6011111111111117 6011000990139424]
25
26
  JCBs = %w[3530111333300000 3566002020360505] # :nodoc:
26
27
 
@@ -30,6 +31,7 @@ module Braintree
30
31
 
31
32
  MasterCards = %w[5105105105105100 5555555555554444]
32
33
 
34
+ Elo = "5066991111111118"
33
35
  Visa = "4012888888881881"
34
36
  VisaInternational = "4009348888881881" # :nodoc:
35
37
  VisaPrepaid = "4500600000000061"
@@ -69,6 +69,13 @@ module Braintree
69
69
  All = constants.map { |c| const_get(c) }
70
70
  end
71
71
 
72
+ module ExternalVault
73
+ module Status
74
+ WillVault = "will_vault"
75
+ Vaulted = "vaulted"
76
+ end
77
+ end
78
+
72
79
  attr_reader :add_ons
73
80
  attr_reader :additional_processor_response # The raw response from the processor.
74
81
  attr_reader :amex_express_checkout_details
@@ -102,6 +109,7 @@ module Braintree
102
109
  attr_reader :ideal_payment_details
103
110
  attr_reader :masterpass_card_details
104
111
  attr_reader :merchant_account_id
112
+ attr_reader :network_transaction_id
105
113
  attr_reader :order_id
106
114
  attr_reader :partial_settlement_transaction_ids
107
115
  attr_reader :payment_instrument_type
@@ -226,6 +226,10 @@ module Braintree
226
226
  {:venmo => [:profile_id]}
227
227
  ]
228
228
  },
229
+ {:external_vault => [
230
+ :status,
231
+ :previous_network_transaction_id,
232
+ ]},
229
233
  {:custom_fields => :_any_key_},
230
234
  {:descriptor => [:name, :phone, :url]},
231
235
  {:paypal_account => [:email, :token, :paypal_data, :payee_id, :payee_email]},
@@ -66,6 +66,37 @@ module Braintree
66
66
  end
67
67
  end
68
68
 
69
+ def self.raise_exception_for_graphql_error(response)
70
+ return if !response[:errors]
71
+
72
+ for error in response[:errors]
73
+ if error[:extensions] && error[:extensions][:errorClass]
74
+ case error[:extensions][:errorClass]
75
+ when "VALIDATION"
76
+ next # skip raising an error if it is a validation error
77
+ when "AUTHENTICATION"
78
+ raise AuthenticationError
79
+ when "AUTHORIZATION"
80
+ raise AuthorizationError, error[:message]
81
+ when "NOT_FOUND"
82
+ raise NotFoundError
83
+ when "UNSUPPORTED_CLIENT"
84
+ raise UpgradeRequiredError, "Please upgrade your client library."
85
+ when "RESOURCE_LIMIT"
86
+ raise TooManyRequestsError
87
+ when "INTERNAL"
88
+ raise ServerError
89
+ when "SERVICE_AVAILABILITY"
90
+ raise DownForMaintenanceError
91
+ else
92
+ raise UnexpectedError, "Unexpected Response: #{error[:message]}"
93
+ end
94
+ else
95
+ raise UnexpectedError, "Unexpected Response: #{error[:message]}"
96
+ end
97
+ end
98
+ end
99
+
69
100
  def self.to_big_decimal(decimal)
70
101
  case decimal
71
102
  when BigDecimal, NilClass
@@ -82,15 +113,19 @@ module Braintree
82
113
  end
83
114
 
84
115
  def self.verify_keys(valid_keys, hash)
85
- flattened_valid_keys = _flatten_valid_keys(valid_keys)
86
- invalid_keys = _flatten_hash_keys(hash) - flattened_valid_keys
87
- invalid_keys = _remove_wildcard_keys(flattened_valid_keys, invalid_keys)
116
+ invalid_keys = _get_invalid_keys(valid_keys, hash)
88
117
  if invalid_keys.any?
89
118
  sorted = invalid_keys.sort_by { |k| k.to_s }.join(", ")
90
119
  raise ArgumentError, "invalid keys: #{sorted}"
91
120
  end
92
121
  end
93
122
 
123
+ def self.keys_valid?(valid_keys, hash)
124
+ invalid_keys = _get_invalid_keys(valid_keys, hash)
125
+
126
+ !invalid_keys.any?
127
+ end
128
+
94
129
  def self._flatten_valid_keys(valid_keys, namespace = nil)
95
130
  valid_keys.inject([]) do |result, key|
96
131
  if key.is_a?(Hash)
@@ -138,6 +173,12 @@ module Braintree
138
173
  end
139
174
  end
140
175
 
176
+ def self._get_invalid_keys(valid_keys, hash)
177
+ flattened_valid_keys = _flatten_valid_keys(valid_keys)
178
+ keys = _flatten_hash_keys(hash) - flattened_valid_keys
179
+ keys = _remove_wildcard_keys(flattened_valid_keys, keys)
180
+ end
181
+
141
182
  module IdEquality
142
183
  def ==(other) # :nodoc:
143
184
  return false unless other.is_a?(self.class)
@@ -6,8 +6,16 @@ module Braintree
6
6
  attr_reader :code
7
7
  attr_reader :message
8
8
 
9
- def initialize(attributes)
10
- set_instance_variables_from_hash attributes
9
+ def initialize(error_hash)
10
+ # parse GraphQL response objects
11
+ if (error_hash[:extensions] &&
12
+ error_hash[:extensions][:errorClass] &&
13
+ error_hash[:extensions][:errorClass] == "VALIDATION")
14
+ error_hash[:code] = error_hash[:extensions][:legacyCode].to_i
15
+ error_hash[:attribute] = error_hash[:path].last
16
+ end
17
+
18
+ set_instance_variables_from_hash error_hash
11
19
  end
12
20
 
13
21
  def inspect # :nodoc:
@@ -3,7 +3,8 @@ module Braintree
3
3
  include Enumerable
4
4
 
5
5
  def initialize(data) # :nodoc:
6
- @errors = data[:errors].map { |hash| Braintree::ValidationError.new(hash) }
6
+ return if !data.is_a? Hash
7
+ @errors = (data[:errors] || {}).map { |hash| Braintree::ValidationError.new(hash) }
7
8
  @nested = {}
8
9
  data.keys.each do |key|
9
10
  next if key == :errors
@@ -1,7 +1,7 @@
1
1
  module Braintree
2
2
  module Version
3
3
  Major = 2
4
- Minor = 90
4
+ Minor = 91
5
5
  Tiny = 0
6
6
 
7
7
  String = "#{Major}.#{Minor}.#{Tiny}"
@@ -41,6 +41,8 @@ module Braintree
41
41
  ConnectedMerchantPayPalStatusChanged = "connected_merchant_paypal_status_changed"
42
42
 
43
43
  GrantedPaymentInstrumentUpdate = "granted_payment_instrument_update"
44
+
45
+ LocalPaymentCompleted = "local_payment_completed"
44
46
  end
45
47
 
46
48
  attr_reader :account_updater_daily_report
@@ -51,8 +53,9 @@ module Braintree
51
53
  attr_reader :granted_payment_instrument_update
52
54
  attr_reader :ideal_payment
53
55
  attr_reader :kind
54
- attr_reader :partner_merchant
56
+ attr_reader :local_payment_completed
55
57
  attr_reader :oauth_access_revocation
58
+ attr_reader :partner_merchant
56
59
  attr_reader :source_merchant_id
57
60
  attr_reader :subscription
58
61
  attr_reader :timestamp
@@ -82,6 +85,7 @@ module Braintree
82
85
  @connected_merchant_status_transitioned = ConnectedMerchantStatusTransitioned._new(@subject[:connected_merchant_status_transitioned]) if @subject.has_key?(:connected_merchant_status_transitioned)
83
86
  @connected_merchant_paypal_status_changed = ConnectedMerchantPayPalStatusChanged._new(@subject[:connected_merchant_paypal_status_changed]) if @subject.has_key?(:connected_merchant_paypal_status_changed)
84
87
  @granted_payment_instrument_update = GrantedPaymentInstrumentUpdate._new(@subject[:granted_payment_instrument_update]) if @subject.has_key?(:granted_payment_instrument_update)
88
+ @local_payment_completed = LocalPaymentCompleted._new(@subject[:local_payment]) if @subject.has_key?(:local_payment)
85
89
  end
86
90
 
87
91
  def merchant_account
@@ -64,6 +64,8 @@ module Braintree
64
64
  _disbursement_sample_xml(id)
65
65
  when Braintree::WebhookNotification::Kind::SubscriptionChargedSuccessfully
66
66
  _subscription_charged_successfully(id)
67
+ when Braintree::WebhookNotification::Kind::SubscriptionChargedUnsuccessfully
68
+ _subscription_charged_unsuccessfully(id)
67
69
  when Braintree::WebhookNotification::Kind::AccountUpdaterDailyReport
68
70
  _account_updater_daily_report_sample_xml(id)
69
71
  when Braintree::WebhookNotification::Kind::ConnectedMerchantStatusTransitioned
@@ -76,6 +78,8 @@ module Braintree
76
78
  _ideal_payment_failed_sample_xml(id)
77
79
  when Braintree::WebhookNotification::Kind::GrantedPaymentInstrumentUpdate
78
80
  _granted_payment_instrument_update_sample_xml(id)
81
+ when Braintree::WebhookNotification::Kind::LocalPaymentCompleted
82
+ _local_payment_completed_sample_xml(id)
79
83
  else
80
84
  _subscription_sample_xml(id)
81
85
  end
@@ -95,6 +99,7 @@ module Braintree
95
99
  <id>#{id}</id>
96
100
  <transactions type="array">
97
101
  <transaction>
102
+ <id>#{id}</id>
98
103
  <status>submitted_for_settlement</status>
99
104
  <amount>49.99</amount>
100
105
  </transaction>
@@ -107,6 +112,26 @@ module Braintree
107
112
  XML
108
113
  end
109
114
 
115
+ def _subscription_charged_unsuccessfully(id)
116
+
117
+ <<-XML
118
+ <subscription>
119
+ <id>#{id}</id>
120
+ <transactions type="array">
121
+ <transaction>
122
+ <id>#{id}</id>
123
+ <status>failed</status>
124
+ <amount>49.99</amount>
125
+ </transaction>
126
+ </transactions>
127
+ <add_ons type="array">
128
+ </add_ons>
129
+ <discounts type="array">
130
+ </discounts>
131
+ </subscription>
132
+ XML
133
+ end
134
+
110
135
  def _subscription_sample_xml(id)
111
136
 
112
137
  <<-XML
@@ -589,7 +614,6 @@ module Braintree
589
614
  end
590
615
 
591
616
  def _ideal_payment_complete_sample_xml(id)
592
-
593
617
  <<-XML
594
618
  <ideal-payment>
595
619
  <id>#{id}</id>
@@ -606,7 +630,6 @@ module Braintree
606
630
  end
607
631
 
608
632
  def _ideal_payment_failed_sample_xml(id)
609
-
610
633
  <<-XML
611
634
  <ideal-payment>
612
635
  <id>#{id}</id>
@@ -623,7 +646,6 @@ module Braintree
623
646
  end
624
647
 
625
648
  def _granted_payment_instrument_update_sample_xml(id)
626
-
627
649
  <<-XML
628
650
  <granted-payment-instrument-update>
629
651
  <grant-owner-merchant-id>vczo7jqrpwrsi2px</grant-owner-merchant-id>
@@ -641,5 +663,14 @@ module Braintree
641
663
  </granted-payment-instrument-update>
642
664
  XML
643
665
  end
666
+
667
+ def _local_payment_completed_sample_xml(id)
668
+ <<-XML
669
+ <local-payment>
670
+ <payment-id>PAY-XYZ123</payment-id>
671
+ <payer-id>ABCPAYER</payer-id>
672
+ </local-payment>
673
+ XML
674
+ end
644
675
  end
645
676
  end
data/lib/braintree.rb CHANGED
@@ -21,6 +21,7 @@ require "braintree/base_module"
21
21
  require "braintree/modification"
22
22
 
23
23
  require "braintree/util"
24
+ require "braintree/http"
24
25
 
25
26
  require "braintree/account_updater_daily_report"
26
27
  require "braintree/ach_mandate"
@@ -70,10 +71,11 @@ require "braintree/error_codes"
70
71
  require "braintree/error_result"
71
72
  require "braintree/errors"
72
73
  require "braintree/gateway"
73
- require "braintree/http"
74
+ require "braintree/graphql_client"
74
75
  require "braintree/ideal_payment"
75
76
  require "braintree/ideal_payment_gateway"
76
77
  require "braintree/transaction/ideal_payment_details"
78
+ require "braintree/local_payment_completed"
77
79
  require "braintree/merchant"
78
80
  require "braintree/merchant_gateway"
79
81
  require "braintree/merchant_account"
@@ -535,6 +535,20 @@ describe Braintree::CreditCard do
535
535
  credit_card.expiration_date.should == "11/2099"
536
536
  end
537
537
  end
538
+
539
+ context "card_type" do
540
+ it "is set to Elo" do
541
+ customer = Braintree::Customer.create!
542
+ result = Braintree::CreditCard.create(
543
+ :customer_id => customer.id,
544
+ :number => Braintree::Test::CreditCardNumbers::Elo,
545
+ :expiration_date => "10/2020",
546
+ )
547
+ result.success?.should == true
548
+ credit_card = result.credit_card
549
+ credit_card.card_type.should == Braintree::CreditCard::CardType::Elo
550
+ end
551
+ end
538
552
  end
539
553
 
540
554
  describe "self.create!" do
@@ -34,6 +34,9 @@ describe Braintree::Dispute do
34
34
 
35
35
  refreshed_dispute = Braintree::Dispute.find(dispute.id)
36
36
  refreshed_dispute.status.should == Braintree::Dispute::Status::Accepted
37
+
38
+ dispute_from_transaction = Braintree::Transaction.find(dispute.transaction.id).disputes[0]
39
+ dispute_from_transaction.status.should == Braintree::Dispute::Status::Accepted
37
40
  end
38
41
 
39
42
  it "returns an error response if the dispute is not in open status" do
@@ -0,0 +1,74 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
2
+
3
+ describe Braintree::GraphQLClient do
4
+ before :each do
5
+ @config = Braintree::Configuration.instantiate
6
+ end
7
+
8
+ describe "initialize" do
9
+ it "assigns overriding graphql headers" do
10
+ expect(@config.graphql_client.instance_variable_get("@graphql_headers")).to be_kind_of(Hash)
11
+ end
12
+ end
13
+
14
+ describe "query" do
15
+ it "makes valid GraphQL queries when given a definition" do
16
+ definition = <<-GRAPHQL
17
+ query {
18
+ ping
19
+ }
20
+ GRAPHQL
21
+
22
+ response = Braintree::GraphQLClient.new(@config).query(definition)
23
+
24
+ expect(response[:data]).to eq({:ping=>"pong"})
25
+ end
26
+
27
+ it "makes valid GraphQL requests when given a definitiona and variable" do
28
+ definition = <<-GRAPHQL
29
+ mutation CreateClientToken($input: CreateClientTokenInput!) {
30
+ createClientToken(input: $input) {
31
+ clientMutationId
32
+ clientToken
33
+ }
34
+ }
35
+ GRAPHQL
36
+
37
+ variables = {
38
+ input: {
39
+ clientMutationId: "abc123",
40
+ clientToken: {
41
+ merchantAccountId: "ABC123"
42
+ }
43
+ }
44
+ }
45
+
46
+ response = Braintree::GraphQLClient.new(@config).query(definition, variables)
47
+
48
+ expect(response[:data][:createClientToken][:clientToken]).to be_a(String)
49
+ end
50
+
51
+ it "returns results parsable into validation errors" do
52
+ definition = <<-GRAPHQL
53
+ query TransactionLevelFeeReport($date: Date!, $merchantAccountId: ID) {
54
+ report {
55
+ transactionLevelFees(date: $date, merchantAccountId: $merchantAccountId) {
56
+ url
57
+ }
58
+ }
59
+ }
60
+ GRAPHQL
61
+
62
+ variables = {
63
+ date: "2018-01-01",
64
+ merchantAccountId: "some_merchant"
65
+ }
66
+
67
+ response = Braintree::GraphQLClient.new(@config).query(definition, variables)
68
+ errors = Braintree::ValidationErrorCollection.new(response)
69
+
70
+ expect(errors.size).to eq(1)
71
+ expect(errors.first.message).to eq("Invalid merchant account id: some_merchant")
72
+ end
73
+ end
74
+ end