minfraud 1.1.0 → 1.5.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 (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