Radar for Fraud Teams: Rules 101

This guide covers a variety of topics related to Radar rules, including over 100 Radar rules you can use and best practices around backtesting, rule writing, and more.

  1. Introduction
  2. The importance of rules order and hierarchy
  3. Rules language cheat sheet
    1. Writing rules using natural language
    2. Rules that help prevent fraud with known risky SKUs
    3. Rules that help prevent trial abuse from prepaid cards
  4. Three types of attributes to create rules
    1. Type 1
    2. Type 2
    3. Attributes based on frequency
    4. Attributes based on card details
    5. Attributes based on payment details
    6. Attributes based on customer details
    7. Type 3
  5. Using saved lists in your rules (e.g., allow lists, block lists)
  6. Write complex rules with multiple conditions
  7. Backtesting rules
    1. Backtesting in the Dashboard
    2. Performing custom backtesting analyses
  8. Common fraud vectors
    1. Testing
    2. Value extraction
  9. Other best practices
    1. Importance of using Stripe.js
  10. Conclusion
    1. Other notes

Stripe’s default rules use machine learning to predict and block a substantial number of fraudulent payments. For businesses that need more control over which payments should be reviewed, allowed, or blocked, rules are a powerful tool to customize your fraud protection.

This guide covers a variety of topics related to Radar rules, including over 100 Radar rules you can use and best practices around backtesting, rule writing, and more.

Let’s get started.

The importance of rules order and hierarchy

The order in which rules are listed in your Radar page matters. Each payment is evaluated against the rules you have created and performed in the following order:

  1. Request 3DS: Rules that request 3D Secure authentication when used with the Payment Intents API or Checkout. Regardless of matches on this rule, Allow, Block, and Review rules are evaluated after.
  2. Allow: Rules that allow a payment to be processed. Allow rules should be carefully implemented, as they override all other rules except 3DS rules, including Stripe’s machine learning models, and should be used with the most caution. Only merchants that have processed over $100,000 can write allow rules.
  3. Block: Rules that block a payment and reject it. If a payment is rejected, it’s not evaluated against any review rules.
  4. Review: These payments are still processed, and the customer charged, but those payments get flagged so that you can take a second look if you wish.

To put this into practice, let’s use the following rules as an example. All payments less than $10 would be processed. This is because the first rule allows the payment, so no further rules are evaluated. In the same vein, following these rules, a $1,500 payment made within the US with a normal risk level would also be allowed, despite the rule to block payments over $1,000. This is because of the second rule in the list, allowing payments made within the US and a normal risk level. Once a particular rule is triggered, no further rules are evaluated.

  • Allow payments less than $10

  • Allow payments within the US and with a risk level of normal

  • Block payments where the risk level is high

  • Block payments greater than $1,000

  • Review payments with a card issued outside the US

Rules language cheat sheet

Rules are similar to SQL and there are different operators you can use based on the type of data you’re using to create your rule. Here’s a cheat sheet.

Operator
String
Metadata
Country
Numeric
Description
Example
=
Equal to

:card_country: = 'us'

!=
Not equal to

:card_funding: != 'prepaid'

<
Less than

:amount_in_gbp: < 10.00

>
Greater than

:amount_in_usd: > 500.00

<=
Less than or equal to

:amount_in_eur:<= 100.00

>=
Greater than or equal to

:amount_in_cad: >= 10.00

IN
Is in the group

:card_country: IN ('gb', 'ie')

INCLUDES
Contains the string

:ip_address: INCLUDES '192.168'

LIKE
Matches the given pattern

:email: LIKE 'fraud%@stripe.com'

If you would like to explicitly check for the existence of an attribute or metadata attribute, don’t use the != operator, but use the is_missing function. Provide this function with the attribute or metadata key that may be missing. For example, you could write a rule to match all payments where you do not have access to a customer’s email address:

  • Review if is_missing(:email_domain:)

Or you might write a rule to match all payments for which a customer’s email address is NOT missing:

  • Review if !(is_missing(:email_domain:))

Writing rules using natural language

If you want to write rules more easily or are not sure which attributes to use to address a specific fraud scenario, the AI-powered Radar Assistant will translate your natural language prompts into rules in Radar’s syntax. You can also backtest the rule directly from Radar Assistant, so you can see how it would have performed historically before implementing the rule.

Block if :total_charges_per_ip​_address_hourly: > 1

This rule is helpful with card testing. It will block charges if an IP address was successfully authorized on your account more than one time.

Block if :blocked_charges_per_ip​_address_hourly: > 1

If you want to be more aggressive in preventing card testing, use this rule in conjunction with :total_charges_per_ip_address_hourly:

Block if :total_charges_per​_card_number_hourly: > 1

This rule is helpful with card cashing. It will block charges if a card number was successfully authorized on your account in the past hour more than one time.

Block if :blocked_charges_per_card​_number_hourly: > 1

If you want to be more aggressive in preventing card cashing, use this rule in conjunction with :total_charges_per_card_number_hourly:

Block if :address_zip_check: != 'pass'

Make sure you’re collecting zip codes in your checkout form to use this rule. This rule will block charges if the card issuer can’t validate the zip code provided with what they have on file for the card.

Block if :cvc_check:: != 'pass'

Make sure you’re collecting CVC in your checkout form to use this rule. This rule will block charges if the card issuer can’t validate the CVC (or CVV) code provided with what they have on file for the card.

Rules that help prevent fraud with known risky SKUs

This rule requires metadata or passing SKU information as the charge description. These payments are still processed, and the customer charged, but they are flagged so that you can take a second look.

Review if ::SKU Category:: IN ('baby formula', 'personal hygiene')

Assume you’re a grocery store and you’re sending us metadata with the SKU category. You’ve noticed that orders that contain items tagged with SKU category 'personal hygiene' or 'baby formula' tend to be more risky. This rule will place any orders with those items in the Manual Review list on your Stripe Dashboard so that you can take a second look. Note that these payments are still processed and the customer charged unless you manually cancel the order.

Review if :charge_description: = 'Trial class'

Assume that you sell two products ('Trial class' and '10 class package') and you send Stripe the product name as the charge description. This rule will place any orders where the charge description is exactly 'Trial class' in the Manual Review list on your Stripe Dashboard so that you can take a second look. Note that these payments are still processed and the customer charged unless you manually cancel the order.

Rules that help prevent trial abuse from prepaid cards

Block if :card_funding: = 'prepaid' OR :card_funding: = 'unknown'

Assume you’re a retailer that offers at-home trials and have noticed a spike in fraudulent actors who use prepaid cards that you’re later unable to charge. This rule will block any orders that are not paid with a credit or debit card.

Three types of attributes to create rules

Type 1

post-authorization attributes: These are available for everyone to use. When using these attributes, you need to use colons before and after the post-authorization attributes like :cvc_check:.

Attributes
Description

:address_line1_check:

A check by the card issuer to match the first line of the provided billing address (typically a street name and number) against the information they have on file for the cardholder.

:address_zip_check:

A check by the card issuer to match the provided postal code against the information they have on file for the cardholder.

:cvc_check​:

A check by the card issuer to match the provided CVC (also referred to as CVV) against the information they have on file for the cardholder.
Possible values
Description

pass

The data provided is correct.

fail

The data provided is incorrect.

unavailable

The customer’s card issuer will not check the data provided. Not all card issuers or countries support address verification.

unchecked

The data was provided, but has not yet been checked. The customer’s card issuer will eventually check the data provided.

not_provided

The data was not provided to Stripe.
The values are case sensitive.

Here’s an example of how to use post-authorization attributes:

  • Block if :address_line1_check: != 'pass' With this rule in place, any charge will be blocked if it doesn’t specifically pass the check by the card issuer to match the first line of the provided billing address against the information they have on file for the cardholder. This means that if this check is ‘unavailable’, if this data was ‘unchecked’ by the issuer, or if this data was ‘not_provided’ by the issuer, the payment will be blocked.

Type 2

standard attributes: These are available for everyone to use. You need to use colons before and after the standard attributes, like :card_bin: We’ve divided our standard attributes into four categories:

  • Attributes based on frequency—useful to prevent card testing or card cashing
  • Attributes based on card details
  • Attributes based on payment details
  • Attributes based on customer details

Some attributes require values as strings, while others require values as numbers. We’ve provided an example for each attribute to clarify this. If the attribute requires a string like :card_bin: does, you’ll see in the example that the number is within ‘ ’. For example, :card_bin: = ‘424242’, while it requires a number, it won’t have the ‘ ’ like :amount_in_usd: > 250.

Attributes based on frequency

There are four types of attributes based on frequency that are particularly useful in preventing stolen card fraud, card testing, and card cashing.

  1. Authorization: based on authorizations by the issuer
  2. Charge: based on charges
  3. Decline: based on declines by the issuer
  4. Block: based on blocks performed by Radar’s machine learning

There are also attributes based on charge outcome, including auths (based on successful authorizations by the issuer), charges (based on charge attempts), declines (based on declines by the issuer), disputes (previous transactions disputed as fraudulent), and blocks (based on blocks performed by Radar’s machine learning). Outcomes are combined with a customer detail (email, IP address, name, or customer ID) to form an attribute.

Additionally, you can combine the frequency of a customer detail (email, name) with the card or IP address used on a transaction. Said another way, frequency rules are available in two types:

  1. Based on charge outcome (e.g., authorized_charges_per_email_hourly, blocked_charges_per_email_hourly) where outcome is successful authorization, charge attempt, declines, disputes, blocks
  2. Based on links between customer information and a card or IP (e.g., name_count_for_card_weekly, email_count_for_ip_hourly)

Frequency rules exclude the payment that you’re currently processing. For example, authorized_charges_per_email_hourly represents the number of previous successful charge attempts from an email in the preceding hour. So, for the first charge attempt in a given hour for an email, authorized_charges_per_email_hourly has a value of 0. If the first succeeds, the second charge attempt within the same hour from that email has a value of 1, and so on.

Attribute
Description

:authorized_charges_per​_card_number_all_time:

The number of charges that resulted in a successful authorization on this card on your account. Takes into account payments from 2020 onward. (Note: upper limit bounded at <= 25)

:authorized_charges_per​_card_number_weekly:

The number of charges that resulted in a successful authorization on this card in the past week on your account. (Note: upper limit bounded at <= 25)

:authorized_charges_per​_card_number_daily:

The number of charges that resulted in a successful authorization on this card in the past day on your account. (Note: upper limit bounded at <= 25)

:authorized_charges_per​_card_number_hourly:

The number of charges that resulted in a successful authorization on this card in the past hour on your account. (Note: upper limit bounded at <= 25)

:authorized_charges_per​_email_all_time:

The number of charges that resulted in a successful authorization from this email on your account. Takes into account payments from 2020 onward. (Note: upper limit bounded at <= 25)

:authorized_charges_per​_email_weekly:

The number of charges that resulted in a successful authorization from this email in the past week on your account. (Note: upper limit bounded at <= 25)

:authorized_charges_per​_email_daily:

The number of charges that resulted in a successful authorization from this email in the past day on your account. (Note: upper limit bounded at <= 25)

:authorized_charges_per​_email_hourly:

The number of charges that resulted in a successful authorization from this email in the past hour on your account. (Note: upper limit bounded at <= 25)

:authorized_charges_per​_ip_address_all_time:

The number of charges that resulted in a successful authorization from this IP address on your account. Takes into account payments from 2020 onward. (Note: upper limit bounded at <= 25)

:authorized_charges_per​_ip_address_weekly:

The number of charges that resulted in a successful authorization from this IP address in the past week on your account. (Note: upper limit bounded at <= 25)

:authorized_charges_per​_ip_address_daily:

The number of charges that resulted in a successful authorization from this IP address in the past day on your account. (Note: upper limit bounded at <= 25)

:authorized_charges_per​_ip_address_hourly:

The number of charges that resulted in a successful authorization from this IP address in the past hour on your account. (Note: upper limit bounded at <= 25)

:authorized_charges_per​_customer_daily:

The number of times a customer was successfully authorized on your account in the past 24 hours. (The count doesn’t include the payment currently being evaluated.)

:authorized_charges_per​_customer_hourly:

The number of times a customer was successfully authorized on your account in the past hour. (The count doesn’t include the payment currently being evaluated.)

:blocked_charges_per​_card_number_daily:

The number of times a card number was blocked by Stripe’s machine learning models on your account in the past 24 hours. The count does not include the payment currently being evaluated (e.g., 4).

:blocked_charges_per​_card_number_hourly:

The number of times a card number was blocked by Stripe’s machine learning models on your account in the past hour. The count does not include the payment currently being evaluated (e.g., 4).

:blocked_charges_per​_customer_daily:

The number of times a customer was blocked by Stripe’s machine learning models on your account in the past 24 hours. The count does not include the payment currently being evaluated (e.g., 4).

:blocked_charges_per​_customer_hourly:

The number of times a customer was blocked by Stripe’s machine learning models on your account in the past hour. The count does not include the payment currently being evaluated (e.g., 4).

:blocked_charges_per​_ip_address_daily:

The number of times an IP address was blocked by Stripe’s machine learning models on your account in the past 24 hours. The count does not include the payment currently being evaluated (e.g., 4).

:blocked_charges_per​_ip_address_hourly:

The number of times an IP address was blocked by Stripe’s machine learning models on your account in the past hour. The count does not include the payment currently being evaluated (e.g., 4).

:total_charges_per​_card_number_daily:

The number of times a charge was attempted for a card on your account in the past 24 hours. The count does not include the payment currently being evaluated (e.g., 4).

:total_charges_per​_card_number_hourly:

The number of times a charge was attempted for a card on your account in the past hour. The count does not include the payment currently being evaluated (e.g., 4).

:total_charges_per​_customer_daily:

The number of times a customer attempted a charge on your account in the past 24 hours. The count does not include the payment currently being evaluated (e.g., 4).

:total_charges_per​_customer_hourly:

The number of times a customer attempted a charge on your account in the past hour. The count does not include the payment currently being evaluated (e.g., 4).

:total_charges_per​_ip_address_daily:

The number of times an IP attempted a charge on your account in the past 24 hours. The count does not include the payment currently being evaluated (e.g., 4).

:total_charges_per​_ip_address_hourly:

The number of times an IP attempted a charge on your account in the past hour. The count does not include the payment currently being evaluated (e.g., 4).

:declined_charges_per​_card_number_daily:

The number of times a card number was declined by the card issuer on your account in the past 24 hours. The count does not include the payment currently being evaluated (e.g., 4).

:declined_charges_per​_card_number_hourly:

The number of times a card number was declined by the card issuer on your account in the past hour. The count does not include the payment currently being evaluated (e.g., 4).

:declined_charges_per​_customer_daily:

The number of times a customer was declined by the card issuer on your account in the past 24 hours. The count does not include the payment currently being evaluated (e.g., 4).

:declined_charges_per​_customer_hourly:

The number of times a customer was declined by the card issuer on your account in the past hour. The count does not include the payment currently being evaluated (e.g., 4).

:declined_charges_per​_ip_address_daily:

The number of times an IP address was declined by the card issuer on your account in the past 24 hours. The count does not include the payment currently being evaluated (e.g., 4).

:declined_charges_per​_ip_address_hourly:

The number of times an IP address was declined by the card issuer on your account in the past hour. The count does not include the payment currently being evaluated (e.g., 4).

:declined_charges_per​_email_all_time:

The number of charges declined from this email on your account. Takes into account payments from 2020 onward. (Note: upper limit bounded at <= 25)

:declined_charges_per​_email_weekly:

The number of charges declined from this email in the past week on your account. (Note: upper limit bounded at <= 25)

:declined_charges_per​_email_daily:

The number of charges declined from this email in the past day on your account. (Note: upper limit bounded at <= 25)

:declined_charges_per​_email_hourly:

The number of charges declined from this email in the past hour on your account. (Note: upper limit bounded at <= 25)

:dispute_count_on_ip_all_time:

Count of fraudulent disputes associated with charges from this IP address on your account. Takes into account payments from 2020 onward. (Note: upper limit bounded at <= 25)

:dispute_count_on_ip_weekly:

Count of fraudulent disputes associated with charges from this IP address on your account in the past week. (Note: upper limit bounded at <= 25)

:dispute_count_on_ip_daily:

Count of fraudulent disputes associated with charges from this IP address on your account in the past day. (Note: upper limit bounded at <= 25)

:dispute_count_on_ip_hourly:

Count of fraudulent disputes associated with charges from this IP address on your account in the past hour. (Note: upper limit bounded at <= 25)

:email_count_for​_card_all_time:

The number of emails associated with this card from transactions on your account. Takes into account payments from 2020 onward. (Note: upper limit bounded at <= 25)

:email_count_for​_card_weekly:

The number of emails associated with this card from transactions on your account in the past week. (Note: upper limit bounded at <= 25)

:email_count_for​_card_daily:

The number of emails associated with this card from transactions on your account in the past day. (Note: upper limit bounded at <= 25)

:email_count_for​_card_hourly:

The number of emails associated with this card from transactions on your account in the past hour. (Note: upper limit bounded at <= 25)

:email_count_for​_ip_all_time:

The number of emails associated with this IP from transactions on your account. Takes into account payments from 2020 onward. (Note: upper limit bounded at <= 25)

:email_count_for​_ip_weekly:

The number of emails associated with this IP from transactions on your account in the past week. (Note: upper limit bounded at <= 25)

:email_count_for​_ip_daily:

The number of emails associated with this IP from transactions on your account in the past day. (Note: upper limit bounded at <= 25)

:email_count_for​_ip_hourly:

The number of emails associated with this IP from transactions on your account in the past hour. (Note: upper limit bounded at <= 25)

:name_count_for​_card_all_time:

The number of names associated with this card from transactions on your account. Takes into account payments from 2020 onward. (Note: upper limit bounded at <= 25)

:name_count_for​_card_weekly:

The number of names associated with this card from transactions on your account in the past week. (Note: upper limit bounded at <= 25)

:name_count_for​_card_daily:

The number of names associated with this card from transactions on your account in the past day. (Note: upper limit bounded at <= 25)

:name_count_for​_card_hourly:

The number of names associated with this card from transactions on your account in the past hour. (Note: upper limit bounded at <= 25).

:total_charges_per​_card_number_all_time:

The total number of charges on this card on your account. Takes into account payments from 2020 onward. (Note: upper limit bounded at <= 25)

:total_charges_per​_card_number_weekly:

The total number of charges on this card in the past week on your account. (Note: upper limit bounded at <= 25)

:total_charges_per​_card_number_daily:

The total number of charges on this card in the past day on your account. (Note: upper limit bounded at <= 25)

:total_charges_per​_card_number_hourly:

The total number of charges on this card in the past hour on your account. (Note: upper limit bounded at <= 25)

:total_charges_per​_email_all_time:

The total number of charges from this email on your account. Takes into account payments from 2020 onward. (Note: upper limit bounded at <= 25)

:total_charges_per​_email_weekly:

The total number of charges from this email in the past week on your account. (Note: upper limit bounded at <= 25)

:total_charges_per​_email_daily:

The total number of charges from this email in the past day on your account. (Note: upper limit bounded at <= 25)

:total_charges_per​_email_hourly:

The total number of charges from this email in the past hour on your account. (Note: upper limit bounded at <= 25)

:total_charges_per​_ip_address_all_time:

The total number of charges from this IP address on your account. Takes into account payments from 2020 onward. (Note: upper limit bounded at <= 25)

:total_charges_per​_ip_address_weekly:

The total number of charges from this IP address in the past week on your account. (Note: upper limit bounded at <= 25)

:total_charges_per​_ip_address_daily:

The total number of charges from this IP address in the past day on your account. (Note: upper limit bounded at <= 25)

:total_charges_per​_ip_address_hourly:

The total number of charges from this IP address in the past hour on your account. (Note: upper limit bounded at <= 25)

Attributes based on card details

Attribute
Description

:card_bin:

The Bank Identification Number (BIN) of the card being used to make the payment. It is the first six digits of the card number (e.g., '424242').

:card_brand:

The brand of the card being used to make the payment. The supported values are: 'amex' (American Express), 'visa' (Visa), 'mc' (Mastercard), 'dscvr' (Discover), 'diners' (Diners Club), 'interac' (Interac), 'jcb' (JCB), and 'cup' (UnionPay).

:card_country:

The two-letter code corresponding to the country where the card was issued (e.g., 'US'). For a list of country codes see this page. To specify multiple countries, use the IN operator: IN ('GB', 'DE', 'AE').

:card_fingerprint:

The fingerprint of the card being used to make the payment. The card fingerprint is a unique identifier of a particular card number—you can find this number by going under Payments and inspecting a payment in the Payment method section (e.g., 'VfE3rx3VlaQhS8Lp'). This is case sensitive.

:card_funding:

Whether the card is a prepaid, debit, or credit card. The supported values are: 'credit', 'debit', 'prepaid', 'unknown'.

:card_3d_secure_support:

The level of 3D Secure support for the card being used to make the payment. The supported values are: 'required', 'recommended', 'optional', and 'not_supported'.

Attributes based on payment details

Attribute
Description

:amount_in_xyz:

The amount of the payment, converted to the currency specified by xyz (e.g., amount_in_usd). Specify one of the following supported currencies and Stripe automatically calculates a converted amount to use: aud, brl, cad, chf, dkk, eur, gbp, hkd, inr, jpy, mxn, nok, nzd, ron, sek, sgd, or usd. Do not use sub-units (e.g., use dollars, not cents) (e.g., :amount_in_usd: > 250)

