Skip to main content

Examples of rules used in Zuora Fraud Protection

Zuora

Examples of rules used in Zuora Fraud Protection

When you configure the fraud protection service in the Microsoft DFP portal, you can create rules that you want to apply to each data screening through Zuora Fraud Protection. For details about how to define rules, see the following Microsoft documentation:

The following examples in this article illustrate how to implement rules in the Microsoft DFP portal:

Since Zuora Fraud Protection is a self-serve product, when you define your own rules, you need to take full responsibility for rule logic, implementation, and outcome.

Rule examples

The following table describes some rule examples that might be frequently used.

Description Rule implementation
Reject if the machine learning risk score is too high. RETURN Reject("High ML score")
WHEN @"riskScore" > 900
Reject if the payment instrument country and IP address country don’t match. RETURN Reject("Blocked by PI and IP country mismatch")
WHEN BIN.Lookup(@"paymentInstrumentList.bin").countryCode != Geo.CountryCode(@"device.ipAddress")
Reject if Device Fingerprinting is enabled but fails to retrieve the Device Fingerprinting information.
 
LET $deviceInfo = Device.GetFullAttributes(@"deviceFingerprinting.id")
RETURN REJECT("Device fingerprinting is enabled but information missing")
WHEN @"deviceContext.provider"=="DFPFingerprinting" AND string.IsNullOrEmpty($deviceInfo.deviceId)
Reject if Device Fingerprinting indicates Virtual Machines from major clouds. LET $deviceInfo = Device.GetFullAttributes(@"deviceFingerprinting.id")
RETURN Reject("VM found")
WHEN $deviceInfo.canvasRenderer.Contains("VMware")
OR $deviceInfo.canvasRenderer.Contains("ANGLE (Microsoft Basic Render Driver")
OR $deviceInfo.canvasRenderer.Contains("ANGLE (Unknown, Microsoft Basic Render Driver")
OR $deviceInfo.canvasRenderer.Contains("VirtualBox")
OR $deviceInfo.canvasRenderer.Contains("Parallels Display")
OR $deviceInfo.canvasRenderer.Contains("ANGLE (GOOGLE, VULKAN")
Reject if Device Fingerprinting shows the user is using a proxy. LET $deviceInfo = Device.GetFullAttributes(@"deviceFingerprinting.id")
RETURN Reject("Proxy users")
WHEN @"deviceContext.provider"=="DFPFingerprinting" and $deviceInfo.proxy=="true"
Reject if BIN is blocked. RETURN Reject("Block high risk BIN")
WHEN @"paymentInstrumentList.bin".ToString() == "123456"
Test card approval. OBSERVE Output(rule = "Test Account List Rule")
//RETURN Approve("Account List Rule")
//WHEN @"paymentInstrumentList.merchantPaymentInstrumentId" == "4111111111111111" // If you only auto approve 1 card, you can use this. 
WHEN IsSafe("Payment Support List",@"paymentInstrumentList.merchantPaymentInstrumentId") //Alternatively, if you would like to manage the list through support list you can do this, especially when there is more than 1 testcard
Approve more than 1 dollar transactions. RETURN Approve("Auto Approve When Transaction is greater than $1")
WHEN @"totalAmount".toDouble() > 1
Reject long digital email domains. LET $emailDomain = @"user.email".Split('@')[1].ToLower()
LET $emailDomainLength = $emailDomain.Length
LET $emailDomainLengthWithoutDigital = $emailDomain.Replace("0", "").Replace("1", "").Replace("2", "").Replace("3", "").Replace("4", "")
.Replace("5", "").Replace("6", "").Replace("7", "").Replace("8", "").Replace("9", "").Length
RETURN Reject("Long Digital Email Domain")
WHEN $emailDomainLength - $emailDomainLengthWithoutDigital > 6
Reject if the city contains numeric digits. RETURN Reject("City cannot include numbers")
WHEN @"paymentInstrumentList.billingAddress.city".Contains("0")
OR @"paymentInstrumentList.billingAddress.city".Contains("1")
OR @"paymentInstrumentList.billingAddress.city".Contains("2")
OR @"paymentInstrumentList.billingAddress.city".Contains("3")
OR @"paymentInstrumentList.billingAddress.city".Contains("4")
OR @"paymentInstrumentList.billingAddress.city".Contains("5")
OR @"paymentInstrumentList.billingAddress.city".Contains("6")
OR @"paymentInstrumentList.billingAddress.city".Contains("7")
OR @"paymentInstrumentList.billingAddress.city".Contains("8")
OR @"paymentInstrumentList.billingAddress.city".Contains("9")
Block when the attacker bypasses the empty device session ID for a specific hosted payment page. RETURN Reject("Security Check Failed - 02")
WHEN
   @"orderType"=="AddPI" && not Exists(@"deviceContext.deviceContextId") && Exists(@"customData.hpmId") && @"customData.hpmId" == "2c92a0076c297de7016c3f1276e34176"
