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.
Prerequisites
- 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
Set up Coinbase Commerce and get your API key and webhook secret
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.
Create the Cloud Function to generate Coinbase Commerce charges
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.
1const functions = require('firebase-functions');2const { defineSecret } = require('firebase-functions/params');3const admin = require('firebase-admin');4const axios = require('axios');56if (!admin.apps.length) admin.initializeApp();7const ccApiKey = defineSecret('COINBASE_COMMERCE_API_KEY');89exports.createCryptoCharge = functions10 .runWith({ secrets: ['COINBASE_COMMERCE_API_KEY'] })11 .https.onCall(async (data, context) => {12 if (!context.auth) throw new functions.https.HttpsError('unauthenticated', 'Login required');1314 const { amount, productName, orderId } = data;15 const response = await axios.post(16 'https://api.commerce.coinbase.com/charges',17 {18 name: productName,19 description: `Payment for ${productName}`,20 pricing_type: 'fixed_price',21 local_price: { amount: String(amount), currency: 'USD' },22 metadata: { orderId, userId: context.auth.uid }23 },24 {25 headers: {26 'X-CC-Api-Key': ccApiKey.value(),27 'X-CC-Version': '2018-03-22',28 'Content-Type': 'application/json'29 }30 }31 );3233 const charge = response.data.data;34 await admin.firestore().collection('orders').doc(orderId).set({35 chargeCode: charge.code,36 chargeId: charge.id,37 hostedUrl: charge.hosted_url,38 status: 'pending_payment',39 amount,40 userId: context.auth.uid,41 createdAt: admin.firestore.FieldValue.serverTimestamp()42 }, { merge: true });4344 return { hostedUrl: charge.hosted_url, chargeCode: charge.code };45 });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.
Wire the charge creation to a Pay with Crypto button in FlutterFlow
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.
Handle Coinbase Commerce webhooks to confirm payment
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'.
1const functions = require('firebase-functions');2const { defineSecret } = require('firebase-functions/params');3const admin = require('firebase-admin');4const crypto = require('crypto');56const webhookSecret = defineSecret('COINBASE_COMMERCE_WEBHOOK_SECRET');78exports.coinbaseWebhook = functions9 .runWith({ secrets: ['COINBASE_COMMERCE_WEBHOOK_SECRET'] })10 .https.onRequest(async (req, res) => {11 const signature = req.headers['x-cc-webhook-signature'];12 const rawBody = req.rawBody;13 const expected = crypto14 .createHmac('sha256', webhookSecret.value())15 .update(rawBody)16 .digest('hex');1718 if (signature !== expected) {19 return res.status(400).send('Invalid signature');20 }2122 const event = req.body;23 const orderId = event.data?.metadata?.orderId;2425 if (event.type === 'charge:confirmed' && orderId) {26 const payment = event.data.payments?.[0];27 await admin.firestore().collection('orders').doc(orderId).update({28 status: 'paid',29 txHash: payment?.transaction_id || null,30 network: payment?.network || null,31 confirmedAt: admin.firestore.FieldValue.serverTimestamp()32 });33 } else if (event.type === 'charge:failed' && orderId) {34 await admin.firestore().collection('orders').doc(orderId).update({35 status: 'payment_failed'36 });37 }38 return res.status(200).send('OK');39 });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.
Display payment status with transaction hash and blockchain explorer link
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 working example
1// Cloud Function: coinbaseWebhook2// Receives Coinbase Commerce payment events3// Verifies webhook signature and updates Firestore order status45const functions = require('firebase-functions');6const { defineSecret } = require('firebase-functions/params');7const admin = require('firebase-admin');8const crypto = require('crypto');910if (!admin.apps.length) admin.initializeApp();11const webhookSecret = defineSecret('COINBASE_COMMERCE_WEBHOOK_SECRET');1213exports.coinbaseWebhook = functions14 .runWith({ secrets: ['COINBASE_COMMERCE_WEBHOOK_SECRET'] })15 .https.onRequest(async (req, res) => {16 if (req.method !== 'POST') return res.status(405).send('Method Not Allowed');1718 // Verify Coinbase signature19 const signature = req.headers['x-cc-webhook-signature'];20 const rawBody = req.rawBody || JSON.stringify(req.body);21 const computed = crypto22 .createHmac('sha256', webhookSecret.value())23 .update(rawBody)24 .digest('hex');2526 if (!signature || signature !== computed) {27 console.error('Invalid webhook signature');28 return res.status(400).send('Invalid signature');29 }3031 const event = req.body;32 const orderId = event.data?.metadata?.orderId;33 const db = admin.firestore();3435 try {36 switch (event.type) {37 case 'charge:confirmed': {38 const payment = event.data.payments?.[0];39 await db.collection('orders').doc(orderId).update({40 status: 'paid',41 txHash: payment?.transaction_id || null,42 network: payment?.network || null,43 cryptoCurrency: payment?.value?.crypto?.currency || null,44 confirmedAt: admin.firestore.FieldValue.serverTimestamp()45 });46 console.log(`Order ${orderId} confirmed. TX: ${payment?.transaction_id}`);47 break;48 }49 case 'charge:failed':50 await db.collection('orders').doc(orderId).update({51 status: 'payment_failed',52 failedAt: admin.firestore.FieldValue.serverTimestamp()53 });54 break;55 case 'charge:pending':56 await db.collection('orders').doc(orderId).update({57 status: 'payment_pending_confirmation'58 });59 break;60 default:61 console.log(`Unhandled event type: ${event.type}`);62 }63 } catch (err) {64 console.error('Webhook processing error:', err);65 return res.status(500).send('Processing error');66 }6768 return res.status(200).send('OK');69 });Common mistakes
Why it's a problem: Marking an order as paid on charge:pending instead of waiting for charge:confirmed
How to avoid: 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.
Why it's a problem: Skipping webhook signature verification in the coinbaseWebhook Cloud Function
How to avoid: 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.
Why it's a problem: Calling the Coinbase Commerce API directly from FlutterFlow's API Manager instead of through a Cloud Function
How to avoid: 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
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I am building a FlutterFlow app that needs to accept cryptocurrency payments. Write me: (1) A Firebase Cloud Function that creates a Coinbase Commerce charge and returns the hosted checkout URL, (2) A second Cloud Function that receives Coinbase Commerce webhooks, verifies the signature with HMAC-SHA256, and updates an order document in Firestore when payment is confirmed, (3) How to display payment status with transaction hash and blockchain explorer link in FlutterFlow.
Add a Pay with Crypto button to my checkout page. When tapped, call the createCryptoCharge Cloud Function with the order amount and product name, store the returned hostedUrl in page state, then launch that URL. After launching, show a payment status card that listens to the Firestore orders document in real time and updates from Pending to Confirmed when the webhook fires.
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.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation