minfraud 1.0.3 → 1.4.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 (76) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/rubocop.yml +12 -0
  3. data/.github/workflows/test.yml +32 -0
  4. data/.gitignore +2 -0
  5. data/.rubocop.yml +108 -0
  6. data/CHANGELOG.md +70 -2
  7. data/CODE_OF_CONDUCT.md +4 -4
  8. data/Gemfile +11 -2
  9. data/LICENSE.txt +2 -1
  10. data/README.dev.md +4 -0
  11. data/README.md +245 -59
  12. data/Rakefile +9 -3
  13. data/bin/console +4 -3
  14. data/lib/maxmind/geoip2/model/city.rb +99 -0
  15. data/lib/maxmind/geoip2/model/country.rb +94 -0
  16. data/lib/maxmind/geoip2/model/insights.rb +38 -0
  17. data/lib/maxmind/geoip2/record/abstract.rb +46 -0
  18. data/lib/maxmind/geoip2/record/city.rb +62 -0
  19. data/lib/maxmind/geoip2/record/continent.rb +61 -0
  20. data/lib/maxmind/geoip2/record/country.rb +78 -0
  21. data/lib/maxmind/geoip2/record/location.rb +97 -0
  22. data/lib/maxmind/geoip2/record/maxmind.rb +41 -0
  23. data/lib/maxmind/geoip2/record/place.rb +52 -0
  24. data/lib/maxmind/geoip2/record/postal.rb +54 -0
  25. data/lib/maxmind/geoip2/record/represented_country.rb +47 -0
  26. data/lib/maxmind/geoip2/record/subdivision.rb +72 -0
  27. data/lib/maxmind/geoip2/record/traits.rb +233 -0
  28. data/lib/minfraud.rb +48 -8
  29. data/lib/minfraud/assessments.rb +118 -49
  30. data/lib/minfraud/components/account.rb +31 -9
  31. data/lib/minfraud/components/addressable.rb +73 -26
  32. data/lib/minfraud/components/base.rb +35 -11
  33. data/lib/minfraud/components/billing.rb +5 -0
  34. data/lib/minfraud/components/credit_card.rb +64 -20
  35. data/lib/minfraud/components/custom_inputs.rb +25 -0
  36. data/lib/minfraud/components/device.rb +51 -10
  37. data/lib/minfraud/components/email.rb +29 -7
  38. data/lib/minfraud/components/event.rb +60 -13
  39. data/lib/minfraud/components/order.rb +60 -22
  40. data/lib/minfraud/components/payment.rb +166 -21
  41. data/lib/minfraud/components/report/transaction.rb +80 -0
  42. data/lib/minfraud/components/shipping.rb +14 -5
  43. data/lib/minfraud/components/shopping_cart.rb +19 -12
  44. data/lib/minfraud/components/shopping_cart_item.rb +42 -13
  45. data/lib/minfraud/enum.rb +22 -8
  46. data/lib/minfraud/error_handler.rb +45 -18
  47. data/lib/minfraud/errors.rb +22 -2
  48. data/lib/minfraud/http_service.rb +22 -8
  49. data/lib/minfraud/http_service/request.rb +19 -18
  50. data/lib/minfraud/http_service/response.rb +49 -12
  51. data/lib/minfraud/model/abstract.rb +20 -0
  52. data/lib/minfraud/model/address.rb +52 -0
  53. data/lib/minfraud/model/billing_address.rb +11 -0
  54. data/lib/minfraud/model/credit_card.rb +75 -0
  55. data/lib/minfraud/model/device.rb +54 -0
  56. data/lib/minfraud/model/disposition.rb +35 -0
  57. data/lib/minfraud/model/email.rb +54 -0
  58. data/lib/minfraud/model/email_domain.rb +24 -0
  59. data/lib/minfraud/model/error.rb +28 -0
  60. data/lib/minfraud/model/factors.rb +24 -0
  61. data/lib/minfraud/model/geoip2_location.rb +25 -0
  62. data/lib/minfraud/model/insights.rb +68 -0
  63. data/lib/minfraud/model/ip_address.rb +82 -0
  64. data/lib/minfraud/model/issuer.rb +49 -0
  65. data/lib/minfraud/model/score.rb +76 -0
  66. data/lib/minfraud/model/score_ip_address.rb +23 -0
  67. data/lib/minfraud/model/shipping_address.rb +30 -0
  68. data/lib/minfraud/model/subscores.rb +178 -0
  69. data/lib/minfraud/model/warning.rb +63 -0
  70. data/lib/minfraud/report.rb +58 -0
  71. data/lib/minfraud/resolver.rb +25 -16
  72. data/lib/minfraud/validates.rb +187 -0
  73. data/lib/minfraud/version.rb +4 -1
  74. data/minfraud.gemspec +23 -18
  75. metadata +113 -39
  76. data/.travis.yml +0 -5