Observe rule tracking Discover transactions with a score greater than 887. Observe Output(reason="High Score for Discover")
WHEN @"orderType" == "AddPI" && @"paymentInstrumentList.type" == "CreditCard" && @"paymentInstrumentList.cardType" == "Discover" && @"riskScore" > 887
Decline if the first name matches the last name. RETURN Reject("Security Check Failed - 03")
WHEN  @"orderType" == "AddPI" && @"paymentInstrumentList.type" == "CreditCard" && @"paymentInstrumentList.billingAddress.firstName" == @"paymentInstrumentList.billingAddress.lastName"
Observe rule for middle initials greater than 2, such as “John HE Doe”. LET $name = @"paymentInstrumentList.holderName"
LET $index1 = @$name.IndexOf(" ")
LET $index2 = @$name.LastIndexOf(" ")
LET $middleName = @$name.Substring($index1 + 1, ($index2 - $index1 - 1))
LET $firstCharMiddleName = $middleName.Substring(0, 1)
LET $secondCharMiddleName = $middleName.Substring(1, 1)
LET $upperCases = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Observe Output(reason="Too Many Middle Initials")
WHEN  @"orderType" == "AddPI" && @"paymentInstrumentList.type" == "CreditCard" && @$middleName.Length >= 2 && @$upperCases.Contains(@$firstCharMiddleName) && @$upperCases.Contains(@$secondCharMiddleName)
Decline if numbers are contained within the cardholder name. RETURN Reject("Security Check Failed - 04")
WHEN  @"orderType" == "AddPI" && @"paymentInstrumentList.type" == "CreditCard" && @"paymentInstrumentList.holderName".ContainsAny(CharSet.Numeric)
Decline if specific alphanumeric combinations are contained in the postal code. LET $specificCombination1 = "A1B"
LET $specificCombination2 = "C2"
RETURN Reject("Security Check Failed - 05")
WHEN  @"orderType" == "AddPI" && @"paymentInstrumentList.type" == "CreditCard" 
&& (@"paymentInstrumentList.billingAddress.zipCode".Contains(@$specificCombination1) || @"paymentInstrumentList.billingAddress.zipCode".Contains(@$specificCombination2))

Rule examples using list

Each of the following examples requires a custom list, myList in the following examples. A column in the custom list file, myColumn, contains the information that you consider risky or safe.

Description Rule implementation Notes
Reject if BIN is in the block list. RETURN Reject("Block high risk BIN")
WHEN ContainsKey("myList", "myColumn", @"paymentInstrumentList.bin")
BIN numbers to be blocked are listed in myColumn of the myList file.
Reject if True IP is from a country code on the block list. LET $TrueIPCountry = Geo.CountryCode(@"deviceAttributes.trueIp")
OBSERVE Output()
WHEN ContainsKey("myList", "myColumn", $TrueIPCountry)
 

Rule examples using velocity check

The following table describes examples of rules that use the velocity check and the related definition of the velocity.

Description Rule implementation Related Velocity Definition
Reject if a payment instrument has been rejected by a bank too many times within a certain time period. RETURN Reject("Blocked by velocity")
WHEN Velocity._BankDeclines_Per_PI(@"paymentInstrumentList.merchantPaymentInstrumentId",1d)>3
OR Velocity._BankDeclines_Per_PI(@"paymentInstrumentList.merchantPaymentInstrumentId",7d)>5

SELECT DistinctCount(@"purchaseId") AS _BankDeclines_Per_PI
FROM BankEvent
WHEN @"status" == "DECLINED"
GROUPBY @"purchase.paymentInstrumentList.merchantPaymentInstrumentId"

