How to Integrate Stripe into a Firebase App
Stripe and Firebase are two of the most popular tools in the startup tech stack, and combining them is something we do on almost every launch-ready product we build. But getting the integration right in production requires more care than most tutorials suggest. This guide covers the architecture, the implementation approach, and the production pitfalls that catch founders out.
The architecture: why you need a backend
The most important thing to understand about Stripe integration is that you cannot call the Stripe API directly from client-side code. Your Stripe secret key would be exposed in the browser, which would allow anyone to make API calls on your behalf.
For Firebase apps, the solution is Firebase Cloud Functions, server-side functions that run in Google's infrastructure, have access to your Stripe secret key via environment variables, and can be called securely from your client app.
The basic flow looks like this:
- User clicks “Subscribe” or “Pay” in your app
- Your client app calls a Firebase Cloud Function (via HTTPS callable)
- The Cloud Function calls the Stripe API using your secret key
- Stripe returns a payment URL or client secret
- Your app redirects the user to Stripe Checkout, or completes the payment using Stripe Elements
- Stripe sends a webhook to your Cloud Function confirming the payment
- Your Cloud Function updates the user's record in Firestore
Setting up Stripe Checkout with Firebase
Stripe Checkout is the fastest way to accept payments, Stripe hosts the payment page, handles card validation, and manages PCI compliance for you. Here is the high-level approach:
1. Create the Checkout Session (Cloud Function)
Your Cloud Function creates a Stripe Checkout Session with the product, price, success URL, and cancel URL. It stores the Checkout Session ID in Firestore against the user's UID, then returns the session URL to the client.
Key things to include in the session:
client_reference_id, set this to the Firebase UID so you can match Stripe events to your users latercustomer_email, pre-fill with the user's email from Firebase Authmetadata, any additional context you need in your webhook handler
2. Redirect to Stripe Checkout (Client)
Call your Cloud Function from the client, receive the session URL, and redirect the user to it. After payment, Stripe redirects back to your success URL.
3. Handle the webhook (Cloud Function)
This is where most integrations go wrong. When a payment is completed, Stripe sends a webhook event to a public URL you have registered in your Stripe Dashboard. You need a Cloud Function with an HTTP trigger (not a callable function) to receive these events.
The most important webhook events for a checkout integration:
checkout.session.completed, payment was successfulpayment_intent.payment_failed, payment failedcustomer.subscription.updated, subscription plan changedcustomer.subscription.deleted, subscription cancelled
Keeping Firestore in sync with Stripe
The most critical part of a production Stripe integration is ensuring your Firestore database always reflects the true state of Stripe. Here is how we structure this:
- User document, store the Stripe Customer ID, subscription status, current plan, and subscription period end date on the user's Firestore document. Use Firestore security rules to restrict access based on subscription status.
- Webhook handler updates Firestore, every webhook event that changes a subscription's status should trigger an update to the corresponding user document. Never rely on the client app to update subscription status.
- Idempotency, Stripe may send the same webhook event multiple times. Your handler should check if the event has already been processed (e.g. by storing processed event IDs in Firestore) before making any changes.
Production pitfalls to avoid
Pitfall 1: Not verifying webhook signatures
Stripe signs every webhook with a secret. If you do not verify this signature, anyone can send fake events to your endpoint. Always use stripe.webhooks.constructEvent() with your webhook signing secret.
Pitfall 2: Trusting the success redirect URL
Do not update your database when the user lands on your success page. The success URL can be visited directly by anyone. Always update the database in your webhook handler, using the webhook event as the source of truth.
Pitfall 3: Not handling failed webhooks
Webhooks can fail, your Cloud Function might time out, or you might have a deployment error. Stripe retries failed webhooks for up to 72 hours. Make your webhook handlers idempotent so retries do not cause duplicate updates.
Pitfall 4: Using Firestore rules that trust Firestore data
If your Firestore security rules grant access based on a field like subscriptionStatus, make sure that field is only writable by your Cloud Functions (never by the client). Otherwise a user could update their own subscription status to “active” without paying.
Want this built for you?
A production-grade Stripe + Firebase integration is one of our most common projects. We have built it for apps with subscription billing, one-off payments, metered usage, and multi-tier plans. We know the edge cases and we build for them from the start.
If you would rather have this done right than figure it out yourself, get in touch and we will scope it and give you a fixed price.
Need a Stripe + Firebase integration built?
We have built this many times before. Get in touch for a fixed-price quote.
See Our Stripe Service