:average_usd_amount​_attempted_on_card_all_time:

The average amount (in USD) of attempted transactions for the card on your account. Takes into account payments from 2020 onward.

:average_usd_amount​_successful_on_card_all_time:

The average amount (in USD) of transactions that resulted in an authorization for the card on your account. Takes into account payments from 2020 onward.

:risk_level:

The risk level of a given payment, as determined by Stripe. The supported values are: ‘normal’, ‘elevated’, ‘highest’, and ‘not_assessed’.

:risk_score:

The risk score of a given payment, as determined by Stripe (e.g., > 50). The values range between 0 (least risky) and 100 (riskiest). A risk score of 65 or above corresponds to a risk level of ‘elevated’, while a risk score of 75 or above corresponds to a risk level of ‘highest’.

:charge_description:

The description supplied with the payment (e.g., ‘Class trial’).

:is_recurring:

Identifies if the payment is recurring—for example, from subscriptions. (This is Boolean so you either use :is_recurring: for cases when it’s true or NOT :is_recurring: for cases when it’s not true. You can’t use !=.)

:is_off_session:

Indicates when a Stripe Billing payment is not triggered by direct user action, or when the off_session flag is set at PaymentIntent confirmation. (This is Boolean so you either use :is_off_session: for cases when it’s true or NOT :is_off_session: for cases when it’s not true. You can’t use !=.)

:digital_wallet:

The type of digital wallet used to store payment information. The supported values are: ‘android_pay’, ‘amex_express_checkout’, ‘apple_pay’, ‘masterpass’, ‘samsung_pay’, ‘unknown’, ’visa_checkout’, ‘none’.

:destination:

For Connect users creating destination charges, the destination account on whose behalf the charge is made (e.g., ‘acct_19KCB9AlaaEw6AgR’). This is case sensitive.

:is_checkout:

Identifies if the payment is processed through Checkout. This attribute only applies to payments processed through the new version of Checkout and does not capture payments through legacy Checkout. (This is Boolean so you either use :is_checkout: for cases when it’s true or NOT :is_checkout: for cases when it’s not true. You can’t use !=.)

:is_3d_secure​_authenticated:

Identifies if the payment follows a successfully completed 3D Secure verification with authentication. Authentication may be either risk-based or challenge-based. (This is Boolean so you either use :is_3d_secure_authenticated: for cases when it’s true or NOT :is_3d_secure_authenticated: for cases when it’s not true. You can’t use !=.)

:is_3d_secure:

Identifies if the payment uses a 3D Secure source. (This is Boolean so you either use :is_3d_secure: for cases when it’s true or NOT :is_3d_secure for cases when it’s not true. You can’t use !=).

:has_liability_shift:

True if the fraud liability has been shifted for this payment (this is Boolean so you either use :has_liability_shift: for cases when it’s true or NOT :has_liability_shift for cases when it’s not true. You can’t use !=.)

:seconds_since_card​_first_seen:

The number of seconds since the card for the payment was first seen on your account. Takes into account payments from 2020 onward.

:seconds_since_first_successful​_auth_on_card:

The number of seconds since the first successful auth for the card associated with the payment happened on your account. Takes into account payments from 2020 onward.

:total_usd_amount_failed​_on_card_all_time:

The total amount (in USD) of transactions from this card that failed (were blocked or declined) on your account. Takes into account payments from 2020 onward.

:total_usd_amount_successful​_on_card_all_time:

The total amount (in USD) of transactions that resulted in an authorization for the card on your account. Takes into account payments from 2020 onward.

Attributes based on customer details

Attribute
Description

:ip_country:

The two-letter code corresponding to the country-level geolocation of the IP address from which the payment originates (e.g., 'GB'). For a list of country codes see this page. To specify multiple countries, use the IN operator: IN ('GB', 'DE', 'AE').

:ip_address:

The IP address from which the payment originates (e.g., '192.168.0.1' to specify one single IP, or if you want to cast a wide net you can use INCLUDES '192.168' to include all IPs that share the first six digits).

:is_anonymous_ip:

Identifies if the IP address from which the payment originates is a known proxy or Tor exit node. This information is updated daily. (This is Boolean so you either use :is_anonymous_ip: for cases when it’s true or NOT :is_anonymous_ip: for cases when it’s not true. You can’t use !=).

:is_my_login_ip:

Identifies if the IP address from which the payment originates has ever been used to log in to your Stripe account. This attribute can be used as a proxy for “is my IP address.” (This is Boolean so you either use :is_my_login_ip: for cases when it’s true or NOT :is_my_login_ip: for cases when it’s not true. You can’t use !=.)

:email:

The email address supplied with the payment (e.g., 'user@example.com').

:email_domain:

The domain of the email address supplied with the payment (e.g., 'example.com').

:is_disposable_email:

Identifies if the email address supplied with the payment is one used with a known throwaway email address provider. Stripe maintains a list of domains corresponding to throwaway email addresses to provide this attribute. (This is Boolean so you either use :is_disposable_email: for cases when it’s true or NOT :is_disposable_email: for cases when it’s not true. You can’t use !=.)

:billing_address:

The full provided cardholder billing address (e.g., '510 Townsend, San Francisco, CA 94110').

:billing_address_line1:

The first line of the provided cardholder billing address—typically a street name and number (e.g., '510 Townsend').

:billing_address_line2:

The second line of the provided cardholder billing address—typically an apartment or unit number (e.g., 'Apt 5b').

