diff --git a/CHANGELOG.md b/CHANGELOG.md index 829c3e4c..78767fb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,14 @@ CHANGELOG ========= -3.6.0 +3.6.0 (2026-01-20) ------------------ +* Added the `anonymizer` property to `MaxMind\MinFraud\Model\IpAddress`. This + contains anonymizer data from the GeoIP2 Insights response, including VPN + detection confidence, provider name, and network last seen date. This was + previously available in the GeoIP2 library but not exposed in minFraud + responses. * Added `banquest`, `summit_payments`, and `yaadpay` to the payment processor validation. diff --git a/README.md b/README.md index 5b299c54..4392e5b3 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ You should now have the file `composer.phar` in your project directory. Run in your project root: ``` -php composer.phar require maxmind/minfraud:^3.5.0 +php composer.phar require maxmind/minfraud:^3.6.0 ``` You should now have the files `composer.json` and `composer.lock` as well as diff --git a/src/MinFraud/Model/IpAddress.php b/src/MinFraud/Model/IpAddress.php index 4c4ea43b..3c671688 100644 --- a/src/MinFraud/Model/IpAddress.php +++ b/src/MinFraud/Model/IpAddress.php @@ -5,6 +5,7 @@ namespace MaxMind\MinFraud\Model; use GeoIp2\Model\Insights; +use GeoIp2\Record\Anonymizer; use GeoIp2\Record\City; use GeoIp2\Record\Continent; use GeoIp2\Record\Country; @@ -18,6 +19,13 @@ */ class IpAddress implements \JsonSerializable { + /** + * @var Anonymizer data for the anonymizer status of the requested IP + * address. This object contains information about whether + * the IP address belongs to an anonymous network. + */ + public readonly Anonymizer $anonymizer; + /** * @var City city data for the requested IP address */ @@ -123,6 +131,7 @@ public function __construct(?array $response, array $locales = ['en']) $this->representedCountry = $insights->representedCountry; $this->subdivisions = $insights->subdivisions; $this->traits = $insights->traits; + $this->anonymizer = $insights->anonymizer; $this->location = new GeoIp2Location($response['location'] ?? []); $this->risk = $response['risk'] ?? null; @@ -143,6 +152,11 @@ public function jsonSerialize(): ?array { $js = []; + $anonymizer = $this->anonymizer->jsonSerialize(); + if (!empty($anonymizer)) { + $js['anonymizer'] = $anonymizer; + } + $city = $this->city->jsonSerialize(); if (!empty($city)) { $js['city'] = $city; diff --git a/src/MinFraud/ServiceClient.php b/src/MinFraud/ServiceClient.php index 87e973a8..f1e2ce02 100644 --- a/src/MinFraud/ServiceClient.php +++ b/src/MinFraud/ServiceClient.php @@ -9,7 +9,7 @@ abstract class ServiceClient { - public const VERSION = 'v3.5.0'; + public const VERSION = 'v3.6.0'; /** * @var Client diff --git a/tests/MaxMind/Test/MinFraud/Model/InsightsTest.php b/tests/MaxMind/Test/MinFraud/Model/InsightsTest.php index 9b788764..a5046383 100644 --- a/tests/MaxMind/Test/MinFraud/Model/InsightsTest.php +++ b/tests/MaxMind/Test/MinFraud/Model/InsightsTest.php @@ -124,6 +124,38 @@ public function testInsightsProperties(): void 'correct mobile network code' ); + // Test anonymizer object + foreach ([ + 'isAnonymous', + 'isAnonymousVpn', + 'isHostingProvider', + 'isPublicProxy', + 'isTorExitNode', + ] as $property) { + $this->assertTrue( + $insights->ipAddress->anonymizer->{$property}, + "anonymizer {$property} is true" + ); + } + + $this->assertSame( + $array['ip_address']['anonymizer']['confidence'], + $insights->ipAddress->anonymizer->confidence, + 'correct anonymizer confidence' + ); + + $this->assertSame( + $array['ip_address']['anonymizer']['provider_name'], + $insights->ipAddress->anonymizer->providerName, + 'correct anonymizer provider name' + ); + + $this->assertSame( + $array['ip_address']['anonymizer']['network_last_seen'], + $insights->ipAddress->anonymizer->networkLastSeen, + 'correct anonymizer network last seen' + ); + $this->assertSame( $array['billing_phone']['country'], $insights->billingPhone->country, diff --git a/tests/data/minfraud/factors-response.json b/tests/data/minfraud/factors-response.json index 0e951022..dea20485 100644 --- a/tests/data/minfraud/factors-response.json +++ b/tests/data/minfraud/factors-response.json @@ -111,6 +111,16 @@ "network": "152.216.7.0/24", "organization": "STONEHOUSE office network", "user_type": "government" + }, + "anonymizer": { + "confidence": 99, + "is_anonymous": true, + "is_anonymous_vpn": true, + "is_hosting_provider": true, + "is_public_proxy": true, + "is_tor_exit_node": true, + "network_last_seen": "2025-01-15", + "provider_name": "TestVPN" } }, "billing_address": { diff --git a/tests/data/minfraud/insights-response.json b/tests/data/minfraud/insights-response.json index ca420943..6af029e0 100644 --- a/tests/data/minfraud/insights-response.json +++ b/tests/data/minfraud/insights-response.json @@ -111,6 +111,16 @@ "network": "152.216.7.0/24", "organization": "STONEHOUSE office network", "user_type": "government" + }, + "anonymizer": { + "confidence": 99, + "is_anonymous": true, + "is_anonymous_vpn": true, + "is_hosting_provider": true, + "is_public_proxy": true, + "is_tor_exit_node": true, + "network_last_seen": "2025-01-15", + "provider_name": "TestVPN" } }, "billing_address": {