minfraud 1.1.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rubocop.yml +12 -0
  3. data/.github/workflows/test.yml +33 -0
  4. data/.rubocop.yml +108 -0
  5. data/CHANGELOG.md +57 -0
  6. data/Gemfile +4 -2
  7. data/README.dev.md +1 -1
  8. data/README.md +170 -55
  9. data/Rakefile +9 -3
  10. data/bin/console +4 -3
  11. data/lib/maxmind/geoip2/model/city.rb +3 -3
  12. data/lib/maxmind/geoip2/model/country.rb +5 -5
  13. data/lib/maxmind/geoip2/record/traits.rb +13 -4
  14. data/lib/minfraud.rb +44 -6
  15. data/lib/minfraud/assessments.rb +113 -53
  16. data/lib/minfraud/components/account.rb +31 -9
  17. data/lib/minfraud/components/addressable.rb +73 -26
  18. data/lib/minfraud/components/base.rb +26 -14
  19. data/lib/minfraud/components/billing.rb +5 -0
  20. data/lib/minfraud/components/credit_card.rb +64 -20
  21. data/lib/minfraud/components/custom_inputs.rb +14 -3
  22. data/lib/minfraud/components/device.rb +45 -15
  23. data/lib/minfraud/components/email.rb +120 -9
  24. data/lib/minfraud/components/event.rb +60 -24
  25. data/lib/minfraud/components/order.rb +59 -22
  26. data/lib/minfraud/components/payment.rb +44 -9
  27. data/lib/minfraud/components/report/transaction.rb +50 -39
  28. data/lib/minfraud/components/shipping.rb +14 -5
  29. data/lib/minfraud/components/shopping_cart.rb +19 -12
  30. data/lib/minfraud/components/shopping_cart_item.rb +42 -13
  31. data/lib/minfraud/enum.rb +22 -8
  32. data/lib/minfraud/error_handler.rb +32 -25
  33. data/lib/minfraud/errors.rb +22 -2
  34. data/lib/minfraud/http_service.rb +23 -8
  35. data/lib/minfraud/http_service/request.rb +19 -18
  36. data/lib/minfraud/http_service/response.rb +19 -14
  37. data/lib/minfraud/model/address.rb +4 -4
  38. data/lib/minfraud/model/credit_card.rb +7 -7
  39. data/lib/minfraud/model/device.rb +2 -2
  40. data/lib/minfraud/model/email.rb +4 -4
  41. data/lib/minfraud/model/error.rb +1 -1
  42. data/lib/minfraud/model/insights.rb +5 -5
  43. data/lib/minfraud/model/ip_address.rb +20 -1
  44. data/lib/minfraud/model/ip_risk_reason.rb +48 -0
  45. data/lib/minfraud/model/issuer.rb +3 -3
  46. data/lib/minfraud/model/score.rb +6 -6
  47. data/lib/minfraud/model/shipping_address.rb +1 -1
  48. data/lib/minfraud/model/subscores.rb +38 -16
  49. data/lib/minfraud/model/warning.rb +2 -2
  50. data/lib/minfraud/report.rb +33 -13
  51. data/lib/minfraud/resolver.rb +25 -17
  52. data/lib/minfraud/validates.rb +187 -0
  53. data/lib/minfraud/version.rb +4 -1
  54. data/minfraud.gemspec +8 -2
  55. metadata +77 -10
  56. data/.travis.yml +0 -22
@@ -1,14 +1,23 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Minfraud
2
4
  module Components
5
+ # Shipping corresponds to the shipping object of a minFraud request.
6
+ #
7
+ # @see https://dev.maxmind.com/minfraud/#Shipping_(/shipping)
3
8
  class Shipping < Addressable
4
9
  include ::Minfraud::Enum
5
- # @attribute delivery_speed
6
- # @return [String] The shipping delivery speed for the order. The valid values are:
10
+
11
+ # The shipping delivery speed for the order. Must be one of +:same_day+,
12
+ # +:overnight+, +:expedited+, or +:standard+.
13
+ #
14
+ # @!attribute delivery_speed
15
+ #
16
+ # @return [Symbol, nil]
7
17
  enum_accessor :delivery_speed, [:same_day, :overnight, :expedited, :standard]