:billing_address_postal_code:

The postal code (ZIP) of the provided cardholder billing address (e.g., '94110').

:billing_address_city:

The city of the provided cardholder billing address (e.g., 'San Francisco').

:billing_address_state:

The state of the provided cardholder billing address (e.g., 'CA').

:billing_address_country:

The two-letter code corresponding to the country of the provided cardholder billing address (e.g., 'US'). For a list of country codes see this page. To specify multiple countries, use the IN operator: IN ('US', 'DE', 'AE').

:seconds_since​_email_first_seen:

The number of seconds since the email address supplied with the payment was first seen on your account. Takes into account payments from 2020 onward.

:seconds_since​_email_first_seen_on_stripe:

The number of seconds since the email address supplied with the payment was first seen on Stripe overall. Takes into account payments from 2020 onward.

:shipping_address:

The full provided shipping address (e.g., '510 Townsend, San Francisco, CA 94110').

:shipping_address_line1:

The first line of the provided shipping address—typically a street name and number (e.g., '510 Townsend').

:shipping_address_line2:

The second line of the provided shipping address—typically an apartment or unit number (e.g., 'Apt 5b').

:shipping_address_postal_code:

The postal code (ZIP) of the provided shipping address (e.g., '94110').

:shipping_address_city:

The city of the provided shipping address (e.g., 'San Francisco').

:shipping_address_state:

The state of the provided shipping address (e.g., 'CA').

:shipping_address_country:

The two-letter code corresponding to the country of the provided shipping address (e.g., 'US'). For a list of country codes see this page. To specify multiple countries, use the IN operator: IN ('US', 'DE', 'AE').

Here’s an example of how to use standard attributes:

  • Block if :card_country: IN ('CA', 'DE', 'AE')

With this rule in place, any charge from a card issued in Canada, Germany, or the United Arab Emirates will be blocked.

Type 3

Metadata attributes: these attributes will depend on what metadata you send to Stripe. With these attributes, you need to use double colons before and after the standard attributes, like ::Customer Age::. Metadata attributes can operate as either strings or numbers. When used as strings, metadata attributes are case sensitive.

Metadata can be used to create very powerful rules, like placing charges in manual review based on the SKU purchased or reducing friction for returning customers. To learn how to pass more metadata, read this guide.

Metadata attributes are written in the following structure:

  • ::[metadata attribute name]:: [operator] [metadata_value]

Suppose we have payments with the following key-value data stored in the metadata field:

Metadata name
Metadata value
Customer age
22
Item ID
5A381D
Category ID
groceries

A rule can be written to place payments that match the following criteria into review.

  • Review if ::Customer Age:: < 30

You can also write rules using both metadata attributes and other supported attributes mentioned in this document. For example, you can write a rule that only places a payment in review if the Item ID matches 5A381D and the payment amount exceeds US$1,000.

  • Review if ::Item ID:: = '5A381D' and :amount_in_usd: > 1000

Metadata attributes also support the IN operator to match against multiple values. For example, you can write a rule that places a payment in review if the Category ID is one of “groceries”, “electronics”, or “clothing”.

  • Review if ::Category ID:: IN ('groceries', 'electronics', 'clothing')

The INCLUDES operator can be used with rules for metadata attributes and other string attributes to match substrings. For example, you can write a rule that places a payment in review if the Item ID includes the string A381. This matches “A381”, “5A381D”, “A381D”, “5A381”, and more.

  • Review if ::Item ID:: INCLUDES 'A381'

Metadata can also be accessed on customer and destination objects (if those are used for a given payment). These attributes are written in the following structure:

  • ::[customer|destination]:[metadata attribute name]::[operator][metadata_value]:

Suppose you had a customer with the following metadata:

Metadata name
Metadata value
Trusted
true

You could write a rule that always allows payments if the customer’s Trusted metadata field is true.

  • Allow if ::customer:Trusted:: = 'true'

Or if you had a destination with the following metadata:

Metadata name
Metadata value
Category
new

You could write a rule that places a payment in review if the destination’s Category metadata field is new.

  • Review if ::destination:Category:: = 'new'

Using saved lists in your rules (e.g., allow lists, block lists)

You can reference a group of values in your rules through lists that you’ve previously created (e.g., allow lists or block lists). If you’re trying to block a list of emails, you should create a block list instead of creating multiple rules for each email you’d like to block.

All list aliases referenced in rules must start with @. To construct a rule referencing a list, you need to follow the structure:

  • {action} [attribute] in [list]

For example, say you have a list of card countries you’d like to block. You could write a rule using several OR clauses:

  • Block if :card_country: = 'CA' OR :card_country: = 'DE' OR :card_country: = 'AE'

You could also write a rule using an inline list:

  • Block if :card_country: IN ('CA', 'DE', 'AE')

You could also create a list of card countries you’d like to block, named card_countries_to_block. You can then add the countries of your choice to the list and reference that list in a rule:

  • Block if :card_country: in @card_countries_to_block

Not only is the rule using a list more concise, but it is also much easier to edit and add a large number of items to.

Note: EU merchants should be aware of the Geo-blocking Regulation and its prohibitions on blocking payments from customers based in EU member states. Learn more about this regulation.

Write complex rules with multiple conditions

You can build complex conditions by joining together basic conditions using the operators AND, OR, and NOT. You can also use their symbolic equivalents: &&, ||, and ! respectively. Similar to programming languages such as C, Python, and SQL, Stripe supports standard operator precedence (order of operations). For instance, the complex condition:

  • {condition_X} OR NOT {condition_Y} AND {condition_Z}

