Protect your FlutterFlow app from fraud with layered defenses: Firebase App Check to verify legitimate clients, Stripe Radar and 3D Secure for payment fraud, email verification before any financial action, server-side IP rate limiting in Cloud Functions, and device fingerprinting for account fraud detection. Never rely on client-side checks alone — fraudsters bypass the FlutterFlow UI entirely.
Multi-Layer Fraud Protection for FlutterFlow Apps
Fraud in app-based businesses takes many forms: fake accounts, stolen payment cards, referral fraud, and API abuse. The critical concept is defense in depth — no single protection is sufficient. Fraudsters who target your app will bypass the UI entirely and call your Firebase and Stripe APIs directly. Every check that only runs inside the FlutterFlow app is invisible to them. This tutorial builds the protection layers that work at the infrastructure level: App Check, Stripe Radar, server-side rate limiting, and anomaly detection.
Prerequisites
- FlutterFlow project with Firebase and Stripe connected
- Firebase Blaze billing plan for Cloud Functions
- Stripe account with payments enabled
- Basic understanding of Firebase Cloud Functions
Step-by-step guide
Enable Firebase App Check to block non-app API calls
Enable Firebase App Check to block non-app API calls
Firebase App Check is the single most impactful fraud prevention tool for Firebase-based apps. It adds a cryptographic attestation to every request from your app, proving to Firebase that the request originated from your legitimate app binary. Without it, anyone can use your public Firebase config to call Firestore, Cloud Functions, and Storage directly from curl or a script. In the Firebase console, go to App Check under the Build section. For Android, enable Play Integrity. For iOS, enable App Attest. For web, enable reCAPTCHA v3 (free). Enable monitoring mode first for 7 days, review the metrics, then click Enforce. After enforcement, requests without valid attestation tokens are automatically rejected by Firebase.
Expected result: The App Check dashboard shows traffic from your app with valid attestation. After enforcement, all API calls from scripts or unknown clients are rejected with a 403 error.
Enable Stripe Radar and require 3D Secure for high-risk charges
Enable Stripe Radar and require 3D Secure for high-risk charges
Stripe Radar is Stripe's machine-learning fraud detection engine and is active on all Stripe accounts at no extra cost. In your Stripe Dashboard, go to Radar → Rules. By default, Radar blocks obviously fraudulent cards. Enhance this by adding custom rules: block charges when 'risk_score > 75', block when 'is_prepaid_card = true' if your business does not accept prepaid cards, and require 3D Secure when 'risk_score > 50'. 3D Secure adds a bank verification step for suspicious charges, dramatically reducing chargebacks. In your FlutterFlow Cloud Function that creates a Payment Intent, add stripe_3d_secure: 'automatic' to trigger it based on Radar's assessment.
1// Cloud Function: createPaymentIntent with fraud protection2const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);34exports.createPaymentIntent = functions.https.onCall(async (data, context) => {5 if (!context.auth) {6 throw new functions.https.HttpsError('unauthenticated', 'Login required');7 }89 const { amount, currency, customerId } = data;1011 // Enforce minimum and maximum amounts server-side12 if (amount < 50 || amount > 1000000) {13 throw new functions.https.HttpsError('invalid-argument', 'Invalid amount');14 }1516 const paymentIntent = await stripe.paymentIntents.create({17 amount,18 currency: currency || 'usd',19 customer: customerId,20 // Let Stripe Radar decide when to trigger 3D Secure21 payment_method_options: {22 card: { request_three_d_secure: 'automatic' },23 },24 // Add metadata for your own fraud review25 metadata: {26 user_id: context.auth.uid,27 ip_address: context.rawRequest?.ip || 'unknown',28 },29 });3031 return { clientSecret: paymentIntent.client_secret };32});Expected result: Stripe Radar automatically declines known fraudulent cards. Suspicious charges require 3DS bank verification before completing.
Require email verification before financial actions
Require email verification before financial actions
Fraudsters create throwaway accounts to test stolen cards. Requiring a verified email before allowing purchases adds a meaningful barrier. In FlutterFlow, add an account status check before any payment flow: verify that the user's emailVerified field in Firebase Auth (or a custom emailVerified field in Firestore if using a custom OTP system) is true. Use a Conditional Action in FlutterFlow: if currentUserEmailVerified is false, navigate to the email verification page instead of the checkout page. Add a similar check in your createPaymentIntent Cloud Function: if the Firebase Auth token does not have email_verified: true, throw an unauthenticated error.
Expected result: Users without a verified email are redirected to the verification screen when attempting to access any payment or financial feature.
Add IP-based rate limiting in Cloud Functions
Add IP-based rate limiting in Cloud Functions
Bot attacks typically make hundreds or thousands of requests from a small number of IP addresses. Add IP-based rate limiting to your Cloud Functions to detect and block this pattern. In each sensitive Cloud Function, extract the client IP from context.rawRequest.ip (for HTTPS callable functions) or req.ip (for HTTP functions). Store a request counter in Firestore under rate_limits/{ipHash} with a 1-minute rolling window. If requests from a single IP exceed your threshold (typically 20-30 per minute for payment endpoints), return a rate-limit error and log the incident. Hash the IP address before storing it to avoid storing PII.
1// IP rate limiting utility for Cloud Functions2const crypto = require('crypto');34async function checkIpRateLimit(db, rawIp, endpoint, maxPerMinute = 20) {5 const ipHash = crypto6 .createHash('sha256')7 .update(rawIp + process.env.RATE_LIMIT_SALT)8 .digest('hex')9 .substring(0, 16);1011 const key = `${ipHash}_${endpoint}`;12 const ref = db.collection('ip_rate_limits').doc(key);13 const now = Date.now();14 const windowStart = now - 60000;1516 const result = await db.runTransaction(async (tx) => {17 const doc = await tx.get(ref);18 const data = doc.exists ? doc.data() : { count: 0, windowStart: now };1920 if (data.windowStart < windowStart) {21 tx.set(ref, { count: 1, windowStart: now,22 expiresAt: new Date(now + 300000) });23 return { allowed: true };24 }25 if (data.count >= maxPerMinute) {26 return { allowed: false, retryAfter: 60 - Math.floor((now - data.windowStart) / 1000) };27 }28 tx.update(ref, {29 count: admin.firestore.FieldValue.increment(1) });30 return { allowed: true };31 });3233 return result;34}Expected result: An IP sending more than 20 requests per minute to any payment endpoint receives a rate-limit error. Legitimate users are never affected.
Implement basic account fraud detection signals
Implement basic account fraud detection signals
Beyond payment fraud, watch for account-level fraud signals: multiple accounts from the same IP, unusually fast sign-up to purchase flows (bots complete checkout in under 5 seconds), email addresses from known disposable email providers, and sign-ups with sequential or obviously fake usernames. In your user registration Cloud Function, check the email domain against a disposable email blocklist (block-list available as an npm package: disposable-email-domains). Log the sign-up IP and time-to-first-purchase. For RapidDev clients building marketplace apps, we recommend flagging new accounts that make a purchase within 60 seconds of sign-up for manual review rather than automatic blocking.
Expected result: New user registrations from known disposable email domains are blocked. Accounts that exhibit rapid bot-like behavior are flagged in the suspicious_accounts collection for review.
Complete working example
1// Reusable fraud check middleware for Cloud Functions2// Call this at the start of any sensitive Cloud Function3const crypto = require('crypto');4const disposableDomains = require('disposable-email-domains');56async function runFraudChecks(db, context, options = {}) {7 const {8 maxRequestsPerMinute = 20,9 requireEmailVerified = true,10 blockDisposableEmails = true,11 } = options;1213 // Check 1: Must be authenticated14 if (!context.auth) {15 throw { code: 'unauthenticated', message: 'Authentication required' };16 }1718 // Check 2: Email verification19 if (requireEmailVerified && !context.auth.token.email_verified) {20 throw { code: 'failed-precondition', message: 'Email must be verified' };21 }2223 // Check 3: Disposable email check24 if (blockDisposableEmails && context.auth.token.email) {25 const domain = context.auth.token.email.split('@')[1];26 if (disposableDomains.includes(domain)) {27 throw { code: 'permission-denied', message: 'Disposable emails not allowed' };28 }29 }3031 // Check 4: IP rate limiting32 const rawIp = context.rawRequest?.ip || '0.0.0.0';33 const ipHash = crypto34 .createHash('sha256')35 .update(rawIp)36 .digest('hex')37 .substring(0, 16);3839 const rateLimitRef = db.collection('ip_rate_limits').doc(ipHash);40 const now = Date.now();41 const windowStart = now - 60000;4243 const limitDoc = await rateLimitRef.get();44 if (limitDoc.exists) {45 const data = limitDoc.data();46 if (data.windowStart >= windowStart && data.count >= maxRequestsPerMinute) {47 throw { code: 'resource-exhausted', message: 'Too many requests' };48 }49 }5051 await rateLimitRef.set(52 { count: require('firebase-admin').firestore.FieldValue.increment(1),53 windowStart: limitDoc.exists && limitDoc.data().windowStart >= windowStart54 ? limitDoc.data().windowStart : now,55 expiresAt: new Date(now + 300000) },56 { merge: true }57 );5859 return { userId: context.auth.uid, ipHash };60}Common mistakes when protecting Your FlutterFlow App from Fraud
Why it's a problem: Relying solely on client-side checks in FlutterFlow to prevent fraud
How to avoid: Move all fraud checks to Cloud Functions (server-side). Client-side validation only improves UX — it never prevents fraud from a determined attacker.
Why it's a problem: Not enabling Firebase App Check before launch
How to avoid: Enable App Check with Play Integrity (Android) and App Attest (iOS) in the Firebase console. Monitor for 7 days then enforce. This one step stops the vast majority of automated abuse.
Why it's a problem: Setting request_three_d_secure to 'any' instead of 'automatic' in Stripe
How to avoid: Use 'automatic' to let Stripe Radar determine when 3DS is needed based on risk signals. Only suspicious transactions get the extra verification step.
Why it's a problem: Storing raw IP addresses in Firestore for rate limiting
How to avoid: Hash IP addresses with a salt before storing: SHA-256(ip + SALT). The hash is sufficient for rate limiting and cannot be reverse-engineered back to the original IP.
Best practices
- Enable Firebase App Check on every new project — it is the highest-leverage fraud prevention tool available with no code required.
- Always validate financial amounts server-side in Cloud Functions, never trust the amount sent from the client app.
- Use Stripe Radar with request_three_d_secure: 'automatic' for a balanced approach that adds friction only to suspicious transactions.
- Hash IP addresses with a secret salt before storing them in Firestore to avoid storing PII.
- Require email verification before allowing any purchase — this is the easiest disposable-account barrier to implement.
- Build a fraud monitoring dashboard in your admin panel showing new sign-ups, first-purchase velocity, and flagged accounts.
- Set up Firebase Alerts for unusual spikes in Cloud Function invocations — a sudden 10x increase in calls is usually a sign of bot activity.
- Review chargebacks weekly in the Stripe dashboard and use the Radar feedback loop to improve fraud detection accuracy for your business.
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I am building a FlutterFlow app with Firebase and Stripe. What are the most important fraud prevention layers I should implement before launch? Explain Firebase App Check, Stripe Radar, email verification requirements, and IP rate limiting in Cloud Functions.
Add fraud protection to my FlutterFlow app. Enable Firebase App Check for App Attest and Play Integrity. Add server-side validation in my createPaymentIntent Cloud Function to check email verification, enforce amount limits, and require Stripe 3D Secure for high-risk charges.
Frequently asked questions
Can fraudsters bypass FlutterFlow's client-side validation?
Yes, completely. Fraudsters make API calls directly to Firebase and Stripe using your public Firebase config credentials. They never interact with your FlutterFlow app UI. Any check that only runs in the app is invisible to them.
What is Firebase App Check and how does it prevent fraud?
App Check adds a cryptographic attestation to every request from your app that proves it originated from your legitimate app binary. Requests without a valid attestation — from scripts, curl, or other apps — are rejected by Firebase before they reach your backend.
Does Stripe Radar cost extra?
Basic Stripe Radar is included with every Stripe account at no additional cost. Radar for Fraud Teams (advanced ML features, custom rules, and reviews) costs $0.02 per screened transaction on top of Stripe's standard payment fee.
How do I test my fraud prevention without using real fraudulent transactions?
Stripe provides a comprehensive set of test card numbers that simulate specific fraud scenarios — declined cards, 3DS-required cards, stolen cards. Find them in the Stripe documentation under Testing. For App Check, use debug tokens during development so your own builds are not blocked.
What is 3D Secure and when should I require it?
3D Secure (3DS) is a bank authentication step where the cardholder confirms the transaction via their banking app or a one-time code. It significantly reduces chargebacks. Use 'automatic' mode to let Stripe Radar decide when to trigger it based on risk signals.
How do I detect account takeover attempts?
Monitor for: multiple failed login attempts in quick succession (Firebase Auth logs these), password reset requests for accounts that have never used password reset before, login from a new country or device within hours of the last login, and profile changes (email, payment method) immediately followed by a purchase.
Do I need FlutterFlow Pro to implement fraud protection?
App Check and Stripe Radar require no code changes — they are configured in the Firebase and Stripe dashboards respectively. IP rate limiting and server-side validation require Cloud Functions (Firebase Blaze plan) but no FlutterFlow Pro plan features.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation