diff --git a/CRM/Stripe/Check.php b/CRM/Stripe/Check.php
index 2b6a10bd42a609ae3519c1f1f813941f0c1bed5c..55549a47f96158dec36313cd1830ce3c4c005166 100644
--- a/CRM/Stripe/Check.php
+++ b/CRM/Stripe/Check.php
@@ -51,6 +51,7 @@ class CRM_Stripe_Check {
     $this->checkExtensionFirewall();
     $this->checkUpgradeMessages();
     $this->checkWebhooks();
+    $this->checkFailedPaymentIntents();
     return $this->messages;
   }
 
@@ -210,4 +211,38 @@ class CRM_Stripe_Check {
     }
   }
 
+  /**
+   * Try to detect if a client is being spammed / credit card fraud.
+   */
+  private function checkFailedPaymentIntents(&$messages) {
+    // Check for a high volume of failed/pending contributions
+    $count = CRM_Core_DAO::singleValueQuery('SELECT count(*)
+      FROM civicrm_stripe_paymentintent
+      WHERE status = "failed"
+        AND TIMESTAMPDIFF(minute, created_date, NOW()) < 60
+      ORDER BY id DESC
+      LIMIT 1000');
+
+    if ($count > 20) {
+      $message = new CRM_Utils_Check_Message(
+        'stripe_paymentintentspam',
+        E::ts('%1 failed Stripe Payment Intents in the past hour. Please check the logs. They are problably hitting the CiviCRM REST API.', [1 => $count]),
+        E::ts('Stripe - High rate of failed contributions'),
+        \Psr\Log\LogLevel::CRITICAL,
+        'fa-check'
+      );
+      $this->messages[] = $message;
+    }
+    else {
+      $message = new CRM_Utils_Check_Message(
+        'stripe_paymentintentspam',
+        E::ts('%1 failed Stripe Payment Intents in the past hour.', [1 => $count]) . ' ' . E::ts('We monitor this in case someone malicious is testing stolen credit cards on public contribution forms.'),
+        E::ts('Stripe - Failed Stripe Payment Intents'),
+        \Psr\Log\LogLevel::INFO,
+        'fa-check'
+      );
+      $this->messages[] = $message;
+    }
+  }
+
 }