# How to Integrate Cryptocurrency Payments in FlutterFlow

- Tool: FlutterFlow
- Difficulty: Beginner
- Time required: 45-60 min
- Compatibility: FlutterFlow Free+
- Last updated: March 2026

## TL;DR

Accept cryptocurrency payments in FlutterFlow using Coinbase Commerce — a Cloud Function creates a charge via the Coinbase Commerce API, returns a hosted checkout URL, and your app launches it. Coinbase handles BTC, ETH, USDC, LTC, and 10+ other currencies. When payment is confirmed, Coinbase sends a webhook to a second Cloud Function that updates the order status in Firestore. Always wait for the charge:confirmed webhook (at least 1 blockchain confirmation) before fulfilling the order.

## Coinbase Commerce hosted checkout for Bitcoin, ETH, and USDC payments

Accepting cryptocurrency payments in FlutterFlow is simpler than most developers expect. Coinbase Commerce provides a hosted payment page that handles wallet connection, blockchain transaction monitoring, and confirmation — similar to how Stripe Checkout handles credit cards. Your FlutterFlow app calls a Cloud Function to create a payment charge, receives a hosted checkout URL, and launches it. Coinbase handles the rest. This tutorial covers the complete crypto payment flow: creating charges, launching the hosted checkout, handling webhook confirmations, and displaying transaction status. The advanced Web3 wallet-to-wallet approach (using the web3dart package) is also introduced for developers who need custom in-app payment UI.

## Before you start

- A Coinbase Commerce account at commerce.coinbase.com (free to create, requires business verification for live payments)
- A Firebase project connected to FlutterFlow with Blaze plan for Cloud Functions
- An existing checkout or purchase flow in your FlutterFlow app where you want to add crypto payment as an option
- Basic understanding of Firebase Cloud Functions and Firestore

## Step-by-step guide

### 1. Set up Coinbase Commerce and get your API key and webhook secret

Sign up at commerce.coinbase.com. After account setup, go to Settings → Security. Generate a new API Key — copy it securely, you will only see it once. Also copy the Webhook Secret displayed on the same page — this is used to verify that webhook requests actually come from Coinbase and not a third party. In Firebase Secret Manager (Google Cloud Console → Secret Manager), create two secrets: COINBASE_COMMERCE_API_KEY and COINBASE_COMMERCE_WEBHOOK_SECRET. Store the respective values. Never put these in FlutterFlow code or API Manager headers. Back in Coinbase Commerce, go to Settings → Notifications and add your webhook endpoint URL — you will create this Cloud Function in Step 3 and add the URL here. The webhook URL format will be: https://us-central1-your-project.cloudfunctions.net/coinbaseWebhook.

**Expected result:** Coinbase Commerce account has an API key and webhook secret stored in Firebase Secret Manager. Webhook endpoint URL is configured in Coinbase Commerce settings.

### 2. Create the Cloud Function to generate Coinbase Commerce charges

Create an HTTPS Cloud Function named createCryptoCharge. It receives a POST request from FlutterFlow with amount, currency (USD), productName, orderId, and userId. Using the Coinbase Commerce API, call POST https://api.commerce.coinbase.com/charges with headers X-CC-Api-Key and X-CC-Version: 2018-03-22. The request body includes name (product name), description, pricing_type: fixed_price, local_price: {amount, currency: USD}, and metadata: {orderId, userId}. The API returns a charge object with a hosted_url — return this URL to FlutterFlow. Also write an order document to Firestore: orders/{orderId} with status 'pending_payment', chargeCode, chargeId, amount, userId, createdAt. FlutterFlow will then launch the hosted_url using the Launch URL action.

```
const functions = require('firebase-functions');
const { defineSecret } = require('firebase-functions/params');
const admin = require('firebase-admin');
const axios = require('axios');

if (!admin.apps.length) admin.initializeApp();
const ccApiKey = defineSecret('COINBASE_COMMERCE_API_KEY');

exports.createCryptoCharge = functions
  .runWith({ secrets: ['COINBASE_COMMERCE_API_KEY'] })
  .https.onCall(async (data, context) => {
    if (!context.auth) throw new functions.https.HttpsError('unauthenticated', 'Login required');

    const { amount, productName, orderId } = data;
    const response = await axios.post(
      'https://api.commerce.coinbase.com/charges',
      {
        name: productName,
        description: `Payment for ${productName}`,
        pricing_type: 'fixed_price',
        local_price: { amount: String(amount), currency: 'USD' },
        metadata: { orderId, userId: context.auth.uid }
      },
      {
        headers: {
          'X-CC-Api-Key': ccApiKey.value(),
          'X-CC-Version': '2018-03-22',
          'Content-Type': 'application/json'
        }
      }
    );

    const charge = response.data.data;
    await admin.firestore().collection('orders').doc(orderId).set({
      chargeCode: charge.code,
      chargeId: charge.id,
      hostedUrl: charge.hosted_url,
      status: 'pending_payment',
      amount,
      userId: context.auth.uid,
      createdAt: admin.firestore.FieldValue.serverTimestamp()
    }, { merge: true });

    return { hostedUrl: charge.hosted_url, chargeCode: charge.code };
  });
```

**Expected result:** Cloud Function is deployed and callable from FlutterFlow. Calling it returns a Coinbase Commerce hosted checkout URL and creates a pending order in Firestore.

### 3. Wire the charge creation to a Pay with Crypto button in FlutterFlow

On your checkout or product page in FlutterFlow, add a Pay with Crypto button (use a Bitcoin or wallet icon from the icon library). In the button's Action Flow: first call the createCryptoCharge Cloud Function (Backend Call → Cloud Function → createCryptoCharge) with the amount from page state and a generated orderId (use the current timestamp as a simple unique ID). Store the returned hostedUrl in a Page State variable cryptoCheckoutUrl. Then add a Launch URL action using the cryptoCheckoutUrl variable. On the device, this opens Coinbase's hosted checkout page in the device's default browser. The hosted page shows a payment address and QR code for the user to send cryptocurrency. After payment, Coinbase redirects back to your app using the redirect_url if you configure one in the charge creation request.

**Expected result:** Tapping Pay with Crypto button creates a charge and opens the Coinbase Commerce hosted checkout page where the user can pay with any supported cryptocurrency.

### 4. Handle Coinbase Commerce webhooks to confirm payment

Create a second Cloud Function named coinbaseWebhook as an HTTP trigger. This function receives POST requests from Coinbase when payment status changes. Coinbase sends events: charge:pending (transaction detected on blockchain), charge:confirmed (enough confirmations, payment complete), charge:failed (transaction failed or expired). In the webhook function, verify the request signature: compute HMAC-SHA256 of the raw request body using the COINBASE_COMMERCE_WEBHOOK_SECRET, compare to the X-CC-Webhook-Signature header. If signatures don't match, return 400. If the event type is charge:confirmed, update the Firestore order: set status to 'paid', add txHash (from event.data.payments[0].transaction_id), network, and confirmedAt. If charge:failed, set status to 'payment_failed'.

```
const functions = require('firebase-functions');
const { defineSecret } = require('firebase-functions/params');
const admin = require('firebase-admin');
const crypto = require('crypto');

const webhookSecret = defineSecret('COINBASE_COMMERCE_WEBHOOK_SECRET');

exports.coinbaseWebhook = functions
  .runWith({ secrets: ['COINBASE_COMMERCE_WEBHOOK_SECRET'] })
  .https.onRequest(async (req, res) => {
    const signature = req.headers['x-cc-webhook-signature'];
    const rawBody = req.rawBody;
    const expected = crypto
      .createHmac('sha256', webhookSecret.value())
      .update(rawBody)
      .digest('hex');

    if (signature !== expected) {
      return res.status(400).send('Invalid signature');
    }

    const event = req.body;
    const orderId = event.data?.metadata?.orderId;

    if (event.type === 'charge:confirmed' && orderId) {
      const payment = event.data.payments?.[0];
      await admin.firestore().collection('orders').doc(orderId).update({
        status: 'paid',
        txHash: payment?.transaction_id || null,
        network: payment?.network || null,
        confirmedAt: admin.firestore.FieldValue.serverTimestamp()
      });
    } else if (event.type === 'charge:failed' && orderId) {
      await admin.firestore().collection('orders').doc(orderId).update({
        status: 'payment_failed'
      });
    }
    return res.status(200).send('OK');
  });
```

**Expected result:** Webhook function receives Coinbase events, verifies signatures, and updates order status in Firestore. FlutterFlow's real-time listener shows payment confirmation within seconds.

### 5. Display payment status with transaction hash and blockchain explorer link

