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.
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