Skip to main content
RapidDev - Software Development Agency
flutterflow-integrationsFlutterFlow Native Integration

Braintree

Connect FlutterFlow to Braintree using the built-in native Payments integration in Settings & Integrations for simple checkout — it's the fastest path and requires almost no code. For custom UI, refunds, subscriptions, or PayPal checkout, build a Firebase or Supabase proxy that generates a client token and processes server-side sales using Braintree's GraphQL API with Basic Auth. The private key must never appear in FlutterFlow.

What you'll learn

  • How to enable Braintree in FlutterFlow's native Payments settings and when that is enough
  • How to get Merchant ID, Public Key, and Private Key from the Braintree Control Panel
  • How to build a Firebase or Supabase proxy for client token generation and Transaction.sale via GraphQL
  • How to enable PayPal in the Braintree sandbox and why it doesn't appear without that step
  • How to switch from sandbox to the live Braintree gateway
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate16 min read1-3 hoursPaymentsLast updated July 2026RapidDev Engineering Team
TL;DR

Connect FlutterFlow to Braintree using the built-in native Payments integration in Settings & Integrations for simple checkout — it's the fastest path and requires almost no code. For custom UI, refunds, subscriptions, or PayPal checkout, build a Firebase or Supabase proxy that generates a client token and processes server-side sales using Braintree's GraphQL API with Basic Auth. The private key must never appear in FlutterFlow.

Quick facts about this guide
FactValue
ToolBraintree
CategoryPayments
MethodFlutterFlow Native Integration
DifficultyIntermediate
Time required1-3 hours
Last updatedJuly 2026

Braintree is natively supported in FlutterFlow — start there, extend if needed

Braintree (owned by PayPal since 2013) is one of the two payment processors FlutterFlow directly supports in its native Payments settings, alongside Stripe. This is a genuine competitive advantage: you can wire up a working Braintree checkout in FlutterFlow without writing a single line of code. Go to Settings & Integrations → Payments → Braintree/PayPal, toggle it on, paste your Merchant ID and keys, and FlutterFlow adds a pre-built payment button to your screens. For a straightforward single-merchant checkout where a customer pays a fixed amount, this native route is the right choice.

