Skip to main content
RapidDev - Software Development Agency
flutterflow-integrationsFlutterFlow API Call

Stripe Connect

Connect FlutterFlow to Stripe Connect by building a Firebase or Supabase proxy that holds your platform secret key, then wiring FlutterFlow API Calls to that proxy. The proxy creates Express accounts, generates Account Link onboarding URLs, and splits payments using application_fee_amount plus transfer_data[destination] — the pattern that powers marketplace payouts in mobile apps.

What you'll learn

  • Why FlutterFlow's built-in Stripe Payments cannot handle marketplace splits and what Stripe Connect adds
  • How to deploy a Firebase Cloud Function or Supabase Edge Function as a secure proxy for the Stripe secret key
  • How to onboard sellers via Stripe Express accounts and Account Links, opened in a WebView
  • How to split a payment using application_fee_amount and transfer_data[destination] on a PaymentIntent
  • How to receive payment and payout webhooks in your backend and stream status into the FlutterFlow app
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Advanced16 min read3-4 hoursPaymentsLast updated July 2026RapidDev Engineering Team
TL;DR

Connect FlutterFlow to Stripe Connect by building a Firebase or Supabase proxy that holds your platform secret key, then wiring FlutterFlow API Calls to that proxy. The proxy creates Express accounts, generates Account Link onboarding URLs, and splits payments using application_fee_amount plus transfer_data[destination] — the pattern that powers marketplace payouts in mobile apps.

Quick facts about this guide
FactValue
ToolStripe Connect
CategoryPayments
MethodFlutterFlow API Call
DifficultyAdvanced
Time required3-4 hours
Last updatedJuly 2026

Why FlutterFlow's built-in Stripe is not enough for marketplaces

FlutterFlow ships a native Stripe Payments toggle in Settings & Integrations → Payments. It wires up a single-merchant checkout with almost no code — great for a straightforward product sale. But if your app connects buyers with multiple sellers (gig marketplaces, creator platforms, service directories), you need Stripe Connect. Connect lets your platform account receive a charge, take an application fee, and route the remainder to a connected seller's Stripe account automatically. That's a fundamentally different flow from what the native toggle provides.

The core security reality: your platform secret key (sk_*) is what authorises every Connect action — creating seller accounts, generating onboarding links, controlling money movement. Because FlutterFlow compiles to a Flutter app running on the user's device, any secret placed in an API Call header ships inside the app bundle and can be extracted. The only safe pattern is to put the sk_* key inside a Firebase Cloud Function or Supabase Edge Function, then call that proxy from FlutterFlow. Your app never touches the key directly.

Stripe Connect pricing layers on top of standard Stripe fees (2.9% + 30¢/charge in the US). Connect-specific fees — account maintenance and payout fees — vary by plan and region; check Stripe's current pricing page for the numbers that apply to your geography. The connect.stripe.com onboarding domain handles the seller KYC flow, and once a seller completes it, their account becomes available as a transfer destination.

Integration method

FlutterFlow API Call

FlutterFlow API Calls route to a Firebase Cloud Function or Supabase Edge Function that holds your Stripe platform secret key. The proxy handles account creation, Account Link generation, and PaymentIntent creation with marketplace fee splits. The publishable key (pk_*) is used client-side in FlutterFlow for the Payment Sheet; the secret key (sk_*) never leaves the server.

Prerequisites

  • A Stripe platform account with Connect enabled (stripe.com/connect → Apply for Connect in the dashboard)
  • A Firebase project (Blaze plan for Cloud Functions) or a Supabase project (for Edge Functions)
  • A FlutterFlow project using Supabase or Firebase as its backend for storing account status
  • Your Stripe publishable key (pk_live_* or pk_test_*) and secret key (sk_live_* or sk_test_*) — keep the secret key out of FlutterFlow
  • Basic familiarity with deploying a Firebase Cloud Function or Supabase Edge Function

Step-by-step guide

1

Enable Stripe Connect and collect your keys

