minfraud 1.0.1 → 1.2.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.
- checksums.yaml +5 -5
- data/.github/workflows/test.yml +46 -0
- data/.gitignore +2 -0
- data/.rubocop.yml +127 -0
- data/.travis.yml +20 -3
- data/CHANGELOG.md +56 -0
- data/CODE_OF_CONDUCT.md +4 -4
- data/Gemfile +11 -2
- data/LICENSE.txt +2 -1
- data/README.dev.md +4 -0
- data/README.md +107 -36
- data/Rakefile +18 -3
- data/bin/console +4 -3
- data/lib/maxmind/geoip2/model/city.rb +99 -0
- data/lib/maxmind/geoip2/model/country.rb +94 -0
- data/lib/maxmind/geoip2/model/insights.rb +38 -0
- data/lib/maxmind/geoip2/record/abstract.rb +46 -0
- data/lib/maxmind/geoip2/record/city.rb +62 -0
- data/lib/maxmind/geoip2/record/continent.rb +61 -0
- data/lib/maxmind/geoip2/record/country.rb +78 -0
- data/lib/maxmind/geoip2/record/location.rb +97 -0
- data/lib/maxmind/geoip2/record/maxmind.rb +41 -0
- data/lib/maxmind/geoip2/record/place.rb +52 -0
- data/lib/maxmind/geoip2/record/postal.rb +54 -0
- data/lib/maxmind/geoip2/record/represented_country.rb +47 -0
- data/lib/maxmind/geoip2/record/subdivision.rb +72 -0
- data/lib/maxmind/geoip2/record/traits.rb +224 -0
- data/lib/minfraud.rb +33 -8
- data/lib/minfraud/assessments.rb +25 -10
- data/lib/minfraud/components/account.rb +3 -1
- data/lib/minfraud/components/addressable.rb +11 -9
- data/lib/minfraud/components/base.rb +29 -5
- data/lib/minfraud/components/billing.rb +2 -0
- data/lib/minfraud/components/credit_card.rb +8 -1
- data/lib/minfraud/components/custom_inputs.rb +16 -0
- data/lib/minfraud/components/device.rb +13 -0
- data/lib/minfraud/components/email.rb +2 -0
- data/lib/minfraud/components/event.rb +15 -8
- data/lib/minfraud/components/order.rb +4 -1
- data/lib/minfraud/components/payment.rb +138 -14
- data/lib/minfraud/components/report/transaction.rb +69 -0
- data/lib/minfraud/components/shipping.rb +2 -4
- data/lib/minfraud/components/shopping_cart.rb +4 -1
- data/lib/minfraud/components/shopping_cart_item.rb +2 -2
- data/lib/minfraud/enum.rb +7 -4
- data/lib/minfraud/error_handler.rb +29 -9
- data/lib/minfraud/errors.rb +2 -0
- data/lib/minfraud/http_service.rb +13 -4
- data/lib/minfraud/http_service/request.rb +4 -1
- data/lib/minfraud/http_service/response.rb +40 -6
- data/lib/minfraud/model/abstract.rb +20 -0
- data/lib/minfraud/model/address.rb +52 -0
- data/lib/minfraud/model/billing_address.rb +11 -0
- data/lib/minfraud/model/credit_card.rb +75 -0
- data/lib/minfraud/model/device.rb +54 -0
- data/lib/minfraud/model/disposition.rb +35 -0
- data/lib/minfraud/model/email.rb +54 -0
- data/lib/minfraud/model/email_domain.rb +24 -0
- data/lib/minfraud/model/error.rb +28 -0
- data/lib/minfraud/model/factors.rb +24 -0
- data/lib/minfraud/model/geoip2_location.rb +25 -0
- data/lib/minfraud/model/insights.rb +68 -0
- data/lib/minfraud/model/ip_address.rb +82 -0
- data/lib/minfraud/model/issuer.rb +49 -0
- data/lib/minfraud/model/score.rb +76 -0
- data/lib/minfraud/model/score_ip_address.rb +23 -0
- data/lib/minfraud/model/shipping_address.rb +30 -0
- data/lib/minfraud/model/subscores.rb +175 -0
- data/lib/minfraud/model/warning.rb +63 -0
- data/lib/minfraud/report.rb +40 -0
- data/lib/minfraud/resolver.rb +16 -13
- data/lib/minfraud/version.rb +3 -1
- data/minfraud.gemspec +21 -15
- metadata +84 -19
@@ -1,22 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Minfraud
|
2
4
|
module ErrorHandler
|
3
5
|
class << self
|
4
|
-
# Returns a response if status code is
|
6
|
+
# Returns a response if status code is 2xx, raises an error otherwise
|
5
7
|
# @param [Minfraud::HTTPService::Response] response
|
6
8
|
# @return [Minfraud::HTTPService::Response] if status code is 200
|
7
|
-
def
|
8
|
-
return response if response.status
|
9
|
+
def examine(response)
|
10
|
+
return response if response.status > 199 && response.status < 300
|
9
11
|
|
10
|
-
raise
|
12
|
+
raise(*STATUS_CODES.fetch(response.code, [ServerError, 'Server error']))
|
11
13
|
end
|
12
14
|
|
13
15
|
# A hash that maps status codes returned by minFraud with errors & messages
|
14
16
|
STATUS_CODES = {
|
15
17
|
IP_ADDRESS_INVALID: [
|
16
|
-
ClientError, 'You have
|
18
|
+
ClientError, 'You have not supplied a valid IPv4 or IPv6 address'
|
17
19
|
],
|
18
20
|
IP_ADDRESS_REQUIRED: [
|
19
|
-
ClientError,
|
21
|
+
ClientError, 'You have not supplied an IP address which is a required field'
|
20
22
|
],
|
21
23
|
IP_ADDRESS_RESERVED: [
|
22
24
|
ClientError, 'You have supplied an IP address which is reserved'
|
@@ -24,14 +26,32 @@ module Minfraud
|
|
24
26
|
JSON_INVALID: [
|
25
27
|
ClientError, 'JSON body cannot be decoded'
|
26
28
|
],
|
29
|
+
MAXMIND_ID_INVALID: [
|
30
|
+
ClientError, 'You have not supplied a valid maxmind_id'
|
31
|
+
],
|
32
|
+
MINFRAUD_ID_INVALID: [
|
33
|
+
ClientError, 'You have not supplied a valid minfraud_id'
|
34
|
+
],
|
35
|
+
PARAMETER_UNKNOWN: [
|
36
|
+
ClientError, 'You have supplied an unknown parameter'
|
37
|
+
],
|
38
|
+
TAG_REQUIRED: [
|
39
|
+
ClientError, 'You have not supplied a tag, which is a required field'
|
40
|
+
],
|
41
|
+
TAG_INVALID: [
|
42
|
+
ClientError, 'You have not supplied a valid tag'
|
43
|
+
],
|
44
|
+
ACCOUNT_ID_REQUIRED: [
|
45
|
+
AuthorizationError, 'You have not supplied a account ID'
|
46
|
+
],
|
27
47
|
AUTHORIZATION_INVALID: [
|
28
|
-
AuthorizationError, 'Invalid license key and / or
|
48
|
+
AuthorizationError, 'Invalid license key and / or account ID'
|
29
49
|
],
|
30
50
|
LICENSE_KEY_REQUIRED: [
|
31
51
|
AuthorizationError, 'You have not supplied a license key'
|
32
52
|
],
|
33
53
|
USER_ID_REQUIRED: [
|
34
|
-
AuthorizationError, 'You have not supplied a
|
54
|
+
AuthorizationError, 'You have not supplied a account id'
|
35
55
|
],
|
36
56
|
INSUFFICIENT_FUNDS: [
|
37
57
|
ClientError, 'The license key you have provided does not have a sufficient funds to use this service'
|
@@ -39,7 +59,7 @@ module Minfraud
|
|
39
59
|
PERMISSION_REQUIRED: [
|
40
60
|
ClientError, 'You do not have permission to use this service'
|
41
61
|
]
|
42
|
-
}
|
62
|
+
}.freeze
|
43
63
|
end
|
44
64
|
end
|
45
65
|
end
|
data/lib/minfraud/errors.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'faraday'
|
2
4
|
require 'faraday_middleware'
|
3
5
|
|
@@ -6,20 +8,27 @@ module Minfraud
|
|
6
8
|
class << self
|
7
9
|
# @return [Hash] default HTTPService configuration
|
8
10
|
def configuration
|
11
|
+
server = DEFAULT_SERVER
|
12
|
+
if !Minfraud.host.nil?
|
13
|
+
server = 'https://' + Minfraud.host + '/minfraud/v2.0'
|
14
|
+
end
|
15
|
+
|
9
16
|
{
|
10
17
|
middleware: DEFAULT_MIDDLEWARE,
|
11
|
-
server:
|
18
|
+
server: server,
|
12
19
|
}
|
13
20
|
end
|
14
21
|
end
|
15
22
|
|
16
23
|
# Minfraud default middleware stack
|
17
|
-
DEFAULT_MIDDLEWARE =
|
24
|
+
DEFAULT_MIDDLEWARE = proc do |builder|
|
18
25
|
builder.request :json
|
19
26
|
|
20
|
-
|
27
|
+
account_id = Minfraud.account_id
|
28
|
+
account_id = Minfraud.user_id if account_id.nil?
|
29
|
+
|
30
|
+
builder.basic_auth account_id, Minfraud.license_key
|
21
31
|
|
22
|
-
builder.response :mashify
|
23
32
|
builder.response :json, content_type: /\bjson$/
|
24
33
|
|
25
34
|
builder.adapter Faraday.default_adapter
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'faraday'
|
2
4
|
|
3
5
|
module Minfraud
|
@@ -14,7 +16,7 @@ module Minfraud
|
|
14
16
|
# Creates Minfraud::HTTPService::Request instance
|
15
17
|
# @param [Hash] params hash of parameters
|
16
18
|
# @return [Minfraud::HTTPService::Request] Request instance
|
17
|
-
def initialize(params)
|
19
|
+
def initialize(params = {})
|
18
20
|
@middleware = params[:middleware]
|
19
21
|
@server = params[:server]
|
20
22
|
end
|
@@ -27,6 +29,7 @@ module Minfraud
|
|
27
29
|
end
|
28
30
|
|
29
31
|
private
|
32
|
+
|
30
33
|
# Creates memoized Faraday::Connection instance
|
31
34
|
# @return [Faraday::Connection] Faraday::Connection instance
|
32
35
|
def adapter
|
@@ -1,12 +1,21 @@
|
|
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
12
|
# @attribute status
|
5
13
|
# @return [Integer] HTTP response status
|
6
14
|
attr_reader :status
|
7
15
|
|
8
16
|
# @attribute body
|
9
|
-
# @return [
|
17
|
+
# @return [Minfraud::Model::Score, Minfraud::Model::Insights,
|
18
|
+
# Minfraud::Model::Factors] HTTP response body
|
10
19
|
attr_reader :body
|
11
20
|
|
12
21
|
# @attribute headers
|
@@ -16,17 +25,42 @@ module Minfraud
|
|
16
25
|
# Creates Minfraud::HTTPService::Response instance
|
17
26
|
# @param [Hash] params hash of parameters
|
18
27
|
# @return [Minfraud::HTTPService::Response] Response instance
|
19
|
-
def initialize(params)
|
28
|
+
def initialize(params = {})
|
20
29
|
@status = params[:status]
|
21
|
-
@body =
|
30
|
+
@body = make_body(
|
31
|
+
params[:endpoint],
|
32
|
+
params[:body],
|
33
|
+
params[:locales]
|
34
|
+
)
|
22
35
|
@headers = params[:headers]
|
23
36
|
end
|
24
37
|
|
25
|
-
# Returns minFraud
|
26
|
-
# @return [
|
38
|
+
# Returns minFraud-specific response code
|
39
|
+
# @return [Symbol, nil] minFraud specific request code
|
27
40
|
def code
|
28
|
-
|
41
|
+
return nil if body.nil?
|
42
|
+
|
43
|
+
body.code.intern if body.respond_to?(:code) && body.code
|
29
44
|
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def make_body(endpoint, body, locales)
|
49
|
+
if @status != 200
|
50
|
+
# Won't be a Hash when the body is not JSON.
|
51
|
+
return nil unless body.is_a?(Hash)
|
52
|
+
|
53
|
+
return Minfraud::Model::Error.new(body)
|
54
|
+
end
|
55
|
+
|
56
|
+
ENDPOINT_TO_CLASS[endpoint].new(body, locales)
|
57
|
+
end
|
58
|
+
|
59
|
+
ENDPOINT_TO_CLASS = {
|
60
|
+
factors: Minfraud::Model::Factors,
|
61
|
+
insights: Minfraud::Model::Insights,
|
62
|
+
score: Minfraud::Model::Score
|
63
|
+
}.freeze
|
30
64
|
end
|
31
65
|
end
|
32
66
|
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,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
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'minfraud/model/abstract'
|
4
|
+
|
5
|
+
module Minfraud
|
6
|
+
module Model
|
7
|
+
# Model with information about the device.
|
8
|
+
#
|
9
|
+
# In order to receive device output from minFraud Insights or minFraud
|
10
|
+
# Factors, you must be using the Device Tracking Add-on
|
11
|
+
# (https://dev.maxmind.com/minfraud/device/).
|
12
|
+
class Device < Abstract
|
13
|
+
# This number represents our confidence that the device_id refers to a
|
14
|
+
# unique device as opposed to a cluster of similar devices. A confidence
|
15
|
+
# of 0.01 indicates very low confidence that the device is unique,
|
16
|
+
# whereas 99 indicates very high confidence.
|
17
|
+
#
|
18
|
+
# @return [Float, nil]
|
19
|
+
attr_reader :confidence
|
20
|
+
|
21
|
+
# A UUID that MaxMind uses for the device associated with this IP
|
22
|
+
# address. Note that many devices cannot be uniquely identified because
|
23
|
+
# they are too common (for example, all iPhones of a given model and OS
|
24
|
+
# release). In these cases, the minFraud service will simply not return a
|
25
|
+
# UUID for that device.
|
26
|
+
#
|
27
|
+
# @return [String, nil]
|
28
|
+
attr_reader :id
|
29
|
+
|
30
|
+
# This is the date and time of the last sighting of the device. This is
|
31
|
+
# an RFC 3339 date-time.
|
32
|
+
#
|
33
|
+
# @return [String, nil]
|
34
|
+
attr_reader :last_seen
|
35
|
+
|
36
|
+
# This is the local date and time of the transaction in the time zone of
|
37
|
+
# the device. This is determined by using the UTC offset associated with
|
38
|
+
# the device. This is an RFC 3339 date-time
|
39
|
+
#
|
40
|
+
# @return [String, nil]
|
41
|
+
attr_reader :local_time
|
42
|
+
|
43
|
+
# @!visibility private
|
44
|
+
def initialize(record)
|
45
|
+
super(record)
|
46
|
+
|
47
|
+
@confidence = get('confidence')
|
48
|
+
@id = get('id')
|
49
|
+
@last_seen = get('last_seen')
|
50
|
+
@local_time = get('local_time')
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'minfraud/model/abstract'
|
4
|
+
|
5
|
+
module Minfraud
|
6
|
+
module Model
|
7
|
+
# Model with the disposition set by custom rules.
|
8
|
+
#
|
9
|
+
# In order to receive a disposition, you must be using minFraud custom
|
10
|
+
# rules.
|
11
|
+
class Disposition < Abstract
|
12
|
+
# The action to take on the transaction as defined by your custom rules.
|
13
|
+
# The current set of values are "accept", "manual_review", and "reject".
|
14
|
+
# If you do not have custom rules set up, this will be nil.
|
15
|
+
#
|
16
|
+
# @return [String, nil]
|
17
|
+
attr_reader :action
|
18
|
+
|
19
|
+
# The reason for the action. The current possible values are
|
20
|
+
# "custom_rule", "block_list", and "default". If you do not have custom
|
21
|
+
# rules set up, this will be nil.
|
22
|
+
#
|
23
|
+
# @return [String, nil]
|
24
|
+
attr_reader :reason
|
25
|
+
|
26
|
+
# @!visibility private
|
27
|
+
def initialize(record)
|
28
|
+
super(record)
|
29
|
+
|
30
|
+
@action = get('action')
|
31
|
+
@reason = get('reason')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|