@@ -1,37 +1,64 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Minfraud
4
+ # ErrorHandler provides a method to raise exceptions on errors.
2
5
  module ErrorHandler
3
6
  class << self
4
- # Returns a response if status code is 200, rises an error otherwise
5
- # @param [Minfraud::HTTPService::Response] response
6
- # @return [Minfraud::HTTPService::Response] if status code is 200
7
- def inspect(response)
8
- return response if response.status == 200
7
+ # Return the response if the HTTP status code is 2xx. Otherwise raise
8
+ # an error.
9
+ #
10
+ # @param response [Minfraud::HTTPService::Response]
11
+ #
12
+ # @return [Minfraud::HTTPService::Response]
13
+ #
14
+ # @raise [Minfraud::AuthorizationError] If there was an authentication
15
+ # problem.
16
+ #
17
+ # @raise [Minfraud::ClientError] If there was a critical problem with one
18
+ # of your inputs.
19
+ #
20
+ # @raise [Minfraud::ServerError] If the server reported an error of some
21
+ # kind.
22
+ def examine(response)
23
+ return response if response.status > 199 && response.status < 300
9
24
 
10
- raise *STATUS_CODES.fetch(response.code, [ServerError, 'Server error'])
25
+ raise(*STATUS_CODES.fetch(response.code, [ServerError, 'Server error']))
11
26
  end
12
27
 