Reject if an account has been rejected by a bank too many times within a certain time period. RETURN Reject("Blocked by velocity")
WHEN Velocity._BankDeclines_Per_Account(@"user.UserID",1d)>5
SELECT Count() AS _BankDeclines_Per_Account
FROM BankEvent
WHEN @"status" == "DECLINED"
GROUPBY @"purchase.user.userId"
Reject if an email has been rejected by a bank too many times within a certain time period. RETURN Reject("Blocked by velocity")
WHEN Velocity._BankDeclines_Per_Email(@"user.email",1d)>5
SELECT DistinctCount(@"purchaseId") AS _BankDeclines_Per_Email
FROM BankEvent
WHEN @"status" == "DECLINED"
GROUPBY @"purchase.user.email"
Reject if an IP address has been rejected by a bank too many times within a certain time period. RETURN Reject("Blocked by velocity")
WHEN Velocity._BankDeclines_Per_IP(@"deviceContext.ipAddress",1d)>5
SELECT DistinctCount(@"purchaseId") AS _BankDeclines_Per_IP
FROM BankEvent
WHEN @"status" == "DECLINED"
GROUPBY @"purchase.deviceContext.ipAddress"
Reject if purchase attempts exceed the max allowance for an account within a certain time period. RETURN Reject("Blocked by velocity")
WHEN Velocity._Purchase_Attempts_Per_Account(@"user.userId",1h)>6
SELECT Count() AS _Purchase_Attempts_Per_Account
FROM Purchase
GROUPBY @"user.userId"
Reject if purchase attempts exceed the max allowance for an email within a certain time period. RETURN Reject("Blocked by velocity")
WHEN Velocity._Purchase_Attempts_Per_Email(@"user.email",1d)>6
SELECT Count() AS _Purchase_Attempts_Per_Email
FROM Purchase
GROUPBY @"user.email"
Reject if purchase attempts exceed the max allowance for a payment instrument within a certain time period. RETURN Reject("Blocked by velocity")
WHEN Velocity._Purchase_Attempts_Per_PI(@"paymentInstrumentList.merchantPaymentInstrumentId",1d)>9
SELECT Count() AS _Purchase_Attempts_Per_PI
FROM Purchase
GROUPBY @"paymentInstrumentList.merchantPaymentInstrumentId"
Reject if purchase attempts exceed the max allowance for an IP address within a certain time period. RETURN Reject("Blocked by velocity")
WHEN Velocity._Purchase_Attempts_Per_IP(@"deviceContext.ipAddress",1h)>5
SELECT Count() AS _Purchase_Attempts_Per_IP
FROM Purchase
GROUPBY @"deviceContext.ipAddress"
Reject if too many payment instruments are used by the same account within a certain time period. RETURN Reject("Blocked by velocity")
WHEN Velocity._Unique_PI_Per_Account(@"user.userId",1d)>3
SELECT DistinctCount(@"paymentInstrumentlist[0].merchantPaymentInstrumentId") AS _Unique_PI_Per_Account
FROM Purchase
GROUPBY @"user.userId"
Reject if too many accounts are used by the same payment instrument within a certain time period. RETURN Reject("Blocked by velocity")
WHEN Velocity._Unique_Account_Per_PI(@"paymentInstrumentList.merchantPaymentInstrumentId",7d)>5
SELECT DistinctCount(@"user.userId") AS _Unique_Account_Per_PI
FROM Purchase
GROUPBY @"paymentInstrumentlist[0].merchantPaymentInstrumentId"
Reject if too many payment instruments are used by the same email within a certain time period. RETURN Reject("Blocked by velocity")
WHEN Velocity._Unique_PI_Per_Email(@"user.email",7d)>6
SELECT DistinctCount(@"paymentInstrumentlist[0].merchantPaymentInstrumentId") AS _Unique_PI_Per_Email
FROM Purchase
GROUPBY @"purchase.user.email"
Reject if too many emails are used for the same payment instrument within a certain time period. RETURN Reject("Blocked by velocity")
WHEN Velocity._Unique_Email_Per_PI(@"paymentInstrumentList.merchantPaymentInstrumentId",7d)>5
SELECT DistinctCount(@"user.email") AS _Unique_Email_Per_PI
FROM Purchase
GROUPBY @"paymentInstrumentlist[0].merchantPaymentInstrumentId"
Reject if too many IP addresses are used by the same payment instrument within a certain time period. RETURN Reject("Blocked by velocity")
WHEN Velocity._Unique_IPs_Per_PI(@"paymentInstrumentList.merchantPaymentInstrumentId", 1d)>3
SELECT DistinctCount(@"deviceContext.ipAddress") AS _unique_IPs_Per_PI
FROM Purchase
GROUPBY @"paymentInstrumentlist[0].merchantPaymentInstrumentId"
Reject if too many payment instruments are used by the same IP address within a certain time period. RETURN Reject("Blocked by velocity")
WHEN Velocity._Unique_PI_Per_IP(@"deviceContext.ipAddress",1d)>3
SELECT DistinctCount(@"paymentinstrumentlist[0].merchantPaymentInstrumentId") AS _Unique_PI_Per_IP
FROM Purchase
GROUPBY @"deviceContext.ipAddress"
Reject if the recent purchase attempt velocity for a BIN is significantly higher than its past long-term velocity, and the bank decline rate is also high for the BIN.

LET $avg_velocity_bin = (Velocity._Purchase_Attempts_Per_BIN(@"paymentInstrumentList.bin", 30d).ToDouble()-Velocity._Purchase_Attempts_Per_BIN(@"paymentInstrumentList.bin", 1d).ToDouble())/29
RETURN Reject("Blocked by sharp increase of purchase attempts for BIN")
WHEN $avg_velocity_bin > 3
AND Velocity._Purchase_Attempts_Per_BIN(@"paymentInstrumentList.bin", 1d) > 10 * $avg_velocity_bin
AND Velocity._BankDeclines_Per_BIN(@"paymentInstrumentList.bin", 1d).ToDouble() > 0.7 * Velocity._Purchase_Attempts_Per_BIN(@"paymentInstrumentList.bin", 1d).ToDouble()

**Included a threshold to exclude BIN with small volume

Number of purchase attempts per BIN:

SELECT Count() AS _Purchase_Attempts_Per_BIN
FROM Purchase
GROUPBY @"paymentInstrumentList.bin"

 

Number of bank declines per BIN:

SELECT DistinctCount(@"purchaseId") AS _BankDeclines_Per_BIN
FROM BankEvent
WHEN @"status" == "DECLINED"
GROUPBY @"purchase.paymentInstrumentList.bin"

Reject if the recent bank decline velocity for a BIN is significantly higher than its past long-term velocity, and the purchase attempt rate is also high for the BIN. LET $avg_bankdecline_velocity_bin = (Velocity._BankDeclines_Per_BIN(@"paymentInstrumentList.bin", 30d).ToDouble()-Velocity._BankDeclines_Per_BIN(@"paymentInstrumentList.bin", 1d).ToDouble())/29
RETURN Reject("Blocked by sharp increase in bank declines for BIN")
WHEN $avg_bankdecline_velocity_bin > 3
AND Velocity._BankDeclines_Per_BIN(@"paymentInstrumentList.bin", 1d) > 10 * $avg_bankdecline_velocity_bin
AND Velocity._BankDeclines_Per_BIN(@"paymentInstrumentList.bin", 1d).ToDouble() > 0.7 * Velocity._Purchase_Attempts_Per_BIN(@"paymentInstrumentList.bin", 1d).ToDouble()

Number of bank declines per BIN:

SELECT DistinctCount(@"purchaseId") AS _BankDeclines_Per_BIN
FROM BankEvent
WHEN @"status" == "DECLINED"
GROUPBY @"purchase.paymentInstrumentList.bin"

 

Number of purchase attempts per BIN:

SELECT Count() AS _Purchase_Attempts_Per_BIN
FROM Purchase
GROUPBY @"paymentInstrumentList.bin"

Reject billing streets over the threshold LET $BillingAddress = @"purchase.paymentInstrumentList.billingAddress.street1" + @"purchase.paymentInstrumentList.billingAddress.street2" + @"purchase.paymentInstrumentList.billingAddress.street3"+@"paymentInstrumentList.billingAddress.city"+@"paymentInstrumentList.billingAddress.state"+@"paymentInstrumentList.billingAddress.country"+@"paymentInstrumentList.billingAddress.district" +@"paymentInstrumentList.billingAddress.zipCode"
RETURN Reject("BillingAddress velocity")
WHEN Velocity.AddPIAttemptOnBillingAddress($BillingAddress,5m)>4
SELECT Count() AS AddPIAttemptOnBillingAddress
FROM Purchase
GROUPBY $BillingAddress
Reject if there is a chargeback in 90 days. RETURN Reject("Rejected due to recent chargeback.")
WHEN Velocity.ChargebackReceivedonPaymentInstrumnetId(@"paymentInstrumentList.merchantPaymentInstrumentId",90d)>0
SELECT Count() AS ChargebackReceivedonPaymentInstrumentId
FROM Chargeback
GROUPBY @"purchase.paymentInstrumentList.merchantPaymentInstrumentId"
Block by device-based rate limiting for a specific hosted payment page. RETURN Reject("Security Check Failed - 01")
WHEN
   @"orderType"=="AddPI" && Exists(@"customData.hpmId") && @"customData.hpmId" == "2c92a0076c297de7016c3f1276e34176" && 
   (Velocity._Purchase_Attempts_Per_DeviceId(@"deviceAttributes.deviceId", 1m) > 2 ||
   Velocity._Purchase_Attempts_Per_DeviceId(@"deviceAttributes.deviceId", 1h) > 5)
SELECT Count() AS _Purchase_Attempts_Per_DeviceId
FROM Purchase
GROUPBY @"deviceAttributes.deviceId"
Decline if there are 5 cardholder names per device ID within 24 hours. LET $cardholderNameCountIn24Hour = Velocity._CardholderName_Attempts_Per_Device(@"deviceAttributes.deviceId", 24h)
RETURN 
    Reject ("Security Check Failed - 06"),
    Output(
        cardholderNameCountIn24Hour = $cardholderNameCountIn24Hour
    )
WHEN @$cardholderNameCountIn24Hour >= 5
SELECT DistinctCount(@"paymentInstrumentList.holderName") AS _CardholderName_Attempts_Per_Device
FROM Purchase
WHEN @"orderType" == "AddPI" && @"paymentInstrumentList.type" == "CreditCard"
GROUPBY @"deviceAttributes.deviceId"