is interpreted as:

  • {condition_X} OR ((NOT {condition_Y}) AND {condition_Z})

Sub-conditional grouping within complex conditions is also supported using parentheses. For instance, the prior example can be amended to explicitly change the evaluation order of sub-predicates:

  • ({condition_X} OR (NOT {condition_Y})) AND {condition_Z}

  • {condition_X} OR NOT ({condition_Y} AND {condition_Z})

By using parentheses in different locations, each of these complex conditions lead to different results.

The is_missing function can also be used in OR or AND conjunctions:

  • Review if is_missing(:email_domain:) OR :email_domain: IN ('yopmail.net', 'yandex.ru')

Or you can use the is_missing function when it’s not missing, in this case if it blocks payments if the :ip_country: is NOT missing and the IP is from either the US or PR.

  • Block if !(is_missing(:ip_country:))AND :ip_country: IN ('US', 'PR')

Backtesting rules

As a general philosophy to rule analysis, there’s a trade-off between preventing fraud and blocking good transactions or false positives. Backtesting helps identify rules that fall within your risk appetite or strike the right balance between disputes prevented and any increase in false positives. To estimate the impact of a rule, you can backtest combinations using transaction data from the last six months via the Radar Dashboard and conduct more targeted analyses to understand how the rule would have performed if recently in place.

Backtesting in the Dashboard

The definitions of what constitutes fraudulent and other successful payments differs based on the rule type you’re testing:

Block rule

  • Disputed, received early fraud warning, or refunded as fraud: successful charges that were disputed or refunded for fraud or successful charges placed in review that were disputed or refunded for fraud

  • Other successful payments: successful charges that were not disputed or refunded for fraud or successful charges placed in review and were not disputed or refunded for fraud

  • Failed payment attempts: declined by issuer or blocked by Radar

Review rule

  • Disputed, received early fraud warning, or refunded as fraud: successful charges that were disputed or refunded for fraud

  • Other successful payments: successful charges that were not disputed or refunded for fraud

  • Failed or already placed in review: declined by issuer, blocked by Radar, or successful charges placed in review (regardless of dispute or refund status)

Allow rule

  • Blocked by Stripe or your custom rules: charges blocked by Radar

  • Disputed, received early fraud warning, or refunded as fraud: successful charges that were disputed or refunded for fraud or successful charges placed in review that were disputed or refunded for fraud

  • Other successful or bank-declined payments: declined by issuer, successful charges that were not disputed, or refunded for fraud or successful charges placed in review and were not disputed or refunded for fraud

Performing custom backtesting analyses

The backtesting feature in the Radar Dashboard focuses on the last six months of transactions and includes disputes, early fraud warnings, and charges refunded as fraudulent.

You may want to perform a more targeted analysis if, for example, you’re at risk of identification in a Visa Fraud Monitoring Program (focused exclusively on early fraud warnings) or you notice a recent spike in fraud coming from a specific IP country or wallet type. To do so, you can build a SQL query in Sigma or export and analyze reports of payments data in the Dashboard. Custom backtesting allows for flexibility in timeframes (beyond six months) and more targeted analyses (for example, you can hone in on just disputes or EFWs). The sample query below backtests on Visa early fraud warnings (EFWs) on transactions > $100 if you hypothetically found that fraud volume was recently spiking on higher value and that elevated risk score transactions created monitoring program risk:

Using fields and tables available in Sigma

with base as (
    select
        c.id,
        c.amount,
        c.captured,
        e.created as efw_created
    from charges c
    left join early_fraud_warnings on e.charge_id = c._id
    where card_brand = ‘visa’
    and (c.amount / 100) >= 100
    and c.captured >= dateadd(‘day’, -180, current_date)
)

select 
    count(case when efw_created >= dateadd(‘day’, -60, current_date) then id else null end) as fraud_charge_count,

sum(case when efw_created >= dateadd(‘day’, -60, current_date) then amount else null end) as fraud_amount,

count(case when efw_created is null and captured between dateadd(‘day’, -120, current_date) and dateadd(‘day’, -60, current_date) then id else null end) as false_positive_charge_count,

count(case when efw_created is null and captured between dateadd(‘day’, -120, current_date) and dateadd(‘day’, -60, current_date) then amount else null end) as false_positive_amount

from base

Backtesting on the last 60 days by EFW creation date hones in on the most recent fraud, while backtesting the last 60–120 days of nonfraudulent sales allows for fraud to more fully mature.

Common fraud vectors

Most fraudulent actors follow a common pattern of committing fraud. First, they validate the stolen payment information (e.g., cards). Once validated that they work, they use these credentials to extract value in the form of physical goods for personal use or resale (luxury goods or electronics), services for personal use or resale (food delivery services), or services and products that help commit further fraud (i.e., web hosting services, message spamming services, etc.).

Read on for more detail on some of the most common fraud vectors and suggested ways to use Radar rules to mitigate them.

Testing

Card testing occurs when fraudulent actors use scripts or manual processes to test if raw stolen card numbers are still active. This phase of fraud is not about getting a physical good or service; it’s to validate the cards are active. These charges are generally for lower dollar transactions or auths. Testing generally occurs in rapid succession and with velocity. Attributes that may be helpful are grouping and velocity features, such as:

  • total_charges_per_customer

  • card_count_for_email

  • card_count_for_ip_address

  • total_charges_per_ip

Fraudulent actors will typically try to get around these by spinning up fake emails and using distinct email addresses. More advanced fraudulent actors will mask IP addresses or even have multiple devices to provide unique device data. At that point, knowing good and typical customer behavior is important. Features such as email domain and IP country, among other broader categories, can help identify higher risk transactions. Many fraudulent customers will use popular email domains from established email providers, like gmail.com. You may see domains like gmail.comms, or gomail.co, which try to mask fraudulent actors' identity. Card country and IP country can also be used to help segment customers and ensure transactions are from areas typical of your user base. Transactions outside these locations might be of interest to review or block.

One final functionality to curb this testing behavior is to institute CAPTCHA.

In Stripe Checkout, CAPTCHA challenges are automatically served when our machine learning detects a card testing attack. To mitigate card testing, Stripe uses a series of automated and manual controls, including rate limiters, alerts, and ongoing reviews alongside training card testing models to automatically detect attacks. These models only serve challenges when a card testing attack is in progress so that real users almost never see a CAPTCHA, just the bots. This has reduced card testing for businesses using Stripe Checkout by a whopping 80% with next to no detectable impact on conversion.

Adding Stripe-managed CAPTCHA for all Checkout users reduced card testing by 80%, with a less than 2bps (0.02%) impact on authorization rates.

Note that you can also write custom rules like “Block if declined more than 3 times from a given IP address” to reduce card testing attacks.

Value extraction

Stolen credit cards (new behavior)

In this fraud vector, the fraudulent actor uses the validated stolen card on their personal device or a device utilized to commit fraud.

This vector is usually abused either through scripted mass attacks or at smaller scales with more targeted fraud rings and pockets. Regardless, using rule attributes that measure the newness of an account on Stripe, like hours_since_email_first_seen_on_stripe, in combination with risk_score and other features, keep these brand-new cardholders at bay. Furthermore, velocity restrictions on IP, emails, and cards can further protect merchants from volume attacks where fraudulent actors are trying to monetize stolen credentials as quickly as possible.

Stolen credit cards (masking behavior)

In this fraud vector, the fraudulent actor uses the validated stolen card on their personal device or a device utilized to commit fraud or the fraudulent actor has compromised a subscription account and gained access to the credit card information stored in the account.

The fraudulent actor will try their best to mask their presence by:

  • Using the same name as previous completed transactions

  • Using the same billing address as previous completed transactions

  • Using a VPN to try to make it seem like they are the original card holder. They may VPN into the same city and sometimes even the same block

  • Changing only a minor detail, like email address or phone number

  • Changing the shipping address from prior transactions, for a physical good, with potentially a large delta in the distance between billing and shipping address. This is a loose signal

The masking behavior described above makes it difficult to parse out who is actually making the transaction—the original card holder or a fraudulent actor who has compromised the account. This often means that this kind of fraud goes undetected for longer, both by the merchant and the original card holder.

The strategy here is the same: the fraudulent actor will try to extract as much value from the stolen credentials as possible. Rules that use velocity-limiting features, along with riskscore, cvccheck fails, or zip check fails, can help protect against this behavior.

Other best practices

Here are additional best practices to help you optimize rule writing in Radar.

Checkout flow
In the checkout flow, include explicit reference to your Terms of Service
In the case of chargebacks, provide a clearly labeled screenshot of the ToS as it appears in the checkout flow, and explain its significance. This will improve your chances of winning.
Validate CVS and Postcode
Allows the Issuer to validate the cardholder. May increase likelihood of winning disputes and often improves authorization rates. Consider blocking failed checks.
Collect as much customer information as possible
Collecting these details helps issuers evaluate your case in the event of chargebacks, improving your chances of winning. They are seen as due diligence.
Gold standard includes: CVC and Postcode, customer name, email address, full billing address, IP address, device information, etc.
Implementing Stripe.js gives Radar IP address, device, and behavioral information to improve fraud detection.
Customer interactions
Blocklist cards with chargebacks in the “fraudulent” category
If a customer disputes a charge as fraudulent, future charges are likely to be disputed as well.
Refund suspicious / fraudulent payments
70–85% of TC40 transactions become disputes, and only full refunds can prevent a dispute.
Implement a clear Statement Descriptor
Reduces the number of unrecognized disputes.

Importance of using Stripe.js

  • Include stripe.js on full payment path for maximum fraud signaling
  • To get the most out of Radar without impacting page load time, load stripe.js async on non-payment pages
  • Simplest to put next to Google Analytics script tags
  • Full stripe.js bundle size is 29.6kb gzipped
    • Future state: radar.js will be able to be included separately from stripe.js

Conclusion

Rules can be an extraordinarily powerful tool to help you customize your fraud protection. By implementing unique logic, informed by some of the best practices outlined in this guide, you can create a fraud prevention setup in Radar specific to your business needs.

If you want to learn more about Radar for Fraud Teams, see here.

If you’re already a Radar for Fraud Teams user, check out the Rules page in your Dashboard to get started with rule writing.

Other notes

Radar for platforms

Are you a platform using Stripe Connect? If so, any rules you create only apply to payments created on the platform account (in Connect terms, these are destination or on-behalf-of
charges). Payments created directly on the Connected account are subject to that account’s rules.

Radar for Terminal

Terminal charges aren’t screened by Radar. This means that if you use Terminal, you can write rules based on IP frequency without worrying about blocking your in-person payments.

Ready to get started?

Create an account and start accepting payments – no contracts or banking details required. Or, contact us to design a custom package for your business.