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

How to Integrate Stripe for Subscription Payments in FlutterFlow

Integrate Stripe subscriptions using Cloud Functions that create Checkout Sessions in subscription mode with your Stripe priceId. A webhook Cloud Function handles lifecycle events: checkout.session.completed creates the subscription record, invoice.paid extends the billing period, and customer.subscription.deleted downgrades the user. The Stripe Customer Portal gives users self-service plan management. Display the current plan, billing history, and an upgrade CTA on the user profile.

What you'll learn

  • How to create a Stripe Checkout Session for subscriptions via Cloud Function
  • How to handle subscription lifecycle webhooks (created, renewed, cancelled)
  • How to integrate the Stripe Customer Portal for self-service billing management
  • How to gate app features based on subscription tier stored in Firestore
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner8 min read30-40 minFlutterFlow Pro+ (Cloud Functions required for Stripe)March 2026RapidDev Engineering Team
TL;DR

Integrate Stripe subscriptions using Cloud Functions that create Checkout Sessions in subscription mode with your Stripe priceId. A webhook Cloud Function handles lifecycle events: checkout.session.completed creates the subscription record, invoice.paid extends the billing period, and customer.subscription.deleted downgrades the user. The Stripe Customer Portal gives users self-service plan management. Display the current plan, billing history, and an upgrade CTA on the user profile.

Setting Up Recurring Stripe Subscriptions in FlutterFlow

Subscriptions are the most common monetization model for apps. This tutorial connects FlutterFlow to Stripe for recurring billing with full lifecycle management. Users subscribe via Stripe Checkout, webhooks keep your Firestore subscription data in sync, and the Customer Portal handles upgrades, downgrades, and cancellations without you building those UIs.

Prerequisites

  • FlutterFlow project with Firebase authentication
  • Stripe account with test mode API keys
  • Stripe product and price created in the Stripe Dashboard
  • Users collection in Firestore with subscription fields

Step-by-step guide

1

Create subscription fields on the Firestore users collection

Add the following fields to your users collection: subscriptionTier (String, default 'free'), stripeCustomerId (String, optional), stripeSubscriptionId (String, optional), currentPeriodEnd (Timestamp, optional). The subscriptionTier field drives feature gating throughout the app. The stripeCustomerId links your user to their Stripe customer object. The currentPeriodEnd tracks when the current billing period expires, useful for showing days remaining and checking if a subscription has lapsed.

Expected result: Users collection has subscription tracking fields ready for Stripe integration.

2

Build the Cloud Function to create a Stripe Checkout Session

Deploy a Cloud Function called createCheckoutSession that receives the userId and priceId. The function checks if the user already has a stripeCustomerId; if not, it creates a new Stripe customer with the user's email. Then it creates a Checkout Session with mode: 'subscription', customer: stripeCustomerId, line_items with the priceId, and success_url and cancel_url pointing back to your app. The function returns the session URL. In FlutterFlow, a Subscribe button calls this Cloud Function via an API call, then launches the returned URL.

create_checkout_session.js
1// Cloud Function: createCheckoutSession
2const functions = require('firebase-functions');
3const admin = require('firebase-admin');
4const stripe = require('stripe')(functions.config().stripe.secret);
5admin.initializeApp();
6
7exports.createCheckoutSession = functions.https
8 .onCall(async (data, context) => {
9 const uid = context.auth.uid;
10 const priceId = data.priceId;
11 const userDoc = await admin.firestore()
12 .collection('users').doc(uid).get();
13 let customerId = userDoc.data()?.stripeCustomerId;
14
15 if (!customerId) {
16 const customer = await stripe.customers.create({
17 email: context.auth.token.email,
18 metadata: { firebaseUID: uid },
19 });
20 customerId = customer.id;
21 await admin.firestore().collection('users')
22 .doc(uid).update({ stripeCustomerId: customerId });
23 }
24
25 const session = await stripe.checkout.sessions.create({
26 mode: 'subscription',
27 customer: customerId,
28 line_items: [{ price: priceId, quantity: 1 }],
29 success_url: 'https://yourapp.com/success?session_id={CHECKOUT_SESSION_ID}',
30 cancel_url: 'https://yourapp.com/pricing',
31 });
32
33 return { url: session.url };
34 });

Expected result: The Cloud Function creates a Stripe Checkout Session and returns the URL for redirect.

3

Set up the webhook Cloud Function for subscription lifecycle events

Deploy an HTTP Cloud Function called stripeWebhook that receives Stripe webhook events. Verify the webhook signature using stripe.webhooks.constructEvent with the raw request body and your webhook signing secret. Handle three critical events: checkout.session.completed retrieves the subscription and updates the user document with stripeSubscriptionId, subscriptionTier (based on the priceId to tier mapping), and currentPeriodEnd. invoice.paid extends the currentPeriodEnd to the new period. customer.subscription.deleted resets subscriptionTier to 'free' and clears subscription fields.

stripe_webhook.js
1// Cloud Function: stripeWebhook
2exports.stripeWebhook = functions.https
3 .onRequest(async (req, res) => {
4 const sig = req.headers['stripe-signature'];
5 const endpointSecret = functions.config().stripe.webhook_secret;
6 let event;
7 try {
8 event = stripe.webhooks.constructEvent(
9 req.rawBody, sig, endpointSecret
10 );
11 } catch (err) {
12 return res.status(400).send(`Webhook Error: ${err.message}`);
13 }
14
15 const db = admin.firestore();
16 switch (event.type) {
17 case 'checkout.session.completed': {
18 const session = event.data.object;
19 const sub = await stripe.subscriptions
20 .retrieve(session.subscription);
21 const userSnap = await db.collection('users')
22 .where('stripeCustomerId', '==', session.customer)
23 .limit(1).get();
24 if (!userSnap.empty) {
25 await userSnap.docs[0].ref.update({
26 stripeSubscriptionId: sub.id,
27 subscriptionTier: mapPriceToTier(sub.items.data[0].price.id),
28 currentPeriodEnd: admin.firestore.Timestamp
29 .fromMillis(sub.current_period_end * 1000),
30 });
31 }
32 break;
33 }
34 case 'invoice.paid': {
35 const invoice = event.data.object;
36 const sub = await stripe.subscriptions
37 .retrieve(invoice.subscription);
38 const userSnap = await db.collection('users')
39 .where('stripeCustomerId', '==', invoice.customer)
40 .limit(1).get();
41 if (!userSnap.empty) {
42 await userSnap.docs[0].ref.update({
43 currentPeriodEnd: admin.firestore.Timestamp
44 .fromMillis(sub.current_period_end * 1000),
45 });
46 }
47 break;
48 }
49 case 'customer.subscription.deleted': {
50 const sub = event.data.object;
51 const userSnap = await db.collection('users')
52 .where('stripeSubscriptionId', '==', sub.id)
53 .limit(1).get();
54 if (!userSnap.empty) {
55 await userSnap.docs[0].ref.update({
56 subscriptionTier: 'free',
57 stripeSubscriptionId: null,
58 currentPeriodEnd: null,
59 });
60 }
61 break;
62 }
63 }
64 res.status(200).send('OK');
65 });

Expected result: Webhooks keep the user's subscription tier in sync with Stripe for all lifecycle events.

4

Add the Stripe Customer Portal for self-service billing

Deploy a Cloud Function called createPortalSession that takes the user's stripeCustomerId and creates a Stripe Billing Portal session. The portal lets users upgrade or downgrade plans, update payment methods, view invoices, and cancel subscriptions without you building any of those UIs. In FlutterFlow, add a Manage Subscription button on the profile or settings page. On tap, call the Cloud Function and launch the returned portal URL. Configure the portal in your Stripe Dashboard under Settings > Billing > Customer portal to allow the actions you want.

Expected result: Users can manage their subscription, update payment methods, and cancel via the Stripe Customer Portal.

5

Display current plan and gate features by subscription tier

On the profile or settings page, add a Container showing the current plan: read the subscriptionTier field from the user document and display it as a badge (Free, Pro, Business). Show the currentPeriodEnd as 'Renews on {date}' formatted text. For feature gating, use Conditional Visibility throughout your app: check if currentUser.subscriptionTier equals 'pro' or 'business' to show premium features. For pages that require a subscription, add an On Page Load action that checks the tier and shows a paywall overlay or redirects to the pricing page if the user is on the free tier.

Expected result: Users see their current plan details and premium features are gated behind the appropriate subscription tier.

Complete working example

