Documentation Index Fetch the complete documentation index at: https://docs.fossapay.com/llms.txt
Use this file to discover all available pages before exploring further.
Overview
This guide walks you through accepting payments using FossaPay’s Fiat Wallet API. You’ll learn how to create fiat wallets, receive payments, handle webhooks, and reconcile transactions.
Quick Start
Create a Fiat Wallet
Generate a unique account number for your customer
Share Account Details
Provide the account number to your customer
Receive Payment
Customer transfers money to the wallet account
Get Notified
Receive instant webhook notification
Credit Customer
Update your customer’s balance or fulfill their order
Creating Fiat Wallets
For Wallet Top-ups
curl -X POST https://api-production.fossapay.com/api/v1/wallets/fiat/create \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"customerId": "cus_123",
"walletName": "Main Wallet",
"walletReference": "wallet_ref_001"
}'
Response:
{
"success" : true ,
"message" : "Fiat wallet created successfully" ,
"data" : {
"walletId" : "wal_abc123xyz" ,
"bankName" : "Wema Bank" ,
"bankCode" : "035" ,
"accountNumber" : "1234567890"
}
}
For Invoice Payments
# Create fiat wallet for customer if not exists
curl -X POST https://api-production.fossapay.com/api/v1/wallets/fiat/create \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"customerId": "cus_456",
"walletName": "Invoice Wallet",
"walletReference": "invoice_INV-001"
}'
Response:
{
"success" : true ,
"message" : "Fiat wallet created successfully" ,
"data" : {
"walletId" : "wal_xyz789" ,
"bankName" : "Wema Bank" ,
"bankCode" : "035" ,
"accountNumber" : "0987654321"
}
}
Share the account details with the customer for payment.
Displaying Account Details
Email Template
< div style = "font-family: Arial, sans-serif;" >
< h2 > Payment Instructions </ h2 >
< p > Please transfer < strong > ₦50,000 </ strong > to the account below: </ p >
< div style = "background: #f5f5f5; padding: 20px; border-radius: 8px;" >
< p >< strong > Bank: </ strong > Wema Bank </ p >
< p >< strong > Account Number: </ strong > 1234567890 </ p >
< p >< strong > Account Name: </ strong > ACME Corp - Jane Smith </ p >
< p >< strong > Amount: </ strong > ₦50,000 </ p >
</ div >
< p >< small > This account is unique to your transaction and expires on Jan 31, 2024 </ small ></ p >
</ div >
In-App Display (React)
function PaymentInstructions ({ account , amount }) {
const [ copied , setCopied ] = useState ( false );
const copyAccountNumber = () => {
navigator . clipboard . writeText ( account . account_number );
setCopied ( true );
setTimeout (() => setCopied ( false ), 2000 );
};
return (
< div className = "payment-card" >
< h3 > Transfer { formatCurrency ( amount ) } to: </ h3 >
< div className = "account-details" >
< div className = "detail-row" >
< span > Bank: </ span >
< strong > { account . bank_name } </ strong >
</ div >
< div className = "detail-row" >
< span > Account Number: </ span >
< div >
< strong > { account . account_number } </ strong >
< button onClick = { copyAccountNumber } >
{ copied ? 'Copied!' : 'Copy' }
</ button >
</ div >
</ div >
< div className = "detail-row" >
< span > Account Name: </ span >
< strong > { account . account_name } </ strong >
</ div >
</ div >
< p className = "note" >
Payments are confirmed instantly
</ p >
</ div >
);
}
Handling Webhook Notifications
Set Up Webhook Endpoint
const express = require ( 'express' );
const crypto = require ( 'crypto' );
const app = express ();
app . use ( express . json ());
app . post ( '/webhooks/fossapay' , async ( req , res ) => {
// 1. Verify signature
if ( ! verifySignature ( req . body , req . headers [ 'x-fossapay-signature' ])) {
return res . status ( 401 ). json ({ error: 'Invalid signature' });
}
// 2. Respond immediately
res . status ( 200 ). json ({ received: true });
// 3. Process asynchronously
const { event , data } = req . body ;
if ( event === 'deposit.completed' ) {
await handlePaymentReceived ( data );
}
});
async function handlePaymentReceived ( payment ) {
console . log ( 'Payment received:' , payment );
// Get customer from metadata
const customerId = payment . metadata . customer_id ;
// Create transaction record
await db . transactions . create ({
id: payment . transaction_id ,
customer_id: customerId ,
amount: payment . amount ,
type: 'credit' ,
status: 'completed' ,
reference: payment . reference ,
created_at: payment . created_at
});
// Credit customer wallet
await db . wallets . increment ({
customer_id: customerId ,
amount: payment . amount
});
// Send notification
await sendNotification ( customerId , {
title: 'Payment Received!' ,
message: `Your wallet has been credited with ₦ ${ payment . amount . toLocaleString () } `
});
// Send confirmation email
await sendEmail ( payment . metadata . customer_email , {
subject: 'Payment Confirmation' ,
template: 'payment-received' ,
data: {
amount: payment . amount ,
reference: payment . reference ,
date: payment . created_at
}
});
}
function verifySignature ( payload , signature ) {
const secret = process . env . FOSSAPAY_WEBHOOK_SECRET ;
const hash = crypto
. createHmac ( 'sha256' , secret )
. update ( JSON . stringify ( payload ))
. digest ( 'hex' );
return hash === signature ;
}
Webhook Payload Example
{
"event" : "deposit.completed" ,
"event_id" : "evt_abc123" ,
"timestamp" : "2024-01-15T10:30:45Z" ,
"data" : {
"transaction_id" : "txn_001" ,
"wallet_id" : "wal_abc123" ,
"account_number" : "1234567890" ,
"amount" : 50000 ,
"currency" : "NGN" ,
"sender_name" : "John Doe" ,
"sender_account" : "0987654321" ,
"sender_bank" : "GTBank" ,
"sender_bank_code" : "058" ,
"reference" : "FP-20240115-001" ,
"narration" : "Payment from John Doe" ,
"customer_id" : "cus_123" ,
"created_at" : "2024-01-15T10:30:40Z"
}
}
Querying Transaction Status
Get Transaction by ID
const transaction = await client . transactions . get ( 'txn_001' );
console . log ( transaction );
// {
// "status": "success",
// "data": {
// "transaction_id": "txn_001",
// "amount": 50000,
// "status": "successful",
// "sender_name": "John Doe",
// "created_at": "2024-01-15T10:30:40Z"
// }
// }
List Virtual Account Transactions
const response = await fetch ( 'https://api-production.fossapay.com/api/v1/wallets/fiat/va_abc123/transactions?startDate=2024-01-01&endDate=2024-01-31&limit=50' , {
headers: {
'x-api-key' : 'YOUR_API_KEY'
}
});
const data = await response . json ();
const transactions = data . transactions ;
Overpayment
async function handlePaymentReceived ( payment ) {
const expectedAmount = await getExpectedAmount ( payment . metadata . invoice_id );
if ( payment . amount > expectedAmount ) {
// Overpayment detected
const excess = payment . amount - expectedAmount ;
// Credit invoice amount
await creditInvoice ( payment . metadata . invoice_id , expectedAmount );
// Credit excess to wallet
await creditWallet ( payment . metadata . customer_id , excess );
// Notify customer
await sendNotification ( payment . metadata . customer_id , {
title: 'Overpayment Detected' ,
message: `You paid ₦ ${ excess . toLocaleString () } extra. We've added it to your wallet.`
});
} else {
// Normal payment
await creditInvoice ( payment . metadata . invoice_id , payment . amount );
}
}
Underpayment
if ( payment . amount < expectedAmount ) {
const shortfall = expectedAmount - payment . amount ;
// Mark invoice as partially paid
await db . invoices . update ( payment . metadata . invoice_id , {
status: 'partially_paid' ,
amount_paid: payment . amount ,
amount_remaining: shortfall
});
// Notify customer
await sendNotification ( payment . metadata . customer_id , {
title: 'Partial Payment Received' ,
message: `You paid ₦ ${ payment . amount . toLocaleString () } . ₦ ${ shortfall . toLocaleString () } remaining.`
});
}
Duplicate Payments
async function handlePaymentReceived ( payment ) {
// Check if already processed
const existing = await db . transactions . findOne ({
id: payment . transaction_id
});
if ( existing ) {
console . log ( 'Duplicate webhook, skipping' );
return ;
}
// Process payment
// ...
}
Best Practices
Set Expiry for Temporary Accounts
Always set expiry dates for invoice payments: expires_at : new Date ( Date . now () + 7 * 24 * 60 * 60 * 1000 ) // 7 days
Verify Webhook Signatures
Never process webhooks without signature verification.
Handle Webhooks Idempotently
Always check for duplicates using transaction_id or event_id.
Always notify customers when payments are received.
Testing
Simulate Payment in Sandbox
curl -X POST https://api-production.fossapay.com/api/v1/sandbox/simulate-payment \
-H "x-api-key: fp_test_sk_xxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"virtual_account_number": "1234567890",
"amount": 50000,
"sender_name": "Test Customer",
"sender_account": "0000000001",
"sender_bank": "058"
}'
Next Steps
Create Fiat Wallet API API reference for creating fiat wallets
Webhooks Guide Learn more about handling webhooks
Reconciliation Guide Best practices for reconciliation
Wallets Concept Understand wallets in depth