minfraud 1.1.0 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/rubocop.yml +12 -0
- data/.github/workflows/test.yml +33 -0
- data/.rubocop.yml +108 -0
- data/CHANGELOG.md +57 -0
- data/Gemfile +4 -2
- data/README.dev.md +1 -1
- data/README.md +170 -55
- data/Rakefile +9 -3
- data/bin/console +4 -3
- data/lib/maxmind/geoip2/model/city.rb +3 -3
- data/lib/maxmind/geoip2/model/country.rb +5 -5
- data/lib/maxmind/geoip2/record/traits.rb +13 -4
- data/lib/minfraud.rb +44 -6
- data/lib/minfraud/assessments.rb +113 -53
- data/lib/minfraud/components/account.rb +31 -9
- data/lib/minfraud/components/addressable.rb +73 -26
- data/lib/minfraud/components/base.rb +26 -14
- data/lib/minfraud/components/billing.rb +5 -0
- data/lib/minfraud/components/credit_card.rb +64 -20
- data/lib/minfraud/components/custom_inputs.rb +14 -3
- data/lib/minfraud/components/device.rb +45 -15
- data/lib/minfraud/components/email.rb +120 -9
- data/lib/minfraud/components/event.rb +60 -24
- data/lib/minfraud/components/order.rb +59 -22
- data/lib/minfraud/components/payment.rb +44 -9
- data/lib/minfraud/components/report/transaction.rb +50 -39
- data/lib/minfraud/components/shipping.rb +14 -5
- data/lib/minfraud/components/shopping_cart.rb +19 -12
- data/lib/minfraud/components/shopping_cart_item.rb +42 -13
- data/lib/minfraud/enum.rb +22 -8
- data/lib/minfraud/error_handler.rb +32 -25
- data/lib/minfraud/errors.rb +22 -2
- data/lib/minfraud/http_service.rb +23 -8
- data/lib/minfraud/http_service/request.rb +19 -18
- data/lib/minfraud/http_service/response.rb +19 -14
- data/lib/minfraud/model/address.rb +4 -4
- data/lib/minfraud/model/credit_card.rb +7 -7
- data/lib/minfraud/model/device.rb +2 -2
- data/lib/minfraud/model/email.rb +4 -4
- data/lib/minfraud/model/error.rb +1 -1
- data/lib/minfraud/model/insights.rb +5 -5
- data/lib/minfraud/model/ip_address.rb +20 -1
- data/lib/minfraud/model/ip_risk_reason.rb +48 -0
- data/lib/minfraud/model/issuer.rb +3 -3
- data/lib/minfraud/model/score.rb +6 -6
- data/lib/minfraud/model/shipping_address.rb +1 -1
- data/lib/minfraud/model/subscores.rb +38 -16
- data/lib/minfraud/model/warning.rb +2 -2
- data/lib/minfraud/report.rb +33 -13
- data/lib/minfraud/resolver.rb +25 -17
- data/lib/minfraud/validates.rb +187 -0
- data/lib/minfraud/version.rb +4 -1
- data/minfraud.gemspec +8 -2
- metadata +77 -10
- data/.travis.yml +0 -22
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'minfraud/model/error'
|
2
4
|
require 'minfraud/model/factors'
|
3
5
|
require 'minfraud/model/insights'
|
@@ -5,24 +7,26 @@ require 'minfraud/model/score'
|
|
5
7
|
|
6
8
|
module Minfraud
|
7
9
|
module HTTPService
|
8
|
-
# Response class for HTTP requests
|
10
|
+
# Response class for HTTP requests.
|
9
11
|
class Response
|
10
|
-
#
|
11
|
-
#
|
12
|
+
# HTTP response status.
|
13
|
+
#
|
14
|
+
# @return [Integer, nil]
|
12
15
|
attr_reader :status
|
13
16
|
|
14
|
-
#
|
17
|
+
# HTTP response model.
|
18
|
+
#
|
15
19
|
# @return [Minfraud::Model::Score, Minfraud::Model::Insights,
|
16
|
-
# Minfraud::Model::Factors]
|
20
|
+
# Minfraud::Model::Factors, nil]
|
17
21
|
attr_reader :body
|
18
22
|
|
19
|
-
#
|
20
|
-
#
|
23
|
+
# HTTP response headers.
|
24
|
+
#
|
25
|
+
# @return [Hash, nil]
|
21
26
|
attr_reader :headers
|
22
27
|
|
23
|
-
#
|
24
|
-
#
|
25
|
-
# @return [Minfraud::HTTPService::Response] Response instance
|
28
|
+
# @param params [Hash] Hash of parameters. +:status+, +:endpoint+,
|
29
|
+
# +:body+, +:locales+, and +:headers+ are used.
|
26
30
|
def initialize(params = {})
|
27
31
|
@status = params[:status]
|
28
32
|
@body = make_body(
|
@@ -33,8 +37,9 @@ module Minfraud
|
|
33
37
|
@headers = params[:headers]
|
34
38
|
end
|
35
39
|
|
36
|
-
#
|
37
|
-
#
|
40
|
+
# Return the minFraud-specific response code.
|
41
|
+
#
|
42
|
+
# @return [Symbol, nil]
|
38
43
|
def code
|
39
44
|
return nil if body.nil?
|
40
45
|
|
@@ -55,9 +60,9 @@ module Minfraud
|
|
55
60
|
end
|
56
61
|
|
57
62
|
ENDPOINT_TO_CLASS = {
|
58
|
-
factors:
|
63
|
+
factors: Minfraud::Model::Factors,
|
59
64
|
insights: Minfraud::Model::Insights,
|
60
|
-
score:
|
65
|
+
score: Minfraud::Model::Score
|
61
66
|
}.freeze
|
62
67
|
end
|
63
68
|
end
|
@@ -42,10 +42,10 @@ module Minfraud
|
|
42
42
|
super(record)
|
43
43
|
|
44
44
|
@distance_to_ip_location = get('distance_to_ip_location')
|
45
|
-
@is_in_ip_country
|
46
|
-
@is_postal_in_city
|
47
|
-
@latitude
|
48
|
-
@longitude
|
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
49
|
end
|
50
50
|
end
|
51
51
|
end
|
@@ -59,16 +59,16 @@ module Minfraud
|
|
59
59
|
def initialize(record)
|
60
60
|
super(record)
|
61
61
|
|
62
|
-
@brand
|
63
|
-
@country
|
64
|
-
@is_business
|
62
|
+
@brand = get('brand')
|
63
|
+
@country = get('country')
|
64
|
+
@is_business = get('is_business')
|
65
65
|
@is_issued_in_billing_address_country = get(
|
66
66
|
'is_issued_in_billing_address_country'
|
67
67
|
)
|
68
|
-
@is_prepaid
|
69
|
-
@is_virtual
|
70
|
-
@issuer
|
71
|
-
@type
|
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
72
|
end
|
73
73
|
end
|
74
74
|
end
|
data/lib/minfraud/model/email.rb
CHANGED
@@ -43,11 +43,11 @@ module Minfraud
|
|
43
43
|
def initialize(record)
|
44
44
|
super(record)
|
45
45
|
|
46
|
-
@domain
|
47
|
-
@first_seen
|
46
|
+
@domain = Minfraud::Model::EmailDomain.new(get('domain'))
|
47
|
+
@first_seen = get('first_seen')
|
48
48
|
@is_disposable = get('is_disposable')
|
49
|
-
@is_free
|
50
|
-
@is_high_risk
|
49
|
+
@is_free = get('is_free')
|
50
|
+
@is_high_risk = get('is_high_risk')
|
51
51
|
end
|
52
52
|
end
|
53
53
|
end
|
data/lib/minfraud/model/error.rb
CHANGED
@@ -52,13 +52,13 @@ module Minfraud
|
|
52
52
|
def initialize(record, locales)
|
53
53
|
super(record, locales)
|
54
54
|
|
55
|
-
@billing_address
|
55
|
+
@billing_address = Minfraud::Model::BillingAddress.new(
|
56
56
|
get('billing_address')
|
57
57
|
)
|
58
|
-
@credit_card
|
59
|
-
@device
|
60
|
-
@email
|
61
|
-
@ip_address
|
58
|
+
@credit_card = Minfraud::Model::CreditCard.new(get('credit_card'))
|
59
|
+
@device = Minfraud::Model::Device.new(get('device'))
|
60
|
+
@email = Minfraud::Model::Email.new(get('email'))
|
61
|
+
@ip_address = Minfraud::Model::IPAddress.new(get('ip_address'), locales)
|
62
62
|
@shipping_address = Minfraud::Model::ShippingAddress.new(
|
63
63
|
get('shipping_address')
|
64
64
|
)
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'maxmind/geoip2/model/insights'
|
4
4
|
require 'minfraud/model/geoip2_location'
|
5
|
+
require 'minfraud/model/ip_risk_reason'
|
5
6
|
|
6
7
|
module Minfraud
|
7
8
|
module Model
|
@@ -13,9 +14,20 @@ module Minfraud
|
|
13
14
|
# @return [Float]
|
14
15
|
attr_reader :risk
|
15
16
|
|
17
|
+
# This field contains IPRiskReason objects identifying the reasons why
|
18
|
+
# the IP address received the associated risk. This will be an empty
|
19
|
+
# array if there are no reasons.
|
20
|
+
#
|
21
|
+
# @return [Array<Minfraud::Model::IPRiskReason>]
|
22
|
+
attr_reader :risk_reasons
|
23
|
+
|
16
24
|
# @!visibility private
|
17
25
|
def initialize(record, locales)
|
18
|
-
|
26
|
+
if record
|
27
|
+
super(record, locales)
|
28
|
+
else
|
29
|
+
super({}, locales)
|
30
|
+
end
|
19
31
|
|
20
32
|
if record
|
21
33
|
@location = Minfraud::Model::GeoIP2Location.new(record.fetch('location', nil))
|
@@ -28,6 +40,13 @@ module Minfraud
|
|
28
40
|
@risk = nil
|
29
41
|
end
|
30
42
|
|
43
|
+
@risk_reasons = []
|
44
|
+
if record && record.key?('risk_reasons')
|
45
|
+
record['risk_reasons'].each do |r|
|
46
|
+
@risk_reasons << Minfraud::Model::IPRiskReason.new(r)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
31
50
|
# Decorate objects with deprecated attributes and names for backwards
|
32
51
|
# compatibility. Do this here rather than with the overhead of
|
33
52
|
# subclasses/modules for them in the hope that one day we can delete
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'minfraud/model/abstract'
|
4
|
+
|
5
|
+
module Minfraud
|
6
|
+
module Model
|
7
|
+
# Reason for the IP risk.
|
8
|
+
#
|
9
|
+
# This class provides both a machine-readable code and a human-readable
|
10
|
+
# explanation of the reason for the IP risk score.
|
11
|
+
#
|
12
|
+
# Although more codes may be added in the future, the current codes are:
|
13
|
+
#
|
14
|
+
# * ANONYMOUS_IP - The IP address belongs to an anonymous network. See the
|
15
|
+
# object at ip_address.traits for more details.
|
16
|
+
# * BILLING_POSTAL_VELOCITY - Many different billing postal codes have been
|
17
|
+
# seen on this IP address.
|
18
|
+
# * EMAIL_VELOCITY - Many different email addresses have been seen on this
|
19
|
+
# IP address.
|
20
|
+
# * HIGH_RISK_DEVICE - A high risk device was seen on this IP address.
|
21
|
+
# * HIGH_RISK_EMAIL - A high risk email address was seen on this IP address
|
22
|
+
# in your past transactions.
|
23
|
+
# * ISSUER_ID_NUMBER_VELOCITY - Many different issuer ID numbers have been
|
24
|
+
# seen on this IP address.
|
25
|
+
# * MINFRAUD_NETWORK_ACTIVITY - Suspicious activity has been seen on this
|
26
|
+
# IP address across minFraud customers.
|
27
|
+
class IPRiskReason < Abstract
|
28
|
+
# This value is a machine-readable code identifying the reason.
|
29
|
+
#
|
30
|
+
# @return [String, nil]
|
31
|
+
attr_reader :code
|
32
|
+
|
33
|
+
# This field provides a human-readable explanation of the reason. The
|
34
|
+
# text may change at any time and should not be matched against.
|
35
|
+
#
|
36
|
+
# @return [String, nil]
|
37
|
+
attr_reader :reason
|
38
|
+
|
39
|
+
# @!visibility private
|
40
|
+
def initialize(record)
|
41
|
+
super(record)
|
42
|
+
|
43
|
+
@code = get('code')
|
44
|
+
@reason = get('reason')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -39,9 +39,9 @@ module Minfraud
|
|
39
39
|
def initialize(record)
|
40
40
|
super(record)
|
41
41
|
|
42
|
-
@name
|
43
|
-
@phone_number
|
44
|
-
@matches_provided_name
|
42
|
+
@name = get('name')
|
43
|
+
@phone_number = get('phone_number')
|
44
|
+
@matches_provided_name = get('matches_provided_name')
|
45
45
|
@matches_provided_phone_number = get('matches_provided_phone_number')
|
46
46
|
end
|
47
47
|
end
|
data/lib/minfraud/model/score.rb
CHANGED
@@ -58,13 +58,13 @@ module Minfraud
|
|
58
58
|
def initialize(record, _locales)
|
59
59
|
super(record)
|
60
60
|
|
61
|
-
@disposition
|
62
|
-
@funds_remaining
|
63
|
-
@id
|
64
|
-
@ip_address
|
61
|
+
@disposition = Minfraud::Model::Disposition.new(get('disposition'))
|
62
|
+
@funds_remaining = get('funds_remaining')
|
63
|
+
@id = get('id')
|
64
|
+
@ip_address = Minfraud::Model::ScoreIPAddress.new(get('ip_address'))
|
65
65
|
@queries_remaining = get('queries_remaining')
|
66
|
-
@risk_score
|
67
|
-
@warnings
|
66
|
+
@risk_score = get('risk_score')
|
67
|
+
@warnings = []
|
68
68
|
if record && record.key?('warnings')
|
69
69
|
record['warnings'].each do |w|
|
70
70
|
@warnings << Minfraud::Model::Warning.new(w)
|
@@ -59,6 +59,12 @@ module Minfraud
|
|
59
59
|
# @return [Float, nil]
|
60
60
|
attr_reader :cvv_result
|
61
61
|
|
62
|
+
# The risk associated with the device. If present, this is a value in the
|
63
|
+
# range of 0.01 to 99.
|
64
|
+
#
|
65
|
+
# @return [Float, nil]
|
66
|
+
attr_reader :device
|
67
|
+
|
62
68
|
# The risk associated with the particular email address. If present, this
|
63
69
|
# is a value in the range 0.01 to 99.
|
64
70
|
#
|
@@ -71,6 +77,13 @@ module Minfraud
|
|
71
77
|
# @return [Float, nil]
|
72
78
|
attr_reader :email_domain
|
73
79
|
|
80
|
+
# The risk associated with the email address local part (the part of
|
81
|
+
# the email address before the @ symbol). If present, this is a value
|
82
|
+
# in the range 0.01 to 99.
|
83
|
+
#
|
84
|
+
# @return [Float, nil]
|
85
|
+
attr_reader :email_local_part
|
86
|
+
|
74
87
|
# The risk associated with the issuer ID number on the email domain. If
|
75
88
|
# present, this is a value in the range 0.01 to 99.
|
76
89
|
#
|
@@ -111,6 +124,12 @@ module Minfraud
|
|
111
124
|
# @return [Float, nil]
|
112
125
|
attr_reader :phone_number
|
113
126
|
|
127
|
+
# The risk associated with the shipping address. If present, this is a
|
128
|
+
# value in the range 0.01 to 99.
|
129
|
+
#
|
130
|
+
# @return [Float, nil]
|
131
|
+
attr_reader :shipping_address
|
132
|
+
|
114
133
|
# The risk associated with the distance between the shipping address and
|
115
134
|
# the IP location for the given IP address. If present, this is a value
|
116
135
|
# in the range 0.01 to 99.
|
@@ -129,27 +148,30 @@ module Minfraud
|
|
129
148
|
def initialize(record)
|
130
149
|
super(record)
|
131
150
|
|
132
|
-
@avs_result
|
133
|
-
@billing_address
|
134
|
-
@billing_address_distance_to_ip_location
|
151
|
+
@avs_result = get('avs_result')
|
152
|
+
@billing_address = get('billing_address')
|
153
|
+
@billing_address_distance_to_ip_location = get(
|
135
154
|
'billing_address_distance_to_ip_location'
|
136
155
|
)
|
137
|
-
@browser
|
138
|
-
@chargeback
|
139
|
-
@country
|
140
|
-
@country_mismatch
|
141
|
-
@cvv_result
|
142
|
-
@
|
143
|
-
@
|
144
|
-
@
|
145
|
-
@
|
146
|
-
@
|
147
|
-
@
|
148
|
-
@
|
156
|
+
@browser = get('browser')
|
157
|
+
@chargeback = get('chargeback')
|
158
|
+
@country = get('country')
|
159
|
+
@country_mismatch = get('country_mismatch')
|
160
|
+
@cvv_result = get('cvv_result')
|
161
|
+
@device = get('device')
|
162
|
+
@email_address = get('email_address')
|
163
|
+
@email_domain = get('email_domain')
|
164
|
+
@email_local_part = get('email_local_part')
|
165
|
+
@email_tenure = get('email_tenure')
|
166
|
+
@ip_tenure = get('ip_tenure')
|
167
|
+
@issuer_id_number = get('issuer_id_number')
|
168
|
+
@order_amount = get('order_amount')
|
169
|
+
@phone_number = get('phone_number')
|
170
|
+
@shipping_address = get('shipping_address')
|
149
171
|
@shipping_address_distance_to_ip_location = get(
|
150
172
|
'shipping_address_distance_to_ip_location'
|
151
173
|
)
|
152
|
-
@time_of_day
|
174
|
+
@time_of_day = get('time_of_day')
|
153
175
|
end
|
154
176
|
end
|
155
177
|
end
|
data/lib/minfraud/report.rb
CHANGED
@@ -1,36 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Minfraud
|
4
|
+
# Report is used to perform minFraud Report Transaction API requests.
|
5
|
+
#
|
6
|
+
# @see https://dev.maxmind.com/minfraud/report-transaction/
|
2
7
|
class Report
|
3
8
|
include ::Minfraud::HTTPService
|
4
9
|
|
5
|
-
#
|
6
|
-
#
|
10
|
+
# The Report::Transaction component.
|
11
|
+
#
|
12
|
+
# @return [Minfraud::Components::Report::Transaction, nil]
|
7
13
|
attr_accessor :transaction
|
8
14
|
|
9
|
-
# @param
|
10
|
-
#
|
15
|
+
# @param params [Hash] Hash of parameters. The only supported key is
|
16
|
+
# +:transaction+, which should have a
|
17
|
+
# +Minfraud::Components::Report::Transaction+ as its value.
|
11
18
|
def initialize(params = {})
|
12
19
|
@transaction = params[:transaction]
|
13
20
|
end
|
14
21
|
|
15
|
-
#
|
16
|
-
#
|
17
|
-
# Raises an error in case of invalid response.
|
22
|
+
# Perform a request to the minFraud Report Transaction API.
|
23
|
+
#
|
18
24
|
# @return [nil]
|
25
|
+
#
|
26
|
+
# @raise [Minfraud::AuthorizationError] If there was an authentication
|
27
|
+
# problem.
|
28
|
+
#
|
29
|
+
# @raise [Minfraud::ClientError] If there was a critical problem with one
|
30
|
+
# of your inputs.
|
31
|
+
#
|
32
|
+
# @raise [Minfraud::ServerError] If the server reported an error of some
|
33
|
+
# kind.
|
19
34
|
def report_transaction
|
20
|
-
raw = request.perform(
|
35
|
+
raw = request.perform(
|
36
|
+
verb: :post,
|
37
|
+
endpoint: 'transactions/report',
|
38
|
+
body: @transaction.to_json,
|
39
|
+
)
|
21
40
|
|
22
41
|
response = ::Minfraud::HTTPService::Response.new(
|
23
|
-
status:
|
24
|
-
body:
|
42
|
+
status: raw.status.to_i,
|
43
|
+
body: raw.body,
|
25
44
|
headers: raw.headers
|
26
45
|
)
|
46
|
+
|
27
47
|
::Minfraud::ErrorHandler.examine(response)
|
28
48
|
|
29
|
-
|
49
|
+
nil
|
30
50
|
end
|
31
51
|
|
32
|
-
|
33
|
-
|
52
|
+
private
|
53
|
+
|
34
54
|
def request
|
35
55
|
@request ||= Request.new(::Minfraud::HTTPService.configuration)
|
36
56
|
end
|