8
18
 
9
- # Creates Minfraud::Components::Shipping instance
10
- # @param [Hash] params hash of parameters
11
- # @return [Minfraud::Components::Shipping] Shipping instance
19
+ # @param params [Hash] Hash of parameters. Each key/value should
20
+ # correspond to one of the available attributes.
12
21
  def initialize(params = {})
13
22
  self.delivery_speed = params[:delivery_speed]
14
23
  super
@@ -1,27 +1,34 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Minfraud
2
4
  module Components
5
+ # ShoppingCart corresponds to the shopping_cart object of a minFraud
6
+ # request.
7
+ #
8
+ # @see https://dev.maxmind.com/minfraud/#Shopping_Cart_(/shoppingcart)
3
9
  class ShoppingCart < Base
4
- # @attribute items
5
- # @return [Array] An array of Minfraud::Components::ShoppingCartItem instances
6
-
10
+ # An array of Minfraud::Components::ShoppingCartItem instances.
11
+ #
12
+ # @return [Array<Minfraud::Components::ShoppingCartItem>]
7
13
  attr_accessor :items
8
- # Creates Minfraud::Components::ShoppingCart instance
9
- # @param [Hash] params hash of parameters
10
- # @return [Minfraud::Components::ShoppingCart] ShoppingCart instance
11
- def initialize(params = {})
14
+
15
+ # @param params [Array] Array of shopping cart items. You may provide
16
+ # each item as either a Hash where each key is a symbol corresponding
17
+ # to an item's field, or as a Minfraud:::Components::ShoppingCartItem
18
+ # object.
19
+ def initialize(params = [])
12
20
  @items = params.map(&method(:resolve))
13
21
  end
14
22
 
15
- # @return [Array] a JSON representation of Minfraud::Components::ShoppingCart items
16
- def to_json
23
+ # A JSON representation of Minfraud::Components::ShoppingCart items.
24
+ #
25
+ # @return [Array]
26
+ def to_json(*_args)
17
27
  @items.map(&:to_json)
18
28
  end
19
29
 
20
30
  private
21
31
 
22
- # @param [Hash] params hash of parameters for Minfraud::Components::ShoppingCartItem
23
- # or Minfraud::Components::ShoppingCartItem instance
24
- # @return [Minfraud::Components::ShoppingCart] ShoppingCart instance
25
32
  def resolve(params)
26
33
  params.is_a?(ShoppingCartItem) ? params : ShoppingCartItem.new(params)
27
34
  end
@@ -1,33 +1,62 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Minfraud
2
4
  module Components
5
+ # ShoppingCartItem corresponds to objects in the shopping_cart object
6
+ # of a minFraud request.
7
+ #
8
+ # @see https://dev.maxmind.com/minfraud/#Shopping_Cart_Item
3
9
  class ShoppingCartItem < Base
4
- # @attribute category
5
- # @return [String] The category of the item
10
+ include Minfraud::Validates
11
+
12
+ # The category of the item. This can also be a hashed value; see link.
13
+ #
14
+ # @see https://dev.maxmind.com/minfraud/#cart-hashing
15
+ #
16
+ # @return [String, nil]
6
17
  attr_accessor :category
7
18
 
8
- # @attribute item_id
9
- # @return [String] The internal ID of the item
19
+ # The internal ID of the item. This can also be a hashed value; see link.
20
+ #
21
+ # @see https://dev.maxmind.com/minfraud/#cart-hashing
22
+ #
23
+ # @return [String, nil]
10
24
  attr_accessor :item_id
11
25
 
12
- # @attribute quantity
13
- # @return [Integer] The quantity of the item in the shopping cart
26
+ # The quantity of the item in the shopping cart. The value must be at
27
+ # least 0, at most 10^13-1, and have no fractional part.
28
+ #
29
+ # @return [Integer, nil]
14
30
  attr_accessor :quantity
