braintree 2.90.0 → 2.91.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 (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