NewWallets, tokens, NFTs, and vaults are live. RPC nodes coming soon.

See the stack →

tutorials 10 min read Source

Add Crypto Payments to Your Chat App in 10 Minutes

Step-by-step guide to integrate USDC payments in any messaging app using FabricBloc's Keyboard SDK. Complete code examples, testing, and deployment.

tutorialschat-app-paymentsmessaging-app-cryptousdc-integrationmobile-payments-sdkquick-start-guide

Imagine users typing *"Thanks for dinner! Here's my half: $47.50"* in your chat app — and the payment actually happens. No external wallet apps, no broken conversation flow, no complex UX.

Today, we'll add USDC payments to a React Native chat app in exactly 10 minutes.

What we're building:

  • Natural language payments (*"send $20 to Alice"*)
  • Bill splitting (*"split $80 four ways"*)
  • Request money (*"Alice owes me $15"*)
  • Group expenses (*"everyone pay $10 for pizza"*)

Before We Start

Prerequisites:

  • React Native development environment
  • Node.js 18+
  • iOS/Android simulator or device

What you'll get:

  • Fully functional crypto payments in chat
  • USDC transactions on Base network (low fees)
  • Biometric authentication (Face ID/Touch ID)
  • Zero gas fees for users (sponsored)

Let's dive in.

Step 1: Project Setup

# Create new React Native app
npx react-native init ChatPayments
cd ChatPayments

# Install FabricBloc SDK
npm install @fabricbloc/keyboard-sdk-react-native
npm install @fabricbloc/ui-components

# Install chat UI dependencies  
npm install react-native-gifted-chat
npm install react-native-vector-icons

# iOS setup
cd ios && pod install && cd ..

Step 2: Initialize FabricBloc

// App.js
import React, { useEffect, useState } from 'react';
import { View, Text } from 'react-native';
import FabricBloc from '@fabricbloc/keyboard-sdk-react-native';
import ChatScreen from './src/components/ChatScreen';

const App = () => {
  const [isReady, setIsReady] = useState(false);
  
  useEffect(() => {
    initializePayments();
  }, []);
  
  const initializePayments = async () => {
    try {
      await FabricBloc.init({
        // Get free API key: fabricbloc.com/developers
        apiKey: 'your-api-key-here',
        
        // Configuration
        environment: 'testnet', // Use 'mainnet' for production
        defaultToken: 'USDC',
        defaultChain: 'base',   // Low fees, fast confirmation
        
        // Enable natural language parsing
        nlpEnabled: true,
        
        // Chat-specific features
        enableBillSplitting: true,
        enablePaymentRequests: true,
        enableGroupPayments: true,
        
        // Biometric security
        requireBiometric: true,
        
        // Auto-confirm small amounts (optional)
        autoConfirmUnder: 5.00
      });
      
      console.log('✅ FabricBloc initialized');
      setIsReady(true);
    } catch (error) {
      console.error('❌ FabricBloc initialization failed:', error);
    }
  };
  
  if (!isReady) {
    return (
      <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
        <Text>Initializing payments...</Text>
      </View>
    );
  }
  
  return <ChatScreen />;
};

export default App;

Step 3: Chat Interface with Payment Detection

// src/components/ChatScreen.js
import React, { useState, useCallback, useEffect } from 'react';
import { GiftedChat, Bubble, Send } from 'react-native-gifted-chat';
import { View, TouchableOpacity, Text } from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';
import FabricBloc from '@fabricbloc/keyboard-sdk-react-native';
import PaymentHandler from '../utils/PaymentHandler';