In FlutterFlow, add a payment status page or modal that shows real-time order status. Add a Backend Query (real-time listener) on the orders/{orderId} document. Display status conditionally: if status is 'pending_payment', show a CircularProgressIndicator with text 'Waiting for payment...'. If status is 'paid', show a green checkmark, the transaction hash truncated (first 8 + last 8 characters), and a Launch URL button that opens the blockchain explorer: for Ethereum mainnet, the URL is https://etherscan.io/tx/{txHash}; for Bitcoin, https://www.blockchain.com/btc/tx/{txHash}. If status is 'payment_failed', show a red error state with a Retry button that creates a new charge. The real-time listener updates the UI within 1-3 seconds of the webhook firing.

**Expected result:** Payment status page updates automatically from pending to confirmed as the transaction processes. Transaction hash is displayed with a link to the blockchain explorer.

## Complete code example

File: `coinbase_webhook.js`

```javascript
// Cloud Function: coinbaseWebhook
// Receives Coinbase Commerce payment events
// Verifies webhook signature and updates Firestore order status

const functions = require('firebase-functions');
const { defineSecret } = require('firebase-functions/params');
const admin = require('firebase-admin');
const crypto = require('crypto');

if (!admin.apps.length) admin.initializeApp();
const webhookSecret = defineSecret('COINBASE_COMMERCE_WEBHOOK_SECRET');

exports.coinbaseWebhook = functions
  .runWith({ secrets: ['COINBASE_COMMERCE_WEBHOOK_SECRET'] })
  .https.onRequest(async (req, res) => {
    if (req.method !== 'POST') return res.status(405).send('Method Not Allowed');

    // Verify Coinbase signature
    const signature = req.headers['x-cc-webhook-signature'];
    const rawBody = req.rawBody || JSON.stringify(req.body);
    const computed = crypto
      .createHmac('sha256', webhookSecret.value())
      .update(rawBody)
      .digest('hex');

    if (!signature || signature !== computed) {
      console.error('Invalid webhook signature');
      return res.status(400).send('Invalid signature');
    }

    const event = req.body;
    const orderId = event.data?.metadata?.orderId;
    const db = admin.firestore();

    try {
      switch (event.type) {
        case 'charge:confirmed': {
          const payment = event.data.payments?.[0];
          await db.collection('orders').doc(orderId).update({
            status: 'paid',
            txHash: payment?.transaction_id || null,
            network: payment?.network || null,
            cryptoCurrency: payment?.value?.crypto?.currency || null,
            confirmedAt: admin.firestore.FieldValue.serverTimestamp()
          });
          console.log(`Order ${orderId} confirmed. TX: ${payment?.transaction_id}`);
          break;
        }
        case 'charge:failed':
          await db.collection('orders').doc(orderId).update({
            status: 'payment_failed',
            failedAt: admin.firestore.FieldValue.serverTimestamp()
          });
          break;
        case 'charge:pending':
          await db.collection('orders').doc(orderId).update({
            status: 'payment_pending_confirmation'
          });
          break;
        default:
          console.log(`Unhandled event type: ${event.type}`);
      }
    } catch (err) {
      console.error('Webhook processing error:', err);
      return res.status(500).send('Processing error');
    }

    return res.status(200).send('OK');
  });
```

## Common mistakes

- **Marking an order as paid on charge:pending instead of waiting for charge:confirmed** — charge:pending means a transaction has been broadcast to the blockchain but has zero confirmations. Zero-confirmation transactions can be reversed (double-spent) within seconds on some networks. Fulfilling an order at this point means you could deliver goods or services for a transaction that never gets confirmed and effectively never happened. Fix: Only update order status to 'paid' and fulfill the order on the charge:confirmed webhook event, which means at least 1 blockchain confirmation has been received. Coinbase Commerce handles the confirmation threshold per network (ETH is 1 confirmation, BTC is 3 confirmations by default). Show the user a 'Payment detected, waiting for confirmation' state between pending and confirmed.
- **Skipping webhook signature verification in the coinbaseWebhook Cloud Function** — Without signature verification, anyone who knows your webhook URL can send a fake charge:confirmed event to your function, triggering order fulfillment for a payment that never happened. This is a direct financial fraud vector. Fix: Always verify the X-CC-Webhook-Signature header using HMAC-SHA256 with your Coinbase Commerce webhook secret before processing any webhook event. Return HTTP 400 for any request with an invalid or missing signature. This is non-negotiable for production systems.
- **Calling the Coinbase Commerce API directly from FlutterFlow's API Manager instead of through a Cloud Function** — Your Coinbase Commerce API key in the FlutterFlow API Manager headers is compiled into the app binary and can be extracted. An attacker with your API key can create and cancel charges, retrieve customer data, and potentially access financial records in your Coinbase Commerce dashboard. Fix: All Coinbase Commerce API calls must go through a Cloud Function that reads the API key from Firebase Secret Manager at runtime. FlutterFlow calls the Cloud Function — the Cloud Function calls Coinbase Commerce.