FlutterFlow Stripe Subscription Integration
1FIRESTORE SCHEMA:
2 users (collection):
3 subscriptionTier: String (free|pro|business)
4 stripeCustomerId: String (optional)
5 stripeSubscriptionId: String (optional)
6 currentPeriodEnd: Timestamp (optional)
7
8CLOUD FUNCTION: createCheckoutSession
9 Input: priceId
10 Create Stripe customer if needed
11 Create Checkout Session (mode: 'subscription')
12 Return session URL
13
14CLOUD FUNCTION: stripeWebhook (HTTP)
15 Verify webhook signature
16 Handle events:
17 checkout.session.completed set tier + subscription fields
18 invoice.paid extend currentPeriodEnd
19 customer.subscription.deleted reset to free tier
20
21CLOUD FUNCTION: createPortalSession
22 Input: stripeCustomerId
23 Create Billing Portal session
24 Return portal URL
25
26PAGE: PricingPage
27 Columns of plan cards (Free, Pro, Business)
28 Each with features list + price
29 Subscribe button createCheckoutSession Launch URL
30
31PAGE: Settings / Profile
32 Container: current plan badge + renewal date
33 Button "Manage Subscription" createPortalSession Launch URL
34 Button "Upgrade" (visible if tier == 'free') Navigate to Pricing
35
36FEATURE GATING:
37 On Page Load: check subscriptionTier
38 If tier < required show paywall overlay or redirect
39 Conditional Visibility on premium features: tier >= 'pro'

Common mistakes

Why it's a problem: Not handling the customer.subscription.deleted webhook

How to avoid: Handle the customer.subscription.deleted webhook by resetting the user's subscriptionTier to free and clearing subscription fields in Firestore.

Why it's a problem: Calling the Stripe API directly from the FlutterFlow frontend

How to avoid: Always call Stripe through Cloud Functions. The frontend only receives the Checkout Session URL or Portal URL, never the secret key.

Why it's a problem: Checking subscription status only on login

How to avoid: Check the subscriptionTier and currentPeriodEnd on every protected page load. If currentPeriodEnd is in the past, trigger a re-check with Stripe or downgrade locally.

Best practices

  • Handle all subscription lifecycle webhooks: created, renewed, cancelled, and failed payment
  • Use the Stripe Customer Portal for billing management instead of building custom UIs
  • Check subscription status on every protected page load, not just on login
  • Store the stripeCustomerId on the user document to avoid creating duplicate customers
  • Use Stripe test mode and test card numbers during development
  • Map Stripe priceIds to your app's tier names in a configuration document for easy updates
  • Add a failed payment handler that notifies users and provides a grace period

Still stuck?

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

ChatGPT Prompt

Set up Stripe subscription payments in a FlutterFlow app. I need Cloud Functions for creating Checkout Sessions (subscription mode), handling webhooks (checkout.session.completed, invoice.paid, customer.subscription.deleted), and generating Customer Portal sessions. Include Firestore schema for tracking subscription tier and billing period. Show how to gate features by tier.

FlutterFlow Prompt

Create a pricing page with three plan cards in a Row (Free, Pro, Business). Each card has a plan name, price, features list, and a Subscribe button. The current plan card has a highlighted border and shows 'Current Plan' instead of the Subscribe button.

Frequently asked questions

How do I test subscriptions without real charges?

Use Stripe test mode with test card number 4242 4242 4242 4242. Stripe test mode creates real subscription objects but never charges real money. You can also use Stripe CLI to trigger test webhook events locally.

Can I offer a free trial before charging?

Yes. Add trial_period_days to the Checkout Session creation: stripe.checkout.sessions.create({ subscription_data: { trial_period_days: 14 } }). The user subscribes but is not charged until the trial ends.

How do I handle failed recurring payments?

Handle the invoice.payment_failed webhook. Update the user document with a paymentFailed flag and show an in-app banner asking them to update their payment method via the Customer Portal. Stripe retries failed payments automatically based on your retry settings.

Can users switch between plans?

Yes. The Stripe Customer Portal handles plan upgrades and downgrades automatically. Configure the available plans in the Stripe Dashboard under Customer Portal settings. Stripe prorates the charge by default.

How do I offer annual vs monthly billing?

Create separate prices in Stripe for the same product: one with interval monthly, another with interval yearly. Show both options on your pricing page and pass the corresponding priceId to the Checkout Session.

Can RapidDev help set up Stripe subscriptions?

Yes. RapidDev can integrate Stripe subscriptions with full webhook handling, feature gating, trial periods, coupon support, and analytics dashboards for your FlutterFlow app.

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.