15
31
 
16
- # @attribute price
17
- # @return [Float] The per-unit price of this item in the shopping cart. This should use the same currency as the order currency
32
+ # The per-unit price of this item in the shopping cart. This should use
33
+ # the same currency as the order currency. The value must be at least 0
34
+ # and at most 1e14 - 1.
35
+ #
36
+ # @return [Float, nil]
18
37
  attr_accessor :price
19
38
 
20
- # Creates Minfraud::Components::ShoppingCartItem instance
21
- # @param [Hash] params hash of parameters
22
- # @return [Minfraud::Components::ShoppingCartItem] ShoppingCartItem instance
39
+ # @param params [Hash] Hash of parameters. Each key/value should
40
+ # correspond to one of the available attributes.
23
41
  def initialize(params = {})
24
42
  @category = params[:category]
25
43
  @item_id = params[:item_id]
26
44
  @quantity = params[:quantity]
27
45
  @price = params[:price]
46
+
47
+ validate
48
+ end
49
+
50
+ private
51
+
52
+ def validate
53
+ return if !Minfraud.enable_validation
54
+
55
+ validate_string('category', 255, @category)
56
+ validate_string('item_id', 255, @item_id)
57
+ validate_nonnegative_integer('quantity', @quantity)
58
+ validate_nonnegative_number('price', @price)
28
59
  end
29
60
  end
30
61
  end
31
62
  end
32
-
33
-
data/lib/minfraud/enum.rb CHANGED
@@ -1,19 +1,30 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Minfraud
4
+ # Enum provides helpers for working with attributes with enumerated types.
2
5
  module Enum
3
6
  def self.included(base)
4
7
  base.extend(ClassMethods)
5
8
  end
6
9
 
10
+ # ClassMethods provides helpers for working with attributes with enumerated
11
+ # types.
7
12
  module ClassMethods
8
- # Returns a hash with in the following format: attribute_name => permitted_values
9
- # @return [Hash] mapping
13
+ # Returns a hash in the following format: attribute_name =>
14
+ # permitted_values
15
+ #
16
+ # @return [Hash]
10
17
  def mapping
11
18
  @mapping ||= {}
12
19
  end
13
20
 
14
- # Creates a set of methods for enum-like behaviour of the attribute
15
- # @param [Symbol] attribute attribute name
16
- # @param [Array] assignable_values a set of values which are permitted
21
+ # Create a set of methods for enum-like behavior of the attribute.
22
+ #
23
+ # @param attribute [Symbol] The attribute name.
24
+ #
25
+ # @param assignable_values [Array] The set of permitted values.
26
+ #
27
+ # @return [nil]
17
28
  def enum_accessor(attribute, assignable_values)
18
29
  mapping[attribute] = assignable_values.map(&:intern)
19
30
 
@@ -21,13 +32,16 @@ module Minfraud
21
32
  define_method("#{attribute}_values") { mapping[attribute] }
22
33
  end
23
34
 
24
- self.class_eval do
25
- define_method("#{attribute}") { instance_variable_get("@#{attribute}") }
35
+ class_eval do
36
+ define_method(attribute.to_s) { instance_variable_get("@#{attribute}") }
26
37
  define_method "#{attribute}=" do |value|
27
- raise NotEnumValueError, 'Value is not permitted' if value && !self.class.mapping[attribute].include?(value.intern)
38
+ raise NotEnumValueError, 'Value is not permitted' if value && !self.class.mapping[attribute].include?(value.intern)
39
+
28
40
  instance_variable_set("@#{attribute}", value ? value.intern : nil)
29
41
  end
30
42
  end
43
+
44
+ nil
31
45
  end
32
46
  end
33
47
  end
@@ -1,62 +1,69 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Minfraud
4
+ # ErrorHandler provides a method to raise exceptions on errors.
4
5
  module ErrorHandler
5
6
  class << self