Log in to your Stripe Dashboard at dashboard.stripe.com and navigate to Connect → Get started. Choose the Express account type — it handles KYC and identity verification on Stripe's side, which means you don't need to build that yourself. Once enabled, go to Developers → API keys. You'll see your publishable key (pk_test_* or pk_live_*) and your secret key (sk_test_* or sk_live_*). Copy both somewhere safe. The publishable key can be placed in FlutterFlow App State or App Values — it's safe for client use. The secret key must go into your backend environment: a Firebase Functions config value, a Supabase project secret, or an environment variable in your cloud functions deployment. Never paste sk_* anywhere inside FlutterFlow itself. Also note your platform's Stripe account ID (acct_*) from the Dashboard top-right — you may need it for some Connect API calls.

Pro tip: Create a second, restricted API key in the Stripe Dashboard (Developers → API keys → Create restricted key) that only has permissions for Connect, accounts, and payment intents. This limits blast radius if the proxy is ever compromised.

Expected result: You have your Stripe publishable key saved for later use in FlutterFlow, and your secret key stored securely outside the app.

2

Deploy a backend proxy for account creation and PaymentIntents

Create a Firebase Cloud Function (Node.js) or Supabase Edge Function (Deno) that handles three endpoints your FlutterFlow app will call: (1) POST /create-account — receives a seller's email and name, calls POST https://api.stripe.com/v1/accounts with account_type=express and the seller's details, then calls POST https://api.stripe.com/v1/account_links with the account ID plus a return_url and refresh_url that deep-link back into your app (use your app's universal link scheme, e.g. myapp://onboarding/complete). Return the account_links.url to the app so it can open the onboarding WebView. (2) POST /create-payment-intent — receives amount (in cents), currency, connected_account_id, and application_fee_amount; calls POST https://api.stripe.com/v1/payment_intents with transfer_data[destination] set to the seller's account ID. Return the client_secret to the app. (3) GET /account-status?account_id=acct_xxx — calls GET https://api.stripe.com/v1/accounts/{id} and returns payouts_enabled and charges_enabled so the app can show onboarding completion state. All three functions authenticate to Stripe using your sk_* from environment config. Store the proxy URLs (not the Stripe key) in FlutterFlow App Values.

index.js
1// Firebase Cloud Function example (Node.js)
2const functions = require('firebase-functions');
3const stripe = require('stripe')(functions.config().stripe.secret_key);
4const express = require('express');
5const cors = require('cors')({ origin: true });
6const app = express();
7app.use(cors);
8app.use(express.json());
9
10// 1. Create Express account + Account Link
11app.post('/create-account', async (req, res) => {
12 const { email, name } = req.body;
13 const account = await stripe.accounts.create({
14 type: 'express',
15 email,
16 capabilities: { card_payments: { requested: true }, transfers: { requested: true } },
17 });
18 const accountLink = await stripe.accountLinks.create({
19 account: account.id,
20 refresh_url: 'https://yourproxy.com/reauth',
21 return_url: 'myapp://onboarding/complete',
22 type: 'account_onboarding',
23 });
24 res.json({ account_id: account.id, onboarding_url: accountLink.url });
25});
26
27// 2. Create PaymentIntent with marketplace split
28app.post('/create-payment-intent', async (req, res) => {
29 const { amount, currency, destination_account, application_fee_amount } = req.body;
30 const paymentIntent = await stripe.paymentIntents.create({
31 amount, // in cents, e.g. 5000 = $50.00
32 currency,
33 application_fee_amount, // e.g. 500 = $5.00 (10% fee)
34 transfer_data: { destination: destination_account },
35 });
36 res.json({ client_secret: paymentIntent.client_secret });
37});
38
39exports.stripeConnect = functions.https.onRequest(app);

Pro tip: If you're using Supabase Edge Functions (Deno), replace the Node.js require() calls with import statements and use Deno.env.get('STRIPE_SECRET_KEY') to read the secret. Deploy with supabase functions deploy stripe-connect and set the secret via supabase secrets set STRIPE_SECRET_KEY=sk_live_xxx.

Expected result: Your proxy is deployed and reachable at a public HTTPS URL. You can test it with a tool like Postman: POST to /create-account with a test email returns a JSON with onboarding_url.

3

Configure FlutterFlow API Groups to call your proxy

In FlutterFlow, click API Calls in the left navigation panel. Click + Add and choose Create API Group. Name it StripeConnectProxy. Set the Base URL to your deployed proxy URL (e.g. https://us-central1-yourproject.cloudfunctions.net/stripeConnect or your Supabase Edge Function URL). You do not need any shared headers here — authentication happens inside the proxy, not from FlutterFlow. Now add individual API Calls within this group. First: click + Add Call → name it CreateAccount → set the Method to POST and the Path to /create-account. Switch to the Variables tab and add two Body variables: email (String) and name (String). In the Body tab, configure it as JSON with body { "email": "{{ email }}", "name": "{{ name }}" }. Click Response & Test, paste a sample response like { "account_id": "acct_xxx", "onboarding_url": "https://connect.stripe.com/express/onboarding/..." }, and click Generate JSON Paths. FlutterFlow will auto-generate $.account_id and $.onboarding_url paths you can bind to variables. Repeat this for CreatePaymentIntent (POST /create-payment-intent with amount, currency, destination_account, application_fee_amount body variables) and GetAccountStatus (GET /account-status with account_id as a query variable). Save all three API Calls.

typescript
1// Sample JSON response for CreateAccount — paste in Response & Test
2{
3 "account_id": "acct_1ExampleXXXXXXXX",
4 "onboarding_url": "https://connect.stripe.com/express/onboarding/o.abc123"
5}
6
7// Sample JSON response for CreatePaymentIntent
8{
9 "client_secret": "pi_3ExampleXXXXXXXX_secret_XXXXXX"
10}
11
12// Sample JSON response for GetAccountStatus
13{
14 "charges_enabled": true,
15 "payouts_enabled": true
16}

Pro tip: Store the onboarding_url returned by CreateAccount in an App State variable (String, persisted: false) so you can pass it into the WebView widget in the next step.

Expected result: Three API Calls appear under StripeConnectProxy in the API Calls panel. Each shows green JSON path detections in the Response & Test tab.

4

Open the seller onboarding URL in an in-app WebView

When a seller taps 'Connect your bank account' in the app, you need to open the Stripe Express onboarding URL returned by CreateAccount. In FlutterFlow, you have two solid options. Option A (simpler): use a url_launcher Custom Action. Go to Custom Code → + Add → Action, name it LaunchUrl, add the pub.dev package url_launcher (version: ^6.2.6) in the Dependencies field, then paste a short Dart snippet that calls launchUrl(Uri.parse(url), mode: LaunchMode.externalApplication). Attach this action to the 'Connect Account' button's Actions panel → + Add Action → Custom Action → LaunchUrl, passing in the onboarding_url from App State. This opens Stripe's flow in the device's default browser. Option B (more integrated): add a WebView widget from the FlutterFlow widget panel (it's in the Layout / Container elements; search 'WebView'). Bind its URL property to the onboarding_url App State variable and place it on a dedicated OnboardingWebView page. Navigate to this page after the CreateAccount API call succeeds. When the seller completes onboarding, Stripe redirects to your return_url (myapp://onboarding/complete). Handle this deep link in your app by navigating back to the seller dashboard and calling GetAccountStatus to confirm charges_enabled and payouts_enabled are both true. Display a success banner when they are. If the seller abandons, Stripe redirects to refresh_url — call CreateAccount again to generate a new link.

custom_action.dart
1// Custom Action: LaunchUrl (url_launcher ^6.2.6)
2import 'package:url_launcher/url_launcher.dart';
3
4Future launchUrl(String url) async {
5 final uri = Uri.parse(url);
6 if (await canLaunchUrl(uri)) {
7 await launchUrl(uri, mode: LaunchMode.externalApplication);
8 } else {
9 throw Exception('Could not launch $url');
10 }
11}

Pro tip: If you'd rather skip building the deep-link handling yourself, RapidDev's team builds FlutterFlow integrations like this every week — free scoping call at rapidevelopers.com/contact.

Expected result: Tapping 'Connect your bank account' opens the Stripe Express onboarding flow. After completion the app returns to the seller dashboard and shows 'Account connected' when GetAccountStatus returns charges_enabled: true.

5

Collect payment and set the marketplace fee split

When a buyer is ready to pay, your app needs to: (1) call CreatePaymentIntent on your proxy, passing the correct amount in cents (important: $29.99 = 2999, NOT 29.99 — this is the single most common beginner bug with Stripe), the destination connected account ID, and the application_fee_amount also in cents; (2) receive the client_secret back; (3) use the Stripe Flutter SDK to present the Payment Sheet to the buyer. In FlutterFlow, go to Custom Code → + Add → Action, name it ShowPaymentSheet, add the package flutter_stripe (^10.1.1) to Dependencies. In the Dart action, initialise Stripe with your publishable key (safe to hardcode or store in App Values), pass the client_secret received from the proxy into Stripe.instance.initPaymentSheet(), then call Stripe.instance.presentPaymentSheet(). The amount shown to the buyer is the full charge amount. The proxy handles the split: application_fee_amount goes to your platform account, (amount - application_fee_amount - Stripe fees) goes to transfer_data[destination]. Wire this Custom Action to your 'Pay Now' button in the Action Flow Editor. After presentPaymentSheet() completes without error, write the transaction record (PaymentIntent ID, seller ID, amount, status 'pending') to Firestore or Supabase from the app — you'll update it to 'succeeded' when the webhook fires.

custom_action.dart
1// Custom Action: ShowPaymentSheet (flutter_stripe ^10.1.1)
2import 'package:flutter_stripe/flutter_stripe.dart';
3
4Future<bool> showPaymentSheet(
5 String clientSecret,
6 String publishableKey,
7) async {
8 Stripe.publishableKey = publishableKey;
9 await Stripe.instance.initPaymentSheet(
10 paymentSheetParameters: SetupPaymentSheetParameters(
11 paymentIntentClientSecret: clientSecret,
12 merchantDisplayName: 'Your Platform Name',
13 ),
14 );
15 await Stripe.instance.presentPaymentSheet();
16 return true; // payment collected
17}

Pro tip: Always convert dollar amounts to cents in FlutterFlow before passing them to the API Call. Use a Formula in the variable binding: multiply the price variable by 100 and convert to int. Sending 29.99 instead of 2999 charges a fraction of the intended amount.

Expected result: Tapping 'Pay Now' presents the Stripe Payment Sheet on the device. After the buyer completes payment, the action returns true and the app writes a pending transaction record.

6

Handle webhooks and stream payout status into the app

Stripe sends asynchronous webhook events to your backend when a payment succeeds (payment_intent.succeeded), when funds transfer to the seller (transfer.created), and when a connected account's status changes (account.updated). FlutterFlow cannot receive webhooks directly — they require a persistent HTTPS endpoint. Add a webhook handler to your Firebase or Supabase function. Register its URL in the Stripe Dashboard under Developers → Webhooks → Add endpoint. In the handler, verify the webhook signature using stripe.webhooks.constructEvent(body, signature, webhookSecret) — the webhookSecret is the whsec_* value Stripe shows you when you add the endpoint. On a verified payment_intent.succeeded event, update the transaction document in Firestore or the row in Supabase from status 'pending' to 'succeeded'. In FlutterFlow, set up a Firestore stream listener or a Supabase realtime subscription on the transactions table filtered to the current user. Bind the status field to a Text widget on the seller dashboard — it updates automatically when the webhook fires. For the buyer, show an 'Order confirmed' screen once the stream emits 'succeeded'. Test the entire flow in Stripe's test mode using test card 4242 4242 4242 4242 with any future expiry and CVC before switching to live mode.

index.js
1// Webhook handler addition to the Firebase Cloud Function (index.js)
2app.post('/webhook', express.raw({ type: 'application/json' }), async (req, res) => {
3 const sig = req.headers['stripe-signature'];
4 let event;
5 try {
6 event = stripe.webhooks.constructEvent(
7 req.body,
8 sig,
9 functions.config().stripe.webhook_secret
10 );
11 } catch (err) {
12 return res.status(400).send(`Webhook Error: ${err.message}`);
13 }
14 if (event.type === 'payment_intent.succeeded') {
15 const pi = event.data.object;
16 await admin.firestore()
17 .collection('transactions')
18 .doc(pi.id)
19 .update({ status: 'succeeded', settled_at: new Date() });
20 }
21 res.json({ received: true });
22});

Pro tip: Use a unique Stripe PaymentIntent ID (pi_xxx) as the document ID in Firestore so the webhook can find and update the right record without scanning the collection.

Expected result: When a test payment completes, the Stripe Dashboard shows the webhook delivered successfully, Firestore updates the transaction status to 'succeeded', and the FlutterFlow app's stream widget refreshes to display 'Payment confirmed'.

Common use cases

Freelancer marketplace app where clients pay and freelancers get their cut

A FlutterFlow app lets clients post jobs and hire freelancers. When the client pays, the platform takes a 10-15% fee and transfers the rest directly to the freelancer's Stripe Express account. The app surfaces the freelancer's payout balance pulled from the proxy.

FlutterFlow Prompt

Build a marketplace where clients tap 'Pay' for a job, the app calls my backend to charge the client and split the payment — platform keeps 12%, freelancer gets 88% — and shows the freelancer their pending balance.

Copy this prompt to try it in FlutterFlow

Creator tip jar app with multi-creator payouts

Fans can browse creator profiles and send tips directly. Each creator onboards via a Stripe Express account link shown in the app. Tips flow through the platform (which keeps a small fee) before settling in each creator's bank account.

FlutterFlow Prompt

Let users send a tip to any creator. Each creator should have connected their Stripe account via an onboarding screen. Show the creator a running balance of tips received.

Copy this prompt to try it in FlutterFlow

Service booking app paying contractors on completion

Customers book and prepay for a home service. The platform holds funds until the contractor marks the job done, then releases payment minus the platform fee to the contractor's Stripe account. The app shows the contractor a history of completed payouts.

FlutterFlow Prompt

Build a booking screen where customers prepay. After the contractor confirms job completion in the app, automatically release 85% of the payment to the contractor and keep 15% as the platform fee.

Copy this prompt to try it in FlutterFlow

Troubleshooting

API call returns 401 Unauthorized or 'No such API key' from Stripe

Cause: The secret key passed in the Authorization header inside the proxy is wrong, missing, or comes from a different Stripe environment (test key used against live endpoint or vice versa).

Solution: Check your Firebase Functions config (firebase functions:config:get) or Supabase secrets to confirm the sk_* value is set and matches the environment your proxy is targeting (test vs live). Ensure the Authorization header in all Stripe calls reads 'Bearer ' + your sk_* key.

Transfer fails with 'The destination account does not have the transfers capability'

Cause: The seller's Express account has not completed onboarding, or the 'transfers' capability has not been requested when the account was created.

Solution: Call the GetAccountStatus endpoint and check that both charges_enabled and payouts_enabled are true before attempting a transfer. If they are false, re-generate an Account Link and send the seller back through onboarding. When creating the account, ensure you pass capabilities: { card_payments: { requested: true }, transfers: { requested: true } }.

Seller is stuck on Stripe's onboarding page and the app never returns

Cause: The return_url or refresh_url passed to /v1/account_links does not match a registered deep link scheme in the iOS or Android app, so Stripe's redirect has nowhere to go.

Solution: In FlutterFlow, go to Settings → App Details → Deep Linking and set up your custom URI scheme (e.g. myapp://). Make sure the return_url you pass to your proxy matches exactly — e.g. myapp://onboarding/complete. On iOS, also register the scheme in Info.plist under CFBundleURLSchemes via FlutterFlow's platform settings. Test on a real device, not the web preview.

Payment amount is drastically wrong (seller receives $0.50 when they expected $50.00)

Cause: The amount was passed in dollars as a decimal (50.00) instead of cents as an integer (5000). Stripe interprets the value as the smallest currency unit.

Solution: In your FlutterFlow API Call variable binding for amount, apply a formula that multiplies the price by 100 and floors it to an integer (e.g. (price * 100).toInt()). Verify the value in the Request & Test tab before going live.

Best practices

  • Never place your Stripe sk_* key in a FlutterFlow API Call header, App Value, or Custom Action — route all secret-key operations through a Firebase Cloud Function or Supabase Edge Function.
  • Use Stripe test mode (pk_test_* / sk_test_*) with test card 4242 4242 4242 4242 for all development and staging flows before switching to live keys.
  • Always pass amounts in the smallest currency unit (cents for USD): multiply your dollar price by 100 and convert to integer before sending to the API.
  • Set a unique idempotency key on PaymentIntent creation (Idempotency-Key header) to prevent duplicate charges if the user double-taps the Pay button or the network retries the request.
  • Verify every Stripe webhook signature using stripe.webhooks.constructEvent() before trusting the event data — an unverified webhook endpoint can be spoofed to mark orders as paid.
  • Store each seller's connected account ID (acct_*) in Firestore or Supabase keyed to their user ID, never in FlutterFlow App State — App State is ephemeral and resets on app restart.
  • Check charges_enabled and payouts_enabled on the seller's account before creating a PaymentIntent with that account as the destination; show a clear 'Finish account setup' CTA if either is false.
  • Use Stripe's restricted API keys in your proxy (permission-scoped to Connect, accounts, payment intents only) rather than the full-access secret key, to limit exposure.

Alternatives

Frequently asked questions

Can I use FlutterFlow's built-in Stripe Payments toggle for a marketplace?

No. The native FlutterFlow Stripe integration handles single-merchant checkouts where all funds go to one Stripe account. Stripe Connect — which enables splitting payments across multiple sellers — must be built manually through a backend proxy and API Calls as described in this guide. The two flows are architecturally separate.

Does this work in FlutterFlow's web Test Mode?

Partially. The API Calls to your proxy will work in Test Mode for basic request/response testing. However, the Stripe Payment Sheet (via the flutter_stripe Custom Action) only works on a real iOS or Android device or a physical build — it will not render in the FlutterFlow canvas preview or web Run Mode. Test your payment flow on a device with a Stripe test card.

What Stripe Connect account type should I use for sellers?

Express accounts are the best starting point for most FlutterFlow marketplaces. Stripe hosts the onboarding and identity verification, and sellers see 'Powered by Stripe' branding — you don't need to build any KYC flows. Custom accounts give you full control over the UX and branding but require you to handle compliance, identity verification, and bank account collection yourself, which is significantly more work.

How do I handle the case where a seller hasn't finished onboarding yet?

Call your GetAccountStatus endpoint before creating any PaymentIntent targeting that seller. If charges_enabled or payouts_enabled is false, show the seller a 'Complete your account setup' button that calls CreateAccount again (Stripe generates a fresh Account Link) and re-opens the onboarding WebView. Don't let buyers attempt to pay a seller whose account isn't ready — the PaymentIntent will fail.

Can I use Stripe Connect in Stripe test mode without real bank accounts?

Yes. In test mode you can create test Express accounts using Stripe's test onboarding (it bypasses real identity verification) and use test routing/account numbers for bank details. Transfers between test accounts happen instantly and don't move real money. Run all your flows in test mode before flipping to live keys.

My webhook is not firing in Stripe's Dashboard. What should I check?

First confirm the endpoint URL in Stripe Dashboard → Developers → Webhooks matches the exact path your function listens on (e.g. /webhook). Second, make sure your Firebase or Supabase function is deployed and publicly accessible — test it with a manual webhook delivery from the Stripe Dashboard. Third, check that the function is using express.raw() (not express.json()) to parse the webhook body, because Stripe's signature verification requires the raw bytes.

RapidDev

Talk to an Expert

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

Book a free consultation

Integrations are where projects stall

We wire FlutterFlow integrations like this one every week — auth, webhooks, edge cases included. Fixed-price builds from $13K, delivered in 6–10 weeks.

Talk to an integration engineer

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.