const ChatScreen = () => {
  const [messages, setMessages] = useState([]);
  const [users] = useState({
    1: { name: 'You', avatar: '🤵' },
    2: { name: 'Alice', avatar: '👩‍💼' },
    3: { name: 'Bob', avatar: '👨‍💻' },
    4: { name: 'Carol', avatar: '👩‍🎨' }
  });
  
  useEffect(() => {
    // Initialize with some sample messages
    setMessages([
      {
        _id: Math.round(Math.random() * 1000000),
        text: 'Great dinner! Who wants to split the bill?',
        createdAt: new Date(),
        user: { _id: 2, name: 'Alice', avatar: '👩‍💼' }
      },
      {
        _id: Math.round(Math.random() * 1000000),
        text: 'I can handle the tip calculation 💰',
        createdAt: new Date(Date.now() - 300000),
        user: { _id: 3, name: 'Bob', avatar: '👨‍💻' }
      }
    ]);
    
    // Listen for payment events
    FabricBloc.on('paymentDetected', handlePaymentDetected);
    FabricBloc.on('paymentSent', handlePaymentSent);
    FabricBloc.on('paymentRequested', handlePaymentRequested);
    
    return () => {
      FabricBloc.off('paymentDetected', handlePaymentDetected);
      FabricBloc.off('paymentSent', handlePaymentSent);
      FabricBloc.off('paymentRequested', handlePaymentRequested);
    };
  }, []);
  
  const handlePaymentDetected = async (paymentData) => {
    const { type, amount, recipients, message } = paymentData;
    
    try {
      switch (type) {
        case 'send':
          await PaymentHandler.handleDirectPayment(amount, recipients[0], message);
          break;
        case 'split':
          await PaymentHandler.handleBillSplit(amount, recipients, message);
          break;
        case 'request':
          await PaymentHandler.handlePaymentRequest(amount, recipients[0], message);
          break;
        case 'group':
          await PaymentHandler.handleGroupPayment(amount, recipients, message);
          break;
      }
    } catch (error) {
      console.error('Payment handling failed:', error);
      addSystemMessage(`Payment failed: ${error.message}`);
    }
  };
  
  const handlePaymentSent = (transactionData) => {
    const { amount, recipient, transactionHash } = transactionData;
    addSystemMessage(
      `✅ Sent $${amount} USDC to ${recipient}`,
      'payment-confirmation',
      { txHash: transactionHash }
    );
  };
  
  const handlePaymentRequested = (requestData) => {
    const { amount, from, message } = requestData;
    addSystemMessage(
      `💸 Payment request: ${from} is asking for $${amount} USDC`,
      'payment-request'
    );
  };
  
  const onSend = useCallback((newMessages = []) => {
    setMessages(previousMessages => 
      GiftedChat.append(previousMessages, newMessages)
    );
    
    // Process message for payment intent
    const message = newMessages[0];
    FabricBloc.parsePaymentIntent(message.text, {
      availableUsers: Object.values(users),
      senderId: message.user._id
    });
  }, [users]);
  
  const addSystemMessage = (text, type = 'system', metadata = {}) => {
    const systemMessage = {
      _id: Math.round(Math.random() * 1000000),
      text,
      createdAt: new Date(),
      system: true,
      type,
      metadata
    };
    
    setMessages(previousMessages => 
      GiftedChat.append(previousMessages, [systemMessage])
    );
  };
  
  const renderBubble = (props) => {
    return (
      <Bubble
        {...props}
        wrapperStyle={{
          right: { backgroundColor: '#007AFF' },
          left: { backgroundColor: '#E5E5EA' }
        }}
        textStyle={{
          right: { color: '#FFFFFF' },
          left: { color: '#000000' }
        }}
      />
    );
  };
  
  const renderSend = (props) => {
    return (
      <Send {...props}>
        <View style={{ marginRight: 10, marginBottom: 5 }}>
          <Icon name="send" size={24} color="#007AFF" />
        </View>
      </Send>
    );
  };
  
  return (
    <View style={{ flex: 1 }}>
      <View style={styles.header}>
        <Text style={styles.headerTitle}>💬 Group Chat</Text>
        <Text style={styles.headerSubtitle}>Payments enabled via FabricBloc</Text>
      </View>
      
      <GiftedChat
        messages={messages}
        onSend={onSend}
        user={{ _id: 1, name: 'You', avatar: '🤵' }}
        renderBubble={renderBubble}
        renderSend={renderSend}
        placeholder="Type a message... (try: 'send $10 to Alice')"
        showUserAvatar
        alwaysShowSend
      />
      
      <PaymentQuickActions onQuickAction={handleQuickAction} />
    </View>
  );
};

const styles = {
  header: {
    backgroundColor: '#007AFF',
    padding: 15,
    paddingTop: 50,
    alignItems: 'center'
  },
  headerTitle: {
    color: 'white',
    fontSize: 18,
    fontWeight: 'bold'
  },
  headerSubtitle: {
    color: 'rgba(255,255,255,0.8)',
    fontSize: 12,
    marginTop: 2
  }
};

export default ChatScreen;

Step 4: Payment Processing Logic

// src/utils/PaymentHandler.js
import FabricBloc from '@fabricbloc/keyboard-sdk-react-native';

class PaymentHandler {
  static async handleDirectPayment(amount, recipient, originalMessage) {
    try {
      // Resolve recipient to wallet address
      const recipientAddress = await this.resolveRecipient(recipient);
      
      if (!recipientAddress) {
        throw new Error(`Could not find wallet for ${recipient}`);
      }
      
      // Create payment confirmation
      const confirmation = await FabricBloc.createPayment({
        type: 'direct',
        amount: amount,
        token: 'USDC',
        to: recipientAddress,
        recipient: recipient,
        message: originalMessage,
        requireConfirmation: amount > 5 // Auto-confirm small amounts
      });
      
      if (confirmation.confirmed) {
        return await FabricBloc.executePayment(confirmation.id);
      }
      
      return confirmation;
    } catch (error) {
      console.error('Direct payment failed:', error);
      throw error;
    }
  }
  
  static async handleBillSplit(totalAmount, participants, originalMessage) {
    const splitAmount = totalAmount / participants.length;
    
    try {
      const payments = [];
      
      for (const participant of participants) {
        if (participant.name !== 'You') { // Don't charge yourself
          const address = await this.resolveRecipient(participant.name);
          if (address) {
            payments.push({
              to: address,
              amount: splitAmount,
              recipient: participant.name
            });
          }
        }
      }
      
      if (payments.length === 0) {
        throw new Error('No valid recipients found for bill split');
      }
      
      // Create batch payment
      const confirmation = await FabricBloc.createBatchPayment({
        type: 'split',
        payments: payments,
        token: 'USDC',
        totalAmount: totalAmount,
        splitAmount: splitAmount,
        message: originalMessage
      });
      
      if (confirmation.confirmed) {
        return await FabricBloc.executeBatchPayment(confirmation.id);
      }
      
      return confirmation;
    } catch (error) {
      console.error('Bill split failed:', error);
      throw error;
    }
  }
  
  static async handlePaymentRequest(amount, from, originalMessage) {
    try {
      // Store payment request (would typically save to backend)
      const request = {
        id: Math.random().toString(36),
        amount: amount,
        from: from,
        to: 'You',
        message: originalMessage,
        status: 'pending',
        createdAt: new Date()
      };
      
      // Notify about the request
      FabricBloc.emit('paymentRequested', request);
      
      return request;
    } catch (error) {
      console.error('Payment request failed:', error);
      throw error;
    }
  }
  
  static async handleGroupPayment(amountPerPerson, participants, originalMessage) {
    try {
      // Everyone except sender pays the amount
      const payers = participants.filter(p => p.name !== 'You');
      
      // For demo, we'll request payments from others
      // In real app, this would create payment requests
      const requests = payers.map(payer => ({
        from: payer.name,
        amount: amountPerPerson,
        message: `Group payment: ${originalMessage}`
      }));
      
      // Emit requests
      requests.forEach(request => {
        FabricBloc.emit('paymentRequested', request);
      });
      
      return { requests, totalRequested: amountPerPerson * payers.length };
    } catch (error) {
      console.error('Group payment failed:', error);
      throw error;
    }
  }
  
  static async resolveRecipient(recipientName) {
    // In a real app, this would:
    // 1. Check your contacts for their wallet address
    // 2. Look up their ENS name (alice.eth)
    // 3. Query your app's user directory
    // 4. Use FabricBloc's resolution service
    
    // For demo, we'll use mock addresses
    const mockAddresses = {
      'Alice': '0x742d35Cc6634C0532925a3b8D0c5F92f7cc4BFB1',
      'Bob': '0x892d35Cc6634C0532925a3b8D0c5F92f7cc4BFB2',
      'Carol': '0x992d35Cc6634C0532925a3b8D0c5F92f7cc4BFB3'
    };
    
    return mockAddresses[recipientName] || null;
  }
}

export default PaymentHandler;

Step 5: Quick Action Buttons

// src/components/PaymentQuickActions.js
import React from 'react';
import { View, TouchableOpacity, Text, StyleSheet } from 'react-native';

const PaymentQuickActions = ({ onQuickAction }) => {
  const quickActions = [
    { 
      title: 'Split Bill', 
      emoji: '🧾', 
      action: () => onQuickAction('split', '$80 split 4 ways') 
    },
    { 
      title: 'Send $10', 
      emoji: '💸', 
      action: () => onQuickAction('send', 'send $10 to Alice') 
    },
    { 
      title: 'Request $20', 
      emoji: '📲', 
      action: () => onQuickAction('request', 'Alice owes me $20') 
    },
    { 
      title: 'Group Pizza', 
      emoji: '🍕', 
      action: () => onQuickAction('group', 'everyone pay $12 for pizza') 
    }
  ];
  
  return (
    <View style={styles.container}>
      <Text style={styles.title}>💡 Try these:</Text>
      <View style={styles.actionsRow}>
        {quickActions.map((action, index) => (
          <TouchableOpacity
            key={index}
            style={styles.actionButton}
            onPress={action.action}
          >
            <Text style={styles.actionEmoji}>{action.emoji}</Text>
            <Text style={styles.actionTitle}>{action.title}</Text>
          </TouchableOpacity>
        ))}
      </View>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    backgroundColor: '#F8F8F8',
    padding: 15,
    borderTopWidth: 1,
    borderTopColor: '#E0E0E0'
  },
  title: {
    fontSize: 12,
    color: '#666',
    marginBottom: 8,
    textAlign: 'center'
  },
  actionsRow: {
    flexDirection: 'row',
    justifyContent: 'space-around'
  },
  actionButton: {
    alignItems: 'center',
    padding: 8
  },
  actionEmoji: {
    fontSize: 20,
    marginBottom: 4
  },
  actionTitle: {
    fontSize: 10,
    color: '#333'
  }
});

export default PaymentQuickActions;

Testing Your Integration

Once you have the code in place, try these test scenarios:

  • Direct payment — Type *"send $5 to Alice"*
  • Bill splitting — Type *"split the $60 bill 3 ways"*
  • Payment request — Type *"Bob owes me $15 for lunch"*
  • Group payment — Type *"everyone chip in $10 for the gift"*

Expected flow: User types a payment message → FabricBloc detects the intent → Shows confirmation → User confirms with Face ID → Transaction broadcasts → Confirmation appears in chat.

---

Production Considerations

User Management — In production, you'll want proper recipient resolution:

// Real recipient resolution
const resolveUser = async (username) => {
  // Check local contacts first
  const contact = await ContactsAPI.findByName(username);
  if (contact?.walletAddress) return contact.walletAddress;
  
  // Try ENS resolution
  const ensAddress = await FabricBloc.resolveENS(`${username}.eth`);
  if (ensAddress) return ensAddress;
  
  // Query your user directory
  const user = await YourAPI.findUser(username);
  if (user?.walletAddress) return user.walletAddress;
  
  // Prompt user to provide address
  throw new Error(`Wallet address not found for ${username}`);
};

Security & Limits — Configure spending controls:

// Configure spending limits
await FabricBloc.setSpendingLimits({
  daily: 500,        // $500 per day
  perTransaction: 100, // $100 per transaction
  requireBiometricOver: 25, // Face ID for >$25
  allowedRecipients: ['Alice', 'Bob'] // Whitelist
});

Error Handling — Handle payment failures gracefully:

// Comprehensive error handling
const handlePaymentError = (error) => {
  switch (error.code) {
    case 'INSUFFICIENT_FUNDS':
      return 'Not enough USDC balance';
    case 'RECIPIENT_NOT_FOUND':
      return 'Could not find wallet address';
    case 'TRANSACTION_FAILED':
      return 'Transaction failed. Please try again';
    case 'USER_CANCELLED':
      return 'Payment cancelled';
    default:
      return 'Payment error. Please contact support';
  }
};

---

Advanced Features

Payment History — Add a transaction history screen:

// Add transaction history screen
const PaymentHistory = () => {
  const [transactions, setTransactions] = useState([]);
  
  useEffect(() => {
    loadTransactions();
  }, []);
  
  const loadTransactions = async () => {
    const history = await FabricBloc.getTransactionHistory({
      limit: 50,
      includeMetadata: true
    });
    setTransactions(history);
  };
  
  return (
    <FlatList
      data={transactions}
      renderItem={({ item }) => (
        <TransactionItem transaction={item} />
      )}
    />
  );
};

Multi-Token Support — Let users pay with different cryptocurrencies:

// Support multiple cryptocurrencies
await FabricBloc.init({
  supportedTokens: ['USDC', 'USDT', 'ETH', 'DAI'],
  defaultToken: 'USDC',
  tokenPricing: true // Show USD values
});

// Let users choose token
const selectToken = (message) => {
  if (message.includes('ETH')) return 'ETH';
  if (message.includes('DAI')) return 'DAI';
  return 'USDC'; // Default
};

Integration with Existing Apps — Adding to your existing chat:

// For existing chat apps
import FabricBloc from '@fabricbloc/keyboard-sdk-react-native';

// Add to your existing message handling
const handleMessage = async (message) => {
  // Your existing message logic
  await yourChatAPI.sendMessage(message);
  
  // Add payment detection
  const paymentIntent = await FabricBloc.detectPaymentIntent(message.text);
  if (paymentIntent) {
    await handlePayment(paymentIntent);
  }
};

Get Your API Key

Ready to add crypto payments to your chat app?

Get your free FabricBloc API key →

Free tier includes:

  • ✅ 5,000 monthly transactions
  • ✅ Full SDK access
  • ✅ Testnet and mainnet support
  • ✅ Developer documentation
  • ✅ 24/7 support

What's Next?

Extend your chat app with:

  • Voice payments: "Hey Siri, send $20 to Bob"
  • QR code scanning: Scan to pay in person
  • Group treasuries: Shared wallets for teams
  • Subscription payments: Recurring friend loans
  • International remittances: Global money transfers

Conclusion

In 10 minutes, you've built what traditional fintech companies spend months creating:

  • ✅ Natural language payments
  • ✅ Multi-party transactions
  • ✅ Instant settlement
  • ✅ Global reach (USDC)
  • ✅ Bank-level security

Your users can now send money as easily as sending messages. No wallet apps, no complex UIs, no broken conversation flow.

The future of payments is conversational. Start building it today.

---

*Questions? Issues? Join our Discord community or check out our complete documentation.*

Next step

Read the docs quickstart

Step-by-step guide to integrate USDC payments in any messaging app using FabricBloc's Keyboard SDK. Complete code examples, testing, and deployment.

Continue

Early access

Get the next FabricBloc note

Product and technical updates tied to the exact topics you are reading.

No spam. Unsubscribe anytime.

FabricBloc editorial team photo
FabricBloc Team Editorial Edited by FabricBloc Editorial