minfraud 2.7.1 → 2.9.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 94219dcd7ef78fdea77b06565b3a73ff8073ea56755f2a9ab25a0d4ba97eb692
4
- data.tar.gz: a7e3cfa6b732da9f5d23877b4abef0cb7e7641ed30d690af2fc141ee6298cec8
3
+ metadata.gz: 7182466f12206a3a2d7639cabb6d7937e10d91dc7bac4684482b38b27dc1237e
4
+ data.tar.gz: 2a304dbe22670677bd3aaa7fbdba6b738ea460652893c351bf686861e63dd56d
5
5
  SHA512:
6
- metadata.gz: d79f083900533abbe2fc0d82b5c67d91c8bf81cee4ac95ef37c42c40eb50cf8f1bd1642acc6b80fdc50234f1cded773f47a62548fa0d360bab92c5fcd6b3cbb5
7
- data.tar.gz: 5d868acf7b455dd212b8481e0c31e1cc9156e5c39a38ddf38d9d230b2344250fbae51551aec91556e532a601f2e51e4424e8c2b02eca024e84b4b23a600f0c2b
6
+ metadata.gz: 347bc9a5c45f747e1bca1608b6d4c0623d11627cdbca48657b45fd3bf7f04b21d4aae579fe81333935477a6dcd5d6e59a95a0ba5df18560a7969385661aab0ac
7
+ data.tar.gz: 90eeb55ddfc1f93fcf3eb981ed34a79402910e6ca097b7e4cb7098c847e414c01c628dc1f5a3d113d299621596172e2a5adef1affd88c6103e0e8217b37206e2
data/.rubocop.yml CHANGED
@@ -1,5 +1,11 @@
1
+ plugins:
2
+ - rubocop-performance
3
+ - rubocop-rake
4
+ - rubocop-rspec
5
+ - rubocop-thread_safety
6
+
1
7
  AllCops:
2
- TargetRubyVersion: '3.0'
8
+ TargetRubyVersion: '3.2'
3
9
  NewCops: enable
4
10
 
5
11
  # Metrics.
@@ -88,3 +94,20 @@ Naming/VariableNumber:
88
94
 
89
95
  Gemspec/DevelopmentDependencies:
90
96
  Enabled: false
97
+
98
+ # This might make sense, but I don't think it's worth moving things around for.
99
+ RSpec/SpecFilePathFormat:
100
+ Enabled: false
101
+ # Sometimes it makes sense to have lots of assertions.
102
+ RSpec/MultipleExpectations:
103
+ Enabled: false
104
+ # Sometimes it makes sense to have long tests.
105
+ RSpec/ExampleLength:
106
+ Enabled: false
107
+ # This seems okay.
108
+ RSpec/MultipleDescribes:
109
+ Enabled: false
110
+ # This seems to give a bunch of false positives. Specifically it's flagging
111
+ # things where we are creating an object and checking no exception happens.
112
+ RSpec/NoExpectationExample:
113
+ Enabled: false
data/CHANGELOG.md CHANGED
@@ -1,5 +1,49 @@
1
1
  # Changelog
2
2
 
3
+ ## v2.9.0 (2025-11-20)
4
+
5
+ * Added the processor `:securepay` to `Minfraud::Components::Payment`.
6
+ * Ruby 3.2+ is now required. If you're using Ruby 3.0 or 3.1, please use
7
+ version 2.8.0 of this gem.
8
+ * Added `/event/type` values `:credit_application` and `:fund_transfer` to
9
+ `Minfraud::Components::Event`.
10
+ * Added the `/event/type` value `:sim_swap` to
11
+ `Minfraud::Components::Event`.
12
+ * Added the input `/event/party`. This is the party submitting the
13
+ transaction. You may provide this using the `party` attribute on
14
+ `Minfraud::Components::Event`.
15
+ * Added the input `/payment/method`. This is the payment method associated
16
+ with the transaction. You may provide this using the `method` attribute
17
+ on `Minfraud::Components::Payment`.
18
+ * Added support for new email domain outputs on `Minfraud::Model::EmailDomain`:
19
+ * `/email/domain/classification` - The domain type (e.g., business,
20
+ education, government, isp_email). Available as the `classification`
21
+ attribute.
22
+ * `/email/domain/risk` - A risk score from 0.01 to 99 for the domain.
23
+ Available as the `risk` attribute.
24
+ * `/email/domain/volume` - Activity across the minFraud network expressed
25
+ as sightings per million. Available as the `volume` attribute.
26
+ * `/email/domain/visit` - Information from an automated visit to the
27
+ domain. Available as the `visit` attribute, which returns a
28
+ `Minfraud::Model::EmailDomainVisit` object.
29
+ * Added support for email domain visit outputs on
30
+ `Minfraud::Model::EmailDomainVisit`:
31
+ * `/email/domain/visit/has_redirect` - Whether the domain redirects to
32
+ another URL. Available as the `has_redirect` attribute.
33
+ * `/email/domain/visit/last_visited_on` - The date the automated visit
34
+ was completed. Available as the `last_visited_on` attribute.
35
+ * `/email/domain/visit/status` - The status of the domain (e.g., live,
36
+ dns_error, parked). Available as the `status` attribute.
37
+ * Updated `maxmind-geoip2` to 1.4, which includes new anonymizer and IP
38
+ risk outputs.
39
+
40
+ ## v2.8.0 (2025-05-23)
41
+
42
+ * Added support for the `/billing_phone/matches_postal` and
43
+ `/shipping_phone/matches_postal` outputs. These are available as the
44
+ `matches_postal` attribute on `Minfraud::Model::Phone`.
45
+ * Added the processor `:cryptomus` to `Minfraud::Components::Payment`.
46
+
3
47
  ## v2.7.1 (2025-02-10)
