diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c4a5376..c784ba41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,19 @@ CHANGELOG ========= -9.1.1 ------------------- - +9.2.0 +------------------ + +* Added the inputs `/billing/phone_verification_method`, + `/billing/phone_was_verification_successful`, + `/billing/phone_verification_time`, + `/shipping/phone_verification_method`, + `/shipping/phone_was_verification_successful`, and + `/shipping/phone_verification_time`. They describe the most recent + verification of the billing or shipping phone number. You may provide them + by providing `phoneVerificationMethod`, `phoneWasVerificationSuccessful`, + and `phoneVerificationTime` to `Billing` or `Shipping`. The + `PhoneVerificationMethod` enum lists the valid methods. * Added `maxmind` as a direct dependency. The `IpAddressWebRecord` type extends `CityResponse` from that package, so it is part of this package's published type surface. It was previously resolved only as a transitive dependency of diff --git a/README.md b/README.md index e8f33e02..df35cfdf 100644 --- a/README.md +++ b/README.md @@ -217,6 +217,9 @@ try { lastName: 'Bar', phoneCountryCode: '1', phoneNumber: '123-456-1234', + phoneVerificationMethod: minFraud.Constants.PhoneVerificationMethod.DeliveredCode, + phoneVerificationTime: new Date('2026-10-01T14:30:00Z'), + phoneWasVerificationSuccessful: true, postal: '12345', region: 'MA', }), @@ -231,6 +234,9 @@ try { lastName: 'Bar', phoneCountryCode: '1', phoneNumber: '123-456-0000', + phoneVerificationMethod: minFraud.Constants.PhoneVerificationMethod.DeliveredCode, + phoneVerificationTime: new Date('2026-10-01T14:30:00Z'), + phoneWasVerificationSuccessful: true, postal: '12345', region: 'MA', }), diff --git a/src/constants.ts b/src/constants.ts index 80f351a6..79032ad3 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -25,6 +25,12 @@ export enum DeliverySpeed { Standard = 'standard', } +export enum PhoneVerificationMethod { + DeliveredCode = 'delivered_code', + Network = 'network', + Other = 'other', +} + export enum PaymentMethod { BankDebit = 'bank_debit', BankRedirect = 'bank_redirect', diff --git a/src/request/location.spec.ts b/src/request/location.spec.ts index cab7ea9c..50e0008d 100644 --- a/src/request/location.spec.ts +++ b/src/request/location.spec.ts @@ -1,4 +1,5 @@ import { describe, expect, it } from 'vitest'; +import { PhoneVerificationMethod } from '../constants.js'; import { ArgumentError } from '../errors.js'; import Location from './location.js'; @@ -20,4 +21,19 @@ describe('Location()', () => { }); }).not.toThrow(); }); + + it('stores the phone verification properties', () => { + const time = new Date('2026-10-01T14:30:00Z'); + const location = new Location({ + country: 'CA', + phoneVerificationMethod: PhoneVerificationMethod.Network, + phoneVerificationTime: time, + phoneWasVerificationSuccessful: false, + }); + + expect(location.country).toBe('CA'); + expect(location.phoneVerificationMethod).toBe('network'); + expect(location.phoneVerificationTime).toBe(time); + expect(location.phoneWasVerificationSuccessful).toBe(false); + }); }); diff --git a/src/request/location.ts b/src/request/location.ts index 4c9577fc..e25e244b 100644 --- a/src/request/location.ts +++ b/src/request/location.ts @@ -1,3 +1,4 @@ +import { PhoneVerificationMethod } from '../constants.js'; import { ArgumentError } from '../errors.js'; export interface LocationProps { @@ -46,6 +47,19 @@ export interface LocationProps { * The phone number, without the country code, associated with the address. */ phoneCountryCode?: string; + /** + * The most recent method used to verify the phone number. + */ + phoneVerificationMethod?: PhoneVerificationMethod; + /** + * The date and time of the most recent verification of the phone number. + */ + phoneVerificationTime?: Date; + /** + * Whether the most recent verification of the phone number succeeded. Omit + * this if no verification was attempted. + */ + phoneWasVerificationSuccessful?: boolean; } const countryRegex = /^[A-Z]{2}$/; @@ -76,6 +90,12 @@ export default class Location implements LocationProps { public phoneNumber?: string; /** @inheritDoc LocationProps.phoneCountryCode */ public phoneCountryCode?: string; + /** @inheritDoc LocationProps.phoneVerificationMethod */ + public phoneVerificationMethod?: PhoneVerificationMethod; + /** @inheritDoc LocationProps.phoneVerificationTime */ + public phoneVerificationTime?: Date; + /** @inheritDoc LocationProps.phoneWasVerificationSuccessful */ + public phoneWasVerificationSuccessful?: boolean; public constructor(location: LocationProps) { if (location.country != null && !countryRegex.test(location.country)) { diff --git a/src/request/transaction.spec.ts b/src/request/transaction.spec.ts index 6d94525f..45fbe9af 100644 --- a/src/request/transaction.spec.ts +++ b/src/request/transaction.spec.ts @@ -1,4 +1,5 @@ import { describe, expect, it, test } from 'vitest'; +import { PhoneVerificationMethod } from '../constants.js'; import { ArgumentError } from '../errors.js'; import Account from './account.js'; import Billing from './billing.js'; @@ -267,6 +268,44 @@ describe('Transaction()', () => { expect(test.toString()).toContain('"billing":{"address_2":"foo"}'); }); + it('it handles billing phone verification fields', () => { + const test = new Transaction({ + billing: new Billing({ + phoneVerificationMethod: PhoneVerificationMethod.DeliveredCode, + phoneVerificationTime: new Date('2026-10-01T14:30:00Z'), + phoneWasVerificationSuccessful: false, + }), + device: new Device({ + ipAddress: '1.1.1.1', + }), + }); + + expect(JSON.parse(test.toString()).billing).toEqual({ + phone_verification_method: 'delivered_code', + phone_verification_time: '2026-10-01T14:30:00.000Z', + phone_was_verification_successful: false, + }); + }); + + it('it handles shipping phone verification fields', () => { + const test = new Transaction({ + shipping: new Shipping({ + phoneVerificationMethod: PhoneVerificationMethod.DeliveredCode, + phoneVerificationTime: new Date('2026-10-01T14:30:00Z'), + phoneWasVerificationSuccessful: false, + }), + device: new Device({ + ipAddress: '1.1.1.1', + }), + }); + + expect(JSON.parse(test.toString()).shipping).toEqual({ + phone_verification_method: 'delivered_code', + phone_verification_time: '2026-10-01T14:30:00.000Z', + phone_was_verification_successful: false, + }); + }); + it('does not mutate the caller’s input objects when serializing', () => { const billing = new Billing({ address2: 'foo' }); const shipping = new Shipping({ address2: 'bar' });