Fraud Protection

Stripe offers integrated, automatic fraud protection for users of Stripe.js and Checkout. If you need help after reading this, check out our answers to common questions or chat live with other developers in #stripe on freenode.

If you use Stripe.js or Checkout, Stripe automatically evaluates charges for fraud risk and blocks those we believe are fraudulent. This process is automatic and does not require any extra integration work, but you can improve the accuracy of our assessments by letting us know if you believe a charge is fraudulent or legitimate.

Getting information on charges Stripe thinks are fraudulent

If Stripe believes a payment is fraudulent, we will block the charge creation attempt. The error hash returned by the API will have a decline_code key with a value of fraudulent:

curl https://api.stripe.com/v1/charges \
   -u sk_test_BQokikJOvBiI2HlWgH4olfQ2: \
   -d card=tok_51ZM3BwtekpEyl \
   -d amount=2000 \
   -d currency=usd

The above returns the following in the response:

{
  "error": {
    "message": "Your card was declined.",
    "type": "card_error",
    "code": "card_declined",
    "decline_code": "fraudulent",
    "charge": "ch_5IZNUkTDe6ir9d"
  }
}

Furthermore, the charge's fraud_details hash will have a stripe_report key with a value of fraudulent:

curl https://api.stripe.com/v1/charges/ch_51ZhLzPgyOpiIo \
   -u sk_test_BQokikJOvBiI2HlWgH4olfQ2:

The above returns the following in the response:

{
  ...
  "statement_description": null,
  "fraud_details": {
    "stripe_report": "fraudulent",
  },
  "receipt_email": null,
  ...
}

Payments blocked by Stripe are also indicated as such in the dashboard. Stripe evaluates fraud risk using machine learning algorithms that predict what is fraudulent based on your charge and dispute history. You will not see risk assessments (in the dashboard or in the API) until you have accumulated enough volume for our algorithms to give you relevant results. This will happen automatically.

Note that testmode charges (other than those using test card numbers intended to simulate CVC or AVS mismatches) will not trigger our fraud controls.

Reporting a charge as fraudulent

If you believe a charge is fraudulent and Stripe did not block it, you should refund it and let Stripe know. We will use the information to improve our fraud detection algorithms. You can "report and refund" a fraudulent payment from the dashboard by clicking the "Report fraudulent payment" link when viewing the payment. Alternatively, you can use the API.

# Set your secret key: remember to change this to your live secret key in production
# See your keys here: https://dashboard.stripe.com/account/apikeys
Stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"

# If you haven't refunded the charge, you can do so and let Stripe
# know it was fraudulent in one step.
charge = Stripe::Charge.retrieve(charge_id)
charge.refunds.create(reason: 'fraudulent')

# If you already refunded the charge (without specifying the
# 'fraudulent' reason), you can still let us know it was fraudulent.
refunded_charge = Stripe::Charge.retrieve(refunded_charge_id)
refunded_charge.mark_as_fraudulent
# Set your secret key: remember to change this to your live secret key in production
# See your keys here: https://dashboard.stripe.com/account/apikeys
stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"

# If you haven't refunded the charge, you can do so and let Stripe
# know it was fraudulent in one step.
charge = stripe.Charge.retrieve(charge_id)
charge.refunds.create(reason='fraudulent')

# If you already refunded the charge (without specifying the
# 'fraudulent' reason), you can still let us know it was fraudulent.
refunded_charge = stripe.Charge.retrieve(refunded_charge_id)
refunded_charge.mark_as_fraudulent()
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
\Stripe\Stripe::setApiKey("sk_test_BQokikJOvBiI2HlWgH4olfQ2");

// If you haven't refunded the charge, you can do so and let Stripe
// know it was fraudulent in one step.
$charge = \Stripe\Charge::retrieve($charge_id);
$charge->refunds->create(array("reason" => "fraudulent"));

// If you already refunded the charge (without specifying the
// 'fraudulent' reason), you can still let us know it was fraudulent.
$refunded_charge = \Stripe\Charge::retrieve($refunded_charge_id);
$refunded_charge->markAsFraudulent();
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
Stripe.apiKey = "sk_test_BQokikJOvBiI2HlWgH4olfQ2";

// If you haven't refunded the charge, you can do so and let Stripe
// know it was fraudulent in one step.
Charge charge = Charge.retrieve(chargeId);

Map<String, Object> refundParams = new HashMap<String, Object>();
refundParams.put("reason", "fraudulent");

charge.refund(refundParams);

