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

How to Create a Virtual Wallet for Storing Digital Assets in FlutterFlow

Build a digital wallet with a wallets collection storing balance and currency, plus a wallet_transactions collection logging every deposit, withdrawal, transfer, and purchase. The wallet page shows the current balance, recent transactions, and action buttons. Add Funds uses Stripe Checkout with a Cloud Function that credits the balance only on webhook confirmation. Peer-to-peer transfers use a Firestore transaction to atomically debit the sender and credit the recipient. A PIN verification Custom Action protects all financial operations.

What you'll learn

  • How to design a Firestore data model for wallets and transactions
  • How to implement Stripe-powered deposits with webhook-based balance crediting
  • How to build peer-to-peer transfers using Firestore transactions
  • How to secure financial operations with PIN verification
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 digital wallet with a wallets collection storing balance and currency, plus a wallet_transactions collection logging every deposit, withdrawal, transfer, and purchase. The wallet page shows the current balance, recent transactions, and action buttons. Add Funds uses Stripe Checkout with a Cloud Function that credits the balance only on webhook confirmation. Peer-to-peer transfers use a Firestore transaction to atomically debit the sender and credit the recipient. A PIN verification Custom Action protects all financial operations.

Building a Digital Wallet App in FlutterFlow

A digital wallet lets users store funds, send money to other users, and view their transaction history. This tutorial builds a complete wallet system with Stripe deposits, peer-to-peer transfers using atomic Firestore transactions, a detailed transaction history, and PIN-based security for all financial operations.

Prerequisites

  • A FlutterFlow project with Firestore and Firebase Authentication configured
  • A Stripe account with API keys stored in Cloud Function environment variables
  • Cloud Functions enabled for server-side transaction processing
  • Basic familiarity with FlutterFlow Action Flows and Backend Queries

Step-by-step guide

1

Set up the Firestore data model for wallets and transactions

Create a wallets collection with fields: userId (String), balance (Number, default 0), currency (String, default 'USD'), lastUpdated (Timestamp), pinHash (String). Create a wallet_transactions collection with: walletId (String), type (String: 'deposit', 'withdrawal', 'transfer_sent', 'transfer_received', 'purchase'), amount (Number), description (String), counterpartyId (String, optional for transfers), timestamp (Timestamp), status (String: 'completed', 'pending', 'failed'). Add a Firestore index on wallet_transactions for walletId + timestamp descending. Create a wallet document automatically when a user signs up using a Cloud Function triggered on user creation.

Expected result: Firestore has wallets and wallet_transactions collections with a wallet auto-created for each new user.

2

Build the wallet main page with balance and action buttons

Create a WalletPage with a Column. At the top, add a Container with a gradient background showing the balance in large headlineLarge Text (formatted as currency), currency label Text, and lastUpdated timestamp. Below, add a Row of three action button Containers: Add Funds (green, plus icon), Send (blue, send icon), Withdraw (orange, download icon). Below the actions, add a 'Recent Transactions' Text heading and a ListView with Backend Query on wallet_transactions filtered by walletId, ordered by timestamp descending, limited to 20. Each transaction row shows: a type-based Icon (colored: green for deposits, red for sent/withdrawn, blue for received), description Text, amount Text (green with + for incoming, red with - for outgoing), and timestamp.

Expected result: A wallet page showing the current balance, action buttons, and a scrollable transaction history.

3

Implement the Add Funds flow with Stripe and webhook crediting

On Add Funds tap, open an AddFundsSheet Bottom Sheet. Display preset amount Containers ($10, $25, $50, $100) and a custom amount TextField. On Confirm, call a Cloud Function createWalletDeposit that creates a Stripe Checkout Session (mode: 'payment') with the amount and user metadata, returning the session URL. Launch URL to open Stripe Checkout. Create a webhook Cloud Function for checkout.session.completed that reads the userId from metadata, finds their wallet, atomically increments the balance using FieldValue.increment(), and creates a wallet_transactions document with type 'deposit' and status 'completed'. This ensures the balance is only credited after confirmed payment.