4
48
 
5
49
  * Re-release due to bug in release workflow.
data/CLAUDE.md ADDED
@@ -0,0 +1,443 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ **minfraud-api-ruby** is MaxMind's official Ruby client library for the minFraud web services:
8
+ - **minFraud Score, Insights, and Factors**: Fraud detection and risk scoring endpoints
9
+ - **minFraud Report Transaction API**: Report fraudulent or legitimate transactions
10
+
11
+ The library provides a request builder pattern through components that are assembled into assessments, then sent to the service endpoints. Responses are returned as strongly-typed model objects.
12
+
13
+ **Key Technologies:**
14
+ - Ruby 3.2+ (uses frozen string literals and modern Ruby features)
15
+ - HTTP gem for web service client functionality and persistent connections
16
+ - ConnectionPool for thread-safe connection management
17
+ - RSpec for testing
18
+ - RuboCop with multiple plugins for code quality
19
+
20
+ ## Code Architecture
21
+
22
+ ### Package Structure
23
+
24
+ ```
25
+ lib/minfraud/
26
+ ├── components/ # Request components (Account, Device, Email, etc.)
27
+ │ └── report/ # Report Transaction components
28
+ ├── model/ # Response models (Score, Insights, Factors, etc.)
29
+ ├── http_service/ # HTTP response wrapper
30
+ ├── assessments.rb # Main request builder for Score/Insights/Factors
31
+ ├── report.rb # Report Transaction API
32
+ ├── resolver.rb # Maps component parameters to component objects
33
+ ├── enum.rb # Enum helper for validated attributes
34
+ ├── validates.rb # Input validation methods
35
+ ├── errors.rb # Custom exceptions
36
+ └── version.rb # Version constant
37
+ ```
38
+
39
+ ### Key Design Patterns
40
+
41
+ #### 1. **Component-Based Request Building**
42
+
43
+ Requests are built using component objects that represent different aspects of a transaction:
44
+
45
+ ```ruby
46
+ assessment = Minfraud::Assessments.new(
47
+ device: { ip_address: '1.2.3.4' },
48
+ event: { type: :purchase },
49
+ billing: { country: 'US' },
50
+ )
51
+ ```
52
+
53
+ **Component Architecture:**
54
+ - All components extend `Minfraud::Components::Base`
55
+ - Components use `attr_accessor` for attributes
56
+ - Components implement `#to_json` to convert to API request format
57
+ - The `Resolver` module maps hash parameters to component objects
58
+ - Components with enum attributes include `Minfraud::Enum` module
59
+
60
+ **Resolver Pattern:**
61
+ The `Minfraud::Resolver` module handles converting hashes to component objects:
62
+ - Maintains a `MAPPING` constant from keys to component classes
63
+ - The `#assign` method creates components from hashes or uses existing component objects
64
+ - Raises `RequestFormatError` for unknown keys
65
+ - Used by `Minfraud::Assessments` in initialization
66
+
67
+ #### 2. **Enum Attributes with Validation**
68
+
69
+ Components with restricted value sets use the `Enum` module:
70
+
71
+ ```ruby
72
+ class Event < Base
73
+ include ::Minfraud::Enum
74
+
75
+ enum_accessor :type, %i[
76
+ account_creation
77
+ purchase
78
+ payout
79
+ recurring_purchase
80
+ referral
81
+ account_login
82
+ ]
83
+ end
84
+ ```
85
+
86
+ **Key Points:**
87
+ - `enum_accessor` creates getter/setter methods with validation
88
+ - Values are stored as symbols internally
89
+ - Raises `NotEnumValueError` if invalid value assigned
90
+ - The class gets a `{attribute}_values` method returning permitted values
91
+
92
+ #### 3. **Optional Client-Side Validation**
93
+
94
+ Components can optionally include `Minfraud::Validates` for input validation:
95
+
96
+ ```ruby
97
+ class Device < Base
98
+ include ::Minfraud::Validates
99
+
100
+ def ip_address=(ip_address)
101
+ validate_ip('ip_address', ip_address) if Minfraud.enable_validation
102
+ @ip_address = ip_address
103
+ end
104
+ end
105
+ ```
106
+
107
+ **Key Points:**
108
+ - Validation is disabled by default
109
+ - Enable with `Minfraud.configure { |c| c.enable_validation = true }`
110
+ - Validation happens in setters before assignment
111
+ - Validates types, formats, lengths, ranges, etc.
112
+ - Raises `InvalidInputError` for invalid values
113
+
114
+ #### 4. **Model Inheritance and Composition**
115
+
116
+ Response models follow a clear hierarchy:
117
+
118
+ ```
119
+ Abstract → Score → Insights → Factors
120
+ ```
121
+
122
+ - `Abstract` provides `#get` method for safe hash access
123
+ - `Score` has basic risk scoring fields
124
+ - `Insights` extends `Score` with detailed fraud data (addresses, phones, device, etc.)
125
+ - `Factors` extends `Insights` with subscores and risk reasons
126
+
127
+ **Model Composition:**
128
+ Models compose smaller model objects representing nested data:
129
+
130
+ ```ruby
131
+ class Insights < Score
132
+ attr_reader :billing_address
133
+ attr_reader :credit_card
134
+ attr_reader :device
135
+ attr_reader :email
136
+
137
+ def initialize(record, locales)
138
+ super
139
+ @billing_address = Minfraud::Model::BillingAddress.new(get('billing_address'))
140
+ @credit_card = Minfraud::Model::CreditCard.new(get('credit_card'))
141
+ # ...
142
+ end
143
+ end
144
+ ```
145
+
146
+ #### 5. **Connection Pooling for Thread Safety**
147
+
148
+ The library uses ConnectionPool for thread-safe persistent HTTP connections:
149
+
150
+ ```ruby
151
+ @connection_pool = ConnectionPool.new(size: 5) do
152
+ HTTP.basic_auth(user: @account_id, pass: @license_key)
153
+ .persistent("https://#{host}")
154
+ end
155
+ ```
156
+
157
+ **Key Points:**
158
+ - Pool is created in `Minfraud.configure`
159
+ - Assessments and Reports use `Minfraud.connection_pool.with { |client| ... }`
160
+ - Enables persistent connections without manual management
161
+ - Safe for multi-threaded use
162
+ - Must call `Minfraud.configure` before using from multiple threads
163
+
164
+ ## Testing Conventions
165
+
166
+ ### Running Tests
167
+
168
+ ```bash
169
+ # Install dependencies
170
+ bundle install
171
+
172
+ # Run all tests
173
+ bundle exec rake spec
174
+
175
+ # Run tests and RuboCop
176
+ bundle exec rake # default task
177
+
178
+ # Run RuboCop only
179
+ bundle exec rake rubocop
180
+
181
+ # Run specific test file
182
+ bundle exec rspec spec/assessments_spec.rb
183
+
184
+ # Run specific test
185
+ bundle exec rspec spec/assessments_spec.rb:10
186
+ ```
187
+
188
+ ### Test Structure
189
+
190
+ Tests use RSpec and are organized by functionality:
191
+ - `spec/assessments_spec.rb` - Main assessment request builder tests
192
+ - `spec/report_spec.rb` - Report Transaction API tests
193
+ - `spec/components/*_spec.rb` - Component tests
194
+ - `spec/model/*_spec.rb` - Response model tests
195
+ - `spec/enum_spec.rb` - Enum validation tests
196
+ - `spec/http_spec.rb` - HTTP integration tests
197
+
198
+ ### Test Patterns
199
+
200
+ Tests use RSpec with test data hashes:
201
+
202
+ ```ruby
203
+ describe Minfraud::Model::Score do
204
+ let(:raw) do
205
+ {
206
+ 'disposition' => { 'action' => 'accept' },
207
+ 'funds_remaining' => 10.01,
208
+ 'id' => '27d26476-e2bc-11e4-92b8-962e705b4af5',
209
+ 'risk_score' => 0.01,
210
+ }
211
+ end
212
+
213
+ let(:model) { described_class.new(raw, ['en']) }
214
+
215
+ it 'has risk_score' do
216
+ expect(model.risk_score).to eq(0.01)
217
+ end
218
+ end
219
+ ```
220
+
221
+ When adding new fields:
222
+ 1. Add field to test data hash
223
+ 2. Add expectation to verify the field is accessible
224
+ 3. Test nil handling if field is optional
225
+ 4. Test validation if applicable
226
+
227
+ ## Working with This Codebase
228
+
229
+ ### Adding New Fields to Components
230
+
231
+ For input components (request data):
232
+
233
+ 1. **Add an attr_accessor**:
234
+ ```ruby
235
+ # A description of the field.
236
+ #
237
+ # @return [String, nil]
238
+ attr_accessor :new_field
239
+ ```
240
+
241
+ 2. **Add validation if needed**:
242
+ ```ruby
243
+ def new_field=(value)
244
+ validate_string('new_field', 255, value) if Minfraud.enable_validation
245
+ @new_field = value
246
+ end
247
+ ```
248
+
249
+ 3. **Update tests** to include the new field
250
+
251
+ ### Adding New Fields to Models
252
+
253
+ For output models (response data):
254
+
255
+ 1. **Add an attr_reader** (models are immutable):
256
+ ```ruby
257
+ # A description of the field.
258
+ #
259
+ # @return [Type, nil]
260
+ attr_reader :new_field
261
+ ```
262
+
263
+ 2. **Initialize in constructor**:
264
+ ```ruby
265
+ def initialize(record, locales)
266
+ super
267
+ @new_field = get('new_field')
268
+ end
269
+ ```
270
+
271
+ 3. **For nested objects**, create a model class:
272
+ ```ruby
273
+ @new_field = Minfraud::Model::NewField.new(get('new_field'))
274
+ ```
275
+
276
+ 4. **Update tests** with test data and expectations
277
+
278
+ ### Adding New Enum Values
279
+
280
+ When adding new values to an enum attribute:
281
+
282
+ 1. **Add to the enum_accessor array**:
283
+ ```ruby
284
+ enum_accessor :type, %i[
285
+ existing_value
286
+ new_value
287
+ ]
288
+ ```
289
+
290
+ 2. **Update CHANGELOG.md** with the new value
291
+ 3. **Add test** to verify the new value is accepted
292
+
293
+ ### Adding New Components
294
+
295
+ When creating a new component class:
296
+
297
+ 1. **Extend Base** and include Enum/Validates if needed:
298
+ ```ruby
299
+ class NewComponent < Base
300
+ include ::Minfraud::Enum
301
+ include ::Minfraud::Validates
302
+ end
303
+ ```
304
+
305
+ 2. **Add to Resolver::MAPPING** in lib/minfraud/resolver.rb:
306
+ ```ruby
307
+ MAPPING = {
308
+ new_component: ::Minfraud::Components::NewComponent,
309
+ # ...
310
+ }.freeze
311
+ ```
312
+
313
+ 3. **Add attr_accessor** to Assessments or Report
314
+ 4. **Require the file** in lib/minfraud.rb
315
+ 5. **Add tests** for the component
316
+
317
+ ### CHANGELOG.md Format
318
+
319
+ Always update `CHANGELOG.md` for user-facing changes.
320
+
321
+ **Important**: Do not add a date to changelog entries until release time.
322
+
323
+ - If there's an existing version entry without a date (e.g., `v2.9.0`), add your changes there
324
+ - If creating a new version entry, don't include a date - it will be added at release time
325
+ - Use past tense for descriptions
326
+ - Use bullet points starting with `*`
327
+
328
+ ```markdown
329
+ ## v2.10.0
330
+
331
+ * Added the `new_field` attribute to `Minfraud::Components::Device`. This
332
+ allows you to provide...
333
+ * Added the `/output/path` to the Insights response. This is available
334
+ as the `field_name` attribute on `Minfraud::Model::Insights`.
335
+ ```
336
+
337
+ ## Common Pitfalls and Solutions
338
+
339
+ ### Problem: Components Not Registering
340
+
341
+ A new component doesn't work when passed to Assessments.
342
+
343
+ **Solution**: Make sure you:
344
+ 1. Added the component to `Resolver::MAPPING`
345
+ 2. Required the file in lib/minfraud.rb
346
+ 3. Added the attr_accessor to Assessments or Report
347
+
348
+ ### Problem: Thread Safety Issues
349
+
350
+ Getting connection errors or state issues in multi-threaded code.
351
+
352
+ **Solution**:
353
+ - Always call `Minfraud.configure` before spawning threads
354
+ - Never share `Minfraud::Assessments` or `Minfraud::Report` objects across threads
355
+ - Create new Assessment/Report instances per thread
356
+
357
+ ### Problem: Validation Not Working
358
+
359
+ Validation doesn't catch invalid inputs.
360
+
361
+ **Solution**:
362
+ - Validation is disabled by default
363
+ - Enable with `Minfraud.configure { |c| c.enable_validation = true }`
364
+ - Make sure the component setter calls the appropriate `validate_*` method
365
+
366
+ ### Problem: Enum Values Rejected
367
+
368
+ A valid enum value raises `NotEnumValueError`.
369
+
370
+ **Solution**:
371
+ - Enum values must be symbols (`:value` not `"value"`)
372
+ - Check the enum_accessor definition includes the value
373
+ - Values are case-sensitive
374
+
375
+ ## Code Style Requirements
376
+
377
+ - **RuboCop enforced** with plugins: performance, rake, rspec, thread_safety
378
+ - **Frozen string literals** (`# frozen_string_literal: true`) in all files
379
+ - **Target Ruby 3.2+**
380
+ - **Max line length: 150 characters**
381
+ - **Hash alignment: table style** (aligned rockets and colons)
382
+ - **Trailing commas allowed** in arrays, hashes, and arguments
383
+ - **Use `if !condition`** instead of `unless condition` (NegatedIf disabled)
384
+ - **Metrics cops disabled** - AbcSize, ClassLength, MethodLength, etc.
385
+
386
+ Key RuboCop configurations:
387
+ - Guard clauses not enforced
388
+ - Conditional assignment not enforced
389
+ - Format string token checks disabled
390
+ - Multiple assertions allowed in RSpec tests
391
+ - Extra spacing with force equal sign alignment enabled
392
+
393
+ ## Development Workflow
394
+
395
+ ### Setup
396
+ ```bash
397
+ bundle install
398
+ ```
399
+
400
+ ### Before Committing
401
+ ```bash
402
+ # Run tests and linting
403
+ bundle exec rake
404
+
405
+ # Or run separately
406
+ bundle exec rake spec
407
+ bundle exec rake rubocop
408
+ ```
409
+
410
+ ### Running Single Test
411
+ ```bash
412
+ bundle exec rspec spec/assessments_spec.rb
413
+ ```
414
+
415
+ ### Testing Against Sandbox
416
+ ```ruby
417
+ Minfraud.configure do |c|
418
+ c.account_id = 12345
419
+ c.license_key = 'your_license_key'
420
+ c.host = 'sandbox.maxmind.com'
421
+ end
422
+ ```
423
+
424
+ ### Version Requirements
425
+ - **Ruby 3.2+** required
426
+ - Target compatibility should match current supported Ruby versions
427
+
428
+ ## API Endpoints
429
+
430
+ The library supports three assessment endpoints:
431
+ - `assessment.score` - Basic risk score (Score model)
432
+ - `assessment.insights` - Score + detailed fraud data (Insights model)
433
+ - `assessment.factors` - Insights + subscores + risk reasons (Factors model)
434
+
435
+ Report Transaction API:
436
+ - `reporter.report_transaction` - Report transaction outcomes
437
+
438
+ ## Additional Resources
439
+
440
+ - [API Documentation](https://www.rubydoc.info/gems/minfraud)
441
+ - [minFraud Web Services Docs](https://dev.maxmind.com/minfraud)
442
+ - [Report Transaction API Docs](https://dev.maxmind.com/minfraud/report-a-transaction)
443
+ - GitHub Issues: https://github.com/maxmind/minfraud-api-ruby/issues
data/README.md CHANGED
@@ -86,6 +86,7 @@ assessment = Minfraud::Assessments.new(
86
86
  user_agent: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.89 Safari/537.36',
87
87
  },
88
88
  event: {
89
+ party: :customer,
89
90
  transaction_id: 'txn3134133',
90
91
  shop_id: 's2123',
91
92
  time: '2012-04-12T23:20:50+00:00',
@@ -127,6 +128,7 @@ assessment = Minfraud::Assessments.new(
127
128
  delivery_speed: :same_day,
128
129
  },
129
130
  payment: {
131
+ method: :card,
130
132
  processor: :stripe,
131
133
  was_authorized: false,
132
134
  decline_code: 'invalid number',
@@ -275,7 +277,7 @@ to the client API, please see
275
277
 
276
278
  ## Requirements
277
279
 
278
- This gem works with Ruby 3.0 and above.
280
+ This gem works with Ruby 3.2 and above.
279
281
 
280
282
  ## Contributing
281
283
 
data/Rakefile CHANGED
@@ -8,5 +8,5 @@ RSpec::Core::RakeTask.new(:spec)
8
8
 
9
9
  RuboCop::RakeTask.new
10
10
 
11
- task default: :spec
12
- task default: :rubocop
11
+ desc 'Run tests and RuboCop'
12
+ task default: %i[spec rubocop]
@@ -42,6 +42,8 @@ module Minfraud
42
42
 
43
43
  # Keys that have to remain boolean
44
44
  BOOLS = %w[was_authorized is_gift has_gift_message].freeze
45
+
46
+ private_constant :BOOLS
45
47
  end
46
48
  end
47
49
  end
@@ -99,7 +99,7 @@ module Minfraud
99
99
  end
100
100
 
101
101
  if domain == 'gmail.com'
102
- local_part.gsub!('.', '')
102
+ local_part.delete!('.')
103
103
  end
104
104
 
105
105
  domain_parts = domain.split('.')
@@ -384,7 +384,7 @@ module Minfraud
384
384
 
385
385
  idx = domain.rindex('.')
386
386
  if !idx.nil?
387
- tld = domain[idx + 1..]
387
+ tld = domain[(idx + 1)..]
388
388
  if TYPO_TLDS.key?(tld)
389
389
  domain = "#{domain[0, idx]}.#{TYPO_TLDS[tld]}"
390
390
  end
@@ -9,6 +9,18 @@ module Minfraud
9
9
  include ::Minfraud::Enum
10
10
  include Minfraud::Validates
11
11
 
12
+ # The party submitting the transaction. This must be one of +:agent+ or
13
+ # +:customer+.
14
+ #
15
+ # @!attribute party
16
+ #
17
+ # @return [Symbol, nil]
18
+ enum_accessor :party,
19
+ %i[
20
+ agent
21
+ customer
22
+ ]
23
+
12
24
  # Your internal ID for the transaction. MaxMind can use this to locate a
13
25
  # specific transaction in logs, and it will also show up in email alerts
14
26
  # and notifications from MaxMind to you. No specific format is required.
@@ -35,9 +47,10 @@ module Minfraud
35
47
  attr_accessor :time
36
48
 
37
49
  # The type of event being scored. This must be one of
38
- # +:account_creation+, +:account_login+, +:email_change+,
39
- # +:password_reset+, +:payout_change+, +:purchase+,
40
- # +:recurring_purchase+, +:referral+, or +:survey+.
50
+ # +:account_creation+, +:account_login+, +:credit_application+,
51
+ # +:email_change+, +:fund_transfer+, +:password_reset+,
52
+ # +:payout_change+, +:purchase+, +:recurring_purchase+, +:referral+,
53
+ # +:sim_swap+, or +:survey+.
41
54
  #
42
55
  # @!attribute type
43
56
  #
@@ -46,18 +59,22 @@ module Minfraud
46
59
  %i[
47
60
  account_creation
48
61
  account_login
62
+ credit_application
49
63
  email_change
64
+ fund_transfer
50
65
  password_reset
51
66
  payout_change
52
67
  purchase
53
68
  recurring_purchase
54
69
  referral
70
+ sim_swap
55
71
  survey
56
72
  ]
57
73
 
58
74
  # @param params [Hash] Hash of parameters. Each key/value should
59
75
  # correspond to one of the available attributes.
60
76
  def initialize(params = {})
77
+ self.party = params[:party]
61
78
  @transaction_id = params[:transaction_id]
62
79
  @shop_id = params[:shop_id]
63
80
  @time = params[:time]
@@ -9,6 +9,28 @@ module Minfraud
9
9
  include ::Minfraud::Enum
10
10
  include Minfraud::Validates
11
11
 
12
+ # The payment method associated with the transaction. This must be one of
13
+ # +:bank_debit+, +:bank_redirect+, +:bank_transfer+, +:buy_now_pay_later+,
14
+ # +:card+, +:crypto+, +:digital_wallet+, +:gift_card+,
15
+ # +:real_time_payment+, or +:rewards+.
16
+ #
17
+ # @!attribute method
18
+ #
19
+ # @return [Symbol, nil]
20
+ enum_accessor :method,
21
+ %i[
22
+ bank_debit
23
+ bank_redirect
24
+ bank_transfer
25
+ buy_now_pay_later
26
+ card
27
+ crypto
28
+ digital_wallet
29
+ gift_card
30
+ real_time_payment
31
+ rewards
32
+ ]
33
+
12
34
  # The payment processor used for the transaction. The value is one
13
35
  # listed as a valid value, as a symbol.
14
36
  #
@@ -50,6 +72,7 @@ module Minfraud
50
72
  coregateway
51
73
  creditguard
52
74
  credorax
75
+ cryptomus
53
76
  ct_payments
54
77
  cuentadigital
55
78
  curopayments
@@ -146,6 +169,7 @@ module Minfraud
146
169
  rewardspay
147
170
  safecharge
148
171
  sagepay
172
+ securepay
149
173
  securetrading
150
174
  shopify_payments
151
175
  simplify_commerce
@@ -194,6 +218,7 @@ module Minfraud
194
218
  def initialize(params = {})
195
219
  @was_authorized = params[:was_authorized]
196
220
  @decline_code = params[:decline_code]
221
+ self.method = params[:method]
197
222
  self.processor = params[:processor]
198
223
 
199
224
  validate
@@ -95,15 +95,15 @@ module Minfraud
95
95
  validate_uuid('minfraud_id', @minfraud_id)
96
96
 
97
97
  if ip_address.nil? &&
98
- (minfraud_id.nil? || empty_uuid(minfraud_id)) &&
98
+ (minfraud_id.nil? || empty_uuid?(minfraud_id)) &&
99
99
  (maxmind_id.nil? || maxmind_id.empty?) &&
100
100
  (transaction_id.nil? || transaction_id.empty?)
101
101
  raise ArgumentError, 'At least one of the following is required: ip_address, minfraud_id, maxmind_id, transaction_id.'
102
102
  end
103
103
  end
104
104
 
105
- def empty_uuid(value)
106
- stripped_value = value.to_s.gsub('-', '')
105
+ def empty_uuid?(value)
106
+ stripped_value = value.to_s.delete('-')
107
107
  stripped_value == '0' * 32
108
108
  end
109
109
  end
@@ -17,7 +17,7 @@ module Minfraud
17
17
  # to an item's field, or as a Minfraud:::Components::ShoppingCartItem
18
18
  # object.
19
19
  def initialize(params = [])
20
- @items = params.map(&method(:resolve))
20
+ @items = params.map { |param| resolve(param) }
21
21
  end
22
22
 
23
23
  # A JSON representation of Minfraud::Components::ShoppingCart items.
data/lib/minfraud/enum.rb CHANGED
@@ -15,7 +15,10 @@ module Minfraud
15
15
  #
16
16
  # @return [Hash]
17
17
  def mapping
18
+ # rubocop:disable ThreadSafety/ClassInstanceVariable
19
+ # This is a false positive - this is set during class definition and then only read
18
20
  @mapping ||= {}
21
+ # rubocop:enable ThreadSafety/ClassInstanceVariable
19
22
  end
20
23
 
21
24
  # Create a set of methods for enum-like behavior of the attribute.
@@ -71,6 +71,8 @@ module Minfraud
71
71
  insights: Minfraud::Model::Insights,
72
72
  score: Minfraud::Model::Score
73
73
  }.freeze
74
+
75
+ private_constant :ENDPOINT_TO_CLASS
74
76
  end
75
77
  end
76
78
  end
@@ -1,11 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'minfraud/model/abstract'
4
+ require 'minfraud/model/email_domain_visit'
4
5
 
5
6
  module Minfraud
6
7
  module Model
7
8
  # Model containing information about the email domain.
8
9
  class EmailDomain < Abstract
10
+ # A classification of the domain. Possible values are:
11
+ # * business
12
+ # * education
13
+ # * government
14
+ # * isp_email
15
+ #
16
+ # @return [String, nil]
17
+ attr_reader :classification
18
+
9
19
  # A date string (e.g. 2017-04-24) to identify the date an email domain
10
20
  # was first seen by MaxMind. This is expressed using the ISO 8601 date
11
21
  # format.
@@ -13,11 +23,33 @@ module Minfraud
13
23
  # @return [String, nil]
14
24
  attr_reader :first_seen
15
25
 
26
+ # A risk score ranging from 0.01 to 99. Higher values indicate greater
27
+ # risk.
28
+ #
29
+ # @return [Float, nil]
30
+ attr_reader :risk
31
+
32
+ # An object containing information about an automated visit to the email
33
+ # domain.
34
+ #
35
+ # @return [Minfraud::Model::EmailDomainVisit, nil]
36
+ attr_reader :visit
37
+
38
+ # Activity across the minFraud network expressed as sightings per
39
+ # million. The value ranges from 0.001 to 1,000,000.
40
+ #
41
+ # @return [Float, nil]
42
+ attr_reader :volume
43
+
16
44
  # @!visibility private
17
45
  def initialize(record)
18
46
  super
19
47
 
20
- @first_seen = get('first_seen')
48
+ @classification = get('classification')
49
+ @first_seen = get('first_seen')
50
+ @risk = get('risk')
51
+ @visit = EmailDomainVisit.new(get('visit'))
52
+ @volume = get('volume')
21
53
  end
22
54
  end
23
55
  end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'minfraud/model/abstract'
4
+
5
+ module Minfraud
6
+ module Model
7
+ # Model containing information about an automated visit to the email
8
+ # domain.
9
+ class EmailDomainVisit < Abstract
10
+ # Whether the domain redirects to another URL.
11
+ #
12
+ # @return [Boolean, nil]
13
+ attr_reader :has_redirect
14
+
15
+ # A date string (e.g. 2025-11-15) identifying the date the automated
16
+ # visit was completed. This is expressed using the ISO 8601 date format.
17
+ #
18
+ # @return [String, nil]
19
+ attr_reader :last_visited_on
20
+
21
+ # The status of the domain. Possible values are:
22
+ # * live - The domain is operational and serving content.
23
+ # * dns_error - The domain has missing, expired, or misconfigured DNS.
24
+ # * network_error - The domain is offline or unreachable.
25
+ # * http_error - The domain is reachable, but has an application error.
26
+ # * parked - The domain is reachable and in a parked state.
27
+ # * pre_development - The domain is reachable and in a pre-development
28
+ # state.
29
+ #
30
+ # @return [String, nil]
31
+ attr_reader :status
32
+
33
+ # @!visibility private
34
+ def initialize(record)
35
+ super
36
+
37
+ @has_redirect = get('has_redirect')
38
+ @last_visited_on = get('last_visited_on')
39
+ @status = get('status')
40
+ end
41
+ end
42
+ end
43
+ end
@@ -21,6 +21,15 @@ module Minfraud
21
21
  # @return [Boolean, nil]
22
22
  attr_reader :is_voip
23
23
 
24
+ # This property is true if the phone number's prefix is commonly
25
+ # associated with the postal code. It is false if the prefix is not
26
+ # associated with the postal code. It is non-nil only when the phone
27
+ # number is in the US, the number prefix is in our database, and the
28
+ # postal code and country are provided in the request.
29
+ #
30
+ # @return [Boolean, nil]
31
+ attr_reader :matches_postal
32
+
24
33
  # The name of the original network operator associated with the phone
25
34
  # number. This attribute does not reflect phone numbers that have been
26
35
  # ported from the original operator to another, nor does it identify
@@ -41,6 +50,7 @@ module Minfraud
41
50
 
42
51
  @country = get('country')
43
52
  @is_voip = get('is_voip')
53
+ @matches_postal = get('matches_postal')
44
54
  @network_operator = get('network_operator')
45
55
  @number_type = get('number_type')
46
56
  end
@@ -14,9 +14,9 @@ module Minfraud
14
14
  # @return [Float]
15
15
  attr_reader :multiplier
16
16
 
17
- # This field contains Risk objects that describe one of the reasons for the multiplier.
17
+ # Reasons for the multiplier.
18
18
  #
19
- # @return [Array<Minfraud::Model::Risk>]
19
+ # @return [Array<Minfraud::Model::Reason>]
20
20
  attr_reader :reasons
21
21
 
22
22
  # @!visibility private
@@ -27,7 +27,7 @@ module Minfraud
27
27
  def validate_uuid(field, value)
28
28
  return if !value
29
29
 
30
- stripped_value = value.to_s.gsub('-', '')
30
+ stripped_value = value.to_s.delete('-')
31
31
 
32
32
  # Define a regex pattern for a valid UUID without dashes
33
33
  uuid_regex = /\A[0-9a-f]{32}\z/i
@@ -78,7 +78,7 @@ module Minfraud
78
78
  raise InvalidInputError, "The #{field} value is not valid. It must contain only non-space printable ASCII characters."
79
79
  end
80
80
 
81
- if /\A[0-9]{1,19}\z/.match(s)
81
+ if /\A[0-9]{1,19}\z/.match?(s)
82
82
  raise InvalidInputError, "The #{field} value is not valid. If it is all digits, it must be longer than 19 characters."
83
83
  end
84
84
  end
@@ -147,7 +147,7 @@ module Minfraud
147
147
  def validate_email(field, value)
148
148
  return if !value
149
149
 
150
- if /.@./.match(value)
150
+ if /.@./.match?(value)
151
151
  validate_string(field, 255, value)
152
152
  return
153
153
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Minfraud
4
4
  # The Gem version.
5
- VERSION = '2.7.1'
5
+ VERSION = '2.9.0'
6
6
  end
data/lib/minfraud.rb CHANGED
@@ -32,6 +32,9 @@ require 'minfraud/report'
32
32
  # for the gem's classes.
33
33
  module Minfraud
34
34
  class << self
35
+ # rubocop:disable ThreadSafety/ClassAndModuleAttributes
36
+ # This is a false positive - these configuration attributes are set during initialization
37
+
35
38
  # The MaxMind account ID that is used for authorization.
36
39
  #
37
40
  # @return [Integer, nil]
@@ -57,6 +60,8 @@ module Minfraud
57
60
  # @return [String, nil]
58
61
  attr_accessor :license_key
59
62
 
63
+ # rubocop:enable ThreadSafety/ClassAndModuleAttributes
64
+
60
65
  # @!visibility private
61
66
  attr_reader :connection_pool
62
67
 
@@ -67,8 +72,11 @@ module Minfraud
67
72
  yield self
68
73
 
69
74
  pool_size = 5
75
+ # rubocop:disable ThreadSafety/ClassInstanceVariable
76
+ # This is a false positive - this configuration is set during initialization
70
77
  host = @host.nil? ? 'minfraud.maxmind.com' : @host
71
78
  @connection_pool = ConnectionPool.new(size: pool_size) do
79
+ # rubocop:enable ThreadSafety/ClassInstanceVariable
72
80
  make_http_client.persistent("https://#{host}")
73
81
  end
74
82
  end
@@ -76,10 +84,13 @@ module Minfraud
76
84
  private
77
85
 
78
86
  def make_http_client
87
+ # rubocop:disable ThreadSafety/ClassInstanceVariable
88
+ # This is a false positive - this configuration is set during initialization
79
89
  HTTP.basic_auth(
80
90
  user: @account_id,
81
91
  pass: @license_key,
82
92
  ).headers(
93
+ # rubocop:enable ThreadSafety/ClassInstanceVariable
83
94
  accept: 'application/json',
84
95
  user_agent: "minfraud-api-ruby/#{Minfraud::VERSION} ruby/#{RUBY_VERSION} http/#{HTTP::VERSION}",
85
96
  )
data/minfraud.gemspec CHANGED
@@ -15,7 +15,7 @@ Gem::Specification.new do |spec|
15
15
  spec.homepage = 'https://github.com/maxmind/minfraud-api-ruby'
16
16
  spec.license = 'MIT'
17
17
 
18
- spec.required_ruby_version = '>= 3.0'
18
+ spec.required_ruby_version = '>= 3.2'
19
19
 
20
20
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^.gitignore$|^(?:\.github|dev-bin|spec)/}) }
21
21
  spec.bindir = 'exe'
@@ -24,13 +24,17 @@ Gem::Specification.new do |spec|
24
24
 
25
25
  spec.add_dependency 'connection_pool', '~> 2.2'
26
26
  spec.add_dependency 'http', '>= 4.3', '< 6.0'
27
- spec.add_dependency 'maxmind-geoip2', '~> 1.2'
27
+ spec.add_dependency 'maxmind-geoip2', '~> 1.4'
28
28
  spec.add_dependency 'simpleidn', '~> 0.1', '>= 0.1.1'
29
29
 
30
30
  spec.add_development_dependency 'bundler', '~> 2.2'
31
31
  spec.add_development_dependency 'rake', '~> 13.0'
32
32
  spec.add_development_dependency 'rspec', '~> 3.0'
33
33
  spec.add_development_dependency 'rubocop', '~> 1.23'
34
+ spec.add_development_dependency 'rubocop-performance'
35
+ spec.add_development_dependency 'rubocop-rake'
36
+ spec.add_development_dependency 'rubocop-rspec'
37
+ spec.add_development_dependency 'rubocop-thread_safety'
34
38
  spec.add_development_dependency 'webmock', '~> 3.14'
35
39
  spec.metadata = {
36
40
  'rubygems_mfa_required' => 'true'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: minfraud
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.7.1
4
+ version: 2.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - kushnir.yb
8
8
  - William Storey
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-02-10 00:00:00.000000000 Z
11
+ date: 1980-01-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: connection_pool
@@ -50,14 +50,14 @@ dependencies:
50
50
  requirements:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: '1.2'
53
+ version: '1.4'
54
54
  type: :runtime
55
55
  prerelease: false
56
56
  version_requirements: !ruby/object:Gem::Requirement
57
57
  requirements:
58
58
  - - "~>"
59
59
  - !ruby/object:Gem::Version
60
- version: '1.2'
60
+ version: '1.4'
61
61
  - !ruby/object:Gem::Dependency
62
62
  name: simpleidn
63
63
  requirement: !ruby/object:Gem::Requirement
@@ -134,6 +134,62 @@ dependencies:
134
134
  - - "~>"
135
135
  - !ruby/object:Gem::Version
136
136
  version: '1.23'
137
+ - !ruby/object:Gem::Dependency
138
+ name: rubocop-performance
139
+ requirement: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ version: '0'
144
+ type: :development
145
+ prerelease: false
146
+ version_requirements: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ - !ruby/object:Gem::Dependency
152
+ name: rubocop-rake
153
+ requirement: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ type: :development
159
+ prerelease: false
160
+ version_requirements: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
165
+ - !ruby/object:Gem::Dependency
166
+ name: rubocop-rspec
167
+ requirement: !ruby/object:Gem::Requirement
168
+ requirements:
169
+ - - ">="
170
+ - !ruby/object:Gem::Version
171
+ version: '0'
172
+ type: :development
173
+ prerelease: false
174
+ version_requirements: !ruby/object:Gem::Requirement
175
+ requirements:
176
+ - - ">="
177
+ - !ruby/object:Gem::Version
178
+ version: '0'
179
+ - !ruby/object:Gem::Dependency
180
+ name: rubocop-thread_safety
181
+ requirement: !ruby/object:Gem::Requirement
182
+ requirements:
183
+ - - ">="
184
+ - !ruby/object:Gem::Version
185
+ version: '0'
186
+ type: :development
187
+ prerelease: false
188
+ version_requirements: !ruby/object:Gem::Requirement
189
+ requirements:
190
+ - - ">="
191
+ - !ruby/object:Gem::Version
192
+ version: '0'
137
193
  - !ruby/object:Gem::Dependency
138
194
  name: webmock
139
195
  requirement: !ruby/object:Gem::Requirement
@@ -157,6 +213,7 @@ files:
157
213
  - ".rspec"
158
214
  - ".rubocop.yml"
159
215
  - CHANGELOG.md
216
+ - CLAUDE.md
160
217
  - CODE_OF_CONDUCT.md
161
218
  - Gemfile
162
219
  - LICENSE.txt
@@ -194,6 +251,7 @@ files:
194
251
  - lib/minfraud/model/disposition.rb
195
252
  - lib/minfraud/model/email.rb
196
253
  - lib/minfraud/model/email_domain.rb
254
+ - lib/minfraud/model/email_domain_visit.rb
197
255
  - lib/minfraud/model/error.rb
198
256
  - lib/minfraud/model/factors.rb
199
257
  - lib/minfraud/model/geoip2_location.rb
@@ -226,14 +284,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
226
284
  requirements:
227
285
  - - ">="
228
286
  - !ruby/object:Gem::Version
229
- version: '3.0'
287
+ version: '3.2'
230
288
  required_rubygems_version: !ruby/object:Gem::Requirement
231
289
  requirements:
232
290
  - - ">="
233
291
  - !ruby/object:Gem::Version
234
292
  version: '0'
235
293
  requirements: []
236
- rubygems_version: 3.6.2
294
+ rubygems_version: 3.6.9
237
295
  specification_version: 4
238
296
  summary: Ruby API for the minFraud Score, Insights, Factors, and Report Transactions
239
297
  services