// If you already refunded the charge (without specifying the
// 'fraudulent' reason), you can still let us know it was fraudulent.
Charge refundedCharge = Charge.retrieve(refundedChargeId);
refundedCharge.markFraudulent((RequestOptions) null);
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
var stripe = require("stripe")("sk_test_BQokikJOvBiI2HlWgH4olfQ2");

// If you haven't refunded the charge, you can do so and let Stripe
// know it was fraudulent in one step.
stripe.charges.createRefund(
  charge_id, {"reason": "fraudulent"})

// If you already refunded the charge (without specifying the
// 'fraudulent' reason), you can still let us know it was fraudulent.
stripe.charges.markAsFraudulent(charge_id);

Reporting a charge as safe

If a charge has been declined and you know that it was made legitimately (for example, you may have a long-standing relationship with the customer), you can check if Stripe blocked it because we believed it was fraudulent. If so, you can mark the charge as safe—this will allow you to retry the charge, and it will also improve Stripe's assessments in the future. You can see which payments Stripe has declined in the dashboard and mark them as safe there, or you can use the API.

# Set your secret key: remember to change this to your live secret key in production
# See your keys here: https://dashboard.stripe.com/account/apikeys
Stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"

# Retrieve the charge and check if Stripe thought it was
# fraudulent.
charge = Stripe::Charge.retrieve(charge_id)

# If Stripe declined the charge due to fraud risk but you know the payment
# was legitimate, you can mark it as safe and then retry.
if charge.fraud_details['stripe_report'] == 'fraudulent'
  charge.mark_as_safe
  # ...retry the payment
end
# Set your secret key: remember to change this to your live secret key in production
# See your keys here: https://dashboard.stripe.com/account/apikeys
stripe.api_key = "sk_test_BQokikJOvBiI2HlWgH4olfQ2"

# Retrieve the charge and check if Stripe thought it was
# fraudulent.
charge = stripe.Charge.retrieve(charge_id)

# If Stripe declined the charge due to fraud risk but you know the payment
# was legitimate, you can mark it as safe and then retry.
if charge.fraud_details.get('stripe_report', None) == 'fraudulent'
  charge.mark_as_safe()
  # ...retry the payment
end
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
\Stripe\Stripe::setApiKey("sk_test_BQokikJOvBiI2HlWgH4olfQ2");

// Retrieve the charge and check if Stripe thought it was
// fraudulent.
$charge = \Stripe\Charge::retrieve($charge_id);

// If Stripe declined the charge due to fraud risk but you know the payment
// was legitimate, you can mark it as safe and then retry.
if ($charge['fraud_details']['stripe_report'] == 'fraudulent') {
  $charge->markAsSafe();
  // ...retry the payment
}
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
Stripe.apiKey = "sk_test_BQokikJOvBiI2HlWgH4olfQ2";

// Retrieve the charge and check if Stripe thought it was
// fraudulent.
Charge charge = Charge.retrieve(chargeId);

// If Stripe declined the charge due to fraud risk but you know the payment
// was legitimate, you can mark it as safe and then retry.
if ((charge.getFraudDetails().getStripeReport()) != null &&
    (charge.getFraudDetails().getStripeReport().equals("fraudulent"))) {
  charge.markSafe((RequestOptions) null);
  // ...retry the payment
}
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
var stripe = require("stripe")("sk_test_BQokikJOvBiI2HlWgH4olfQ2");

// Retrieve the charge and check if Stripe thought it was
// fraudulent.
stripe.charges.retrieve(charge_id, {},
  function(err, charge) {
  // If Stripe declined the charge due to fraud risk but you know the payment
  // was legitimate, you can mark it as safe and then retry.
  if(charge['fraud_details']['stripe_report'] == 'fraudulent') {
    stripe.charges.markAsSafe(charge_id);
    // ...retry the payment
  }
});

Improving fraud detection

Besides marking charges as fraudulent or safe, you can improve Stripe's risk assessments by sending us as much information as possible about each charge. For example, billing addresses, which you can capture with Stripe.js, often provide hints as to the legitimacy of a transaction. If you ship physical goods, sending us shipping addresses will also help. If you have any other data that you think might be indicative of fraud, include it in the metadata for your charges. Our systems are constantly evolving, and we're ever on the lookout for additional signals to incorporate.

For more information, see the documentation for creating and updating charges (links below).

Checkout automatically captures a number of parameters that we know to be highly suggestive of fraud. Using Checkout provides the highest level of fraud protection from Stripe.

Testing

You can make testmode charges with the card number 4100000000000019 to simulate a charge that Stripe thinks is fraudulent.

More information