Integration Guides
Comprehensive step-by-step guides for integrating Fiatsend into your applications for different use cases and scenarios.
Quick Navigation
🏪 Merchant Integration
Accept crypto payments and receive fiat settlements
Scroll down to view →
👥 P2P Transfer App
Build a peer-to-peer money transfer application
Scroll down to view →
📱 Mobile Money Integration
Integrate with African mobile money providers
Scroll down to view →
🔄 Onramp/Offramp Service
Build fiat-to-crypto conversion services
Scroll down to view →
🔐 Identity Management
Implement NFT-based identity verification
Scroll down to view →
🌐 Webhook Integration
Set up real-time event notifications
Scroll down to view →
Merchant Integration
Learn how to integrate Fiatsend into your e-commerce or business application to accept crypto payments.
Step 1: Setup and Configuration
import { FiatsendSDK } from "@fiatsend/sdk";
const sdk = new FiatsendSDK({
apiKey: process.env.FIATSEND_API_KEY,
environment: "production"
});
Step 2: Create Payment Request
// Create a payment request for an order
const paymentRequest = await sdk.payment.generateRequestAddress({
amount: 100,
currency: "USDT",
description: "Order #12345 - T-Shirt",
merchantId: "merchant_123",
orderId: "order_12345",
callbackUrl: "https://your-store.com/webhook/payment",
successUrl: "https://your-store.com/order/success",
cancelUrl: "https://your-store.com/order/cancel"
});
console.log("Payment Address:", paymentRequest.address);
console.log("QR Code:", paymentRequest.qrCode);
Step 3: Display Payment Options
import React, { useState, useEffect } from 'react';
function PaymentComponent({ orderId, amount, currency }) {
const [paymentRequest, setPaymentRequest] = useState(null);
const [paymentStatus, setPaymentStatus] = useState('pending');
useEffect(() => {
// Create payment request when component mounts
const createPaymentRequest = async () => {
const request = await sdk.payment.generateRequestAddress({
amount,
currency,
description: `Order #${orderId}`,
merchantId: process.env.MERCHANT_ID,
orderId,
callbackUrl: `${window.location.origin}/webhook/payment`
});
setPaymentRequest(request);
};
createPaymentRequest();
}, [orderId, amount, currency]);
return (
<div className="payment-container">
<h3>Complete Your Payment</h3>
<p>Amount: {amount} {currency}</p>
{paymentRequest && (
<div>
<p>Send payment to:</p>
<code>{paymentRequest.address}</code>
<div className="qr-code">
<img src={paymentRequest.qrCode} alt="Payment QR Code" />
</div>
<p>Status: {paymentStatus}</p>
</div>
)}
</div>
);
}
Step 4: Handle Payment Webhooks
// Express.js webhook handler
app.post('/webhook/payment', async (req, res) => {
try {
const result = await sdk.payment.handlePaymentWebhook(req.body);
if (result.status === 'completed') {
// Update order status in your database
await updateOrderStatus(result.orderId, 'paid');
// Send confirmation email
await sendOrderConfirmation(result.orderId);
console.log('Payment completed:', result.transactionId);
}
res.json({ success: true });
} catch (error) {
console.error('Webhook error:', error);
res.status(400).json({ error: error.message });
}
});
Step 5: Check Payment Status
// Check payment status for an order
const checkPaymentStatus = async (orderId) => {
const status = await sdk.payment.getPaymentStatus(orderId);
switch (status.status) {
case 'completed':
// Payment successful
break;
case 'pending':
// Payment still processing
break;
case 'failed':
// Payment failed
break;
}
return status;
};
P2P Transfer App
Build a peer-to-peer money transfer application using mobile numbers.
Step 1: Setup React Application
import React, { useState } from 'react';
import { FiatsendSDK } from "@fiatsend/sdk";
const sdk = new FiatsendSDK({
apiKey: process.env.REACT_APP_FIATSEND_API_KEY,
environment: "production"
});
function TransferApp() {
const [recipient, setRecipient] = useState('');
const [amount, setAmount] = useState('');
const [currency, setCurrency] = useState('USDT');
const [loading, setLoading] = useState(false);
const handleTransfer = async (e) => {
e.preventDefault();
setLoading(true);
try {
const transfer = await sdk.payment.initiatePayment({
amount: parseFloat(amount),
currency,
recipient,
description: `P2P Transfer to ${recipient}`,
country: 'GH'
});
console.log('Transfer initiated:', transfer.transactionId);
// Show success message
} catch (error) {
console.error('Transfer failed:', error);
// Show error message
} finally {
setLoading(false);
}
};
return (
<form onSubmit={handleTransfer}>
<div>
<label>Recipient Mobile Number:</label>
<input
type="tel"
value={recipient}
onChange={(e) => setRecipient(e.target.value)}
placeholder="+233551234567"
required
/>
</div>
<div>
<label>Amount:</label>
<input
type="number"
value={amount}
onChange={(e) => setAmount(e.target.value)}
placeholder="100"
required
/>
</div>
<div>
<label>Currency:</label>
<select value={currency} onChange={(e) => setCurrency(e.target.value)}>
<option value="USDT">USDT</option>
<option value="USDC">USDC</option>
<option value="GHSFIAT">GHSFIAT</option>
</select>
</div>
<button type="submit" disabled={loading}>
{loading ? 'Sending...' : 'Send Money'}
</button>
</form>
);
}
Step 2: Add Contact Management
import React, { useState, useEffect } from 'react';
function ContactManager() {
const [contacts, setContacts] = useState([]);
const [newContact, setNewContact] = useState({ name: '', phone: '' });
const addContact = async () => {
// Check if phone number exists in Fiatsend
const exists = await sdk.identity.checkNumberExists(newContact.phone);
if (exists.exists) {
setContacts([...contacts, newContact]);
setNewContact({ name: '', phone: '' });
} else {
alert('Phone number not found in Fiatsend network');
}
};
return (
<div>
<h3>My Contacts</h3>
<div>
<input
type="text"
placeholder="Contact Name"
value={newContact.name}
onChange={(e) => setNewContact({...newContact, name: e.target.value})}
/>
<input
type="tel"
placeholder="Phone Number"
value={newContact.phone}
onChange={(e) => setNewContact({...newContact, phone: e.target.value})}
/>
<button onClick={addContact}>Add Contact</button>
</div>
<ul>
{contacts.map((contact, index) => (
<li key={index}>
{contact.name} - {contact.phone}
</li>
))}
</ul>
</div>
);
}
Step 3: Transaction History
import React, { useState, useEffect } from 'react';
function TransactionHistory({ walletAddress }) {
const [transactions, setTransactions] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchTransactions = async () => {
try {
const result = await sdk.wallet.getTransactions({
walletAddress,
page: 1,
limit: 50
});
setTransactions(result.data);
} catch (error) {
console.error('Failed to fetch transactions:', error);
} finally {
setLoading(false);
}
};
fetchTransactions();
}, [walletAddress]);
if (loading) return <div>Loading transactions...</div>;
return (
<div>
<h3>Transaction History</h3>
<ul>
{transactions.map((tx) => (
<li key={tx.id}>
<div>
<strong>{tx.type}</strong> - {tx.amount} {tx.currency}
</div>
<div>Status: {tx.status}</div>
<div>Date: {new Date(tx.createdAt).toLocaleDateString()}</div>
</li>
))}
</ul>
</div>
);
}
Mobile Money Integration
Integrate with African mobile money providers for seamless fiat transactions.
Step 1: MTN Mobile Money Integration
// MTN Mobile Money payment
const mtnPayment = await sdk.payment.createMTNPayment({
amount: 100,
phoneNumber: "0551234567",
externalRef: "PAY-12345",
wallet: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6",
description: "Payment for services"
});
console.log("MTN Payment ID:", mtnPayment.paymentId);
console.log("Status:", mtnPayment.status);
Step 2: AirtelTigo Money Integration
// AirtelTigo Money payment
const airtelPayment = await sdk.payment.createAirtelTigoPayment({
amount: 50,
phoneNumber: "0241234567",
externalRef: "PAY-12346",
wallet: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6",
description: "Payment for services"
});
console.log("AirtelTigo Payment ID:", airtelPayment.paymentId);
Step 3: Telecel Cash Integration
// Telecel Cash payment
const telecelPayment = await sdk.payment.createTelecelPayment({
amount: 75,
phoneNumber: "0271234567",
externalRef: "PAY-12347",
wallet: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6",
description: "Payment for services"
});
console.log("Telecel Payment ID:", telecelPayment.paymentId);
Step 4: Universal Mobile Money Component
import React, { useState } from 'react';
function MobileMoneyPayment({ amount, currency, onSuccess, onError }) {
const [provider, setProvider] = useState('mtn');
const [phoneNumber, setPhoneNumber] = useState('');
const [loading, setLoading] = useState(false);
const handlePayment = async () => {
setLoading(true);
try {
let payment;
switch (provider) {
case 'mtn':
payment = await sdk.payment.createMTNPayment({
amount,
phoneNumber,
externalRef: `PAY-${Date.now()}`,
wallet: userWalletAddress,
description: `Payment of ${amount} ${currency}`
});
break;
case 'airtel':
payment = await sdk.payment.createAirtelTigoPayment({
amount,
phoneNumber,
externalRef: `PAY-${Date.now()}`,
wallet: userWalletAddress,
description: `Payment of ${amount} ${currency}`
});
break;
case 'telecel':
payment = await sdk.payment.createTelecelPayment({
amount,
phoneNumber,
externalRef: `PAY-${Date.now()}`,
wallet: userWalletAddress,
description: `Payment of ${amount} ${currency}`
});
break;
}
onSuccess(payment);
} catch (error) {
onError(error);
} finally {
setLoading(false);
}
};
return (
<div>
<h3>Mobile Money Payment</h3>
<div>
<label>Provider:</label>
<select value={provider} onChange={(e) => setProvider(e.target.value)}>
<option value="mtn">MTN Mobile Money</option>
<option value="airtel">AirtelTigo Money</option>
<option value="telecel">Telecel Cash</option>
</select>
</div>
<div>
<label>Phone Number:</label>
<input
type="tel"
value={phoneNumber}
onChange={(e) => setPhoneNumber(e.target.value)}
placeholder="0551234567"
/>
</div>
<div>
<label>Amount:</label>
<span>{amount} {currency}</span>
</div>
<button onClick={handlePayment} disabled={loading}>
{loading ? 'Processing...' : 'Pay with Mobile Money'}
</button>
</div>
);
}
Onramp/Offramp Service
Build a service for converting between fiat and crypto currencies.
Step 1: Onramp Service (Fiat to Crypto)
// Create onramp service
class OnrampService {
constructor(sdk) {
this.sdk = sdk;
}
async createOnrampRequest(amount, currency, userWallet) {
// Generate unique request ID
const requestId = `ONRAMP-${Date.now()}`;
// Create payment request
const paymentRequest = await this.sdk.payment.generateRequestAddress({
amount,
currency: 'USDT', // Convert to USDT
description: `Onramp request for ${amount} ${currency}`,
merchantId: 'onramp_service',
orderId: requestId,
callbackUrl: `${process.env.BASE_URL}/webhook/onramp`
});
return {
requestId,
paymentAddress: paymentRequest.address,
qrCode: paymentRequest.qrCode,
amount,
currency: 'USDT',
instructions: `Send ${amount} ${currency} to the provided address to receive USDT`
};
}
async processOnramp(requestId, transactionHash) {
// Verify transaction on blockchain
const transaction = await this.sdk.core.getTransaction(transactionHash);
if (transaction && transaction.status === 'confirmed') {
// Transfer USDT to user's wallet
const transfer = await this.sdk.wallet.transferTokens({
to: userWallet,
amount: transaction.amount,
currency: 'USDT',
description: `Onramp completion for request ${requestId}`
});
return {
success: true,
transferId: transfer.transactionId,
amount: transaction.amount,
currency: 'USDT'
};
}
return { success: false, error: 'Transaction not confirmed' };
}
}
Step 2: Offramp Service (Crypto to Fiat)
// Create offramp service
class OfframpService {
constructor(sdk) {
this.sdk = sdk;
}
async createOfframpRequest(amount, currency, recipientPhone, provider) {
const requestId = `OFFRAMP-${Date.now()}`;
// Check user balance
const balance = await this.sdk.wallet.getBalance(userWallet);
if (balance.balances[currency] < amount) {
throw new Error('Insufficient balance');
}
// Create mobile money payment
let payment;
switch (provider) {
case 'mtn':
payment = await this.sdk.payment.createMTNPayment({
amount,
phoneNumber: recipientPhone,
externalRef: requestId,
wallet: userWallet,
description: `Offramp request ${requestId}`
});
break;
case 'airtel':
payment = await this.sdk.payment.createAirtelTigoPayment({
amount,
phoneNumber: recipientPhone,
externalRef: requestId,
wallet: userWallet,
description: `Offramp request ${requestId}`
});
break;
}
return {
requestId,
paymentId: payment.paymentId,
status: payment.status,
amount,
currency,
recipientPhone,
provider
};
}
async checkOfframpStatus(requestId) {
const status = await this.sdk.payment.getPaymentStatus(requestId);
return status;
}
}
Step 3: Onramp/Offramp UI Component
import React, { useState } from 'react';
function OnrampOfframpService() {
const [mode, setMode] = useState('onramp'); // 'onramp' or 'offramp'
const [amount, setAmount] = useState('');
const [currency, setCurrency] = useState('GHS');
const [phoneNumber, setPhoneNumber] = useState('');
const [provider, setProvider] = useState('mtn');
const [loading, setLoading] = useState(false);
const [result, setResult] = useState(null);
const handleOnramp = async () => {
setLoading(true);
try {
const onrampService = new OnrampService(sdk);
const request = await onrampService.createOnrampRequest(
parseFloat(amount),
currency,
userWallet
);
setResult(request);
} catch (error) {
console.error('Onramp failed:', error);
} finally {
setLoading(false);
}
};
const handleOfframp = async () => {
setLoading(true);
try {
const offrampService = new OfframpService(sdk);
const request = await offrampService.createOfframpRequest(
parseFloat(amount),
'USDT',
phoneNumber,
provider
);
setResult(request);
} catch (error) {
console.error('Offramp failed:', error);
} finally {
setLoading(false);
}
};
return (
<div>
<div>
<button
onClick={() => setMode('onramp')}
className={mode === 'onramp' ? 'active' : ''}
>
Onramp (Fiat → Crypto)
</button>
<button
onClick={() => setMode('offramp')}
className={mode === 'offramp' ? 'active' : ''}
>
Offramp (Crypto → Fiat)
</button>
</div>
{mode === 'onramp' ? (
<div>
<h3>Convert Fiat to Crypto</h3>
<div>
<label>Amount:</label>
<input
type="number"
value={amount}
onChange={(e) => setAmount(e.target.value)}
/>
</div>
<div>
<label>Currency:</label>
<select value={currency} onChange={(e) => setCurrency(e.target.value)}>
<option value="GHS">GHS</option>
<option value="USD">USD</option>
</select>
</div>
<button onClick={handleOnramp} disabled={loading}>
{loading ? 'Processing...' : 'Create Onramp Request'}
</button>
</div>
) : (
<div>
<h3>Convert Crypto to Fiat</h3>
<div>
<label>Amount (USDT):</label>
<input
type="number"
value={amount}
onChange={(e) => setAmount(e.target.value)}
/>
</div>
<div>
<label>Recipient Phone:</label>
<input
type="tel"
value={phoneNumber}
onChange={(e) => setPhoneNumber(e.target.value)}
placeholder="0551234567"
/>
</div>
<div>
<label>Provider:</label>
<select value={provider} onChange={(e) => setProvider(e.target.value)}>
<option value="mtn">MTN Mobile Money</option>
<option value="airtel">AirtelTigo Money</option>
</select>
</div>
<button onClick={handleOfframp} disabled={loading}>
{loading ? 'Processing...' : 'Create Offramp Request'}
</button>
</div>
)}
{result && (
<div>
<h4>Result:</h4>
<pre>{JSON.stringify(result, null, 2)}</pre>
</div>
)}
</div>
);
}
Identity Management
Implement NFT-based identity verification and management.
Step 1: Identity Verification Component
import React, { useState, useEffect } from 'react';
function IdentityVerification({ walletAddress }) {
const [hasIdentity, setHasIdentity] = useState(false);
const [phoneNumber, setPhoneNumber] = useState('');
const [verificationStatus, setVerificationStatus] = useState('unverified');
const [loading, setLoading] = useState(false);
useEffect(() => {
checkIdentityStatus();
}, [walletAddress]);
const checkIdentityStatus = async () => {
try {
const hasAccount = await sdk.core.hasAccount(walletAddress);
setHasIdentity(hasAccount);
if (hasAccount) {
const mobile = await sdk.resolver.resolveMobile(walletAddress);
setPhoneNumber(mobile.phoneNumber);
setVerificationStatus('verified');
}
} catch (error) {
console.error('Failed to check identity status:', error);
}
};
const mintIdentity = async () => {
setLoading(true);
try {
const identity = await sdk.identity.mintIdentity({
phoneNumber,
countryCode: 'GH',
metadata: 'User identity verification',
walletAddress
});
console.log('Identity minted:', identity.tokenId);
setHasIdentity(true);
setVerificationStatus('verified');
} catch (error) {
console.error('Failed to mint identity:', error);
} finally {
setLoading(false);
}
};
const createKycSession = async () => {
try {
const session = await sdk.identity.createVeriffSession(walletAddress);
// Redirect to Veriff verification
window.open(session.verificationUrl, '_blank');
} catch (error) {
console.error('Failed to create KYC session:', error);
}
};
return (
<div>
<h3>Identity Verification</h3>
{!hasIdentity ? (
<div>
<p>You need to create an identity NFT to use Fiatsend services.</p>
<div>
<label>Phone Number:</label>
<input
type="tel"
value={phoneNumber}
onChange={(e) => setPhoneNumber(e.target.value)}
placeholder="+233551234567"
/>
</div>
<button onClick={mintIdentity} disabled={loading}>
{loading ? 'Creating Identity...' : 'Create Identity NFT'}
</button>
</div>
) : (
<div>
<p>✅ Identity verified</p>
<p>Phone: {phoneNumber}</p>
<p>Status: {verificationStatus}</p>
{verificationStatus === 'unverified' && (
<button onClick={createKycSession}>
Complete KYC Verification
</button>
)}
</div>
)}
</div>
);
}
Step 2: Phone Number Resolution
import React, { useState } from 'react';
function PhoneResolver() {
const [phoneNumber, setPhoneNumber] = useState('');
const [resolution, setResolution] = useState(null);
const [loading, setLoading] = useState(false);
const resolvePhone = async () => {
setLoading(true);
try {
const result = await sdk.identity.resolvePhoneToAddress(phoneNumber);
setResolution(result);
} catch (error) {
console.error('Failed to resolve phone number:', error);
} finally {
setLoading(false);
}
};
return (
<div>
<h3>Phone Number Resolution</h3>
<div>
<label>Phone Number:</label>
<input
type="tel"
value={phoneNumber}
onChange={(e) => setPhoneNumber(e.target.value)}
placeholder="+233551234567"
/>
<button onClick={resolvePhone} disabled={loading}>
{loading ? 'Resolving...' : 'Resolve'}
</button>
</div>
{resolution && (
<div>
<h4>Resolution Result:</h4>
<p>Wallet Address: {resolution.wallet}</p>
<p>Has Identity: {resolution.hasIdentity ? 'Yes' : 'No'}</p>
{resolution.hasIdentity && (
<p>Token ID: {resolution.tokenId}</p>
)}
</div>
)}
</div>
);
}
Webhook Integration
Set up real-time event notifications for your application.
Step 1: Webhook Server Setup
const express = require('express');
const crypto = require('crypto');
const app = express();
app.use(express.json());
// Webhook verification middleware
const verifyWebhook = (req, res, next) => {
const signature = req.headers['x-fiatsend-signature'];
const payload = JSON.stringify(req.body);
const secret = process.env.WEBHOOK_SECRET;
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
if (signature !== expectedSignature) {
return res.status(401).send('Unauthorized');
}
next();
};
// Payment webhook
app.post('/webhook/payment', verifyWebhook, async (req, res) => {
const { event, data } = req.body;
switch (event) {
case 'payment.completed':
await handlePaymentCompleted(data);
break;
case 'payment.failed':
await handlePaymentFailed(data);
break;
default:
console.log('Unknown event:', event);
}
res.json({ success: true });
});
// Withdrawal webhook
app.post('/webhook/withdrawal', verifyWebhook, async (req, res) => {
const { event, data } = req.body;
switch (event) {
case 'withdrawal.completed':
await handleWithdrawalCompleted(data);
break;
case 'withdrawal.failed':
await handleWithdrawalFailed(data);
break;
}
res.json({ success: true });
});
// Identity webhook
app.post('/webhook/identity', verifyWebhook, async (req, res) => {
const { event, data } = req.body;
if (event === 'identity.verified') {
await handleIdentityVerified(data);
}
res.json({ success: true });
});
// Event handlers
async function handlePaymentCompleted(data) {
console.log('Payment completed:', data.transactionId);
// Update order status in database
await updateOrderStatus(data.orderId, 'paid');
// Send confirmation email
await sendOrderConfirmation(data.orderId);
// Update inventory
await updateInventory(data.orderId);
}
async function handlePaymentFailed(data) {
console.log('Payment failed:', data.transactionId);
// Update order status
await updateOrderStatus(data.orderId, 'failed');
// Send failure notification
await sendPaymentFailureNotification(data.orderId);
}
async function handleWithdrawalCompleted(data) {
console.log('Withdrawal completed:', data.withdrawalId);
// Update user balance
await updateUserBalance(data.userId, data.amount);
// Send confirmation
await sendWithdrawalConfirmation(data.userId);
}
async function handleIdentityVerified(data) {
console.log('Identity verified:', data.walletAddress);
// Update user verification status
await updateUserVerificationStatus(data.walletAddress, 'verified');
// Send verification confirmation
await sendVerificationConfirmation(data.walletAddress);
}
app.listen(3000, () => {
console.log('Webhook server running on port 3000');
});
Step 2: Webhook Configuration
// Configure webhooks for your application
const webhookConfig = {
payment: {
url: 'https://your-app.com/webhook/payment',
events: ['payment.completed', 'payment.failed']
},
withdrawal: {
url: 'https://your-app.com/webhook/withdrawal',
events: ['withdrawal.completed', 'withdrawal.failed']
},
identity: {
url: 'https://your-app.com/webhook/identity',
events: ['identity.verified']
}
};
// Register webhooks
const registerWebhooks = async () => {
for (const [type, config] of Object.entries(webhookConfig)) {
await sdk.webhooks.create({
type,
url: config.url,
events: config.events,
secret: process.env.WEBHOOK_SECRET
});
}
};
Step 3: Webhook Testing
// Test webhook locally using ngrok
const ngrok = require('ngrok');
async function testWebhooks() {
// Start ngrok tunnel
const url = await ngrok.connect(3000);
console.log('Webhook URL:', url);
// Update webhook URLs
await sdk.webhooks.update('payment', {
url: `${url}/webhook/payment`
});
// Test webhook
await sdk.webhooks.test('payment', {
event: 'payment.completed',
data: {
transactionId: 'test-123',
orderId: 'order-123',
amount: 100,
currency: 'USDT'
}
});
}
Best Practices
Security
-
API Key Management
- Store API keys securely using environment variables
- Rotate keys regularly
- Use different keys for different environments
-
Webhook Security
- Always verify webhook signatures
- Use HTTPS for webhook endpoints
- Implement rate limiting
-
Error Handling
- Implement comprehensive error handling
- Log errors for debugging
- Provide user-friendly error messages
Performance
-
Caching
- Cache frequently accessed data
- Use Redis for session storage
- Implement proper cache invalidation
-
Rate Limiting
- Implement client-side rate limiting
- Handle rate limit responses gracefully
- Use exponential backoff for retries
-
Monitoring
- Monitor API response times
- Set up alerts for failures
- Track key metrics
User Experience
-
Loading States
- Show loading indicators during API calls
- Provide progress feedback for long operations
- Handle network timeouts gracefully
-
Error Messages
- Provide clear, actionable error messages
- Suggest solutions for common errors
- Implement retry mechanisms
-
Offline Support
- Cache critical data locally
- Queue operations when offline
- Sync when connection is restored
Need Help?
- Documentation: API Reference
- Support: Discord (opens in a new tab)
- GitHub: Examples (opens in a new tab)
- Email: support@fiatsend.network