Skip to main content
RapidDev - Software Development Agency
flutterflow-tutorials

How to Set Up a Donation Platform Within a FlutterFlow App

Build a donation platform supporting both one-time and recurring monthly donations via Stripe Checkout. Causes are stored in Firestore with goal amounts and progress tracking. Donors select a cause, choose a preset or custom amount, toggle recurring, and pay through Stripe. A webhook updates the cause's raised amount and creates a donation record. A Cloud Function generates tax receipt PDFs and emails them. A donor wall displays recent contributors with optional anonymity. The platform handles recurring subscription cancellations via Stripe webhook events.

What you'll learn

  • How to model causes and donations with Firestore for progress tracking
  • How to integrate Stripe Checkout for one-time and recurring donations
  • How to generate and email tax receipt PDFs via Cloud Functions
  • How to build a donor wall with anonymity controls
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner8 min read30-40 minFlutterFlow Free+March 2026RapidDev Engineering Team
TL;DR

Build a donation platform supporting both one-time and recurring monthly donations via Stripe Checkout. Causes are stored in Firestore with goal amounts and progress tracking. Donors select a cause, choose a preset or custom amount, toggle recurring, and pay through Stripe. A webhook updates the cause's raised amount and creates a donation record. A Cloud Function generates tax receipt PDFs and emails them. A donor wall displays recent contributors with optional anonymity. The platform handles recurring subscription cancellations via Stripe webhook events.

Building a Donation Platform in FlutterFlow

This tutorial creates a donation platform for nonprofits, charities, and social causes. Unlike a generic payment integration, this focuses on donation-specific features: preset amounts, recurring monthly giving, progress toward fundraising goals, donor recognition, and tax receipts. The platform uses Stripe Checkout for secure payment processing and handles both one-time and subscription payments. A Cloud Function generates PDF tax receipts and emails them to donors automatically. This pattern works for charities, churches, schools, community organizations, and crowdfunding campaigns.

Prerequisites

  • A FlutterFlow project with Firestore and Authentication configured
  • A Stripe account with both one-time and subscription pricing configured
  • Firebase Cloud Functions enabled (Blaze plan)
  • Basic understanding of Backend Queries and Stripe Checkout flow

Step-by-step guide

1

Create the Firestore data model for causes and donations

Create a causes collection with fields: title (String), description (String), imageUrl (String), goalAmount (double), raisedAmount (double, default 0), isActive (bool). Create a donations collection with fields: userId (String), causeId (String), amount (double), frequency (String: once, monthly), paymentId (String, Stripe session or subscription ID), isRecurring (bool), isAnonymous (bool), displayName (String, donor name or 'Anonymous'), timestamp (Timestamp), status (String: completed, cancelled). Add 3-4 sample causes with different goal amounts. Set Stripe up with a one-time price and a recurring monthly price for donation processing.

Expected result: Firestore has causes with fundraising goals and a donations collection for tracking all contributions.

2

Build the cause browsing page with funding progress

Create a CausesPage. Query the causes collection where isActive equals true. Display each cause as a Card: imageUrl at the top, title and description below, a LinearPercentIndicator showing raisedAmount divided by goalAmount as a percentage, and Text showing the dollar amounts (e.g., '$12,450 raised of $25,000 goal'). Add a Donate Now button on each card that navigates to the DonatePage passing the causeId. Sort causes by raisedAmount ascending to promote causes that need more support.

Expected result: Users browse active causes with visual funding progress bars and donation buttons.

3

Create the donation form with preset amounts and recurring toggle

Create a DonatePage with Route Parameter causeId. Display the cause title and image at the top. Add preset amount buttons as tappable Containers in a Row: $5, $10, $25, $50. When tapped, set Page State selectedAmount. Add a Custom Amount TextField for other values. Below, add a recurring monthly Switch toggle with label 'Make this a monthly donation'. Add an anonymous Switch toggle with label 'Donate anonymously'. Show a summary: amount, frequency, and cause name. Add a Donate button. On tap, call a Cloud Function that creates a Stripe Checkout Session with mode payment for one-time or mode subscription for recurring, passing the amount, causeId, userId, isAnonymous, and displayName as metadata.

Expected result: Users select an amount, optionally enable recurring, and proceed to Stripe Checkout.

4

Handle the Stripe webhook to record donations and update progress

Create a Cloud Function that handles Stripe webhook events. On checkout.session.completed for one-time payments: read the metadata (causeId, userId, amount, isAnonymous, displayName), create a donations document with status completed and frequency once, and atomically increment the cause raisedAmount by the donation amount using FieldValue.increment. On invoice.paid for recurring subscriptions: do the same for each monthly payment. On customer.subscription.deleted: find the donation document by paymentId and update its status to cancelled so the app no longer shows the user as an active monthly donor. Verify the Stripe webhook signature for security.

handleDonationWebhook.js
1// Cloud Function: handleDonationWebhook
2const functions = require('firebase-functions');
3const admin = require('firebase-admin');
4const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
5admin.initializeApp();
6
7exports.handleDonationWebhook = functions.https.onRequest(async (req, res) => {
8 const sig = req.headers['stripe-signature'];
9 let event;
10 try {
11 event = stripe.webhooks.constructEvent(
12 req.rawBody, sig, process.env.STRIPE_WEBHOOK_SECRET
13 );
14 } catch (err) {
15 return res.status(400).send(`Webhook Error: ${err.message}`);
16 }
17
18 if (event.type === 'checkout.session.completed') {
19 const session = event.data.object;
20 const { causeId, userId, isAnonymous, displayName } = session.metadata;
21 const amount = session.amount_total / 100;
22
23 await admin.firestore().collection('donations').add({
24 userId, causeId, amount,
25 frequency: session.mode === 'subscription' ? 'monthly' : 'once',
26 paymentId: session.id,
27 isRecurring: session.mode === 'subscription',
28 isAnonymous: isAnonymous === 'true',
29 displayName: isAnonymous === 'true' ? 'Anonymous' : displayName,
30 timestamp: admin.firestore.FieldValue.serverTimestamp(),
31 status: 'completed',
32 });
33
34 await admin.firestore().collection('causes').doc(causeId)
35 .update({ raisedAmount: admin.firestore.FieldValue.increment(amount) });
36 }
37
38 if (event.type === 'customer.subscription.deleted') {
39 const sub = event.data.object;
40 const donations = await admin.firestore().collection('donations')
41 .where('paymentId', '==', sub.id).get();
42 donations.docs.forEach(d => d.ref.update({ status: 'cancelled' }));
43 }
44
45 res.status(200).send('OK');
46});

Expected result: Stripe webhook creates donation records, updates cause progress, and handles subscription cancellations.

5

Build the donor wall and tax receipt generation

On each cause detail page, add a Donor Wall section. Query donations for the causeId ordered by timestamp descending, limit 20. Display each donation as a Row: donor displayName (or 'Anonymous' if isAnonymous), amount, and a relative time label. Add a monthly badge icon for recurring donors. For tax receipts, create a Cloud Function triggered on new donation documents. The function generates a PDF using the pdf package with the organization name, donor name, amount, date, cause title, and a tax-exempt statement. Upload the PDF to Firebase Storage and email the download link to the donor using SendGrid or a Firebase Extension. Store the receipt URL on the donation document.

Expected result: A donor wall shows recent contributors, and each donor receives a tax receipt PDF by email.

Complete working example

