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

How to Add Payments to Your FlutterFlow Project

Add Stripe payments to FlutterFlow by deploying a Firebase Cloud Function that creates a Stripe Checkout Session, calling it from a Custom Action, and launching the returned URL with FlutterFlow's Launch URL action. A second webhook Cloud Function listens for checkout.session.completed to create order records in Firestore. Never put your Stripe secret key in FlutterFlow Custom Action code — it compiles into your app and becomes publicly visible.

What you'll learn

  • How to deploy a Firebase Cloud Function to create Stripe Checkout Sessions
  • How to call the Cloud Function from a FlutterFlow Custom Action
  • How to handle post-payment order creation via Stripe webhooks
  • How to test with Stripe test cards before going live
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner10 min read45-60 minFlutterFlow Standard+ (Custom Actions required)March 2026RapidDev Engineering Team
TL;DR

Add Stripe payments to FlutterFlow by deploying a Firebase Cloud Function that creates a Stripe Checkout Session, calling it from a Custom Action, and launching the returned URL with FlutterFlow's Launch URL action. A second webhook Cloud Function listens for checkout.session.completed to create order records in Firestore. Never put your Stripe secret key in FlutterFlow Custom Action code — it compiles into your app and becomes publicly visible.

Why Stripe Needs a Cloud Function Middleman

FlutterFlow apps run on users' devices, which means any code you write is potentially visible to anyone who decompiles your app. Stripe's secret key (sk_test_xxx or sk_live_xxx) must never appear in your FlutterFlow Custom Action code — it would be exposed. The correct architecture uses a Firebase Cloud Function as a secure server: your FlutterFlow app calls the Cloud Function, the Cloud Function calls Stripe with the secret key (stored as a Firebase environment variable), and returns the checkout URL. Your app then opens that URL. Stripe handles the payment form, card security, and PCI compliance.

Prerequisites

  • A Stripe account (free at stripe.com) with at least test mode enabled
  • A Firebase project with Blaze plan (Cloud Functions require billing enabled)
  • FlutterFlow project connected to the Firebase project
  • FlutterFlow Standard plan or higher (Custom Actions required)

Step-by-step guide

1

Create a Stripe Account and Get Your API Keys

Sign up at stripe.com if you don't have an account. Verify your email. In the Stripe Dashboard, click Developers in the left sidebar, then API Keys. You'll see two key types: Publishable key (pk_test_xxx) — safe for client-side code, and Secret key (sk_test_xxx) — server-side only, never in app code. You're in test mode by default, which is correct for now. Copy your Secret key and save it somewhere secure (a password manager). In your Stripe Dashboard, also navigate to Products > Create a product to create at least one product with a price — you'll need the price ID (price_xxx) when creating Checkout Sessions.

Expected result: You have your Stripe test Secret key saved securely and at least one product with a price ID (price_xxx) in your Stripe account.

2

Deploy a Cloud Function to Create Checkout Sessions

In Firebase Console, go to Functions and click Get Started. Initialize functions in your terminal with 'firebase init functions' (select JavaScript). Install the Stripe SDK: 'npm install stripe' inside the functions/ directory. Set your Stripe secret key as a Firebase environment config variable: 'firebase functions:config:set stripe.secret_key=sk_test_xxx'. Deploy the createCheckoutSession function below. This function receives a price ID and user ID, creates a Stripe Checkout Session, and returns the session URL. Register the function URL in Stripe Webhooks after deployment.

create_checkout_session.js
1const functions = require('firebase-functions');
2const Stripe = require('stripe');
3const cors = require('cors')({ origin: true });
4
5exports.createCheckoutSession = functions.https.onRequest((req, res) => {
6 cors(req, res, async () => {
7 if (req.method !== 'POST') {
8 return res.status(405).send('Method Not Allowed');
9 }
10 try {
11 const stripe = Stripe(functions.config().stripe.secret_key);
12 const { priceId, userId, successUrl, cancelUrl } = req.body;
13
14 if (!priceId || !userId) {
15 return res.status(400).json({ error: 'Missing priceId or userId' });
16 }
17
18 const session = await stripe.checkout.sessions.create({
19 payment_method_types: ['card'],
20 mode: 'payment', // or 'subscription' for recurring
21 line_items: [{ price: priceId, quantity: 1 }],
22 metadata: { userId },
23 success_url: successUrl || 'https://yourapp.com/success',
24 cancel_url: cancelUrl || 'https://yourapp.com/cancel',
25 });
26
27 res.json({ sessionUrl: session.url, sessionId: session.id });
28 } catch (error) {
29 console.error('Checkout session error:', error);
30 res.status(500).json({ error: error.message });
31 }
32 });
33});

Expected result: Cloud Function is deployed and accessible at a URL like https://us-central1-your-project.cloudfunctions.net/createCheckoutSession.

3

Call the Cloud Function from a FlutterFlow Custom Action

In FlutterFlow, go to Custom Code > Custom Actions and create a new action called 'initiateStripeCheckout'. This action takes the priceId as a parameter, calls your Cloud Function, and returns the checkout URL. Add the 'http' package to your project dependencies. The action posts to your Cloud Function URL with the priceId and currentUserUID, receives the session URL in the response, and returns it as a String. Keep your Cloud Function URL in a project constant (not hardcoded in the action) for easy updates between environments.

initiate_stripe_checkout.dart
1import 'dart:convert';
2import 'package:http/http.dart' as http;
3
4Future<String> initiateStripeCheckout(String priceId) async {
5 const cloudFunctionUrl =
6 'https://us-central1-YOUR-PROJECT.cloudfunctions.net/createCheckoutSession';
7
8 try {
9 final response = await http.post(
10 Uri.parse(cloudFunctionUrl),
11 headers: {'Content-Type': 'application/json'},
12 body: jsonEncode({
13 'priceId': priceId,
14 'userId': currentUserUid, // FlutterFlow built-in
15 'successUrl': 'https://yourapp.com/payment-success',
16 'cancelUrl': 'https://yourapp.com/payment-cancel',
17 }),
18 );
19
20 if (response.statusCode == 200) {
21 final data = jsonDecode(response.body);
22 return data['sessionUrl'] as String;
23 } else {
24 debugPrint('Cloud Function error: ${response.statusCode} ${response.body}');
25 return '';
26 }
27 } catch (e) {
28 debugPrint('initiateStripeCheckout error: $e');
29 return '';
30 }
31}

Expected result: The Custom Action calls your Cloud Function and returns a valid Stripe Checkout Session URL string.

4

Wire Up the Buy Button Action Chain

On your product detail page or pricing page in FlutterFlow, select the Buy Now button. Open its On Tap action chain. Add these actions in sequence: (1) Show Loading overlay, (2) Call the initiateStripeCheckout Custom Action (pass your price ID), (3) Hide Loading overlay, (4) Add a Conditional action: if the returned URL is not empty, launch it using FlutterFlow's Launch URL action (set to open in a new tab/browser). If the URL is empty, show a SnackBar with an error message. The Launch URL action opens the Stripe-hosted checkout page in the browser where the user enters card details.

Expected result: Tapping Buy Now shows a loading indicator, then opens the Stripe Checkout page in the browser with your product details and price displayed.

5

Deploy a Webhook to Create Orders After Payment

After Stripe processes a payment, your app needs to know about it. Create a second Cloud Function that receives Stripe webhook events and writes order records to Firestore. Register this function's URL as a webhook endpoint in Stripe Dashboard > Developers > Webhooks. Listen for the 'checkout.session.completed' event. Always verify the Stripe signature using your webhook signing secret (different from your API secret key) to prevent fake payment events. Once the signature is verified, create an order document in Firestore with status 'paid', the user ID from session.metadata.userId, the amount, and the Stripe session ID for reference.

Expected result: After completing a test payment, a new order document appears in your Firestore 'orders' collection with status 'paid' and the correct user ID.

6

Test with Stripe Test Cards and Go Live

Test your full payment flow using Stripe's test card numbers. In the Stripe Checkout page, enter card number 4242 4242 4242 4242, any future expiry date, any 3-digit CVC, and any postal code. This simulates a successful payment. Test a declined card with 4000 0000 0000 0002. After testing confirms the order flow works end-to-end, go live: activate your Stripe account (complete the account verification), get your live API keys from Stripe Dashboard, update your Firebase config: 'firebase functions:config:set stripe.secret_key=sk_live_xxx', update your webhook with the live signing secret, and redeploy your Cloud Functions.

Expected result: End-to-end payment works: user taps Buy, enters test card, payment succeeds, order document created in Firestore, user sees payment confirmation in app.

Complete working example

stripe_webhook_orders.js
1const functions = require('firebase-functions');
2const admin = require('firebase-admin');
3const Stripe = require('stripe');
4
5// Ensure admin is initialized
6if (!admin.apps.length) admin.initializeApp();
7
8exports.stripeWebhookOrderCreate = functions.https.onRequest(async (req, res) => {
9 const stripe = Stripe(functions.config().stripe.secret_key);
10 const webhookSecret = functions.config().stripe.webhook_secret;
11 const sig = req.headers['stripe-signature'];
12
13 let event;
14 try {
15 event = stripe.webhooks.constructEvent(req.rawBody, sig, webhookSecret);
16 } catch (err) {
17 console.error('Webhook signature failed:', err.message);
18 return res.status(400).send(`Webhook Error: ${err.message}`);
19 }
20
21 if (event.type === 'checkout.session.completed') {
22 const session = event.data.object;
23 const db = admin.firestore();
24
25 try {
26 await db.collection('orders').add({
27 userId: session.metadata.userId || null,
28 stripeSessionId: session.id,
29 stripePaymentIntentId: session.payment_intent || null,
30 amount: session.amount_total ? session.amount_total / 100 : 0,
31 currency: session.currency || 'usd',
32 status: 'paid',
33 customerEmail: session.customer_details?.email || null,
34 createdAt: admin.firestore.FieldValue.serverTimestamp(),
35 paidAt: admin.firestore.FieldValue.serverTimestamp(),
36 });
37 console.log('Order created for session:', session.id);
38 } catch (dbError) {
39 console.error('Firestore write failed:', dbError);
40 return res.status(500).json({ error: 'Failed to create order' });
41 }
42 }
43
44 res.json({ received: true });
45});

Common mistakes when adding Payments to Your FlutterFlow Project

Why it's a problem: Putting the Stripe secret key (sk_test or sk_live) directly in a Custom Action

How to avoid: Store the Stripe secret key only as a Firebase Functions config variable. Your Cloud Function uses it server-side where it is never exposed to users or app binaries.

Why it's a problem: Using URL parameters on the success redirect to confirm payment

How to avoid: Always confirm payment via a Stripe webhook event (checkout.session.completed). The webhook is server-to-server and cannot be faked without the webhook signing secret.

Why it's a problem: Not testing declined payments and cancellation flows

How to avoid: Test with Stripe's decline test card (4000 0000 0000 0002) and verify your app shows a meaningful error. Test closing the Stripe Checkout tab without paying and verify the cart remains available.

Best practices

  • Never expose your Stripe secret key in client-side code — always use a Cloud Function intermediary
  • Always verify Stripe webhook signatures before processing any payment event
  • Store the Stripe session ID and payment intent ID in every Firestore order document for debugging
  • Test your full payment flow with all Stripe test cards (success, decline, insufficient funds) before going live
  • Show a loading indicator while the checkout URL is being generated — the Cloud Function call takes 1-3 seconds
  • Use Stripe's test mode webhook sending to verify your webhook handler works before enabling live mode
  • Display clear payment confirmation within your app (not just relying on Stripe's success page) by checking the order document status

Still stuck?

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

ChatGPT Prompt

I'm building a FlutterFlow app and want to add Stripe one-time payments. I have Firebase connected. Walk me through: (1) the Firebase Cloud Function I need to create Stripe Checkout Sessions, (2) how to call it from FlutterFlow, and (3) the webhook to confirm payments in Firestore. Include the complete Node.js code for both functions.

FlutterFlow Prompt

In FlutterFlow, I have a Custom Action that calls my Stripe Cloud Function and receives a checkout URL string. Write the complete action chain I should add to my Buy button: show loading, call the action, hide loading, check if URL is valid, launch URL or show error snackbar.

Frequently asked questions

Can I add Stripe payments to FlutterFlow without any code?

Not for secure production payments. Stripe requires a server-side component to create Checkout Sessions safely — this is a Firebase Cloud Function. The Cloud Function requires JavaScript code. However, the FlutterFlow side uses a Custom Action (Dart code provided above) which you can copy-paste without modification, and FlutterFlow's Launch URL action handles opening the Stripe Checkout page with zero custom code.

Which Stripe pricing model works best with FlutterFlow?

Stripe Checkout (hosted payment page) is the easiest integration — your app simply opens the Stripe URL. This works for one-time payments and subscriptions. Stripe Payment Links (no code at all, just a URL) work if you only need a single fixed product. For custom payment forms inside your app, Stripe Elements requires more advanced Custom Widget code.

How do I test Stripe payments in FlutterFlow without real money?

Use Stripe test mode keys (they start with sk_test_ and pk_test_). On the Stripe Checkout page, enter test card 4242 4242 4242 4242 with any future date and any CVC. No real money is charged. Switch to live keys only when you're ready to accept real payments from real users.

Does Stripe work with FlutterFlow mobile apps (iOS and Android)?

Yes. The Stripe Checkout Session approach (Cloud Function + Launch URL) opens the checkout in the device's default browser, which works on all platforms. For an in-app payment experience without leaving the app, you would need the Stripe Flutter SDK integrated as a Custom Widget, which is more complex.

How do I handle subscription payments vs one-time payments?

Change the 'mode' parameter in your Cloud Function from 'payment' to 'subscription'. Create a recurring price in your Stripe product (instead of a one-time price). The checkout flow is identical for users. For subscriptions, listen for the invoice.paid webhook event (not just checkout.session.completed) to track ongoing billing status.

What happens if my Firebase Cloud Function goes down during a payment?

If the Cloud Function fails before creating the Checkout Session, the user sees an error in your app and no charge occurs — Stripe hasn't been called yet. If the payment succeeds but your webhook Cloud Function fails, the order won't be created in Firestore. Stripe retries failed webhooks automatically for 72 hours, so a temporary function outage self-recovers. Check your Firebase Function logs for any persistent webhook failures.

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.