diff --git a/CHANGELOG.md b/CHANGELOG.md index 701e5aea..8e340f9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,12 @@ CHANGELOG ========= -4.1.0 +4.1.0 (2026-01-20) ------------------ +* Added `anonymizer` property to `IpAddress` response model. This contains + information about whether the IP address is an anonymous network, including + confidence score, VPN provider name, and various anonymizer flags. * Added `BANQUEST`, `SUMMIT_PAYMENTS`, and `YAADPAY` to the `Payment.Processor` enum. diff --git a/README.md b/README.md index 1b0690f3..2d4519f3 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ To do this, add the dependency to your pom.xml: com.maxmind.minfraud minfraud - 4.0.0 + 4.1.0 ``` @@ -29,7 +29,7 @@ repositories { mavenCentral() } dependencies { - implementation 'com.maxmind.minfraud:minfraud:4.0.0' + implementation 'com.maxmind.minfraud:minfraud:4.1.0' } ``` @@ -300,6 +300,6 @@ This API uses [Semantic Versioning](https://semver.org/). ## Copyright and License ## -This software is Copyright (c) 2015-2025 by MaxMind, Inc. +This software is Copyright (c) 2015-2026 by MaxMind, Inc. This is free software, licensed under the Apache License, Version 2.0. diff --git a/pom.xml b/pom.xml index 0a5d23b5..4f416f00 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.maxmind.minfraud minfraud - 4.0.0 + 4.1.0 MaxMind minFraud API MaxMind minFraud Score, Insights, Factors and Report Transaction API http://dev.maxmind.com/minfraud @@ -53,22 +53,22 @@ com.fasterxml.jackson.core jackson-core - 2.20.1 + 2.21.0 com.fasterxml.jackson.core jackson-databind - 2.20.1 + 2.21.0 com.fasterxml.jackson.core jackson-annotations - 2.20 + 2.21 com.fasterxml.jackson.datatype jackson-datatype-jsr310 - 2.20.1 + 2.21.0 com.maxmind.geoip2 @@ -96,7 +96,7 @@ com.fasterxml.jackson.jr jackson-jr-objects - 2.20.1 + 2.21.0 test @@ -252,7 +252,7 @@ org.codehaus.mojo versions-maven-plugin - 2.20.1 + 2.21.0 org.sonatype.central @@ -270,7 +270,7 @@ UTF-8 - 4.0.0 + 4.1.0 diff --git a/src/main/java/com/maxmind/minfraud/response/IpAddress.java b/src/main/java/com/maxmind/minfraud/response/IpAddress.java index 1644fe50..266bce76 100644 --- a/src/main/java/com/maxmind/minfraud/response/IpAddress.java +++ b/src/main/java/com/maxmind/minfraud/response/IpAddress.java @@ -1,6 +1,7 @@ package com.maxmind.minfraud.response; import com.fasterxml.jackson.annotation.JsonProperty; +import com.maxmind.geoip2.record.Anonymizer; import com.maxmind.geoip2.record.City; import com.maxmind.geoip2.record.Continent; import com.maxmind.geoip2.record.Country; @@ -14,6 +15,8 @@ /** * This class contains minFraud response data related to the IP location. * + * @param anonymizer Anonymizer record for the requested IP address. This contains + * information about whether the IP address is an anonymous network. * @param city City record for the requested IP address. * @param continent Continent record for the requested IP address. * @param country Country record for the requested IP address. @@ -29,6 +32,8 @@ * @param traits Traits record for the requested IP address. */ public record IpAddress( + @JsonProperty("anonymizer") + Anonymizer anonymizer, @JsonProperty("city") City city, @@ -67,6 +72,7 @@ public record IpAddress( * Compact canonical constructor that sets defaults for null values. */ public IpAddress { + anonymizer = anonymizer != null ? anonymizer : new Anonymizer(); location = location != null ? location : new GeoIp2Location(); riskReasons = riskReasons != null ? List.copyOf(riskReasons) : List.of(); subdivisions = subdivisions != null ? List.copyOf(subdivisions) : List.of(); @@ -77,7 +83,42 @@ public record IpAddress( */ public IpAddress() { this(null, null, null, null, null, null, - null, null, null, null, null); + null, null, null, null, null, null); + } + + /** + * Constructs an instance of {@code IpAddress}. + * + * @param city City record for the requested IP address. + * @param continent Continent record for the requested IP address. + * @param country Country record for the requested IP address. + * @param location Location record for the requested IP address. + * @param postal Postal record for the requested IP address. + * @param registeredCountry Registered country record for the requested IP address. + * @param representedCountry Represented country record for the requested IP address. + * @param risk The risk associated with the IP address. + * @param riskReasons List of risk reason objects. + * @param subdivisions List of subdivision records for the requested IP address. + * @param traits Traits record for the requested IP address. + * @deprecated Use the canonical constructor instead. This constructor will be removed in 5.0.0. + */ + @Deprecated(since = "4.1.0", forRemoval = true) + public IpAddress( + City city, + Continent continent, + Country country, + GeoIp2Location location, + Postal postal, + Country registeredCountry, + RepresentedCountry representedCountry, + Double risk, + List riskReasons, + List subdivisions, + Traits traits + ) { + this(null, city, continent, country, location, postal, + registeredCountry, representedCountry, risk, riskReasons, + subdivisions, traits); } /** diff --git a/src/test/java/com/maxmind/minfraud/response/InsightsResponseTest.java b/src/test/java/com/maxmind/minfraud/response/InsightsResponseTest.java index b9a36735..fd4f71e5 100644 --- a/src/test/java/com/maxmind/minfraud/response/InsightsResponseTest.java +++ b/src/test/java/com/maxmind/minfraud/response/InsightsResponseTest.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import com.fasterxml.jackson.jr.ob.JSON; @@ -28,6 +29,17 @@ public void testInsights() throws Exception { .end() .end() .startObjectField("ip_address") + .startObjectField("anonymizer") + .put("confidence", 99) + .put("is_anonymous", true) + .put("is_anonymous_vpn", true) + .put("is_hosting_provider", true) + .put("is_public_proxy", true) + .put("is_residential_proxy", true) + .put("is_tor_exit_node", true) + .put("network_last_seen", "2025-01-15") + .put("provider_name", "TestVPN") + .end() .startObjectField("country") .put("iso_code", "US") .end() @@ -120,5 +132,25 @@ public void testInsights() throws Exception { ); assertEquals("152.216.7.110", insights.ipAddress().traits().ipAddress().getHostAddress()); assertEquals("81.2.69.0/24", insights.ipAddress().traits().network().toString()); + + // Test anonymizer + assertNotNull(insights.ipAddress().anonymizer(), "anonymizer should not be null"); + assertEquals( + Integer.valueOf(99), + insights.ipAddress().anonymizer().confidence(), + "correct anonymizer confidence" + ); + assertTrue(insights.ipAddress().anonymizer().isAnonymous(), "correct isAnonymous"); + assertTrue(insights.ipAddress().anonymizer().isAnonymousVpn(), "correct isAnonymousVpn"); + assertTrue(insights.ipAddress().anonymizer().isHostingProvider(), "correct isHostingProvider"); + assertTrue(insights.ipAddress().anonymizer().isPublicProxy(), "correct isPublicProxy"); + assertTrue(insights.ipAddress().anonymizer().isResidentialProxy(), "correct isResidentialProxy"); + assertTrue(insights.ipAddress().anonymizer().isTorExitNode(), "correct isTorExitNode"); + assertEquals( + LocalDate.parse("2025-01-15"), + insights.ipAddress().anonymizer().networkLastSeen(), + "correct networkLastSeen" + ); + assertEquals("TestVPN", insights.ipAddress().anonymizer().providerName(), "correct providerName"); } } diff --git a/src/test/resources/test-data/factors-response.json b/src/test/resources/test-data/factors-response.json index b9832db7..1e8b789b 100644 --- a/src/test/resources/test-data/factors-response.json +++ b/src/test/resources/test-data/factors-response.json @@ -15,6 +15,17 @@ "reason": "Suspicious activity has been seen on this IP address across minFraud customers." } ], + "anonymizer": { + "confidence": 99, + "is_anonymous": true, + "is_anonymous_vpn": true, + "is_hosting_provider": true, + "is_public_proxy": true, + "is_residential_proxy": true, + "is_tor_exit_node": true, + "network_last_seen": "2025-01-15", + "provider_name": "TestVPN" + }, "city": { "confidence": 42, "geoname_id": 2643743, diff --git a/src/test/resources/test-data/insights-response.json b/src/test/resources/test-data/insights-response.json index 80528c05..643f09ab 100644 --- a/src/test/resources/test-data/insights-response.json +++ b/src/test/resources/test-data/insights-response.json @@ -15,6 +15,17 @@ "reason": "Suspicious activity has been seen on this IP address across minFraud customers." } ], + "anonymizer": { + "confidence": 99, + "is_anonymous": true, + "is_anonymous_vpn": true, + "is_hosting_provider": true, + "is_public_proxy": true, + "is_residential_proxy": true, + "is_tor_exit_node": true, + "network_last_seen": "2025-01-15", + "provider_name": "TestVPN" + }, "city": { "confidence": 42, "geoname_id": 2643743,