Connect FlutterFlow to Square using an API Call group against connect.squareup.com/v2 — but the secret access token must never sit in a client header, so card tokenization is handled by the square_in_app_payments Custom Action (or a Web Payments Custom Widget), and the final charge is processed by a Firebase Cloud Function. Always include an Idempotency-Key to prevent double charges.
| Fact | Value |
|---|---|
| Tool | Square |
| Category | Payments |
| Method | FlutterFlow API Call |
| Difficulty | Advanced |
| Time required | 90 minutes |
| Last updated | July 2026 |
Square in FlutterFlow: Tokenize First, Charge Server-Side
Unlike Stripe, which FlutterFlow supports natively, Square has no built-in integration — no toggle in Settings, no auto-deployed Cloud Function. Instead you build it yourself using three coordinated pieces: a card tokenization step on the client, a secure charge step on the server, and an API Call in FlutterFlow to connect them.
The security constraint is fundamental. FlutterFlow compiles to a Flutter app that runs on the user's device. Any credential placed in an API Call header — including Square's access token — ships inside the app bundle and can be extracted by anyone who downloads your app. Square's access token is powerful: it can create payments, issue refunds, and read your entire transaction history. It must stay on a server. The solution is a Firebase Cloud Function that holds the token in its environment variables and receives a minimal, safe payload from the FlutterFlow app: a card nonce (a short-lived, single-use token representing the customer's card) and an amount. The function then calls Square's API directly.
Card tokenization is the first technical challenge. Square provides a native SDK (available as the square_in_app_payments pub.dev package) for iOS and Android that presents a card entry UI, validates the card, and returns a nonce. In FlutterFlow, you add this package as a Custom Action dependency, write a small Dart function that calls the SDK's payment flow, and use the returned nonce as input to your Cloud Function API Call. For web builds, the Square Web Payments SDK runs in a browser context that's better handled in a Custom Widget WebView. Square offers a separate Sandbox environment at connect.squareupsandbox.com/v2 with dedicated test credentials — never mix sandbox and production tokens, as they return 401 UNAUTHORIZED.
Square charges 2.6% + 10¢ for in-person and 2.9% + 30¢ for online payments with no monthly fee, and the developer sandbox is free to use immediately after creating a Square Developer account.
Integration method
Square has no native FlutterFlow support, so you build it in three parts: a square_in_app_payments Custom Action (or WebView Custom Widget) tokenizes the customer's card into a short-lived nonce, a Firebase Cloud Function holds your Square access token and calls POST /v2/payments with that nonce and a unique Idempotency-Key, and a FlutterFlow API Call hits your Cloud Function URL — not Square's servers directly. The tokenize-then-proxy pattern is mandatory because the Square access token is a secret credential that must not compile into the Flutter app bundle.
Prerequisites
- A Square Developer account at developer.squareup.com — sandbox is available immediately after signup
- Sandbox Application ID, sandbox access token, and sandbox location ID from the Square Developer Console
- A Firebase project on the Blaze (pay-as-you-go) plan for Cloud Functions
- A FlutterFlow project with Firebase connected (Settings → Firebase)
- Basic familiarity with Firebase Cloud Functions (Node.js) — you'll deploy a small proxy function
Step-by-step guide
Get Square sandbox credentials and locate your location ID
Every Square payment requires three pieces: an Application ID (identifies your app), an Access Token (authorizes the API call), and a Location ID (identifies the business location where the sale is made). In the Square Developer Console at developer.squareup.com, log in and create a new application or select an existing one. Navigate to the Credentials tab. You'll see two panels side by side: Sandbox and Production. Start with Sandbox. Copy the Sandbox Application ID (starts with 'sandbox-sq0idb-...') — this is safe to place in FlutterFlow as an App State constant. Copy the Sandbox Access Token (starts with 'EAAAl...' in sandbox) — this must go in your Cloud Function environment, NEVER in FlutterFlow. Keep it in a secure note for the next step. Now find your Location ID. In the Square Developer Console, click the Locations tab. Every Square account has at least one location; it represents a physical store or online presence. Copy the Location ID for your sandbox location (starts with 'L...'). Store this in FlutterFlow as a constant App Value (App Settings → App Values) — it's not a secret, just a configuration value. Jot down the sandbox base URL: https://connect.squareupsandbox.com/v2. The production base URL is https://connect.squareup.com/v2. Both return 401 UNAUTHORIZED if you mix tokens across environments — it's the single most common setup error.
Pro tip: Pin the Square API version by adding a Square-Version header (e.g., '2024-02-22') to your Cloud Function requests. Square deprecates old versions and will warn you via email before breaking changes — pinning prevents surprise failures after an auto-upgrade.
Expected result: You have Sandbox Application ID (safe, goes in FlutterFlow), Sandbox Access Token (secret, goes in Cloud Function), and Sandbox Location ID (safe, goes in FlutterFlow App Values) ready.
Add the square_in_app_payments Custom Action for card tokenization
Card tokenization is what turns a customer's raw card number into a safe, short-lived nonce that you can send to your server. The square_in_app_payments pub.dev package provides a native iOS and Android card entry UI managed entirely by Square — your code never sees the raw card data, which keeps you out of direct PCI DSS scope. In FlutterFlow, go to Custom Code in the left nav (code icon) → click '+ Add' → select 'Action'. Name it 'SquareCardTokenize'. In the Dependencies field, type 'square_in_app_payments' and accept the latest stable version (check pub.dev for the current version — it's in the 2.x range as of 2026). Add a second dependency: 'square_reader_sdk' is NOT needed; only 'square_in_app_payments' is required for in-app card entry. In the Return Value section, set the return type to String (the nonce). In the Arguments section, add one argument: `applicationId` (String). Paste the Dart code below into the code editor. The function initializes the Square SDK with your Application ID, calls the built-in card entry flow, and returns the nonce on success or throws an error on cancellation. Save the action. IMPORTANT: Custom Actions using native SDKs like this one do NOT work in FlutterFlow's web Run mode preview. You must test on a real device via the Companion app or a downloaded build. Web builds require a different approach (Custom Widget WebView with the Square Web Payments JavaScript SDK) — this action is for iOS/Android only.
1import 'package:square_in_app_payments/square_in_app_payments.dart';2import 'package:square_in_app_payments/models.dart';34Future<String> squareCardTokenize(String applicationId) async {5 // Initialize the Square SDK with your Application ID6 await SquareInAppPayments.setSquareApplicationId(applicationId);78 // Completer to bridge the callback-based SDK to async/await9 final completer = Completer<String>();1011 await SquareInAppPayments.startCardEntryFlow(12 onCardNonceRequestSuccess: (CardDetails result) async {13 // Close the card entry UI and return the nonce14 await SquareInAppPayments.completeCardEntry(15 onCardEntryComplete: () {16 completer.complete(result.nonce);17 },18 );19 },20 onCardEntryCancel: () {21 completer.completeError(Exception('User cancelled card entry'));22 },23 );2425 return completer.future;26}Pro tip: If you need to support web as well as mobile, build a Custom Widget that loads Square's Web Payments JavaScript SDK in a WebView — the native SDK won't run in the browser renderer. See Square's developer docs for the JS SDK setup.
Expected result: The SquareCardTokenize Custom Action appears in Custom Code with a return type of String. No compilation errors in the code editor.
Deploy a Firebase Cloud Function as the secure payment proxy
Your Cloud Function receives the nonce from FlutterFlow, attaches the Square access token and Idempotency-Key, and calls POST /v2/payments. The Idempotency-Key is critical: if the network drops after Square processes the charge but before your app receives the response, a retry without the key creates a double charge. Generate a UUID on the client (FlutterFlow custom function), pass it to the Cloud Function, and Square deduplicates the charge for up to 45 days. In the Firebase Console, go to Build → Functions → Get Started (follow the setup wizard if first time). In your terminal, navigate to your Firebase project's `functions/` directory and open `index.js`. Paste the code below. Set the environment variable before deploying: `firebase functions:config:set square.access_token='YOUR_SANDBOX_ACCESS_TOKEN' square.location_id='YOUR_SANDBOX_LOCATION_ID'`. Then run `firebase deploy --only functions:processSquarePayment`. After deployment, the Firebase Console shows your function URL: something like `https://us-central1-YOUR_PROJECT_ID.cloudfunctions.net/processSquarePayment`. Copy this URL — you'll paste it into the FlutterFlow API Call in the next step. For production, set the production access token and location ID via the same config command with your production values, and update the base URL in the function code to `https://connect.squareup.com/v2`.
1const functions = require('firebase-functions');2const axios = require('axios');3const { v4: uuidv4 } = require('uuid');45exports.processSquarePayment = functions.https.onRequest(async (req, res) => {6 // CORS for FlutterFlow web builds (not needed for mobile)7 res.set('Access-Control-Allow-Origin', '*');8 if (req.method === 'OPTIONS') {9 res.set('Access-Control-Allow-Methods', 'POST');10 res.set('Access-Control-Allow-Headers', 'Content-Type');11 return res.status(204).send('');12 }1314 const { nonce, amountMoney, idempotencyKey } = req.body;1516 if (!nonce || !amountMoney || !idempotencyKey) {17 return res.status(400).json({ error: 'Missing required fields' });18 }1920 const accessToken = functions.config().square.access_token;21 const locationId = functions.config().square.location_id;22 // Switch to https://connect.squareup.com/v2 for production23 const baseUrl = 'https://connect.squareupsandbox.com/v2';2425 try {26 const response = await axios.post(27 `${baseUrl}/payments`,28 {29 source_id: nonce,30 idempotency_key: idempotencyKey,31 amount_money: amountMoney, // e.g. { amount: 1000, currency: 'USD' }32 location_id: locationId,33 },34 {35 headers: {36 'Authorization': `Bearer ${accessToken}`,37 'Content-Type': 'application/json',38 'Square-Version': '2024-02-22',39 },40 }41 );4243 return res.status(200).json({44 status: response.data.payment.status,45 paymentId: response.data.payment.id,46 });47 } catch (error) {48 const errData = error.response?.data || { error: error.message };49 return res.status(error.response?.status || 500).json(errData);50 }51});Pro tip: Include 'axios' and 'uuid' as dependencies in your functions/package.json. Run 'npm install' inside the functions/ directory before deploying.
Expected result: Firebase Console shows processSquarePayment as 'Deployed'. You have the function URL ready.
Create the FlutterFlow API Call pointing at your Cloud Function
Now connect FlutterFlow to the Cloud Function. In the left nav, click API Calls → + Add → Create API Group. Name the group 'SquarePayments'. Set the base URL to your Cloud Function URL (the full URL up to but NOT including the path, e.g. https://us-central1-YOUR_PROJECT_ID.cloudfunctions.net). Leave Authentication as 'None' — the authentication is handled inside the Cloud Function via Square's access token. Inside the group, click '+ Add API Call'. Name it 'ProcessPayment'. Set the Method to POST and the endpoint path to '/processSquarePayment'. In the Body tab, set the body type to JSON and add three variables: - `nonce` (String) — the card nonce from the Custom Action - `amountMoney` (Object) — JSON object with `amount` (integer, in cents) and `currency` (string, e.g. 'USD') - `idempotencyKey` (String) — a UUID generated by a Custom Function The body template should be: ``` { "nonce": "{{ nonce }}", "amountMoney": {{ amountMoney }}, "idempotencyKey": "{{ idempotencyKey }}" } ``` In the Response & Test tab, paste a sample success response from your Cloud Function (you can get one by calling it manually with a test nonce from the Square sandbox). Click 'Generate JSON Paths' — FlutterFlow creates path variables like `status` mapped to `$.status` and `paymentId` mapped to `$.paymentId`. These are the values you'll use in your Action Flow. Click Test — FlutterFlow sends a real request. If you get a 400 or 500, check that your Cloud Function is deployed and the URL is correct. A successful test returns `{ "status": "COMPLETED", "paymentId": "..." }`.
1{2 "nonce": "{{ nonce }}",3 "amountMoney": {{ amountMoney }},4 "idempotencyKey": "{{ idempotencyKey }}"5}Pro tip: For the amountMoney variable in FlutterFlow, use a JSON string like '{"amount": 1000, "currency": "USD"}' and set the variable type to JSON — FlutterFlow passes it unquoted in the body template.
Expected result: API Call test in FlutterFlow returns status 200 with a JSON body containing 'status' and 'paymentId'. JSON Paths are generated for both fields.
Wire the full checkout flow in the Action Flow Editor
With all pieces ready — the Custom Action for tokenization and the API Call to the Cloud Function — connect them in your checkout button's Action Flow. Select your 'Pay' button → Actions panel → + Add Action. Step 1: Add a 'Custom Action' action and select SquareCardTokenize. Set the `applicationId` argument to your Sandbox Application ID constant from App Values. Set the output variable name, e.g. `cardNonce`, using the 'Define Output Variable' option. This action blocks until the user enters card details and taps 'Pay' in the Square card entry UI, then returns the nonce string. Step 2: After the Custom Action, add an 'Update App State' action to store the nonce in a page state variable (String) named `squareNonce`. This makes it available to the next action without chaining variable outputs directly (FlutterFlow handles chaining, but explicit state is clearer to debug). Step 3: Add a 'Backend/API Call' action. Select SquarePayments → ProcessPayment. Bind the variables: `nonce` → page state `squareNonce`, `idempotencyKey` → a UUID generated by a custom function (use Dart's `const Uuid().v4()` in a tiny custom function returning String), `amountMoney` → set as a static JSON string or bind to page state if the amount is user-defined. Step 4: After the API Call, add a Conditional action checking `response.status == 'COMPLETED'`. On TRUE: write to Firestore ('orders' collection with userId, paymentId, amount, status 'paid', createdAt) then navigate to confirmation. On FALSE: show a Snack Bar with 'Payment failed — please try a different card'. Test the entire flow on device with Square's sandbox test nonces or by running the card entry UI and entering a test card from Square's sandbox documentation.
Pro tip: If you get a 'MissingPluginException' when the SquareCardTokenize action runs, it means the FlutterFlow Companion app doesn't have the square_in_app_payments plugin compiled in. Download a local build (FlutterFlow → Developer Tools → Download Code) and run it from Xcode or Android Studio instead.
Expected result: Tapping 'Pay' opens Square's native card entry UI, a successful test charge returns COMPLETED status, and a Firestore order document is created with the paymentId.
Switch to sandbox test cards and validate, then prepare for production
Square's sandbox includes specific test card numbers that simulate different outcomes. Use these in the card entry UI opened by the Custom Action: - **Successful charge**: any Visa card number (e.g., 4111 1111 1111 1111), any future expiry, any CVV - **Declined card**: 4000 0000 0000 0002 - **Insufficient funds**: 4000 0000 0000 9995 Verify that each scenario triggers the correct FlutterFlow Action Flow branch. Check the Square Developer Console → Sandbox → Payments to see the test transactions listed. Also test the Idempotency-Key behavior: submit the same payment twice with the same key — Square should return the same paymentId (deduplication) rather than charging twice. Check the Developer Console; you should see one payment, not two. For production readiness: in the Firebase Cloud Function, change the `baseUrl` variable from `https://connect.squareupsandbox.com/v2` to `https://connect.squareup.com/v2`, and update the Firebase config to use your production access token and location ID. In FlutterFlow App Values, update the Application ID constant to your production Application ID. Run `firebase deploy --only functions:processSquarePayment` to redeploy with production settings. Important: Square also offers Webhooks (in the Square Dashboard → Webhooks) to receive server-side payment notifications for `payment.completed` and `payment.failed` events. Register a webhook pointing to a second Firebase Cloud Function to handle reconciliation and order status updates asynchronously — do not rely solely on the app-level API Call response for order fulfillment.
Pro tip: Store the Idempotency-Key in Firestore alongside the order document. If a user retries a failed payment, generate a new UUID — only use the same key to retry a request that you believe Square received but didn't respond to (network timeout scenario).
Expected result: Sandbox test charges appear in Square Developer Console. Declined cards trigger the FALSE branch. Idempotency-Key deduplication works (one charge, not two). Production function deployed.
Common use cases
Retail pop-up or market stall app with card processing
Build a FlutterFlow point-of-sale app for a market vendor or pop-up shop. The seller selects items, the customer taps 'Pay', the square_in_app_payments Custom Action presents a card entry UI, the nonce is sent to a Cloud Function, and Square creates the charge. Receipts are emailed automatically by Square.
A simple POS app for a farmers market vendor: item list with prices, tap to add to cart, 'Charge' button that opens Square's card entry UI and processes payment — receipt sent to customer's email.
Copy this prompt to try it in FlutterFlow
Service booking app with Square checkout
A salon or tutoring app where customers book appointments and pay online. The app collects the card nonce through a Square Web Payments Custom Widget WebView, a Cloud Function charges the card, and a Firestore document records the booking + payment status.
A tutoring booking app where students select a session, enter card details via Square's secure form, and pay $50 per hour — booking confirmed only after Square returns COMPLETED status.
Copy this prompt to try it in FlutterFlow
Non-profit donation kiosk on tablet
A donation kiosk FlutterFlow app running on an iPad at a charity event. The donor enters an amount, taps 'Donate', and the square_in_app_payments SDK handles card entry. The Cloud Function creates a Square payment with a donation description. Square's receipt emails are configured in the Square Dashboard.
A tablet kiosk app where donors choose a preset amount ($10, $25, $50) or type a custom amount, enter their card, and see a 'Thank you!' screen after the charge succeeds.
Copy this prompt to try it in FlutterFlow
Troubleshooting
401 UNAUTHORIZED returned by the Cloud Function when calling Square
Cause: The sandbox access token is being used against the production URL (connect.squareup.com/v2) or vice versa — sandbox and production tokens are entirely separate and non-interchangeable.
Solution: Verify that the baseUrl in your Cloud Function matches the environment your access token belongs to. Sandbox token → https://connect.squareupsandbox.com/v2. Production token → https://connect.squareup.com/v2. Check the Firebase config with 'firebase functions:config:get' to confirm the token stored there matches the environment.
Double charge on retry — customer billed twice for the same order
Cause: The Idempotency-Key was missing from the POST /v2/payments request, or a new UUID was generated on each retry instead of reusing the key from the first attempt.
Solution: Generate the idempotency key ONCE per order (before the first payment attempt) and store it in Firestore alongside the order document. On retry, read the key from Firestore and pass the same value. Square deduplicates charges with the same Idempotency-Key for 45 days.
MissingPluginException when SquareCardTokenize Custom Action runs
Cause: The FlutterFlow Companion app doesn't include the square_in_app_payments native plugin because it was compiled without your custom dependencies. Custom Actions using native plugins can't run inside the precompiled Companion app.
Solution: Download your FlutterFlow project code (Developer Tools → Download Code → Flutter Project) and run it from Xcode or Android Studio on a simulator or real device. This compiles all your custom dependencies, including square_in_app_payments, into the app.
Card entry UI does not appear / crashes immediately on web builds
Cause: The square_in_app_payments package is a native-only plugin (iOS + Android) and does not work in web builds or in FlutterFlow's web Test/Run preview.
Solution: For web builds, replace the Custom Action with a Custom Widget that loads the Square Web Payments JavaScript SDK inside a WebView. The web SDK uses a different tokenization flow (JavaScript-based) and requires the Square Application ID to initialize. Mobile and web paths must be handled separately in your FlutterFlow Action Flow using a Platform-specific condition.
Best practices
- Never put the Square access token in a FlutterFlow API Call header — it compiles into the app bundle and can be extracted; always proxy charges through a Firebase Cloud Function.
- Generate a UUID Idempotency-Key once per order (before the first attempt) and reuse it on retries — Square deduplicates for 45 days, preventing double charges.
- Pin the Square-Version header in your Cloud Function (e.g., '2024-02-22') to avoid automatic breaking changes when Square releases API updates.
- Store the Square location_id as a FlutterFlow App Value (constant) — it's not a secret and can be referenced across multiple screens without hitting App State.
- Always test sandbox and production with their respective base URLs and tokens — a common deploy mistake is swapping these and getting silent 401 errors in production.
- Register a Square Webhook (payment.completed event) pointing to a Firebase Cloud Function for server-side order confirmation — never trust the app-level API Call response alone for order fulfillment.
- For web builds, implement the Square Web Payments JavaScript SDK in a Custom Widget WebView — the native square_in_app_payments package is iOS/Android only.
- Show a loading spinner while the Cloud Function processes the charge — Square's API typically responds in under 2 seconds, but network latency plus Cold Start times can push this to 5+ seconds on first call.
Alternatives
Stripe is natively supported in FlutterFlow with a built-in Payment action — choose Stripe over Square if you want a zero-custom-code checkout that just works, especially for digital goods and SaaS.
Braintree is the other natively supported processor in FlutterFlow and includes PayPal alongside card payments — better than Square if your customers expect a PayPal checkout option.
Adyen is a better fit than Square for multi-currency enterprise commerce with negotiated rates, though it also requires a custom API Call setup in FlutterFlow.
Frequently asked questions
Can I accept Square payments in a FlutterFlow web app?
Yes, but not with the same Custom Action used for mobile. The square_in_app_payments package is native iOS/Android only. For web builds, you need to create a Custom Widget that embeds the Square Web Payments JavaScript SDK in a WebView. The web SDK handles card tokenization via JavaScript and works in browser-based Flutter web apps. Mobile and web will have separate code paths in your Action Flow.
Does Square support in-person card readers from a FlutterFlow app?
Square has a Reader SDK (square_reader_sdk on pub.dev) for Bluetooth card readers used in in-person POS apps. You'd implement it as a separate Custom Action alongside the in-app payments package. Note that Reader SDK has additional approval requirements from Square — you need to apply for Reader SDK access through the Square Developer Portal before you can use it. The charge flow is the same: tokenize via Reader SDK → proxy to Cloud Function → POST /v2/payments.
What happens if the Cloud Function times out mid-payment?
This is exactly why the Idempotency-Key exists. If your Cloud Function times out after Square processes the charge but before it returns a response, a retry with the same key won't create a second charge — Square returns the original payment record. Set your Cloud Function timeout to at least 30 seconds (default is 60s) in the Firebase Console to give the Square API request enough time to complete.
Can FlutterFlow receive Square webhooks directly?
No — FlutterFlow apps can't listen for incoming webhook events. Your webhook endpoint must be a Firebase Cloud Function (or Supabase Edge Function) that receives the Square event, verifies the signature using the webhook signature key from your Square Developer Console, and updates Firestore with the order status. If you'd rather skip the Cloud Function setup entirely, RapidDev's team builds FlutterFlow payment integrations like this every week — free scoping call at rapidevelopers.com/contact.
Why does my test charge show 'PENDING' instead of 'COMPLETED'?
Some Square sandbox test scenarios return PENDING status to simulate asynchronous payment processing (like ACH bank transfers). For card payments in sandbox, COMPLETED is the expected status. If you're seeing PENDING with a card nonce, check that you're using the correct sandbox test card number from Square's documentation, and that your payment request includes the correct source_id (nonce) rather than a saved card ID.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation