Skip to content

Commit

Permalink
2.91.0
Browse files Browse the repository at this point in the history
  • Loading branch information
braintreeps committed Oct 10, 2018
1 parent 26077d8 commit a2e505a
Show file tree
Hide file tree
Showing 31 changed files with 1,163 additions and 157 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
# 2.91.0
* Add local payment webhook support
* Add subscription charged unsuccessfully sample webhook to webhook testing gateway
* Fix issue where environment was not automatically converted from a string to a symbol (#148, #164)
* Fix issue where logger could not be set on gateway instance
* Fix dispute results in transactions not showing the correct status sometimes
* Whitelist usage of `external_vault` in transaction create.
* Visa transactions will now contain a `network_transaction_id` in the response

# 2.90.0
* Add `processor_response_code` and `processor_response_text` to authorization adjustments subfield in transaction response.
* Stop restricting `transaction_source` values for Disputes search, allow searching by OAuth Application client_id
Expand Down
4 changes: 3 additions & 1 deletion lib/braintree.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
require "braintree/modification"

require "braintree/util"
require "braintree/http"

require "braintree/account_updater_daily_report"
require "braintree/ach_mandate"
Expand Down Expand Up @@ -70,10 +71,11 @@
require "braintree/error_result"
require "braintree/errors"
require "braintree/gateway"
require "braintree/http"
require "braintree/graphql_client"
require "braintree/ideal_payment"
require "braintree/ideal_payment_gateway"
require "braintree/transaction/ideal_payment_details"
require "braintree/local_payment_completed"
require "braintree/merchant"
require "braintree/merchant_gateway"
require "braintree/merchant_account"
Expand Down
40 changes: 39 additions & 1 deletion lib/braintree/configuration.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
module Braintree
class Configuration
API_VERSION = "4" # :nodoc:
API_VERSION = "5" # :nodoc:
DEFAULT_ENDPOINT = "api" # :nodoc:
GRAPHQL_API_VERSION = "2018-09-10" # :nodoc:

READABLE_ATTRIBUTES = [
:merchant_id,
Expand Down Expand Up @@ -44,6 +45,7 @@ class << self
end
attr_reader *READABLE_ATTRIBUTES
attr_reader *NON_REQUIRED_READABLE_ATTRIBUTES
attr_writer *WRITABLE_ATTRIBUTES

def self.expectant_reader(*attributes) # :nodoc:
attributes.each do |attribute|
Expand Down Expand Up @@ -113,6 +115,8 @@ def initialize(options = {})
instance_variable_set "@#{attr}", options[attr]
end

@environment = @environment.to_sym if @environment

_check_for_mixed_credentials(options)

parser = Braintree::CredentialsParser.new
Expand Down Expand Up @@ -158,6 +162,10 @@ def api_version # :nodoc:
API_VERSION
end

def graphql_api_version # :nodoc:
GRAPHQL_API_VERSION
end

def base_merchant_path # :nodoc:
"/merchants/#{merchant_id}"
end
Expand All @@ -166,6 +174,10 @@ def base_url
"#{protocol}://#{server}:#{port}"
end

def graphql_base_url
"#{protocol}://#{graphql_server}:#{graphql_port}/graphql"
end

def base_merchant_url # :nodoc:
"#{base_url}#{base_merchant_path}"
end
Expand All @@ -182,6 +194,10 @@ def http # :nodoc:
Http.new(self)
end

def graphql_client
GraphQLClient.new(self)
end

def logger
@logger ||= self.class._default_logger
end
Expand All @@ -195,6 +211,15 @@ def port # :nodoc:
end
end

def graphql_port # :nodoc:
case @environment
when :development, :integration
ENV['GRAPHQL_PORT'] || 8080
when :production, :qa, :sandbox
443
end
end

def protocol # :nodoc:
ssl? ? "https" : "http"
end
Expand All @@ -220,6 +245,19 @@ def server # :nodoc:
end
end

def graphql_server # :nodoc:
case @environment
when :development, :integration
ENV['GRAPHQL_HOST'] || "graphql.bt.local"
when :production
"payments.braintree-api.com"
when :qa
"payments-qa.dev.braintree-api.com"
when :sandbox
"payments.sandbox.braintree-api.com"
end
end

def auth_url
case @environment
when :development, :integration
Expand Down
1 change: 1 addition & 0 deletions lib/braintree/credit_card.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ module CardType
ChinaUnionPay = "China UnionPay"
DinersClubInternational = "Diners Club"
Discover = "Discover"
Elo = "Elo"
JCB = "JCB"
Laser = "Laser"
UK_Maestro = "UK Maestro"
Expand Down
8 changes: 8 additions & 0 deletions lib/braintree/error_codes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ module Transaction
FinalAuthSubmitForSettlementForDifferentAmount = "95601"
HasAlreadyBeenRefunded = "91512"
IdealPaymentNotComplete = "815141"
PaymentInstrumentWithExternalVaultIsInvalid = "915176"
TooManyLineItems = "915157"
LineItemsExpected = "915158"
DiscountAmountFormatIsInvalid = "915159"
Expand Down Expand Up @@ -439,6 +440,13 @@ module TravelCruise
LodgingCheckOutDateIsInvalid = "93413"
end
end

module ExternalVault
StatusIsInvalid = "915175"
CardTypeIsInvalid = "915178"
StatusWithPreviousNetworkTransactionIdIsInvalid = "915177"
PreviousNetworkTransactionIdIsInvalid = "915179"
end
end

module TransactionLineItem
Expand Down
28 changes: 28 additions & 0 deletions lib/braintree/graphql_client.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
module Braintree
class GraphQLClient < Http # :nodoc:

def initialize(config)
@config = config
@graphql_headers = {
'Accept' => 'application/json',
'Braintree-Version' => @config.graphql_api_version,
'Content-Type' => 'application/json'
}
end

def query(definition, variables = {}, operationName = nil)
graphql_connection = _setup_connection(@config.graphql_server, @config.graphql_port)

request = {}
request['query'] = definition
request['operationName'] = operationName if operationName
request['variables'] = variables

response = _http_do Net::HTTP::Post, @config.graphql_base_url, request.to_json, nil, graphql_connection, @graphql_headers
data = JSON.parse(response.body, :symbolize_names => true)
Util.raise_exception_for_graphql_error(data)

data
end
end
end
30 changes: 21 additions & 9 deletions lib/braintree/http.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,19 +67,34 @@ def _build_query_string(params)
end
end

def _http_do(http_verb, path, body = nil, file = nil)
def _setup_connection(server = @config.server, port = @config.port)
if @config.proxy_address
connection = Net::HTTP.new(
@config.server,
@config.port,
server,
port,
@config.proxy_address,
@config.proxy_port,
@config.proxy_user,
@config.proxy_pass
)
else
connection = Net::HTTP.new(@config.server, @config.port)
connection = Net::HTTP.new(server, port)
end
end

def _compose_headers(header_overrides = {})
headers = {}
headers["Accept"] = "application/xml"
headers["User-Agent"] = @config.user_agent
headers["Accept-Encoding"] = "gzip"
headers["X-ApiVersion"] = @config.api_version
headers["Content-Type"] = "application/xml"

headers.merge(header_overrides)
end

def _http_do(http_verb, path, body = nil, file = nil, connection = nil, header_overrides = {})
connection ||= _setup_connection

connection.open_timeout = @config.http_open_timeout
connection.read_timeout = @config.http_read_timeout
Expand All @@ -90,12 +105,10 @@ def _http_do(http_verb, path, body = nil, file = nil)
connection.ca_file = @config.ca_file
connection.verify_callback = proc { |preverify_ok, ssl_context| _verify_ssl_certificate(preverify_ok, ssl_context) }
end

connection.start do |http|
request = http_verb.new(path)
request["Accept"] = "application/xml"
request["User-Agent"] = @config.user_agent
request["Accept-Encoding"] = "gzip"
request["X-ApiVersion"] = @config.api_version
_compose_headers(header_overrides).each { |header, value| request[header] = value }
if @config.client_credentials?
request.basic_auth @config.client_id, @config.client_secret
elsif @config.access_token
Expand All @@ -117,7 +130,6 @@ def _http_do(http_verb, path, body = nil, file = nil)
request.body = form_params.collect {|p| "--" + boundary + "#{LINE_FEED}" + p}.join("") + "--" + boundary + "--"
@config.logger.debug _format_and_sanitize_body_for_log(_build_xml(body))
else
request["Content-Type"] = "application/xml"
request.body = body
@config.logger.debug _format_and_sanitize_body_for_log(body)
end
Expand Down
20 changes: 20 additions & 0 deletions lib/braintree/local_payment_completed.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module Braintree
class LocalPaymentCompleted
include BaseModule

attr_reader :payment_id
attr_reader :payer_id

def initialize(attributes) # :nodoc:
set_instance_variables_from_hash(attributes)
end

class << self
protected :new
end

def self._new(*args) # :nodoc:
self.new *args
end
end
end
2 changes: 2 additions & 0 deletions lib/braintree/test/credit_card.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ module CardTypeIndicators
CarteBlanches = %w[30569309025904] # :nodoc:
DinersClubs = %w[38520000023237] # :nodoc:

Discover = "6011111111111117"
Discovers = %w[6011111111111117 6011000990139424]
JCBs = %w[3530111333300000 3566002020360505] # :nodoc:

Expand All @@ -30,6 +31,7 @@ module CardTypeIndicators

MasterCards = %w[5105105105105100 5555555555554444]

Elo = "5066991111111118"
Visa = "4012888888881881"
VisaInternational = "4009348888881881" # :nodoc:
VisaPrepaid = "4500600000000061"
Expand Down
8 changes: 8 additions & 0 deletions lib/braintree/transaction.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,13 @@ module Type # :nodoc:
All = constants.map { |c| const_get(c) }
end

module ExternalVault
module Status
WillVault = "will_vault"
Vaulted = "vaulted"
end
end

attr_reader :add_ons
attr_reader :additional_processor_response # The raw response from the processor.
attr_reader :amex_express_checkout_details
Expand Down Expand Up @@ -102,6 +109,7 @@ module Type # :nodoc:
attr_reader :ideal_payment_details
attr_reader :masterpass_card_details
attr_reader :merchant_account_id
attr_reader :network_transaction_id
attr_reader :order_id
attr_reader :partial_settlement_transaction_ids
attr_reader :payment_instrument_type
Expand Down
4 changes: 4 additions & 0 deletions lib/braintree/transaction_gateway.rb
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,10 @@ def self._create_signature # :nodoc:
{:venmo => [:profile_id]}
]
},
{:external_vault => [
:status,
:previous_network_transaction_id,
]},
{:custom_fields => :_any_key_},
{:descriptor => [:name, :phone, :url]},
{:paypal_account => [:email, :token, :paypal_data, :payee_id, :payee_email]},
Expand Down
47 changes: 44 additions & 3 deletions lib/braintree/util.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,37 @@ def self.raise_exception_for_status_code(status_code, message=nil)
end
end

def self.raise_exception_for_graphql_error(response)
return if !response[:errors]

for error in response[:errors]
if error[:extensions] && error[:extensions][:errorClass]
case error[:extensions][:errorClass]
when "VALIDATION"
next # skip raising an error if it is a validation error
when "AUTHENTICATION"
raise AuthenticationError
when "AUTHORIZATION"
raise AuthorizationError, error[:message]
when "NOT_FOUND"
raise NotFoundError
when "UNSUPPORTED_CLIENT"
raise UpgradeRequiredError, "Please upgrade your client library."
when "RESOURCE_LIMIT"
raise TooManyRequestsError
when "INTERNAL"
raise ServerError
when "SERVICE_AVAILABILITY"
raise DownForMaintenanceError
else
raise UnexpectedError, "Unexpected Response: #{error[:message]}"
end
else
raise UnexpectedError, "Unexpected Response: #{error[:message]}"
end
end
end

def self.to_big_decimal(decimal)
case decimal
when BigDecimal, NilClass
Expand All @@ -82,15 +113,19 @@ def self.inspect_amount(amount)
end

def self.verify_keys(valid_keys, hash)
flattened_valid_keys = _flatten_valid_keys(valid_keys)
invalid_keys = _flatten_hash_keys(hash) - flattened_valid_keys
invalid_keys = _remove_wildcard_keys(flattened_valid_keys, invalid_keys)
invalid_keys = _get_invalid_keys(valid_keys, hash)
if invalid_keys.any?
sorted = invalid_keys.sort_by { |k| k.to_s }.join(", ")
raise ArgumentError, "invalid keys: #{sorted}"
end
end

def self.keys_valid?(valid_keys, hash)
invalid_keys = _get_invalid_keys(valid_keys, hash)

!invalid_keys.any?
end

def self._flatten_valid_keys(valid_keys, namespace = nil)
valid_keys.inject([]) do |result, key|
if key.is_a?(Hash)
Expand Down Expand Up @@ -138,6 +173,12 @@ def self._remove_wildcard_keys(valid_keys, invalid_keys)
end
end

def self._get_invalid_keys(valid_keys, hash)
flattened_valid_keys = _flatten_valid_keys(valid_keys)
keys = _flatten_hash_keys(hash) - flattened_valid_keys
keys = _remove_wildcard_keys(flattened_valid_keys, keys)
end

module IdEquality
def ==(other) # :nodoc:
return false unless other.is_a?(self.class)
Expand Down
Loading

0 comments on commit a2e505a

Please sign in to comment.