Effective payment monitoring combines Stripe Dashboard for financial metrics with Firestore order sync for in-app status displays. Use Stripe webhooks to write order records to Firestore after payment succeeds. Build a simple revenue dashboard with fl_chart in a Custom Widget. Set up Stripe email alerts for failed payments and refunds. Monitoring both systems lets you catch issues fast and show payment status to your users.
Why You Need Two Payment Monitoring Systems
Stripe Dashboard is the source of truth for your money — it shows every charge, payout, refund, and dispute. But your users can't see Stripe Dashboard. They need to see payment confirmation inside your app. This requires syncing Stripe events to Firestore via webhooks and displaying the order status from Firestore. The two systems serve different purposes: Stripe for your business metrics, Firestore for your users' experience. Setting up both takes under an hour and prevents the most common payment support complaints — 'I paid but nothing happened' and 'where is my order?'
Prerequisites
- A Stripe account with at least one test payment made
- A FlutterFlow project with Firebase and Firestore connected
- A Cloud Function deployed for your payment flow (or Stripe Checkout sessions)
- FlutterFlow Pro plan for Custom Widget (Standard works for basic order status display)
Step-by-step guide
Configure Stripe Dashboard Metrics and Alerts
Configure Stripe Dashboard Metrics and Alerts
Log in to dashboard.stripe.com. The Home tab shows your key metrics: gross volume, net volume, successful payment rate, and dispute rate. Navigate to Payments > Reports to access Revenue, Payouts, and Reconciliation reports. Enable email alerts by going to Settings > Email Notifications and enabling: failed payments, disputes, refunds, and successful payouts. Set a spend threshold alert at Settings > Billing Alerts so you know when you hit monthly revenue milestones. Bookmark the Stripe Radar dashboard (Radar > Overview) if you're in a fraud-prone category — it shows suspicious activity patterns.
Expected result: You receive email alerts for every payment failure, dispute, and refund, and can see key revenue metrics on the Stripe Dashboard home screen.
Create a Firestore Orders Collection for Payment Sync
Create a Firestore Orders Collection for Payment Sync
In FlutterFlow, open your Firestore schema and create an 'orders' collection with these fields: userId (String), productId (String), amount (double), currency (String), status (String — values: pending, paid, failed, refunded), stripePaymentIntentId (String), stripeSessionId (String), createdAt (Timestamp), paidAt (Timestamp, nullable). This schema lets you display order history to users and track payment status without exposing Stripe details. The stripePaymentIntentId and stripeSessionId fields let you look up the corresponding Stripe event for any order, which is essential for debugging payment disputes.
Expected result: An 'orders' collection is defined in your FlutterFlow Firestore schema and ready to receive data from your payment webhook.
Deploy a Stripe Webhook Cloud Function to Sync Orders
Deploy a Stripe Webhook Cloud Function to Sync Orders
In Firebase Console, go to Functions and create a new Cloud Function. This function receives Stripe webhook events and writes order records to Firestore. Register the webhook endpoint URL in Stripe at Dashboard > Developers > Webhooks. Listen for these events: checkout.session.completed (creates the order document), payment_intent.payment_failed (updates status to failed), charge.refunded (updates status to refunded). Always verify the Stripe webhook signature using the webhook secret — this prevents attackers from faking payment success events.
1const functions = require('firebase-functions');2const admin = require('firebase-admin');3const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);45exports.stripeWebhook = functions.https.onRequest(async (req, res) => {6 const sig = req.headers['stripe-signature'];7 let event;8 try {9 event = stripe.webhooks.constructEvent(10 req.rawBody,11 sig,12 process.env.STRIPE_WEBHOOK_SECRET13 );14 } catch (err) {15 console.error('Webhook signature verification failed:', err.message);16 return res.status(400).send(`Webhook Error: ${err.message}`);17 }1819 const db = admin.firestore();2021 if (event.type === 'checkout.session.completed') {22 const session = event.data.object;23 await db.collection('orders').add({24 userId: session.metadata.userId,25 productId: session.metadata.productId,26 amount: session.amount_total / 100,27 currency: session.currency,28 status: 'paid',29 stripeSessionId: session.id,30 createdAt: admin.firestore.FieldValue.serverTimestamp(),31 paidAt: admin.firestore.FieldValue.serverTimestamp(),32 });33 }3435 if (event.type === 'payment_intent.payment_failed') {36 const intent = event.data.object;37 await db.collection('orders')38 .where('stripePaymentIntentId', '==', intent.id)39 .get()40 .then(snapshot => {41 snapshot.forEach(doc => {42 doc.ref.update({43 status: 'failed',44 errorMessage: intent.last_payment_error?.message || 'Payment failed',45 });46 });47 });48 }4950 res.json({ received: true });51});Expected result: Stripe payment events automatically create or update order documents in Firestore within seconds of the payment completing.
Display Order History and Payment Status in Your App
Display Order History and Payment Status in Your App
In FlutterFlow, create an Order History page. Add a ListView with a Firestore query on the 'orders' collection filtered by userId == currentUserUID and ordered by createdAt descending. In each list item, display: amount formatted as currency, product name (look up from productId or denormalize the name into the order document), paidAt date, and a status badge. Style the status badge with conditional colors: green Container for 'paid', red for 'failed', orange for 'pending', grey for 'refunded'. This gives users a clear view of their payment history and resolves the most common support question — 'did my payment go through?'
Expected result: Users see a list of their orders with status badges, amounts, and dates — with paid orders showing green and failed orders showing red.
Set Up Revenue Dashboard with Key Metrics
Set Up Revenue Dashboard with Key Metrics
For your admin or personal tracking, create a Payment Analytics page visible only to users with an 'admin' role. Use Firestore aggregate queries (via a Custom Action) to calculate: total revenue (sum of amount where status == paid), order count, average order value, and failure rate. Display these in metric cards using a Row of stat containers. For a simple revenue chart showing daily totals, use fl_chart in a Custom Widget — add fl_chart as a package dependency in your project settings. Alternatively, embed a Stripe Dashboard link in an iFrame for web deployments (Stripe allows this for internal tools).
Expected result: An admin page shows total revenue, order count, and failure rate — giving you a quick pulse on payment health without opening Stripe Dashboard.
Complete working example
1// Custom Function: Format order amount for display2String formatOrderAmount(double amount, String currency) {3 final formatter = NumberFormat.currency(4 symbol: currency.toUpperCase() == 'USD' ? '\$' : currency.toUpperCase() + ' ',5 decimalDigits: 2,6 );7 return formatter.format(amount);8}910// Custom Function: Get status badge color11Color getStatusColor(String status) {12 switch (status.toLowerCase()) {13 case 'paid':14 return const Color(0xFF22C55E); // green-50015 case 'pending':16 return const Color(0xFFF59E0B); // amber-50017 case 'failed':18 return const Color(0xFFEF4444); // red-50019 case 'refunded':20 return const Color(0xFF6B7280); // gray-50021 default:22 return const Color(0xFF6B7280);23 }24}2526// Custom Function: Get status display label27String getStatusLabel(String status) {28 switch (status.toLowerCase()) {29 case 'paid':30 return 'Payment Successful';31 case 'pending':32 return 'Processing';33 case 'failed':34 return 'Payment Failed';35 case 'refunded':36 return 'Refunded';37 default:38 return 'Unknown';39 }40}4142// Custom Function: Format order date43String formatOrderDate(DateTime? timestamp) {44 if (timestamp == null) return 'Date unknown';45 return DateFormat('MMM d, yyyy \'at\' h:mm a').format(timestamp);46}Common mistakes when monitoring Your FlutterFlow App's Payments
Why it's a problem: Relying only on Stripe Dashboard without syncing to Firestore
How to avoid: Deploy the webhook Cloud Function to sync every checkout.session.completed event to a Firestore 'orders' document. This creates the user-facing payment record automatically.
Why it's a problem: Not verifying the Stripe webhook signature
How to avoid: Always use stripe.webhooks.constructEvent() with your webhook secret. This cryptographically verifies the event came from Stripe.
Why it's a problem: Calculating revenue aggregates from Firestore on every page load
How to avoid: Use a scheduled Cloud Function to pre-calculate aggregates (daily revenue, order count) and store them in a single 'metrics' document. Display that document on your admin page.
Best practices
- Always verify Stripe webhook signatures — never trust unsigned payment events
- Denormalize product names and user emails into order documents to reduce additional Firestore reads
- Store Stripe payment intent IDs and session IDs in orders so you can cross-reference with Stripe Dashboard
- Set up Stripe email alerts for failed payments, disputes, and refunds on day one
- Use Firestore security rules to ensure users can only read their own orders
- Test your webhook with the Stripe CLI before deploying to production
- Set up budget alerts in Firebase Console to catch unexpected Firestore read spikes from payment queries
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I have a FlutterFlow app with Stripe Checkout integration. I want to set up payment monitoring so: (1) I get notified of failed payments, (2) users can see their order history in the app, and (3) I have a simple admin view of daily revenue. Walk me through the Firebase Cloud Functions and Firestore schema I need to make this work.
In my FlutterFlow project, create a Custom Action called 'fetchOrderSummary' that queries the 'orders' Firestore collection for the current user, calculates the total amount paid (sum of amount field where status == 'paid'), and returns it as a double. Use the FlutterFlow Firestore helper to make the query.
Frequently asked questions
How do I know if a Stripe payment succeeded in my FlutterFlow app?
The most reliable method is a Stripe webhook: Stripe POSTs a checkout.session.completed event to your Cloud Function, which creates an order document in Firestore with status 'paid'. Your app queries this document to confirm payment. Never rely on URL parameters from Stripe's success redirect URL, as users can manipulate them.
Can I display Stripe payment status directly in FlutterFlow without a backend?
No. Stripe's API requires your secret key, which must never be in client-side code. You must use a Cloud Function or server to call Stripe and return the status. The Cloud Function creates an order record in Firestore, which FlutterFlow then reads directly — that is safe.
How do I handle failed payments in my FlutterFlow app?
Listen for the payment_intent.payment_failed Stripe webhook event in your Cloud Function. Update the corresponding order document's status to 'failed' and store the error message from intent.last_payment_error.message. Display a clear message to the user with the failure reason and a link to retry payment.
What Stripe Dashboard metrics should I check daily?
Check gross volume (total revenue), successful payment rate (anything below 95% needs investigation), dispute rate (above 0.5% risks losing Stripe access), and payout status (to confirm funds are arriving in your bank account on schedule).
How do I handle refunds in my FlutterFlow app?
Issue refunds from Stripe Dashboard (Payments > select payment > Refund). Listen for the charge.refunded webhook event in your Cloud Function and update the order document's status to 'refunded'. Show the refunded status in your order history so users can see it without contacting support.
Is it safe to store Stripe payment intent IDs in Firestore?
Yes. Payment intent IDs (pi_xxx) and session IDs (cs_xxx) are not sensitive — they're identifiers, not credentials. Storing them in Firestore lets you cross-reference Firestore orders with Stripe Dashboard events for debugging and dispute resolution. Never store secret keys or full card details.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation