Build an affiliate platform in FlutterFlow with three Firestore collections: affiliates (code, userId, commissionRate), referral_clicks (refCode, timestamp, sessionId), and conversions (affiliateId, orderId, amount, status). Generate unique referral codes, capture the ref URL parameter on app open, store it in Secure Storage to survive page refreshes, and attribute conversions in a Cloud Function that calculates and records commissions.
Building Referral Tracking That Actually Works
Most affiliate system tutorials show you how to read a URL parameter — and that is only 10% of the problem. The real challenge is attribution: the user clicks a referral link, browses for 20 minutes, and finally signs up. By that time the URL parameter is gone. You need to persist the referral code across navigation, page refreshes, and app restarts. This tutorial builds a complete affiliate pipeline: unique code generation, persistent attribution via Secure Storage, click tracking, conversion recording in a Cloud Function, commission calculation, and a dashboard showing earnings.
Prerequisites
- FlutterFlow Pro plan with code export enabled
- Firebase project with Firestore and Authentication configured
- Firebase Cloud Functions with Blaze billing plan
- Basic familiarity with FlutterFlow Custom Actions and App State
Step-by-step guide
Design the three Firestore collections for affiliate tracking
Design the three Firestore collections for affiliate tracking
In FlutterFlow's Firestore panel, create three collections. The affiliates collection stores one document per affiliate with: userId (String), code (String — unique 8-character code), commissionRate (Number — e.g., 0.10 for 10%), status (String — 'active' or 'suspended'), totalEarnings (Number), totalClicks (Integer), and totalConversions (Integer). The referral_clicks collection logs each link click with: refCode (String), sessionId (String), ipHash (String), userAgent (String), and created_at (Timestamp). The conversions collection records completed purchases with: affiliateId (String), orderId (String), orderAmount (Number), commissionAmount (Number), status (String — 'pending', 'approved', 'paid'), and created_at (Timestamp).
Expected result: Three Firestore collections are visible in FlutterFlow's schema editor with all fields typed correctly.
Generate unique affiliate codes and onboard affiliates
Generate unique affiliate codes and onboard affiliates
Create a Cloud Function named createAffiliateAccount that accepts a userId and an optional requested code. The function generates a random 8-character alphanumeric code (or validates the requested one for uniqueness), creates the affiliates document, and returns the code to the app. In FlutterFlow, add an 'Apply to Become Affiliate' button on your settings or profile page. When tapped, it calls a Custom Action that invokes the createAffiliateAccount Cloud Function and stores the returned code in the user's Firestore profile. Display the referral link as: https://yourapp.com?ref=YOURCODE in a copyable Text widget.
Expected result: After tapping the affiliate application button, the user sees their unique referral URL displayed in a copyable text field on their profile page.
Capture the ref URL parameter and persist it in Secure Storage
Capture the ref URL parameter and persist it in Secure Storage
When a new user opens your app via a referral link, you must capture the ref parameter immediately and store it somewhere that survives navigation and page refreshes. In FlutterFlow, create a Custom Action named captureReferralCode. This action reads the current URL's query parameters using Uri.base.queryParameters['ref'] (web) or the initial route (mobile via uni_links package). If a ref code is found, write it to Secure Storage using flutter_secure_storage with the key 'pending_ref_code'. Also log a click event to the referral_clicks collection. Call this action from your app's first page On Page Load event — before any navigation logic runs.
1// Custom Action: captureReferralCode2import 'package:flutter_secure_storage/flutter_secure_storage.dart';3import 'package:cloud_firestore/cloud_firestore.dart';4import 'dart:html' as html show window; // web only56Future<String?> captureReferralCode() async {7 final storage = const FlutterSecureStorage();89 // Check if we already have a pending ref10 final existing = await storage.read(key: 'pending_ref_code');11 if (existing != null) return existing;1213 // Parse ref from URL (web)14 String? refCode;15 try {16 final uri = Uri.parse(html.window.location.href);17 refCode = uri.queryParameters['ref'];18 } catch (_) {}1920 if (refCode == null || refCode.isEmpty) return null;2122 // Verify the code exists23 final doc = await FirebaseFirestore.instance24 .collection('affiliates')25 .doc(refCode.toUpperCase())26 .get();2728 if (!doc.exists) return null;2930 // Persist to survive navigation31 await storage.write(key: 'pending_ref_code', value: refCode.toUpperCase());3233 // Log the click34 await FirebaseFirestore.instance.collection('referral_clicks').add({35 'refCode': refCode.toUpperCase(),36 'created_at': FieldValue.serverTimestamp(),37 'userAgent': html.window.navigator.userAgent,38 });3940 // Increment click counter on affiliate doc41 await FirebaseFirestore.instance42 .collection('affiliates')43 .doc(refCode.toUpperCase())44 .update({'totalClicks': FieldValue.increment(1)});4546 return refCode.toUpperCase();47}Expected result: When the app is opened with ?ref=CODE in the URL, the code is stored in Secure Storage and a click event is logged. Refreshing the page or navigating away does not lose the attribution.
Record conversions in a Cloud Function at purchase time
Record conversions in a Cloud Function at purchase time
When a user completes a purchase (or sign-up, depending on your model), trigger a Cloud Function named recordConversion. This function reads the pending_ref_code from the user's profile or the order document (set it when the order is created from the app state), calculates the commission as orderAmount * commissionRate from the affiliate document, writes a conversion document, increments the affiliate's totalConversions and totalEarnings, and clears the pending referral attribution from the user's profile. Using a Cloud Function here (rather than a direct Firestore write from the app) prevents clients from manually inflating their own commissions.
1// functions/index.js — recordConversion Cloud Function2const functions = require('firebase-functions');3const admin = require('firebase-admin');45exports.recordConversion = functions.https.onCall(async (data, context) => {6 if (!context.auth) {7 throw new functions.https.HttpsError('unauthenticated', 'Login required');8 }910 const { orderId, orderAmount, refCode } = data;1112 if (!refCode) return { success: false, reason: 'no_referral' };1314 const db = admin.firestore();15 const affiliateRef = db.collection('affiliates').doc(refCode);16 const affiliateDoc = await affiliateRef.get();1718 if (!affiliateDoc.exists || affiliateDoc.data().status !== 'active') {19 return { success: false, reason: 'invalid_affiliate' };20 }2122 const rate = affiliateDoc.data().commissionRate || 0.10;23 const commission = parseFloat((orderAmount * rate).toFixed(2));2425 const batch = db.batch();2627 batch.set(db.collection('conversions').doc(), {28 affiliateId: affiliateDoc.id,29 affiliateUserId: affiliateDoc.data().userId,30 referredUserId: context.auth.uid,31 orderId,32 orderAmount,33 commissionAmount: commission,34 status: 'pending',35 created_at: admin.firestore.FieldValue.serverTimestamp(),36 });3738 batch.update(affiliateRef, {39 totalConversions: admin.firestore.FieldValue.increment(1),40 totalEarnings: admin.firestore.FieldValue.increment(commission),41 });4243 await batch.commit();44 return { success: true, commission };45});Expected result: After a purchase, a new conversion document appears in Firestore with the correct commission amount, and the affiliate's totalEarnings field increases accordingly.
Build the affiliate dashboard page in FlutterFlow
Build the affiliate dashboard page in FlutterFlow
Create a new page in FlutterFlow named AffiliatesDashboard. Query the affiliates collection filtering by userId == currentUser.uid to fetch the current user's affiliate document. Display totalClicks, totalConversions, and totalEarnings as metric cards using Column and Container widgets with large number text. Add a second query on the conversions collection filtered by affiliateId == affiliateDoc.id and ordered by created_at descending. Show each conversion in a ListView with the order date, amount, commission earned, and status badge (Pending, Approved, or Paid). Add a Share button that copies the referral URL to the clipboard using the Clipboard action in FlutterFlow.
Expected result: Affiliates can view their referral link, total clicks, conversion count, pending earnings, and a history of all conversions with their commission amounts.
Complete working example
1// Custom Action: applyReferralAtSignUp2// Call this immediately after a new user registers3import 'package:flutter_secure_storage/flutter_secure_storage.dart';4import 'package:cloud_firestore/cloud_firestore.dart';5import 'package:cloud_functions/cloud_functions.dart';6import 'package:firebase_auth/firebase_auth.dart';78Future<void> applyReferralAtSignUp() async {9 final user = FirebaseAuth.instance.currentUser;10 if (user == null) return;1112 // Read the persisted referral code from Secure Storage13 const storage = FlutterSecureStorage();14 final refCode = await storage.read(key: 'pending_ref_code');1516 if (refCode == null || refCode.isEmpty) return;1718 final db = FirebaseFirestore.instance;1920 // Verify affiliate is still active21 final affiliateDoc = await db.collection('affiliates').doc(refCode).get();22 if (!affiliateDoc.exists || affiliateDoc.data()?['status'] != 'active') {23 await storage.delete(key: 'pending_ref_code');24 return;25 }2627 // Prevent self-referral28 if (affiliateDoc.data()?['userId'] == user.uid) {29 await storage.delete(key: 'pending_ref_code');30 return;31 }3233 // Store referral on the new user's profile for later conversion tracking34 await db.collection('users').doc(user.uid).set(35 {36 'referredBy': refCode,37 'referredAt': FieldValue.serverTimestamp(),38 },39 SetOptions(merge: true),40 );4142 // If your model pays on sign-up (not just purchase), record conversion now43 // final callable = FirebaseFunctions.instance.httpsCallable('recordConversion');44 // await callable.call({'orderId': 'signup_${user.uid}', 'orderAmount': 0, 'refCode': refCode});4546 // Clear the pending code after attribution47 await storage.delete(key: 'pending_ref_code');48}Common mistakes when building an Affiliate Marketing Platform in FlutterFlow
Why it's a problem: Attributing referrals via URL parameter only without persisting to Secure Storage
How to avoid: On app open, immediately read the ref URL parameter, verify it against Firestore, and write it to flutter_secure_storage. Read from Secure Storage at sign-up time to attribute the conversion correctly.
Why it's a problem: Allowing clients to write directly to the conversions collection
How to avoid: Only allow conversion records to be written by your Cloud Function. Set Firestore rules to deny direct writes to the conversions collection: 'allow write: if false;' for client access.
Why it's a problem: Not preventing self-referrals in the attribution logic
How to avoid: In your attribution action and Cloud Function, compare the affiliate's userId with the new user's uid. If they match, discard the referral code and log the attempt.
Why it's a problem: Storing commissionRate on the conversion document instead of calculating it at conversion time from the affiliates document
How to avoid: In the Cloud Function, read the current commissionRate from the affiliates document at conversion time and store both the rate and the calculated amount on the conversion record.
Best practices
- Persist referral codes in Secure Storage immediately on app open so attribution survives navigation and page refreshes.
- Use Cloud Functions for all commission calculations — never let clients write to conversions or update affiliate earnings directly.
- Implement a self-referral prevention check comparing affiliate userId with the converting user's uid.
- Add a 30-day attribution window: if the pending_ref_code in Secure Storage is older than 30 days, discard it rather than attributing a stale referral.
- Set Firestore rules so only Cloud Functions (via Admin SDK) can write to the conversions collection — clients should have no write access.
- Build an admin review step before paying out commissions — mark conversions as 'approved' only after confirming no fraud indicators.
- Include a fraud detection rule that flags affiliates whose conversion rate exceeds 80% — legitimate referral programs rarely see conversion rates above 30%.
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I am building an affiliate marketing platform in FlutterFlow with Firebase. Explain the complete data architecture for tracking referral clicks, attributing conversions to the correct affiliate across page navigations, and calculating commissions in a Cloud Function.
Add affiliate tracking to my FlutterFlow app. When the app opens with ?ref=CODE in the URL, capture the code and persist it in Secure Storage. When a user signs up, read the code from storage and store it on their user profile. Call a Cloud Function to record the conversion and calculate commission.
Frequently asked questions
Why do I need Secure Storage for referral codes — can I just use App State?
App State is cleared when the app is force-closed or restarted. Secure Storage persists across app restarts and is encrypted on the device. If a user clicks a referral link, opens the app, then closes and reopens it before signing up, App State loses the code but Secure Storage keeps it.
What is a realistic affiliate commission rate?
Most SaaS affiliate programs pay 20-30% of the first payment or 10-15% recurring. One-time purchase apps typically offer 10-20%. Store the rate on the affiliate document so you can adjust individual affiliate rates without changing your code.
Can I track affiliate performance for mobile app installs?
Yes, but it requires a mobile deep link setup. Use Firebase Dynamic Links or Branch.io to create trackable install links. When the app is first opened after install, read the referral parameter from the deep link payload and write it to Secure Storage.
How do I pay affiliates their commissions?
The most common approach is manual Stripe transfers to affiliates' connected Stripe accounts, or using Stripe Payouts. Create a payout_requests collection where affiliates request withdrawals above a minimum threshold. An admin approves and triggers a Stripe transfer from a Cloud Function.
What happens if two affiliates try to claim the same conversion?
Your attribution logic should use last-click attribution: the most recently stored referral code wins. Since you clear the pending_ref_code from Secure Storage after the first sign-up attribution, subsequent referral attempts by the same user are ignored.
Do I need FlutterFlow Pro for an affiliate system?
The basic click tracking and Firestore writes can be done on Free plan. However, persisting referral codes to Secure Storage requires the flutter_secure_storage package — a Custom Action that needs code export, which is a Pro plan feature.
How do I display an affiliate's referral link in the app?
Build the URL dynamically: 'https://yourapp.com?ref=' + affiliateCode. Display it in a Text widget and add a Copy to Clipboard button using FlutterFlow's built-in Clipboard action. For mobile, add a Share button using the share_plus package.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation