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. The following examples in this article illustrate how to implement rules in the Microsoft DFP portal:

For details about how to define rules, see the following Microsoft documentation:

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.

Zuora Master Observe Rule for Anomalous Behavior

Zuora Master Observe Rule for Anomalous Behavior is an Observe rule enabled by default. You cannot edit it or turn it off. 

Condition WHEN @"orderType".ToUpper() == "ADDPI"

This condition determines that this master rule runs when a payment method is added through the Hosted Payment Page or the Payment Form.

The following table outlines the structure of this master rule. Note that the velocity must be defined in Rules configuration > Velocities before it can be utilized in the clause.

Clause Name Rule implementation Related Velocity Definition
IP & Issuing Country Mismatch - Observe OBSERVE Output(BINCountry=BIN.Lookup(@"paymentInstrumentList.bin").countryCode)
WHEN !string.IsNullOrEmpty(Geo.CountryCode(@"deviceAttributes.trueIp"))
AND !string.IsNullOrEmpty(BIN.Lookup(@"paymentInstrumentList.bin").countryCode)
AND (BIN.Lookup(@"paymentInstrumentList.bin").countryCode).ToLower() != Geo.CountryCode(@"deviceAttributes.trueIp").ToLower()
 
Same IP w Mult PI - Observe OBSERVE Output()
WHEN Velocity._Unique_PI_Per_IP(@"deviceAttributes.trueIp",7d)>2
SELECT DistinctCount(@"paymentinstrumentlist[0].merchantPaymentInstrumentId") AS _Unique_PI_Per_IP
FROM Purchase
GROUPBY @"deviceAttributes.trueIp"
Same PI w Mult IP - Observe OBSERVE Output()
WHEN Velocity._Unique_IPs_Per_PI(@"paymentInstrumentList.merchantPaymentInstrumentId", 7d)>1
SELECT DistinctCount(@"deviceAttributes.trueIp") AS _unique_IPs_Per_PI
FROM Purchase
GROUPBY @"paymentInstrumentlist[0].merchantPaymentInstrumentId"
PI w mult bank declines daily- Observe OBSERVE Output()
WHEN Velocity._BankDeclines_Per_PI(@"paymentInstrumentList.merchantPaymentInstrumentId",1d)>1
The required velocity is a default one in the system. No action is needed.
PI w mult bank declines weekly- Observe OBSERVE Output()
WHEN Velocity._BankDeclines_Per_PI(@"paymentInstrumentList.merchantPaymentInstrumentId",7d)>2
The required velocity is a default one in the system. No action is needed.
Device Fingerprinting Missing - Observe OBSERVE Output()
WHEN @"orderType" == "AddPI" && string.IsNullOrEmpty(@"deviceAttributes.trueIp")
 

Rule examples of Lists

Each of the following examples requires a custom list. myList is used as a placeholder in the following examples. A column in the custom list file, myColumn as a placeholder, contains the information that you consider risky or safe. For more information about the predefined lists included in Microsoft Dynamic 365 Fraud Protection, see Microsoft documentation.

If you want to copy and use the following examples, replace myList and myColumn with relevant names for reporting.

Description Rule Implementation Notes
Block by user ID RETURN Reject("Block high risk user")
WHEN ContainsKey("myList", "myColumn", @"user.userId")
User IDs to be blocked are listed in myColumn of the myList file.
Block by email RETURN Reject("User email on block list")
WHEN ContainsKey("myList", "myColumn", @"user.email")
Emails to be blocked are listed in myColumn of the myList file.
Approve by IP address RETURN Approve()
WHEN ContainsKey("myList", "myColumn", @"device.ipAddress")
IP addresses trusted are listed in myColumn of the myList file.
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)
Country codes to be blocked are listed in myColumn of the myList file.

Rule examples of Velocities

The following table describes examples of rules that use the velocity check and the related definition of the velocity. For more information about velocity checks, see Microsoft documentation.

If you want to copy and use the following examples, update the threshold value in the rule according to your requirements.

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 purchase attempts exceed the max allowance for an IP address within a certain time period using the true IP returned in the screening response RETURN Reject("Blocked by velocity")
WHEN Velocity._Purchase_Attempts_Per_True_IP(@"deviceContext.trueIp",1h)>5
SELECT Count() AS _Purchase_Attempts_Per_True_IP
FROM Purchase
GROUPBY @"deviceContext.trueIp"
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"

Rule examples of suspicious behavior

Description Rule Implementation
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"

Rule examples of Machine Learning score

If you want to copy and use the following examples, update the threshold value in the rule according to your requirements.

Description Rule Implementation
Reject if the machine learning risk score is too high RETURN Reject("High ML score")
WHEN @"riskScore" > 900

Examples of Observe rule

If you want to copy and use the following examples, replace the placeholder value in the rule.

Description Rule Implementation
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
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
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)

Other examples

If you want to copy and use the following examples, replace the placeholder value in the rule.

Description Rule Implementation
Reject if BIN is blocked RETURN Reject("Block high risk BIN")
WHEN @"paymentInstrumentList.bin".ToString() == "123456"
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"
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"
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))