Skip to content

Commit b2c6e38

Browse files
oschwaldclaude
andcommitted
Add email verification inputs
Add was_verification_successful and verification_time to withEmail(), as array keys or named arguments. These describe the most recent verification of the email address. As an array value, the success flag must be a boolean, even with input validation disabled. With input validation enabled, the time must be an RFC 3339 date-time. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 7f759ba commit b2c6e38

5 files changed

Lines changed: 160 additions & 13 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ CHANGELOG
4646
`phoneWasVerificationSuccessful`, and `phoneVerificationTime` named
4747
arguments. Omit `phone_was_verification_successful` if no verification was
4848
attempted.
49+
* Added the `was_verification_successful` and `verification_time` inputs to
50+
the `/email` object. These describe the most recent verification of the
51+
email address. Use them with `withEmail()` as array keys or as the
52+
`wasVerificationSuccessful` and `verificationTime` named arguments. Omit
53+
`was_verification_successful` if no verification was attempted.
4954

5055
3.7.0 (2026-07-21)
5156
------------------

‎README.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,9 @@ $request = $mf->withDevice(
172172
usernameMd5: '4f9726678c438914fa04bdb8c1a24088'
173173
)->withEmail(
174174
address: 'test@maxmind.com',
175-
domain: 'maxmind.com'
175+
domain: 'maxmind.com',
176+
wasVerificationSuccessful: true,
177+
verificationTime: '2026-10-01T14:30:00Z'
176178
)->withBilling(
177179
firstName: 'First',
178180
lastName: 'Last',

‎src/MinFraud.php‎

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -487,14 +487,26 @@ public function withAccount(
487487
* @link https://dev.maxmind.com/minfraud/api-documentation/requests/?lang=en#schema--request--email
488488
* minFraud email API docs
489489
*
490-
* @param array<string, mixed> $values An array of email data. The keys are the same as
491-
* the JSON keys. You may use either this or the named
492-
* arguments, but not both.
493-
* @param string|null $address The email address used in the transaction.
494-
* This field must be a valid email address.
495-
* @param string|null $domain The domain of the email address used in the
496-
* transaction. Do not include the `@` in this
497-
* field.
490+
* @param array<string, mixed> $values An array of email data. The keys are the same as
491+
* the JSON keys. You may use either this or the named
492+
* arguments, but not both.
493+
* @param string|null $address The email address used in the transaction.
494+
* This field must be a valid email address.
495+
* @param string|null $domain The domain of the email address used in the
496+
* transaction. Do not include the `@` in this
497+
* field.
498+
* @param string|null $verificationTime The date and time of the most recent
499+
* attempt to verify the email address, if
500+
* known, in RFC 3339 date-time format. The
501+
* time must not be before
502+
* 1970-01-01T00:00:00Z or more than two days
503+
* in the future.
504+
* @param bool|null $wasVerificationSuccessful Whether the most recent verification
505+
* that the end user controls the email
506+
* address succeeded, for example by
507+
* confirming a link sent to the address.
508+
* Omit this if no verification was
509+
* attempted.
498510
*
499511
* @throws InvalidInputException if a value has the wrong type, or if
500512
* input validation is enabled and a value is
@@ -507,6 +519,8 @@ public function withEmail(
507519
array $values = [],
508520
?string $address = null,
509521
?string $domain = null,
522+
?string $verificationTime = null,
523+
?bool $wasVerificationSuccessful = null,
510524
): self {
511525
if (\count($values) !== 0) {
512526
if (\func_num_args() !== 1) {
@@ -516,6 +530,12 @@ public function withEmail(
516530
}
517531
$address = $this->remove($values, 'address');
518532
$domain = $this->remove($values, 'domain');
533+
$verificationTime = $this->remove($values, 'verification_time');
534+
$wasVerificationSuccessful = $this->remove(
535+
$values,
536+
'was_verification_successful',
537+
['boolean'],
538+
);
519539

520540
$this->verifyEmpty($values);
521541
}
@@ -535,6 +555,15 @@ public function withEmail(
535555
$values['domain'] = $domain;
536556
}
537557

558+
if ($verificationTime !== null) {
559+
$this->verifyRfc3339DateTime($verificationTime);
560+
$values['verification_time'] = $verificationTime;
561+
}
562+
563+
if ($wasVerificationSuccessful !== null) {
564+
$values['was_verification_successful'] = $wasVerificationSuccessful;
565+
}
566+
538567
$new = clone $this;
539568
$new->content['email'] = $values;
540569

‎tests/MaxMind/Test/MinFraudTest.php‎

Lines changed: 112 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ public function testWrongTypeWithoutValidation(string $method, string $field): v
9898
'phone_was_verification_successful',
9999
'was_3d_secure_successful',
100100
'was_authorized',
101+
'was_verification_successful',
101102
];
102103
if (\in_array($field, $booleans, true)) {
103104
$values[] = 'true';
@@ -141,7 +142,7 @@ public static function allFields(): array
141142
'withDevice' => [
142143
'accept_language', 'ip_address', 'session_age', 'session_id', 'tracking_token', 'user_agent',
143144
],
144-
'withEmail' => ['address', 'domain'],
145+
'withEmail' => ['address', 'domain', 'verification_time', 'was_verification_successful'],
145146
'withEvent' => ['party', 'shop_id', 'time', 'transaction_id', 'type'],
146147
'withOrder' => [
147148
'affiliate_id', 'amount', 'currency', 'discount_code', 'has_gift_message', 'is_gift',
@@ -342,7 +343,11 @@ public static function invalidValues(): array
342343
'token' => '4111111111111111',
343344
]],
344345
'withDevice' => ['device', ['ip_address' => 'not an IP', 'session_age' => -1.5, 'session_id' => '']],
345-
'withEmail' => ['email', ['address' => 'not an email', 'domain' => 'not a domain']],
346+
'withEmail' => ['email', [
347+
'address' => 'not an email',
348+
'domain' => 'not a domain',
349+
'verification_time' => 'not a time',
350+
]],
346351
'withEvent' => ['event', ['party' => 'other', 'time' => 'not a time', 'type' => 'other']],
347352
'withOrder' => ['order', ['amount' => -1.5, 'currency' => 'usd', 'referrer_uri' => 'not a URL']],
348353
'withPayment' => ['payment', ['method' => 'other', 'processor' => 'other']],
@@ -481,7 +486,9 @@ public function testFullInsightsRequestUsingNamedArgs(string $class, string $ser
481486
)
482487
->withEmail(
483488
address: '977577b140bfb7c516e4746204fbdb01',
484-
domain: 'maxmind.com'
489+
domain: 'maxmind.com',
490+
wasVerificationSuccessful: true,
491+
verificationTime: '2026-10-01T14:30:00Z'
485492
)
486493
->withEvent(
487494
party: 'customer',
@@ -1208,6 +1215,108 @@ public function testBadShippingPhoneWasVerificationSuccessful(): void
12081215
)->withShipping(['phone_was_verification_successful' => 'true']);
12091216
}
12101217

1218+
/**
1219+
* @dataProvider goodTimes
1220+
*/
1221+
public function testGoodEmailVerificationTimes(string $time): void
1222+
{
1223+
$result = $this->createMinFraudRequestWithFullResponse(
1224+
'insights',
1225+
0
1226+
)->withEmail(['verification_time' => $time]);
1227+
1228+
$this->assertSame(
1229+
$time,
1230+
$result->jsonSerialize()['content']['email']['verification_time'],
1231+
);
1232+
}
1233+
1234+
/**
1235+
* @dataProvider badTimes
1236+
*/
1237+
public function testBadEmailVerificationTime(string $time): void
1238+
{
1239+
$this->expectException(InvalidInputException::class);
1240+
$this->expectExceptionMessage('valid RFC 3339');
1241+
1242+
$this->createMinFraudRequestWithFullResponse(
1243+
'insights',
1244+
0
1245+
)->withEmail(['verification_time' => $time]);
1246+
}
1247+
1248+
public function testEmailWasVerificationSuccessfulFalse(): void
1249+
{
1250+
$result = $this->createMinFraudRequestWithFullResponse(
1251+
'insights',
1252+
0
1253+
)->withEmail(wasVerificationSuccessful: false);
1254+
1255+
$this->assertFalse(
1256+
$result->jsonSerialize()['content']['email']['was_verification_successful'],
1257+
);
1258+
}
1259+
1260+
public function testBadEmailWasVerificationSuccessful(): void
1261+
{
1262+
$this->expectException(InvalidInputException::class);
1263+
$this->expectExceptionMessage(
1264+
'Expected was_verification_successful to be in [boolean] but was string',
1265+
);
1266+
1267+
$this->createMinFraudRequestWithFullResponse(
1268+
'insights',
1269+
0
1270+
)->withEmail(['was_verification_successful' => 'true']);
1271+
}
1272+
1273+
public function testEmailWasVerificationSuccessfulFalseArray(): void
1274+
{
1275+
$result = $this->createMinFraudRequestWithFullResponse(
1276+
'insights',
1277+
0
1278+
)->withEmail(['was_verification_successful' => false]);
1279+
1280+
$this->assertFalse(
1281+
$result->jsonSerialize()['content']['email']['was_verification_successful'],
1282+
);
1283+
}
1284+
1285+
public function testEmailWasVerificationSuccessfulFalseWith(): void
1286+
{
1287+
$result = $this->createMinFraudRequestWithFullResponse(
1288+
'insights',
1289+
0
1290+
)->with(['email' => ['was_verification_successful' => false]]);
1291+
1292+
$this->assertFalse(
1293+
$result->jsonSerialize()['content']['email']['was_verification_successful'],
1294+
);
1295+
}
1296+
1297+
public function testEmailVerificationWithHashing(): void
1298+
{
1299+
$result = $this->createMinFraudRequestWithFullResponse(
1300+
'insights',
1301+
0,
1302+
['hashEmail' => true],
1303+
)->withEmail([
1304+
'address' => 'test@gmail.com',
1305+
'verification_time' => '2026-10-01T14:30:00Z',
1306+
'was_verification_successful' => false,
1307+
]);
1308+
1309+
$this->assertSame(
1310+
[
1311+
'address' => '1aedb8d9dc4751e229a335e371db8058',
1312+
'verification_time' => '2026-10-01T14:30:00Z',
1313+
'was_verification_successful' => false,
1314+
'domain' => 'gmail.com',
1315+
],
1316+
$result->jsonSerialize()['content']['email'],
1317+
);
1318+
}
1319+
12111320
/**
12121321
* @dataProvider badIins
12131322
*/

‎tests/data/minfraud/full-request.json‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
},
1313
"email": {
1414
"address": "977577b140bfb7c516e4746204fbdb01",
15-
"domain": "maxmind.com"
15+
"domain": "maxmind.com",
16+
"was_verification_successful": true,
17+
"verification_time": "2026-10-01T14:30:00Z"
1618
},
1719
"billing": {
1820
"first_name": "First",

0 commit comments

Comments
 (0)