onDepositComplete.js
1// Cloud Function: onDepositComplete (Stripe webhook)
2const admin = require('firebase-admin');
3const db = admin.firestore();
4
5exports.onDepositComplete = async (event) => {
6 const session = event.data.object;
7 const { userId } = session.metadata;
8 const amountCents = session.amount_total;
9
10 const walletSnap = await db.collection('wallets')
11 .where('userId', '==', userId).limit(1).get();
12 const walletRef = walletSnap.docs[0].ref;
13
14 const batch = db.batch();
15 batch.update(walletRef, {
16 balance: admin.firestore.FieldValue.increment(amountCents),
17 lastUpdated: admin.firestore.FieldValue.serverTimestamp(),
18 });
19 batch.create(db.collection('wallet_transactions').doc(), {
20 walletId: walletRef.id,
21 type: 'deposit',
22 amount: amountCents,
23 description: 'Added funds via card',
24 timestamp: admin.firestore.FieldValue.serverTimestamp(),
25 status: 'completed',
26 });
27 await batch.commit();
28};

Expected result: Users can add funds via Stripe. The balance updates only after the webhook confirms successful payment.

4

Build the peer-to-peer transfer flow with atomic transactions

On Send tap, open a SendMoneySheet Bottom Sheet. Add a TextField for recipient lookup (email or username) with a search action that queries the users collection and displays the matching user with avatar and name. Add an amount TextField and an optional note TextField. On Confirm, first verify the PIN (next step), then call a Cloud Function processTransfer that uses a Firestore transaction to: read sender wallet balance, verify sufficient funds, decrement sender balance, increment recipient balance, and create two wallet_transactions documents (transfer_sent and transfer_received). The Firestore transaction ensures atomicity: if any step fails, everything rolls back.

Expected result: Users can send money to other users with atomic balance updates that prevent inconsistencies.

5

Add PIN verification for all financial operations

Create a PINVerification Component that appears as a Bottom Sheet before any financial action. It shows a Text ('Enter your PIN'), four dot indicators, and a number pad Grid. On first use, prompt the user to set a PIN via a SetPIN flow: enter PIN, confirm PIN, hash with SHA-256, store pinHash on the wallet document. On subsequent uses, hash the entered PIN and compare with the stored pinHash. On match, trigger a callback action (the financial operation). After 5 failed attempts, lock the wallet for 30 minutes by setting a lockedUntil Timestamp on the wallet document. Require PIN verification before Add Funds, Send, and Withdraw actions.

Expected result: All financial operations require PIN verification, and the wallet locks after multiple failed attempts.

6

Add transaction detail and filtering to the history

Enhance the transaction ListView with filtering. Add a Row above the list with ChoiceChips for transaction types: All, Deposits, Sent, Received, Purchases. Filter the Backend Query based on the selected type. Add a DateTimePicker for date range filtering. Tap a transaction row to navigate to a TransactionDetailPage showing: type with colored icon, amount (large), description, counterparty name (for transfers), timestamp, and status badge. For pending transactions, show a status indicator. Add a search TextField above the list for searching transactions by description.

Expected result: Users can filter and search their transaction history and view full details for each transaction.

Complete working example