The authentication model is different from Stripe. Braintree uses Basic Auth: your Public Key is the username and your Private Key is the password, plus a Merchant ID that identifies your gateway account. You get these from the Braintree Control Panel (your Merchant ID is in the top-right corner after login, and API keys are under Account → My User → API Keys). Braintree is actively pushing the GraphQL API (https://payments.braintree-api.com/graphql) over its legacy REST SDK — new integrations should target GraphQL for transaction creation, vaulting, and refunds.

For anything beyond the native checkout — custom payment UI, refunds via the API, subscription billing, PayPal-through-Braintree, or vaulting payment methods for repeat customers — you build the standard Braintree flow manually: your backend generates a client token, the Flutter app uses it to collect a payment method nonce (a one-time token representing the card), and the backend runs Transaction.sale with the nonce. The Private Key is a server secret that must never appear in FlutterFlow API Call headers or Custom Actions — it would ship inside the app bundle. Route all sale and refund calls through Firebase Cloud Functions or Supabase Edge Functions.

Integration method

FlutterFlow Native Integration

FlutterFlow ships a native Braintree/PayPal Payments toggle in Settings & Integrations → Payments. Enabling it wires up a ready-made checkout flow with minimal configuration — the fastest path for simple one-off charges. For advanced flows (custom UI, refunds, subscriptions, PayPal button, vaulting), the integration switches to a manual client-token → payment-nonce → server-side-sale pattern proxied through Firebase or Supabase, using Braintree's GraphQL API with Basic Auth (Public Key = username, Private Key = password).

Prerequisites

  • A Braintree sandbox account — create one free at sandbox.braintreegateway.com (separate from the live gateway at braintreegateway.com)
  • Your sandbox Merchant ID, Public Key, and Private Key from the Braintree Control Panel (Account → My User → API Keys → Generate New API Key)
  • For the native FlutterFlow path: no additional setup beyond the keys
  • For the custom proxy path: a Firebase project (Blaze plan) or Supabase project for the backend function
  • For PayPal checkout via Braintree: PayPal must be enabled in the sandbox Control Panel under Processing → Payment Methods → Enable PayPal

Step-by-step guide

1

Get your Braintree credentials from the Control Panel

Go to sandbox.braintreegateway.com and sign in (or create a sandbox account — it's free and separate from the live gateway). Once inside, your Merchant ID is visible in the top-right corner of the page, sometimes under Account → Business. To get API keys, navigate to Account → My User → API Keys. If no key exists, click Generate New API Key. Each key row shows a Public Key and a Private Key (click the eye icon or expand the row). The Public Key is safe to use client-side — it's how the Braintree client SDK identifies your gateway without authentication rights. The Private Key is the server secret that authorises sale transactions and refunds — never paste this in FlutterFlow. Store all three values (Merchant ID, Public Key, Private Key) securely. For the native FlutterFlow integration, you'll paste the Merchant ID and Public Key into the Payments settings. For custom flows, the Private Key goes into Firebase Functions config or Supabase secrets.

Pro tip: Generate a separate API key pair for your sandbox and your live gateway. Do not reuse sandbox keys for production — they only work against sandbox.braintreegateway.com. The live gateway is at braintreegateway.com and requires a fully activated Braintree merchant account.

Expected result: You have your Braintree sandbox Merchant ID, Public Key, and Private Key. The Private Key is saved securely and is not yet placed anywhere in FlutterFlow.

2

Enable native Braintree checkout in FlutterFlow (simple flows)

For simple one-off payment collection — a product purchase, a service fee, a donation — FlutterFlow's native Payments integration is all you need. In FlutterFlow, navigate to Settings & Integrations in the left panel → Payments. You'll see two options: Stripe and Braintree/PayPal. Enable the Braintree/PayPal toggle. FlutterFlow will ask for your Merchant ID and Public Key (not your Private Key — the native integration uses the client-side Drop-in UI component, which only needs your public identifier to tokenise the card). Paste your sandbox Merchant ID and Public Key from the previous step. Once configured, FlutterFlow exposes a Braintree Payment action in the Action Flow Editor. On your checkout screen, add a button, open its Actions panel, click + Add Action → Payments → Braintree. Configure the amount (you can bind it to a variable), currency, and optional item description. When the user taps the button, the Braintree Drop-in UI appears, the user enters their card (or taps PayPal if enabled in your Control Panel), and the nonce is sent to FlutterFlow's internal server-side handler. Note: for production you'll update the Merchant ID and Public Key to your live gateway credentials.

Pro tip: The native FlutterFlow Braintree integration does not support PayPal by default. To show the PayPal button in the Drop-in UI, you must first enable PayPal in your Braintree Control Panel under Processing → Payment Methods → Enable PayPal → choose sandbox PayPal. Without that toggle, the PayPal button never appears regardless of FlutterFlow settings.

Expected result: In FlutterFlow's Settings → Payments, the Braintree/PayPal toggle shows as active with your Merchant ID and Public Key entered. On a test device, the 'Pay' button triggers the Braintree Drop-in UI. Test card 4111 1111 1111 1111 (any future expiry) completes a sandbox transaction.

3

Deploy a Firebase or Supabase proxy for custom flows

For any flow beyond the native checkout — refunds, subscription billing with vaulted cards, custom payment UI, or reading transaction history — you need to call Braintree's API from a backend proxy. Braintree uses Basic Auth: the Authorization header is 'Basic ' followed by base64(publicKey:privateKey). Braintree actively recommends its GraphQL API (https://payments.braintree-api.com/graphql) for new integrations over the legacy REST SDK. For the client-token → nonce → sale flow: Endpoint 1 — POST /client-token: calls Braintree's GraphQL mutation createClientToken{clientToken} and returns the token to the app. Endpoint 2 — POST /create-sale: receives a paymentMethodNonce (from the Drop-in UI or SDK in the app), amount, and optional customer_id; runs the chargePaymentMethod GraphQL mutation with the nonce and returns the transaction ID and status. For refunds: POST /refund-transaction receives a transactionId and amount, runs the reverseTransaction or refundTransaction GraphQL mutation. Store the Public Key and Private Key in Firebase Functions config or Supabase secrets — not in the FlutterFlow API Call headers.

index.js
1// Firebase Cloud Function: Braintree proxy (Node.js)
2const functions = require('firebase-functions');
3const axios = require('axios');
4const cors = require('cors')({ origin: true });
5const express = require('express');
6const app = express();
7app.use(cors);
8app.use(express.json());
9
10const BT_GRAPHQL = 'https://payments.braintree-api.com/graphql';
11// For sandbox: 'https://payments.sandbox.braintree-api.com/graphql'
12
13function btHeaders() {
14 const publicKey = functions.config().braintree.public_key;
15 const privateKey = functions.config().braintree.private_key;
16 const credentials = Buffer.from(`${publicKey}:${privateKey}`).toString('base64');
17 return {
18 Authorization: `Basic ${credentials}`,
19 'Content-Type': 'application/json',
20 'Braintree-Version': '2019-01-01',
21 };
22}
23
24// 1. Generate client token
25app.post('/client-token', async (req, res) => {
26 const query = `mutation { createClientToken { clientToken } }`;
27 const response = await axios.post(BT_GRAPHQL, { query }, { headers: btHeaders() });
28 res.json({ token: response.data.data.createClientToken.clientToken });
29});
30
31// 2. Create sale from nonce
32app.post('/create-sale', async (req, res) => {
33 const { nonce, amount } = req.body;
34 const query = `
35 mutation ChargePaymentMethod($input: ChargePaymentMethodInput!) {
36 chargePaymentMethod(input: $input) {
37 transaction { id status }
38 }
39 }`;
40 const variables = {
41 input: {
42 paymentMethodId: nonce,
43 transaction: { amount },
44 },
45 };
46 const response = await axios.post(BT_GRAPHQL, { query, variables }, { headers: btHeaders() });
47 const tx = response.data.data.chargePaymentMethod.transaction;
48 res.json({ transaction_id: tx.id, status: tx.status });
49});
50
51exports.braintree = functions.https.onRequest(app);

Pro tip: The Braintree sandbox GraphQL endpoint is https://payments.sandbox.braintree-api.com/graphql (note 'sandbox' in the subdomain). The live endpoint is https://payments.braintree-api.com/graphql. Switch via an environment variable rather than hardcoding.

Expected result: The proxy is deployed. Calling /client-token returns a Base64 client token string. Calling /create-sale with a sandbox nonce (generated by a test Drop-in UI) returns a transaction ID with status SUBMITTED_FOR_SETTLEMENT.

4

Wire FlutterFlow API Calls to the proxy and collect the nonce

In FlutterFlow, click API Calls in the left navigation panel → + Add → Create API Group. Name it BraintreeProxy. Set the Base URL to your deployed Firebase Cloud Function or Supabase Edge Function URL. Add two API Calls: GetClientToken (POST /client-token, no body variables needed) and CreateSale (POST /create-sale, body variables: nonce [String], amount [String]). In Response & Test for GetClientToken, paste { "token": "eyJy...base64..." } and generate paths to get $.token. For CreateSale, paste { "transaction_id": "abc123", "status": "SUBMITTED_FOR_SETTLEMENT" } and generate paths. To collect the nonce from the user (their card details), the simplest path in a custom flow is to use Braintree's Drop-in UI by adding an in-app WebView that loads a small hosted HTML page you control (hosted on Firebase Hosting or Supabase Storage) that renders the Braintree Drop-in UI using their JavaScript SDK, then returns the nonce back to the app via a deep link or JavaScript bridge. Alternatively, for simple card entry without a native SDK, use the client token with Braintree's hosted fields — a more advanced pattern. For most FlutterFlow builders, the native Payments toggle (Step 2) plus the proxy for back-end operations (refunds, reporting) is the right split: use native for collecting payment, use the proxy API Calls for everything after.

typescript
1// Sample responses for FlutterFlow API Call → Response & Test:
2
3// GetClientToken:
4{ "token": "eyJ2ZXJzaW9uIjoyLCJhdXRob3JpemF0aW9uRmluZ2VycHJpbnQiOi..." }
5
6// JSON Path: $.token
7
8// CreateSale:
9{
10 "transaction_id": "dj3fhk",
11 "status": "SUBMITTED_FOR_SETTLEMENT"
12}
13// JSON Paths:
14// $.transaction_id
15// $.status

Pro tip: Store the transaction_id returned by CreateSale in Firestore or Supabase alongside the order record. You need this ID to issue refunds via Braintree's refundTransaction mutation — the ID is your only reference to the original charge.

Expected result: API Calls for GetClientToken and CreateSale appear under BraintreeProxy in FlutterFlow, each with auto-detected JSON paths. Triggering GetClientToken from a test button in Run Mode returns a client token from the deployed proxy.

5

Test with sandbox cards and switch to the live gateway

Braintree sandbox accepts specific test card numbers. Use 4111 1111 1111 1111 (Visa, always succeeds), 4000 1111 1111 1112 (Visa, always fails with processor declined), and 4000 0000 0000 0002 (Visa, CVV verification failure) to exercise success and error paths. Any future expiry date and any 3-digit CVV work with these sandbox numbers. In the Braintree Control Panel sandbox, go to Transactions to see each test charge appear in near-real-time. Test your proxy endpoints: call /client-token, then use the returned token to simulate a nonce (Braintree sandbox provides 'fake-valid-nonce' and 'fake-processor-declined-nonce' as hardcoded test nonces for direct proxy testing without a UI). Pass these test nonces to /create-sale and confirm you see the transaction in the Control Panel. For PayPal testing: the sandbox PayPal button opens a fake PayPal login — use the sandbox buyer account credentials from your Braintree Control Panel under Sandbox → Test Accounts. Once all flows are validated in sandbox, switch to live by: (1) creating or activating your live Braintree merchant account at braintreegateway.com; (2) updating Firebase config with your live Public Key, Private Key, and Merchant ID; (3) changing the GraphQL endpoint from sandbox to live in the proxy; (4) updating FlutterFlow's Payments settings with your live Merchant ID and Public Key. If you want RapidDev to handle the proxy setup and testing for you, free scoping call at rapidevelopers.com/contact.

typescript
1// Braintree sandbox test card numbers:
2// 4111 1111 1111 1111 — Visa, always succeeds
3// 4000 1111 1111 1112 — Visa, processor declined
4// 4000 0000 0000 0002 — Visa, CVV verification failure
5// Any future expiry (e.g. 12/30), any CVV (e.g. 123)
6
7// Hardcoded sandbox test nonces for direct proxy testing:
8// fake-valid-nonce — succeeds
9// fake-processor-declined-nonce — fails with processor_declined
10// fake-paypal-one-time-nonce — PayPal one-time payment
11
12// Switch environments via Firebase config:
13// firebase functions:config:set braintree.env="live" braintree.public_key="live_pub" braintree.private_key="live_priv"

Pro tip: Before switching to live mode, run at least one real card transaction with a small amount ($1.00) using your own live card to confirm the end-to-end flow works correctly in production. Then void or refund that transaction from the Braintree Control Panel.

Expected result: Sandbox test cards trigger expected success and failure responses in the proxy and appear in the Braintree Control Panel. The PayPal sandbox button (after enabling PayPal in Control Panel) shows a fake PayPal login that completes successfully. Live gateway responds correctly to a $1.00 test charge with a real card.

Common use cases

Subscription box app that charges monthly via saved payment methods

Customers subscribe to a monthly box through the FlutterFlow app. Braintree vaults their card on first checkout. A Firebase scheduled Cloud Function runs on the 1st of each month, calls Transaction.sale for each active subscriber using their vaulted payment method token, and writes the result to Firestore. The app shows a 'Next charge' date and payment history.

FlutterFlow Prompt

Build a subscription screen where users enter their card once. Each month, automatically charge them without asking for the card again. Show them their subscription status and last charge date.

Copy this prompt to try it in FlutterFlow

Event ticketing app with PayPal checkout option

Users browse events in the FlutterFlow app, select a ticket tier, and choose to pay by card or PayPal. Braintree handles both — the PayPal button appears in the Drop-in UI after PayPal is enabled in the Braintree sandbox. The backend creates the Transaction.sale and writes the booking to Firestore on success.

FlutterFlow Prompt

Let users buy event tickets and choose Card or PayPal at checkout. Show a confirmation screen with the booking details after payment succeeds.

Copy this prompt to try it in FlutterFlow

On-demand services app with partial refund support

Service providers use the app to accept payment on job completion. If a customer is unsatisfied, the admin can issue a partial refund from a dashboard screen. The refund calls the Braintree API's GraphQL refundTransaction mutation server-side — the original transaction ID is stored in Firestore alongside the booking record.

FlutterFlow Prompt

Add a 'Refund' button for admin users on the booking detail screen. When tapped, issue a partial refund (specified amount) for the original transaction and update the booking status to 'Refunded'.

Copy this prompt to try it in FlutterFlow

Troubleshooting

PayPal button never appears in the Braintree Drop-in UI

Cause: PayPal is not enabled in the Braintree Control Panel under Processing → Payment Methods. The Drop-in UI only shows PayPal as an option if it is explicitly enabled on the merchant account.

Solution: Log in to your Braintree Control Panel (sandbox.braintreegateway.com for sandbox). Go to Processing → Payment Methods → PayPal, and click Enable. For sandbox, choose the sandbox PayPal configuration. For live, you'll need to link a PayPal Business account. After enabling, the Drop-in UI will show the PayPal button on the next app load.

Proxy returns 401 Unauthorized from Braintree GraphQL

Cause: The Basic Auth header is incorrect — either the Public Key and Private Key are swapped, the credentials are from the wrong environment (sandbox vs live), or the Base64 encoding is wrong.

Solution: The Basic Auth string must be Base64 of 'publicKey:privateKey' (colon-separated, Public Key first). In Node.js: Buffer.from(`${publicKey}:${privateKey}`).toString('base64'). Confirm the keys are sandbox keys if you're hitting payments.sandbox.braintree-api.com, and live keys for payments.braintree-api.com. Check your Firebase Functions config values are set correctly.

Transaction returns status PROCESSOR_DECLINED instead of SUBMITTED_FOR_SETTLEMENT

Cause: The test card number triggers a decline simulation, or in live mode, the card was actually declined by the issuing bank.

Solution: In sandbox, use 4111 1111 1111 1111 for a successful charge. Decline outcomes are deterministic by card number in Braintree sandbox — check Braintree's testing documentation for the full list of decline-triggering numbers. In live mode, a PROCESSOR_DECLINED means the bank declined the charge — display a user-friendly message and prompt the user to try a different card.

Mixing sandbox credentials against the live gateway (or vice versa) causes auth failure

Cause: Sandbox credentials (Merchant ID, Public Key, Private Key from sandbox.braintreegateway.com) only work against sandbox endpoints. Live credentials only work against live endpoints. Using the wrong credentials for the endpoint always returns an authentication error.

Solution: Use an environment variable in your proxy to toggle between sandbox and live configuration. Set all three credentials (Merchant ID, Public Key, Private Key) together as a unit — never mix a sandbox Public Key with a live Private Key. Update FlutterFlow's Payments settings with the live Merchant ID and Public Key only after the proxy is switched to live.

Best practices

  • Use FlutterFlow's native Braintree Payments toggle for simple checkout flows — it is the fastest path and requires no custom code.
  • Never place your Braintree Private Key in a FlutterFlow API Call header or Custom Action — route all sale and refund calls through a Firebase Cloud Function or Supabase Edge Function.
  • Enable PayPal in the Braintree Control Panel (Processing → Payment Methods → PayPal) before expecting the PayPal button to appear in the Drop-in UI — it is off by default.
  • Use Braintree's GraphQL API (payments.braintree-api.com/graphql) for all new custom integrations rather than the legacy REST SDK — it is Braintree's recommended path and has richer query capabilities.
  • Store transaction IDs (returned by chargePaymentMethod) in Firestore or Supabase alongside the order — you need these to issue refunds via refundTransaction later.
  • Test with specific sandbox card numbers (4111 1111 1111 1111 = success, 4000 1111 1111 1112 = decline) and the hardcoded sandbox nonces (fake-valid-nonce, fake-processor-declined-nonce) before testing with a real UI.
  • Switch environment entirely when moving from sandbox to live — use live Merchant ID, Public Key, Private Key, and the live GraphQL endpoint together as a unit; never mix sandbox and live credentials.
  • For vaulted cards (repeat customers), store the Braintree payment method token server-side in Firestore/Supabase keyed to the user ID — never send vaulted payment method tokens to the Flutter client.

Alternatives

Frequently asked questions

What is the difference between FlutterFlow's native Braintree integration and building a custom Braintree proxy?

The native integration (Settings & Integrations → Payments → Braintree) sets up a pre-built Drop-in UI for one-off charges with minimal configuration. It handles the client-side payment collection and basic transaction processing without any code. The custom proxy approach gives you full control: you can issue refunds, vault payment methods for subscription billing, accept PayPal-via-Braintree, read transaction history, and run custom server-side logic. Most apps start with the native path and add the proxy only when they hit its limits.

Can I use Braintree to accept PayPal in a FlutterFlow app?

Yes, PayPal checkout is available through Braintree — Braintree is a PayPal company and has native PayPal support. In the native FlutterFlow integration, PayPal appears as a button in the Braintree Drop-in UI after you enable it in your Braintree Control Panel (Processing → Payment Methods → Enable PayPal). Without enabling it there first, the PayPal button will not appear regardless of app settings.

Does Braintree support subscriptions and recurring billing?

Yes. Braintree has a built-in subscription system (Braintree Subscriptions) that you manage through the Braintree API. Vault the customer's payment method on first charge (the vault call returns a paymentMethodToken), then use that token with the subscription API to create recurring plans. All subscription management runs server-side in your Firebase or Supabase proxy — FlutterFlow queries your backend for subscription status rather than calling Braintree directly.

Is Braintree cheaper than Stripe for processing payments?

Standard US card rates are the same for both: 2.9% + 30¢ per transaction. The actual cost difference comes from volume discounts, international cards, and additional products. For marketplace or Connect-equivalent features, Stripe's Connect fees stack on top, while Braintree has its own pricing for marketplace flows. Check both providers' current pricing pages and negotiate volume discounts once your transaction volume justifies it.

How do I issue a refund through Braintree from the FlutterFlow app?

Refunds must run server-side. Add a POST /refund-transaction endpoint to your Firebase or Supabase proxy that accepts a transaction_id and optional amount. The proxy calls Braintree's GraphQL refundTransaction mutation with your credentials. In FlutterFlow, add an API Call to BraintreeProxy targeting this endpoint, bind the transaction_id from the relevant Firestore or Supabase order record, and wire it to a 'Refund' button visible to admin users. Never call Braintree's refund endpoint directly from the Flutter client — it requires your Private Key.

My test charge says 'Processor Declined' even though I'm in sandbox mode. Why?

Braintree sandbox uses card numbers to simulate specific outcomes. Only specific numbers succeed — most notably 4111 1111 1111 1111 (always succeeds). If you're using a different number, check Braintree's official testing documentation for the list of sandbox card numbers and their simulated outcomes. For proxy testing without a UI, use the hardcoded sandbox nonce fake-valid-nonce in the nonce field of your /create-sale request body.

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.