13
- # A hash that maps status codes returned by minFraud with errors & messages
28
+ # @!visibility private
14
29
  STATUS_CODES = {
15
- IP_ADDRESS_INVALID: [
16
- ClientError, 'You have no supplied a valid IPv4 or IPv6 address'
30
+ JSON_INVALID: [
31
+ ClientError, 'JSON body cannot be decoded'
17
32
  ],
18
- IP_ADDRESS_REQUIRED: [
19
- ClientError, 'You have not supplied an IP address which is required filed'
33
+ MAXMIND_ID_INVALID: [
34
+ ClientError, 'You have not supplied a valid maxmind_id'
20
35
  ],
21
- IP_ADDRESS_RESERVED: [
22
- ClientError, 'You have supplied an IP address which is reserved'
36
+ MINFRAUD_ID_INVALID: [
37
+ ClientError, 'You have not supplied a valid minfraud_id'
23
38
  ],
24
- JSON_INVALID: [
25
- ClientError, 'JSON body cannot be decoded'
39
+ PARAMETER_UNKNOWN: [
40
+ ClientError, 'You have supplied an unknown parameter'
41
+ ],
42
+ REQUEST_INVALID: [
43
+ ClientError, 'The request did not contain any valid input values.'
44
+ ],
45
+ TAG_REQUIRED: [
46
+ ClientError, 'You have not supplied a tag, which is a required field'
47
+ ],
48
+ TAG_INVALID: [
49
+ ClientError, 'You have not supplied a valid tag'
50
+ ],
51
+ ACCOUNT_ID_REQUIRED: [
52
+ AuthorizationError, 'You have not supplied a account ID'
26
53
  ],
27
54
  AUTHORIZATION_INVALID: [
28
- AuthorizationError, 'Invalid license key and / or user id'
55
+ AuthorizationError, 'Invalid license key and / or account ID'
29
56
  ],
30
57
  LICENSE_KEY_REQUIRED: [
31
58
  AuthorizationError, 'You have not supplied a license key'
32
59
  ],
33
60
  USER_ID_REQUIRED: [
34
- AuthorizationError, 'You have not supplied a user id'
61
+ AuthorizationError, 'You have not supplied a account id'
35
62
  ],
36
63
  INSUFFICIENT_FUNDS: [
37
64
  ClientError, 'The license key you have provided does not have a sufficient funds to use this service'
@@ -39,7 +66,7 @@ module Minfraud
39
66
  PERMISSION_REQUIRED: [
40
67
  ClientError, 'You do not have permission to use this service'
41
68
  ]
42
- }
69
+ }.freeze
43
70
  end
44
71
  end
45
72
  end
@@ -1,10 +1,30 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Minfraud
2
- # A list of Minfraud custom errors
3
- class BaseError < StandardError; end
4
+ # The base error class for Minfraud exceptions.
5
+ class BaseError < StandardError; end
6
+
7
+ # An error indicating the input value is not valid.
8
+ class InvalidInputError < BaseError; end
4
9
 
10
+ # An error indicating the value is not valid for the enumerated type.
5
11
  class NotEnumValueError < BaseError; end
12
+
13
+ # An error indicating the field is not recognized.
6
14
  class RequestFormatError < BaseError; end
15
+
16
+ # An error indicating minFraud cannot serve your request as there is a
17
+ # problem on the client side.
18
+ #
19
+ # For example, this may happen if there is a problem with the format or
20
+ # contents of your request, if you lack funds to use the service, or if you
21
+ # don't have permission to use the service.
7
22
  class ClientError < BaseError; end
23
+
24
+ # An error indicating there is a problem with authorization or
25
+ # authentication.
8
26
  class AuthorizationError < BaseError; end
27
+
28
+ # An error indicating the server had an error handling the request.
9
29
  class ServerError < BaseError; end
10
30
  end
@@ -1,31 +1,45 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'faraday'
2
4
  require 'faraday_middleware'
3
5
 
4
6
  module Minfraud
7
+ # HTTPService holds the HTTP client configuration.
5
8
  module HTTPService
6
9
  class << self
7
- # @return [Hash] default HTTPService configuration
10
+ # The default HTTPService configuration.
11
+ #
12
+ # @return [Hash]
8
13
  def configuration
14
+ server = DEFAULT_SERVER
15
+ if !Minfraud.host.nil?
16
+ server = "https://#{Minfraud.host}/minfraud/v2.0"
17
+ end
18
+
9
19
  {
10
20
  middleware: DEFAULT_MIDDLEWARE,
11
- server: DEFAULT_SERVER
21
+ server: server,
12
22
  }
13
23
  end
14
24
  end
15
25
 
16
- # Minfraud default middleware stack
17
- DEFAULT_MIDDLEWARE = Proc.new do |builder|
26
+ # @!visibility private
27
+ DEFAULT_MIDDLEWARE = proc do |builder|
18
28
  builder.request :json
19
29
 
20
- builder.basic_auth *::Minfraud.configuration.values
30
+ account_id = Minfraud.account_id
31
+ account_id = Minfraud.user_id if account_id.nil?
32
+
33
+ builder.basic_auth account_id, Minfraud.license_key
21
34
 
22
- builder.response :mashify
23
35
  builder.response :json, content_type: /\bjson$/
24
36
 
25
- builder.adapter Faraday.default_adapter
37
+ builder.adapter :net_http_persistent, pool_size: 5 do |http|
38
+ http.idle_timeout = 30
39
+ end
26
40
  end
27
41
 
28
- # Minfraud default server
42
+ # Default base URL for minFraud.
29
43
  DEFAULT_SERVER = 'https://minfraud.maxmind.com/minfraud/v2.0'
30
44
  end
31
45
  end
@@ -1,36 +1,37 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'faraday'
2
4
 
3
5
  module Minfraud
4
6
  module HTTPService
7
+ # Request performs HTTP requests.
5
8
  class Request
6
- # @attribute middleware
7
- # @return [Proc] A proc containing Faraday configuration
9
+ # A proc containing Faraday configuration.
10
+ #
11
+ # @return [Proc, nil]
8
12
  attr_reader :middleware
9
13
 
10
- # @attribute server
11
- # @return [String] an API endpoint
14
+ # The API endpoint.
15
+ #
16
+ # @return [String, nil]
12
17
  attr_reader :server
13
18
 
14
- # Creates Minfraud::HTTPService::Request instance
15
- # @param [Hash] params hash of parameters
16
- # @return [Minfraud::HTTPService::Request] Request instance
19
+ # @param params [Hash] Hash of parameters. Each key/value should
20
+ # correspond to one of the available attributes.
17
21
  def initialize(params = {})
18
22
  @middleware = params[:middleware]
19
23
  @server = params[:server]
20
24
  end
21
25
 
22
- # Performs an HTTP request to the specified endpoint with given body
23
- # @param [Hash] params hash of parameters.
24
- # @return [Farday::Response] Faraday::Response instance
26
+ # Perform an HTTP request to the specified endpoint with given body.
27
+ #
28
+ # @param params [Hash] Hash of parameters, including +:verb+,
29
+ # +:endpoint+, and +:body+.
30
+ #
31
+ # @return [Farday::Response]
25
32
  def perform(params)
26
- adapter.send(*params.values_at(:verb, :endpoint, :body))
27
- end
28
-
29
- private
30
- # Creates memoized Faraday::Connection instance
31
- # @return [Faraday::Connection] Faraday::Connection instance
32
- def adapter
33
- @adapter ||= Faraday.new(server, {}, &middleware)
33
+ connection = Minfraud.connection
34
+ connection.send(*params.values_at(:verb, :endpoint, :body))
34
35
  end
35
36
  end
36
37
  end
@@ -1,32 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'minfraud/model/error'
4
+ require 'minfraud/model/factors'
5
+ require 'minfraud/model/insights'
6
+ require 'minfraud/model/score'
7
+
1
8
  module Minfraud
2
9
  module HTTPService
10
+ # Response class for HTTP requests.
3
11
  class Response
4
- # @attribute status
5
- # @return [Integer] HTTP response status
12
+ # HTTP response status.
13
+ #
14
+ # @return [Integer, nil]
6
15
  attr_reader :status
7
16
 
8
- # @attribute body
9
- # @return [Hash] HTTP response body
17
+ # HTTP response model.
18
+ #
19
+ # @return [Minfraud::Model::Score, Minfraud::Model::Insights,
20
+ # Minfraud::Model::Factors, nil]
10
21
  attr_reader :body
11
22
 
12
- # @attribute headers
13
- # @return [Hash] HTTP response headers
23
+ # HTTP response headers.
24
+ #
25
+ # @return [Hash, nil]
14
26
  attr_reader :headers
15
27
 
16
- # Creates Minfraud::HTTPService::Response instance
17
- # @param [Hash] params hash of parameters
18
- # @return [Minfraud::HTTPService::Response] Response instance
28
+ # @param params [Hash] Hash of parameters. +:status+, +:endpoint+,
29
+ # +:body+, +:locales+, and +:headers+ are used.
19
30
  def initialize(params = {})
20
31
  @status = params[:status]
21
- @body = params[:body]
32
+ @body = make_body(
33
+ params[:endpoint],
34
+ params[:body],
35
+ params[:locales]
36
+ )
22
37
  @headers = params[:headers]
23
38
  end
24
39
 
25
- # Returns minFraud specific response code
26
- # @return [Symbol] minFraud specific request code
40
+ # Return the minFraud-specific response code.
41
+ #
42
+ # @return [Symbol, nil]
27
43
  def code
44
+ return nil if body.nil?
45
+
28
46
  body.code.intern if body.respond_to?(:code) && body.code
29
47
  end
48
+
49
+ private
50
+
51
+ def make_body(endpoint, body, locales)
52
+ if @status != 200
53
+ # Won't be a Hash when the body is not JSON.
54
+ return nil unless body.is_a?(Hash)
55
+
56
+ return Minfraud::Model::Error.new(body)
57
+ end
58
+
59
+ ENDPOINT_TO_CLASS[endpoint].new(body, locales)
60
+ end
61
+
62
+ ENDPOINT_TO_CLASS = {
63
+ factors: Minfraud::Model::Factors,
64
+ insights: Minfraud::Model::Insights,
65
+ score: Minfraud::Model::Score
66
+ }.freeze
30
67
  end
31
68
  end
32
69
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Minfraud
4
+ module Model
5
+ # @!visibility private
6
+ class Abstract
7
+ def initialize(record)
8
+ @record = record
9
+ end
10
+
11
+ protected
12
+
13
+ def get(key)
14
+ return nil if @record.nil? || !@record.key?(key)
15
+
16
+ @record[key]
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'minfraud/model/abstract'
4
+
5
+ module Minfraud
6
+ module Model
7
+ # Abstract model for a postal address.
8
+ class Address < Abstract
9
+ # The distance in kilometers from the address to the IP location.
10
+ #
11
+ # @return [Integer, nil]
12
+ attr_reader :distance_to_ip_location
13
+
14
+ # This property is true if the address is in the IP country. The property
15
+ # is false when the address is not in the IP country. If the address
16
+ # could not be parsed or was not provided or if the IP address could not
17
+ # be geolocated, the property will be nil.
18
+ #
19
+ # @return [Boolean, nil]
20
+ attr_reader :is_in_ip_country
21
+
22
+ # This property is true if the postal code provided with the address is
23
+ # in the city for the address. The property is false when the postal code
24
+ # is not in the city. If the address was not provided or could not be
25
+ # parsed, the property will be nil.
26
+ #
27
+ # @return [Boolean, nil]
28
+ attr_reader :is_postal_in_city
29
+
30
+ # The latitude associated with the address.
31
+ #
32
+ # @return [Float, nil]
33
+ attr_reader :latitude
34
+
35
+ # The longitude associated with the address.
36
+ #
37
+ # @return [Float, nil]
38
+ attr_reader :longitude
39
+
40
+ # @!visibility private
41
+ def initialize(record)
42
+ super(record)
43
+
44
+ @distance_to_ip_location = get('distance_to_ip_location')
45
+ @is_in_ip_country = get('is_in_ip_country')
46
+ @is_postal_in_city = get('is_postal_in_city')
47
+ @latitude = get('latitude')
48
+ @longitude = get('longitude')
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'minfraud/model/address'
4
+
5
+ module Minfraud
6
+ module Model
7
+ # Model containing information about the billing address.
8
+ class BillingAddress < Address
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'minfraud/model/abstract'
4
+ require 'minfraud/model/issuer'
5
+
6
+ module Minfraud
7
+ module Model
8
+ # Model with details about the credit card used.
9
+ class CreditCard < Abstract
10
+ # The card brand, such as "Visa", "Discover", "American Express", etc.
11
+ #
12
+ # @return [String, nil]
13
+ attr_reader :brand
14
+
15
+ # This property contains the two letter ISO 3166-1 alpha-2 country code
16
+ # (https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) associated with the
17
+ # location of the majority of customers using this credit card as
18
+ # determined by their billing address. In cases where the location of
19
+ # customers is highly mixed, this defaults to the country of the bank
20
+ # issuing the card.
21
+ #
22
+ # @return [String, nil]
23
+ attr_reader :country
24
+
25
+ # This property is true if the card is a business card.
26
+ #
27
+ # @return [Boolean, nil]
28
+ attr_reader :is_business
29
+
30
+ # This property is true if the country of the billing address matches the
31
+ # country of the majority of customers using this credit card. In cases
32
+ # where the location of customers is highly mixed, the match is to the
33
+ # country of the bank issuing the card.
34
+ #
35
+ # @return [Boolean, nil]
36
+ attr_reader :is_issued_in_billing_address_country
37
+
38
+ # This property is true if the card is a prepaid card.
39
+ #
40
+ # @return [Boolean, nil]
41
+ attr_reader :is_prepaid
42
+
43
+ # This property is true if the card is a virtual card.
44
+ #
45
+ # @return [Boolean, nil]
46
+ attr_reader :is_virtual
47
+
48
+ # An object containing information about the credit card issuer.
49
+ #
50
+ # @return [Minfraud::Model::Issuer]
51
+ attr_reader :issuer
52
+
53
+ # The card's type. The valid values are: charge, credit, debit.
54
+ #
55
+ # @return [String, nil]
56
+ attr_reader :type
57
+
58
+ # @!visibility private
59
+ def initialize(record)
60
+ super(record)
61
+
62
+ @brand = get('brand')
63
+ @country = get('country')
64
+ @is_business = get('is_business')
65
+ @is_issued_in_billing_address_country = get(
66
+ 'is_issued_in_billing_address_country'
67
+ )
68
+ @is_prepaid = get('is_prepaid')
69
+ @is_virtual = get('is_virtual')
70
+ @issuer = Minfraud::Model::Issuer.new(get('issuer'))
71
+ @type = get('type')
72
+ end
73
+ end
74
+ end
75
+ end