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:
- Zuora Master Observe Rule for Anomalous Behavior
- Rule examples of list
- Rule examples of velocity check
- Rule examples of suspicious behavior
- Rule examples of Machine Learning score
- Examples of Observe rule
- Other examples
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 |
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 **Included a threshold to exclude BIN with small volume |
Number of purchase attempts per BIN: SELECT Count() AS _Purchase_Attempts_Per_BIN
Number of bank declines per BIN: SELECT DistinctCount(@"purchaseId") AS _BankDeclines_Per_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
Number of purchase attempts per BIN: SELECT Count() AS _Purchase_Attempts_Per_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)) |