## Best practices

- Set a charge expiration time in your createCryptoCharge call (Coinbase Commerce default is 1 hour) — expired charges prevent stale payment links from being used days later
- Show the supported cryptocurrencies prominently on the payment selection screen — Coinbase Commerce supports BTC, ETH, LTC, USDC, DAI, APE, DOGE, and more, and users appreciate knowing their preferred currency is accepted
- Display real-time conversion amounts in the payment selection screen (e.g., $49.99 ≈ 0.00052 BTC) by calling CoinGecko's free price API, so users know the crypto amount before going to checkout
- Store the chargeCode in Firestore alongside orderId — Coinbase Commerce charge codes are short alphanumeric strings (e.g., 'ABCD1234') that are easier to reference in customer support conversations than long UUIDs
- Add a payment polling fallback: if the webhook fails to arrive within 30 minutes, have the FlutterFlow app call an HTTP Cloud Function that queries the Coinbase Commerce API directly for charge status
- Test your integration in Coinbase Commerce test mode before going live — use the sandbox environment to simulate charge:confirmed and charge:failed events without real transactions

## Frequently asked questions

### Which cryptocurrencies does Coinbase Commerce support?

Coinbase Commerce currently supports Bitcoin (BTC), Ethereum (ETH), USD Coin (USDC), Litecoin (LTC), Bitcoin Cash (BCH), Dogecoin (DOGE), DAI, Shiba Inu (SHIB), ApeCoin (APE), and several others. The list changes as Coinbase adds or removes support for networks. Check commerce.coinbase.com for the current supported currencies list. Your charge automatically generates payment addresses for all supported currencies, and customers choose which to pay with on the hosted checkout page.

### How long does a Coinbase Commerce payment take to confirm?

Confirmation time depends on the cryptocurrency used and network congestion. ETH is typically 15-30 seconds for one confirmation. BTC is 10-60 minutes per confirmation (Coinbase requires 3 confirmations). USDC on Ethereum is the same as ETH. For time-sensitive transactions, USDC on faster chains (Polygon, Base) is the quickest option. Coinbase Commerce's hosted checkout page shows the user a real-time confirmation countdown.

### Do I need a Coinbase account to accept Coinbase Commerce payments?

Yes. Coinbase Commerce requires a Coinbase account for business verification and to receive payouts. Funds collected via Coinbase Commerce are held in your Coinbase Commerce wallet and can be converted to USD and transferred to a bank account, or held as cryptocurrency. Payouts are handled entirely through the Coinbase Commerce dashboard, not FlutterFlow.

### Can I use Coinbase Commerce in countries where Coinbase is restricted?

Coinbase Commerce is available in most countries where Coinbase operates. Some countries have restrictions on Coinbase (China, Iran, North Korea, and other sanctioned countries). Your customers can be in any country that can legally use cryptocurrency and access the Coinbase Commerce hosted page. Check Coinbase's supported regions list at commerce.coinbase.com/faq for the current availability.

### What fees does Coinbase Commerce charge?

Coinbase Commerce charges 1% fee on all transactions. This is deducted from the received amount before it appears in your Commerce wallet. There are no monthly fees or setup costs — you only pay the 1% when you receive a payment. Compare: Stripe charges 2.9% + $0.30 per transaction for credit cards. Crypto via Coinbase Commerce at 1% is cheaper for medium and large transaction amounts.

### Can RapidDev help integrate a more advanced crypto payment system in FlutterFlow?

Yes. Advanced crypto payment requirements — multi-currency support with real-time conversion rates, WalletConnect integration for direct MetaMask payments, token-gated content access, NFT-based subscriptions, or crypto invoicing with automatic USD conversion — go significantly beyond the Coinbase Commerce hosted checkout pattern. RapidDev has built Web3 payment integrations for FlutterFlow apps across DeFi, gaming, and creator economy use cases.

---

Source: https://www.rapidevelopers.com/flutterflow-tutorials/how-to-integrate-cryptocurrency-payments-in-flutterflow
© RapidDev — https://www.rapidevelopers.com/flutterflow-tutorials/how-to-integrate-cryptocurrency-payments-in-flutterflow