FlutterFlow Digital Wallet Setup
1FIRESTORE DATA MODEL:
2 wallets/{walletId}
3 userId: String
4 balance: Integer (cents)
5 currency: "USD"
6 lastUpdated: Timestamp
7 pinHash: String
8 lockedUntil: Timestamp (nullable)
9
10 wallet_transactions/{txId}
11 walletId: String
12 type: "deposit" | "withdrawal" | "transfer_sent" | "transfer_received" | "purchase"
13 amount: Integer (cents)
14 description: String
15 counterpartyId: String (nullable)
16 timestamp: Timestamp
17 status: "completed" | "pending" | "failed"
18
19WALLET PAGE:
20 Column
21 Container (gradient bg)
22 Text (balance, headlineLarge, formatted $X.XX)
23 Text (currency + lastUpdated)
24 Row (action buttons)
25 Container (Add Funds, green + icon)
26 Container (Send, blue send icon)
27 Container (Withdraw, orange download icon)
28 ChoiceChips (All, Deposits, Sent, Received)
29 ListView (wallet_transactions, ordered timestamp desc)
30 Row
31 Icon (type-based, colored)
32 Column
33 Text (description)
34 Text (timestamp, small)
35 Text (amount, green +/ red -)
36
37ADD FUNDS FLOW:
38 1. Open AddFundsSheet
39 2. Select/enter amount
40 3. PIN verification
41 4. API Call: createWalletDeposit Stripe Checkout URL
42 5. Launch URL
43 6. Webhook: credit balance + create transaction
44
45TRANSFER FLOW:
46 1. Open SendMoneySheet
47 2. Search recipient by email
48 3. Enter amount + note
49 4. PIN verification
50 5. Cloud Function: Firestore transaction
51 - Read sender balance
52 - Verify sufficient funds
53 - Decrement sender, increment recipient
54 - Create 2 transaction docs

Common mistakes when creating a Virtual Wallet for Storing Digital Assets in FlutterFlow

Why it's a problem: Crediting the wallet balance before Stripe payment confirmation

How to avoid: Only credit the wallet balance inside the Stripe webhook Cloud Function after checkout.session.completed confirms successful payment.

Why it's a problem: Updating sender and recipient balances in separate writes instead of a Firestore transaction

How to avoid: Use a Firestore transaction (runTransaction) that atomically reads both balances, verifies funds, and writes both updates. If any step fails, everything rolls back.

Why it's a problem: Storing wallet balance as a floating-point dollar amount

How to avoid: Store balance in cents as an integer. Display as dollars by dividing by 100 and formatting. All arithmetic operations use integers.

Best practices

  • Store balances in cents (integer) to avoid floating-point precision issues
  • Only credit wallet balance in Stripe webhook handlers, never optimistically on the client
  • Use Firestore transactions for peer-to-peer transfers to ensure atomic balance updates
  • Require PIN verification before all financial operations
  • Lock the wallet after 5 failed PIN attempts to prevent brute-force attacks
  • Log every balance change as a wallet_transactions document for audit trail
  • Auto-create wallet documents on user signup via Cloud Function trigger

Still stuck?

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

ChatGPT Prompt

I want to build a digital wallet in FlutterFlow. Show me the Firestore data model for wallets and transactions, a wallet page with balance display and action buttons, Stripe deposit flow with webhook crediting, peer-to-peer transfer using Firestore transactions, PIN verification, and transaction history with filtering.

FlutterFlow Prompt

Create a wallet page with a large balance display at the top with a gradient background, three action buttons in a row below it (add funds, send, withdraw), and a scrollable transaction list underneath.

Frequently asked questions

Can I support multiple currencies in the wallet?

Yes. Add a currency field to each wallet and transaction. For multi-currency wallets, create separate wallet documents per currency. For currency conversion, use an exchange rate API in a Cloud Function to convert between currencies at transfer time.

How do I handle withdrawal to a bank account?

Use Stripe Connect payouts. The user links their bank account via Stripe, and a Cloud Function initiates a payout. Deduct the amount from the wallet balance and create a withdrawal transaction with status 'pending' until Stripe confirms the payout.

What happens if a transfer fails mid-transaction?

Firestore transactions are atomic. If any read or write within the transaction fails, all changes roll back automatically. Neither the sender nor recipient balance is modified, and no transaction documents are created.

Can I add transaction notifications?

Yes. After creating a wallet_transactions document, trigger a Cloud Function that sends a push notification to the affected user. For transfers, notify both sender (debit confirmation) and recipient (incoming funds).

How do I prevent negative wallet balances?

The Cloud Function processTransfer reads the sender balance inside the Firestore transaction and returns an error if balance < amount. This check is atomic with the write, so even concurrent transfers cannot overdraw the wallet.

Can RapidDev help build a complete payment platform?

Yes. RapidDev can implement a full wallet system including multi-currency support, bank withdrawals via Stripe Connect, recurring payments, spending analytics, and fraud detection.

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.