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

How to Implement a Peer-to-Peer Payment System in FlutterFlow

Build a peer-to-peer payment system using Stripe Connect with Express accounts for each user. Cloud Functions handle transfers via stripe.transfers.create(), manage KYC verification through Stripe Connect onboarding, and track balances. Users send money by selecting a recipient and entering an amount, which triggers a Cloud Function that creates a Stripe transfer between connected accounts. Each user completes identity verification through Stripe's hosted onboarding flow before receiving payouts.

What you'll learn

  • How to set up Stripe Connect Express accounts for each user in FlutterFlow
  • How to process peer-to-peer transfers via Cloud Functions with stripe.transfers.create()
  • How to guide users through Stripe KYC identity verification
  • How to display transfer history and available balance from the Stripe API
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 peer-to-peer payment system using Stripe Connect with Express accounts for each user. Cloud Functions handle transfers via stripe.transfers.create(), manage KYC verification through Stripe Connect onboarding, and track balances. Users send money by selecting a recipient and entering an amount, which triggers a Cloud Function that creates a Stripe transfer between connected accounts. Each user completes identity verification through Stripe's hosted onboarding flow before receiving payouts.

Building Payment Infrastructure for User-to-User Transfers

Peer-to-peer payments require more than just charging a card. This tutorial covers the Stripe Connect infrastructure: creating Express accounts for each user, handling KYC identity verification, executing transfers between accounts, and tracking balances. Stripe handles the complex compliance, tax reporting, and payout logistics while your FlutterFlow app provides the user interface.

Prerequisites

  • A FlutterFlow project with Firestore and authentication configured
  • A Stripe account with Connect enabled (Settings > Connect in Stripe Dashboard)
  • Stripe API secret key stored in Cloud Function environment variables
  • Cloud Functions environment configured for your project

Step-by-step guide

1

Set up Stripe Connect and the Firestore data model

In the Stripe Dashboard, enable Connect and configure your platform settings. Choose Express account type for simplicity. In Firestore, extend user documents with: stripeConnectId (String, nullable), stripeOnboardingComplete (Boolean, default false), stripePayoutsEnabled (Boolean). Create a transfers collection: fromUserId (String), toUserId (String), amount (Integer, in cents), currency (String, 'usd'), stripeTransferId (String), status (String: 'pending', 'completed', 'failed'), createdAt (Timestamp), note (String, optional message from sender). This model tracks all transfers with their Stripe reference IDs for reconciliation.

Expected result: Stripe Connect is enabled and Firestore has user payment fields and a transfers collection for tracking all transactions.

2

Create Cloud Functions for Stripe Connect account onboarding

Build two Cloud Functions. First, createConnectAccount: creates a Stripe Express account for the user via stripe.accounts.create() with type 'express', stores the account ID in the user's stripeConnectId field, then generates an Account Link URL via stripe.accountLinks.create() with return and refresh URLs pointing to your app. Second, checkOnboardingStatus: checks the Stripe account's charges_enabled and payouts_enabled fields and updates the user's Firestore document accordingly. The user opens the Account Link URL in a WebView or external browser to complete Stripe's hosted identity verification form.

stripeConnect.js
1// Cloud Function: createConnectAccount
2const functions = require('firebase-functions');
3const admin = require('firebase-admin');
4const stripe = require('stripe')(
5 process.env.STRIPE_SECRET_KEY);
6
7exports.createConnectAccount = functions.https
8 .onCall(async (data, context) => {
9 const userId = context.auth.uid;
10 const user = (await admin.firestore()
11 .doc(`users/${userId}`).get()).data();
12 // Create Express account
13 const account = await stripe.accounts.create({
14 type: 'express',
15 email: user.email,
16 capabilities: {
17 transfers: { requested: true },
18 },
19 });
20 await admin.firestore().doc(`users/${userId}`)
21 .update({ stripeConnectId: account.id });
22 // Create onboarding link
23 const accountLink = await stripe.accountLinks
24 .create({
25 account: account.id,
26 refresh_url: `${data.baseUrl}/onboarding-refresh`,
27 return_url: `${data.baseUrl}/onboarding-complete`,
28 type: 'account_onboarding',
29 });
30 return { url: accountLink.url, accountId: account.id };
31 });
32
33exports.checkOnboardingStatus = functions.https
34 .onCall(async (data, context) => {
35 const userId = context.auth.uid;
36 const user = (await admin.firestore()
37 .doc(`users/${userId}`).get()).data();
38 const account = await stripe.accounts
39 .retrieve(user.stripeConnectId);
40 await admin.firestore().doc(`users/${userId}`)
41 .update({
42 stripeOnboardingComplete:
43 account.details_submitted,
44 stripePayoutsEnabled:
45 account.payouts_enabled,
46 });
47 return {
48 complete: account.details_submitted,
49 payoutsEnabled: account.payouts_enabled
50 };
51 });

Expected result: Users create a Stripe Connect account and complete identity verification through Stripe's hosted onboarding flow.

3

Build the identity verification and wallet setup UI

Create a WalletSetupPage. Check the user's stripeOnboardingComplete field. If false, show a setup card explaining that identity verification is required to send and receive money. Add a Verify Identity button that calls the createConnectAccount Cloud Function and opens the returned URL in a WebView or launches it externally. When the user returns, call checkOnboardingStatus to update their status. If onboarding is complete, show a green checkmark with 'Identity Verified' status. Display a KYC status indicator: 'Not Started' (grey), 'In Progress' (yellow), 'Verified' (green), 'Action Required' (red, when Stripe needs more info).

Expected result: Users see their verification status and can complete Stripe's identity verification flow. Status updates reflect in real-time.

4

Implement the send money flow with Cloud Function transfers

Create a SendMoneyPage with a user search TextField to find the recipient by name or email. Display the selected recipient's name and avatar. Add an amount TextField (numeric, formatted as currency) and an optional note TextField. On send, call a transferMoney Cloud Function that: validates both sender and recipient have verified Stripe Connect accounts, creates a charge on the sender via stripe.charges.create() with the platform as destination, then creates a transfer to the recipient via stripe.transfers.create(). Write the transfer record to Firestore with status 'completed'. Show a success animation and transfer confirmation with details.

transferMoney.js
1// Cloud Function: transferMoney
2exports.transferMoney = functions.https
3 .onCall(async (data, context) => {
4 const { toUserId, amount, note } = data;
5 const fromUserId = context.auth.uid;
6 const db = admin.firestore();
7 const fromUser = (await db.doc(
8 `users/${fromUserId}`).get()).data();
9 const toUser = (await db.doc(
10 `users/${toUserId}`).get()).data();
11 if (!fromUser.stripePayoutsEnabled ||
12 !toUser.stripePayoutsEnabled) {
13 throw new functions.https.HttpsError(
14 'failed-precondition',
15 'Both users must complete verification');
16 }
17 // Create transfer
18 const transfer = await stripe.transfers.create({
19 amount: amount, // in cents
20 currency: 'usd',
21 destination: toUser.stripeConnectId,
22 transfer_group: `p2p_${fromUserId}_${toUserId}`,
23 });
24 // Record in Firestore
25 await db.collection('transfers').add({
26 fromUserId, toUserId, amount,
27 currency: 'usd',
28 stripeTransferId: transfer.id,
29 status: 'completed',
30 note: note || '',
31 createdAt: admin.firestore
32 .FieldValue.serverTimestamp()
33 });
34 return { success: true,
35 transferId: transfer.id };
36 });

Expected result: Users search for a recipient, enter an amount, and send money. The Cloud Function processes the Stripe transfer and records it in Firestore.

5

Display transfer history and available balance

Create a WalletPage showing the user's current balance and transfer history. Call a Cloud Function that retrieves the Stripe balance for the user's Connect account via stripe.balance.retrieve({stripeAccount: connectId}). Display the available balance prominently at the top. Below, add a ListView with a Backend Query on transfers where fromUserId equals current user OR toUserId equals current user, ordered by createdAt descending. Each transfer row shows: direction icon (up arrow for sent, down arrow for received), recipient or sender name, amount (red for sent, green for received), note text if present, date, and status badge. Add filter ChoiceChips for All, Sent, and Received.

Expected result: Users see their available balance and a filterable list of all sent and received transfers with amounts and dates.

Complete working example

FlutterFlow P2P Payment Setup
1FIRESTORE DATA MODEL:
2 users/{userId} (extended)
3 stripeConnectId: String (nullable)
4 stripeOnboardingComplete: Boolean
5 stripePayoutsEnabled: Boolean
6
7 transfers/{transferId}
8 fromUserId: String
9 toUserId: String
10 amount: Integer (cents)
11 currency: 'usd'
12 stripeTransferId: String
13 status: 'pending' | 'completed' | 'failed'
14 note: String (optional)
15 createdAt: Timestamp
16
17CLOUD FUNCTIONS:
18 1. createConnectAccount
19 stripe.accounts.create({ type: 'express' })
20 stripe.accountLinks.create() return onboarding URL
21
22 2. checkOnboardingStatus
23 stripe.accounts.retrieve(connectId)
24 update user doc with verification status
25
26 3. transferMoney(toUserId, amount, note)
27 validate both users verified
28 stripe.transfers.create({ destination: connectId })
29 record in transfers collection
30
31 4. getBalance
32 stripe.balance.retrieve({ stripeAccount: connectId })
33 return available balance
34
35PAGE: WalletSetupPage
36 Column
37 KYC Status Indicator (grey/yellow/green/red)
38 If not verified:
39 Card: explanation + Verify Identity button
40 calls createConnectAccount opens onboarding URL
41 If verified:
42 Text: 'Identity Verified' + green checkmark
43
44PAGE: SendMoneyPage
45 Column
46 TextField (search recipient by name/email)
47 Container (selected recipient: avatar + name)
48 TextField (amount, numeric, currency format)
49 TextField (optional note)
50 Button: Send Money
51 Cloud Function transferMoney
52 success animation + confirmation
53
54PAGE: WalletPage
55 Column
56 Container (balance card: available amount)
57 ChoiceChips (All | Sent | Received)
58 ListView (transfers, ordered by date desc)
59 Row: direction icon + name + amount + note + date

Common mistakes when implementing a Peer-to-Peer Payment System in FlutterFlow

Why it's a problem: Using regular Stripe charges and manual payouts instead of Stripe Connect

How to avoid: Use Stripe Connect with Express accounts. Stripe handles KYC verification, identity checks, tax reporting (1099s), and payout scheduling automatically.

Why it's a problem: Allowing transfers before both users complete identity verification

How to avoid: Check stripePayoutsEnabled for both sender and recipient before processing any transfer. Show clear status indicators and verification prompts.

Why it's a problem: Storing Stripe API keys in FlutterFlow frontend code or Firestore

How to avoid: Store the Stripe secret key only in Cloud Function environment variables. All Stripe API calls happen server-side in Cloud Functions, never from the client.

Best practices

  • Use Stripe Connect Express accounts so Stripe handles KYC, compliance, and payouts
  • Process all Stripe API calls through Cloud Functions, never from the client
  • Verify both sender and recipient have completed onboarding before allowing transfers
  • Store transfer records in Firestore for your own audit trail alongside Stripe's records
  • Display KYC status prominently with clear calls to action for incomplete verification
  • Use amounts in cents (integers) to avoid floating-point rounding errors
  • Implement webhook listeners for transfer status updates from Stripe

Still stuck?

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

ChatGPT Prompt

I want to build a peer-to-peer payment system in FlutterFlow using Stripe Connect. Show me the Cloud Functions for creating Express accounts, processing transfers with stripe.transfers.create(), handling KYC onboarding, and retrieving balances. Include the Firestore data model and FlutterFlow page layouts for wallet setup, send money, and transfer history.

FlutterFlow Prompt

Create a wallet page with a large balance amount at the top inside a colored container, choice chips below for All, Sent, and Received filters, and a list view of transfer rows showing an icon, name, amount, and date.

Frequently asked questions

What is Stripe Connect and why do I need it for P2P payments?

Stripe Connect is Stripe's platform for enabling payments between users. It handles identity verification, compliance, tax reporting, and payouts for each user. Without it, you would need to build all of this infrastructure yourself, which is legally complex and time-consuming.

How long does Stripe identity verification take?

Most verifications complete instantly or within minutes. Some cases requiring manual document review can take 1-2 business days. Users can check their status in the app and Stripe sends email updates.

What are the fees for P2P transfers using Stripe Connect?

Stripe charges 2.9% + 30 cents per transaction for card payments, plus 0.25% + 25 cents per payout to connected accounts. You can choose whether the sender, recipient, or your platform absorbs these fees.

Can users withdraw their balance to a bank account?

Yes. Stripe Connect Express accounts include automatic payout scheduling. Users set their bank account during onboarding and Stripe sends payouts on a configurable schedule (daily, weekly, monthly).

How do I handle transfer disputes or refunds?

Create a dispute flow where users can flag a transfer. An admin reviews disputes and can initiate a refund via stripe.refunds.create() in a Cloud Function. Stripe also has built-in dispute handling for card chargebacks.

Can RapidDev help build a payment platform with advanced features?

Yes. RapidDev can implement split payments, escrow holds, multi-currency transfers, recurring payments, group expense splitting, and detailed financial reporting dashboards.

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.