6
- # Returns a response if status code is 2xx, raises an error otherwise
7
- # @param [Minfraud::HTTPService::Response] response
8
- # @return [Minfraud::HTTPService::Response] if status code is 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.
9
22
  def examine(response)
10
23
  return response if response.status > 199 && response.status < 300
11
24
 
12
- raise *STATUS_CODES.fetch(response.code, [ServerError, 'Server error'])
25
+ raise(*STATUS_CODES.fetch(response.code, [ServerError, 'Server error']))
13
26
  end
14
27
 
15
- # A hash that maps status codes returned by minFraud with errors & messages
28
+ # @!visibility private
16
29
  STATUS_CODES = {
17
- IP_ADDRESS_INVALID: [
18
- ClientError, 'You have not supplied a valid IPv4 or IPv6 address'
19
- ],
20
- IP_ADDRESS_REQUIRED: [
21
- ClientError, 'You have not supplied an IP address which is a required field'
22
- ],
23
- IP_ADDRESS_RESERVED: [
24
- ClientError, 'You have supplied an IP address which is reserved'
25
- ],
26
- JSON_INVALID: [
30
+ JSON_INVALID: [
27
31
  ClientError, 'JSON body cannot be decoded'
28
32
  ],
29
- MAXMIND_ID_INVALID: [
33
+ MAXMIND_ID_INVALID: [
30
34
  ClientError, 'You have not supplied a valid maxmind_id'
31
35
  ],
32
- MINFRAUD_ID_INVALID: [
36
+ MINFRAUD_ID_INVALID: [
33
37
  ClientError, 'You have not supplied a valid minfraud_id'
34
38
  ],
35
- PARAMETER_UNKNOWN: [
39
+ PARAMETER_UNKNOWN: [
36
40
  ClientError, 'You have supplied an unknown parameter'
37
41
  ],
38
- TAG_REQUIRED: [
42
+ REQUEST_INVALID: [
43
+ ClientError, 'The request did not contain any valid input values.'
44
+ ],
45
+ TAG_REQUIRED: [
39
46
  ClientError, 'You have not supplied a tag, which is a required field'
40
47
  ],
41
- TAG_INVALID: [
48
+ TAG_INVALID: [
42
49
  ClientError, 'You have not supplied a valid tag'
43
50
  ],
44
- ACCOUNT_ID_REQUIRED: [
51
+ ACCOUNT_ID_REQUIRED: [
45
52
  AuthorizationError, 'You have not supplied a account ID'
46
53
  ],
47
54
  AUTHORIZATION_INVALID: [
48
55
  AuthorizationError, 'Invalid license key and / or account ID'
49
56
  ],
50
- LICENSE_KEY_REQUIRED: [
57
+ LICENSE_KEY_REQUIRED: [
51
58
  AuthorizationError, 'You have not supplied a license key'
52
59
  ],
53
- USER_ID_REQUIRED: [
60
+ USER_ID_REQUIRED: [
54
61
  AuthorizationError, 'You have not supplied a account id'
55
62
  ],
56
- INSUFFICIENT_FUNDS: [
63
+ INSUFFICIENT_FUNDS: [
57
64
  ClientError, 'The license key you have provided does not have a sufficient funds to use this service'
58
65
  ],
59
- PERMISSION_REQUIRED: [
66
+ PERMISSION_REQUIRED: [
60
67
  ClientError, 'You do not have permission to use this service'
61
68
  ]
62
69
  }.freeze
@@ -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,30 +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
35
  builder.response :json, content_type: /\bjson$/
23
36
 
24
- builder.adapter Faraday.default_adapter
37
+ builder.adapter :net_http_persistent, pool_size: 5 do |http|
38
+ http.idle_timeout = 30
39
+ end
25
40
  end
26
41
 
27
- # Minfraud default server
28
- DEFAULT_SERVER = 'https://minfraud.maxmind.com/minfraud/v2.0'.freeze
42
+ # Default base URL for minFraud.
43
+ DEFAULT_SERVER = 'https://minfraud.maxmind.com/minfraud/v2.0'
29
44
  end
30
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