Flutter

How to Add Stripe Payments to Your Flutter App (Step-by-Step)

Building an app that accepts payments? Flutter's official Stripe SDK handles the complexity so you don't have to reinvent the wheel. Whether you're on Android or iOS, Stripe's Payment Sheet gives you a secure, ready-made checkout UI in minutes.

This guide walks through the actual steps: what to set up, how the code flows, and the production checklist that keeps your integration safe.

At a Glance

  • Official flutter_stripe package works on both Android and iOS
  • Payment Sheet requires a backend to create payment intents securely
  • Test mode lets you verify everything before processing real money
  • Secret keys must never live in your app code

What We're Building

A Flutter app that accepts card payments through Stripe. The flow: user taps a button, the Payment Sheet appears, they enter details, and your app handles the result. Simple on the surface, but the architecture matters.

Step 1: Set Up Your Stripe Account

Before touching code, you need four things from Stripe's dashboard:

  • Your publishable key (safe to use in the app)
  • Your secret key (stays on the server)
  • A payment intent (created server-side)
  • Test mode activated

Create a Stripe account if you haven't. Navigate to Developers > API keys and copy your publishable key. Keep the secret key on your backend server—never in Flutter code.

Step 2: Initialize Stripe in Your App

Add flutter_stripe to your pubspec.yaml, then initialize with your publishable key:

“`dart

Stripe.publishableKey = "pk_test_XXXXXXXXXX";

“`

This goes in your app's initialization code, typically before rendering the home screen.

Step 3: The Payment Flow

The UI is intentionally minimal. A single button triggers everything:

“`dart

ElevatedButton(

OnPressed: makePayment,

Child: Text("Pay Now"),

)

“`

The real logic happens in two steps: initialize the Payment Sheet, then present it.

First, pass your payment intent client secret (fetched from your backend) along with your merchant name:

“`dart

Await Stripe.instance.initPaymentSheet(

PaymentSheetParameters: SetupPaymentSheetParameters(

PaymentIntentClientSecret: clientSecret,

MerchantDisplayName: "My Store",

),

);

“`

Then present the sheet to the user:

“`dart

Await Stripe.instance.presentPaymentSheet();

“`

The SDK handles card input, validation, and 3D Secure if needed. Your app just waits for the result.

Why This Matters

Most payment tutorials gloss over the architecture. Here's the part that actually matters: the client secret isn't just a string—it's proof your backend authorized this transaction. Without a backend creating payment intents, you have no payment security at all.

Stripe designed the Payment Sheet this way intentionally. Your Flutter code never touches raw card numbers, which keeps you out of PCI compliance headaches. The tradeoff is clear: you need a lightweight backend endpoint, even if it's a simple serverless function.

A Real Example

Say you're building a marketplace app. A freelancer invoices a client, the client taps "Pay" in your app, and Stripe processes the card. Your backend receives the webhook when payment succeeds, then marks the invoice paid. No manual reconciliation, no lost payments.

For a simpler case like a one-time purchase, the flow shrinks to three calls: fetch the client secret from your backend, present the sheet, and check the result. That's it.

Production Checklist

Stripe's test mode is thorough, but going live requires attention:

  • Verify secret keys are server-side only
  • Ensure payment intents are created through your backend, not the app
  • Switch from test to live mode in the dashboard
  • Test with Stripe's test cards to simulate declines and 3D Secure
  • Set up webhook endpoint and validate the signature flow

Most failures in production come from skipping the webhook validation. Stripe sends confirmation to your server—your app shouldn't trust the UI result alone.

What to Watch Next

Payment edge cases will bite you. Cards decline, networks fail, and users cancel mid-flow. Handle each result from presentPaymentSheet explicitly. Also watch for currency and amount mismatches—Stripe is strict about what you claim versus what the payment intent specifies.

For apps needing subscriptions or dynamic amounts, explore Stripe's Customer objects and SetupIntents. The Payment Sheet handles one-time payments well, but recurring billing needs extra setup.

Bottom line: Flutter Stripe payment integration works reliably once you respect the backend requirement. Use the test environment obsessively before launch, and you'll ship with confidence.

Key takeaways

  • Flutter_stripe works cross-platform with one codebase
  • Payment intents must come from your backend—never the app
  • The Payment Sheet abstracts PCI compliance away from your code
  • Test thoroughly with Stripe's test cards before going live
  • Handle success, failure, and cancellation explicitly in your code

Frequently asked questions

Do I need a backend server for Stripe in Flutter?

Yes. Your backend creates the payment intent and returns the client secret. Never expose your secret key in app code.

Can I test payments without real money?

Stripe's test mode uses fake cards that simulate transactions without processing real funds. Always verify in test mode first.

What happens if a payment fails?

The SDK returns an error result. Check the error code and show appropriate feedback—card declined, insufficient funds, or user cancelled.

Is the Payment Sheet secure for users?

Yes. Stripe handles card entry, validation, and 3D Secure authentication. Your app never sees raw card numbers.

How do I know a payment actually succeeded?

Use webhooks. Stripe sends a signed event to your server when a payment completes. Don't rely solely on the app's UI result.

Related