FlutterFlow Donation Platform Setup
1FIRESTORE DATA MODEL:
2 causes/{causeId}
3 title: String
4 description: String
5 imageUrl: String
6 goalAmount: double
7 raisedAmount: double (default 0)
8 isActive: bool
9
10 donations/{donationId}
11 userId: String
12 causeId: String
13 amount: double
14 frequency: String (once / monthly)
15 paymentId: String
16 isRecurring: bool
17 isAnonymous: bool
18 displayName: String
19 timestamp: Timestamp
20 status: String (completed / cancelled)
21 receiptUrl: String (optional)
22
23PAGE: CausesPage
24 WIDGET TREE:
25 ListView (causes where isActive, orderBy raisedAmount asc)
26 CauseCard
27 Image (imageUrl)
28 Text (title, description)
29 LinearPercentIndicator (raisedAmount / goalAmount)
30 Text ("$12,450 raised of $25,000")
31 Button ("Donate Now" DonatePage)
32
33PAGE: DonatePage
34 Route Parameter: causeId
35 Page State: selectedAmount (double), isRecurring (bool), isAnonymous (bool)
36
37 WIDGET TREE:
38 Column
39 Image + Text (cause title)
40 Row (preset amounts: $5, $10, $25, $50)
41 Container per amount
42 On Tap: set selectedAmount
43 TextField ("Custom amount")
44 Row
45 Text ("Monthly donation")
46 Switch (isRecurring)
47 Row
48 Text ("Donate anonymously")
49 Switch (isAnonymous)
50 Summary: amount + frequency + cause
51 Button ("Donate")
52 On Tap: call Cloud Function Stripe Checkout URL
53
54DONOR WALL (on cause detail page):
55 ListView (donations for causeId, orderBy timestamp desc, limit 20)
56 Row
57 Text (displayName or "Anonymous")
58 Text (amount formatted)
59 Badge ("Monthly" if isRecurring)
60 Text (relative time)
61
62CLOUD FUNCTIONS:
63 createDonationCheckout Stripe Checkout Session
64 handleDonationWebhook process payment events
65 generateTaxReceipt PDF generation + email on new donation

Common mistakes

Why it's a problem: Not handling Stripe subscription cancellation webhook events

How to avoid: Listen for the customer.subscription.deleted webhook event and update the corresponding donation document's status to cancelled.

Why it's a problem: Incrementing raisedAmount from the client after payment

How to avoid: Only update raisedAmount in the Stripe webhook Cloud Function after verifying the payment was successful with signature validation.

Why it's a problem: Not providing an anonymity option for donors

How to avoid: Add an isAnonymous toggle on the donation form. Display 'Anonymous' instead of the donor name on the donor wall when enabled.

Best practices

  • Handle both checkout.session.completed and customer.subscription.deleted webhook events
  • Update raisedAmount only server-side via webhook to prevent manipulation
  • Offer preset donation amounts for quick selection and a custom field for flexibility
  • Provide anonymity controls so donors can choose whether their name is shown
  • Generate and email tax receipts automatically for every donation
  • Show funding progress as a percentage bar to motivate additional contributions
  • Display a recurring badge on the donor wall to recognize ongoing monthly supporters

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I want to build a donation platform in FlutterFlow with one-time and recurring monthly donations via Stripe Checkout, a Firestore data model for causes with goal tracking, a donor wall with anonymity controls, and Cloud Functions for webhook handling and tax receipt PDF generation. Give me the data model, widget tree, Stripe integration flow, and webhook Cloud Function code.

FlutterFlow Prompt

Create a donation page with a cause image at the top, a row of preset amount buttons ($5, $10, $25, $50), a custom amount text field, a monthly recurring toggle switch, an anonymous donation toggle, and a large Donate button at the bottom.

Frequently asked questions

How do I handle different donation currencies?

Add a currency field to each cause document. When creating the Stripe Checkout Session, pass the currency code. Display amounts with the correct currency symbol on the cause page and donor wall.

Can donors update or cancel their recurring donation?

Yes. Create a My Donations page that queries donations for the current user where isRecurring is true and status is completed. Add a Cancel button that calls a Cloud Function to cancel the Stripe subscription using the paymentId.

How do I add matching donations from a corporate sponsor?

Add a matchMultiplier field on the cause document (e.g., 2 for dollar-for-dollar matching). In the webhook, increment raisedAmount by amount times matchMultiplier. Display 'Your $25 becomes $50 with corporate matching' on the donation page.

Can I set a minimum donation amount?

Yes. Stripe requires a minimum of 50 cents. In the donation form, validate that selectedAmount is at least 1.00 before enabling the Donate button. Show an error message if the custom amount is too low.

How do I generate year-end tax summary reports?

Create a Cloud Function that queries all donations for a user within a calendar year, sums the total, and generates a summary PDF with each donation listed by date and amount. Trigger it manually or schedule it for January each year.

Can RapidDev help build a complete fundraising platform?

Yes. RapidDev can build full fundraising platforms with peer-to-peer campaigns, team fundraising, employer matching, event-based giving, multi-currency support, and integrated donor management CRM.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.