DEV Community

Cover image for Understanding Virtual Accounts with Flutterwave

Understanding Virtual Accounts with Flutterwave

If you’ve ever tried collecting payments from customers across different African countries, you know how tricky it can get, especially when dealing with local payments. Nigerians prefer bank transfers, Kenyans rely on M-Pesa, Ghanaians use Mobile Money, and South Africans lean toward cards or electronic fund transfers (EFTs). Even within a single country, customers still use different payment methods.

Africa’s complex payment ecosystem makes it hard for businesses to manage and reconcile incoming payments efficiently. That’s where virtual account management comes in. They simplify things by bringing all payment methods into one unified flow that’s easier to track and reconcile.

In this guide, you’ll learn what virtual accounts are, how they work, and how to integrate them into your application using Flutterwave.

What is a Virtual Account?

A virtual account is a unique bank account automatically generated for a specific customer or transaction. It allows businesses to send, receive, and hold money online, which makes the reconciliation process simple. Behind the scenes, all payments made to these generated accounts go into one primary account that you control. It’s like giving every customer their own unique bank account number, while all the funds still flow into your main account.

Once customers transfer money into their assigned virtual accounts, your system receives an instant webhook notification from Flutterwave. You can then automatically confirm the payment and process their order. No manual tracking is needed.

There are two main types of Flutterwave virtual accounts:

Static vs Dynamic Virtual Accounts

  1. Static Virtual Accounts: These accounts are permanently tied to a specific customer. They are perfect for businesses that need to track payments per customer, such as wallet top-ups or recurring payments.
  2. Dynamic Virtual Accounts: These are temporary accounts created for one-time transactions. They expire after a short period and work best for time-sensitive payments such as e-commerce checkouts or invoice payments.

Benefits of Virtual Accounts

Now that we’ve covered what virtual accounts are, let’s look at the value they bring to your payment operations:

  • Simplified Reconciliation: Unlike physical accounts, virtual accounts make it easy to track who made a payment. Each customer or order gets its own account, so when money comes in, you immediately know the source without manually matching transactions or dealing with confusing references.
  • Cost Efficiency: Instead of opening multiple bank accounts for different customers or regions, you can generate thousands of virtual accounts under one main account. It is cheaper, cleaner, and removes the hassle of managing traditional bank accounts.
  • Better Customer Experience: Customers get a unique virtual account number just for them, so they do not need to type reference codes or send payment screenshots. The payment is automatically confirmed, which builds trust and reduces confusion.
  • Manage Multiple Currencies: Virtual accounts support cash flows with multiple currencies, including NGN and GHS. This account structure allows customers to pay in their local currency, while you can choose to settle in your preferred currency.

How Do Virtual Accounts Work with Flutterwave?

The following overview explains the process behind how Flutterwave manages and operates virtual accounts:

How Virtual Account Works

  1. Create a Virtual Account: The process starts with you creating a virtual account for a customer. This account is tied to their personal details such as name, email, and phone number.
  2. Customer Makes a Payment: You display the generated account details (virtual bank account name and account number) at checkout or on any payment channel so the customer can transfer funds using their preferred payment method.
  3. Payment Receipt: Once the transfer is complete, the payment is detected and credited to your Flutterwave balance.
  4. Webhook Notification: Flutterwave sends a webhook notification to your server with complete payment details, including who paid, how much, when, and which virtual account received the payment.
  5. Process the Payment: After confirming the payment, your system can automatically update the customer’s balance, mark an invoice as paid, or trigger delivery, depending on your workflow.

See how merchants like PiggyVest use Flutterwave to boost growth and customer adoption. Watch their story below:

https://www.youtube.com/watch?v=PA-7hs6fcIU&

https://youtu.be/PA-7hs6fcIU

How to Set Up Virtual Accounts with Flutterwave

Before you start implementing virtual accounts, make sure you have the following in place:

  • Verified Flutterwave Account: Complete your KYC verification. This usually involves providing your business registration documents and bank details so Flutterwave can process settlements.
  • API Credentials: You need your Flutterwave secret key for backend operations. Always keep your keys secure and never expose them in client-side code.
  • Webhook Endpoint: Set up a secure server endpoint to receive notifications from Flutterwave about transactions, failed charges, and other events.

To implement virtual accounts, follow the steps below:

Note: This implementation uses the Flutterwave v3 API.

Step 1: Create a Virtual Account

Depending on your use case, you can create either a dynamic or static virtual account.

To create a dynamic NGN virtual account, send a POST request to the create virtual account endpoint with the customer’s details and amount:

curl --request POST 'https://api.flutterwave.com/v3/virtual-account-numbers' \
  --header 'Authorization: Bearer FLW_SECRET_KEY' \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "email":"john.doe@example.com",
    "amount": 1000,
    "currency": "NGN",
    "tx_ref": "apex_tx-002201",
    "is_permanent": false,
    "phonenumber":"08100000000",
    "firstname":"John",
    "lastname":"Doe",
    "narration":"virtual account test"
}'
Enter fullscreen mode Exit fullscreen mode

For a static virtual account, the request is slightly different. You need to include the customer’s Bank Verification Number (BVN) or National Identification Number (NIN) and set the is_permanent flag to true.

curl --request POST 'https://api.flutterwave.com/v3/virtual-account-numbers' \
  --header 'Authorization: Bearer FLW_SECRET_KEY' \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "email":"john.doe@example.com",
    "tx_ref":"apex_tx_ref-002201",
    "phonenumber":"08100000000",
    "is_permanent": true,
    "currency": "NGN",
    "firstname":"John",
    "lastname":"Doe",
    "narration":"virtual account test",
    "bvn": "1234567890"
}'
Enter fullscreen mode Exit fullscreen mode

Check out the virtual accounts documentation to learn more about supported countries and available parameters for customizing the account.

Once the request is successful, you’ll receive a response containing a unique account number and the bank name where the customer can send funds.

{
    "status": "success",
    "message": "Virtual account created",
    "data": {
        "response_code": "02",
        "response_message": "Transaction in progress",
        "flw_ref": "FLW-211b4eea7b3d465d8797fa354c127f79",
        "order_ref": "URF_1726608838391_595235",
        "account_number": "8548497837",
        "frequency": "N/A",
        "bank_name": "WEMA BANK",
        "created_at": "2024-09-17 21:33:58",
        "expiry_date": "N/A",
        "note": "Please make a bank transfer to testing functions",
        "amount": "1000"
    }
}
Enter fullscreen mode Exit fullscreen mode

For a dynamic virtual account, the response includes an expiry_date, which captures the time the virtual account expires. It is valid for one hour.

Step 2: Display the Virtual Account to the Customer

Once you receive the generated account details, update your user interface to show the bank name, account number, amount, and any other information the customer needs to complete the transfer.

Step 3: Handle Webhook Notifications

When a customer makes a payment to their virtual account, Flutterwave sends a webhook notification to the URL you configured. Always verify the event and payment status before providing value or updating the customer’s balance.

A sample webhook payload looks like this:

{
  "event": "charge.completed",
  "data": {
    "id": 1549596704,
    "tx_ref": "apex_tx_ref-002201",
    "flw_ref": "000030240917214228820592962026",
    "device_fingerprint": "N/A",
    "amount": 100,
    "currency": "NGN",
    "charged_amount": 101.4,
    "app_fee": 1.4,
    "merchant_fee": 0,
    "processor_response": "success",
    "auth_model": "AUTH",
    "ip": "::ffff:172.16.85.133",
    "narration": "Kids Foundation",
    "status": "successful",
    "payment_type": "bank_transfer",
    "created_at": "2024-09-17T20:47:20.000Z",
    "account_id": 47337,
    "customer": {
      "id": 980070204,
      "name": "Flutterwave Developers",
      "phone_number": "08100000000",
      "email": "abraham@flutterwavego.com",
      "created_at": "2024-09-17T20:38:43.000Z"
    }
  },
  "event.type": "BANK_TRANSFER_TRANSACTION"
}
Enter fullscreen mode Exit fullscreen mode

With what virtual accounts are, their types, and how to implement them now covered, let’s look at a few key points to help you build a reliable system.

Best Practices for Virtual Accounts

When creating virtual accounts for your customers, it’s important to follow these best practices:

  1. Give Each Customer a Unique Account: Avoid reusing virtual account numbers. Each customer should have a unique account number that stays permanently tied to them. This removes confusion and makes reconciliation straightforward.
  2. Store Account Data Securely: Encrypt sensitive details before saving them to your database. Treat virtual account information with the same level of security as payment card data.
  3. Use Webhooks Reliably: Always validate webhook payloads using your Flutterwave secret hash to prevent spoofing. Add idempotency so that if Flutterwave retries a webhook, your system does not double-credit a customer.
  4. Handle Failed or Delayed Payments Gracefully: Network issues or user mistakes can happen. Include a fallback check that queries the Flutterwave transaction verification endpoint when webhooks fail.
  5. Clean Up Temporary Accounts: If you use dynamic accounts, regularly deactivate or remove expired ones to keep your system organized. Keep logs of failed account creation attempts so you can spot patterns and work with Flutterwave support when needed.
  6. Communicate Clearly with Customers: Do not assume customers already understand virtual accounts. When showing payment details, include simple, step-by-step instructions such as: “Transfer the exact amount to this account number using your bank’s mobile app, internet banking, or by visiting any bank branch.” Always show the bank name clearly since customers need it to complete their transfer.

Wrapping Up

Virtual accounts give businesses a scalable and automated way to collect payments across borders, especially in regions like Africa, where bank transfers are still the most common digital payment method. They remove the hassle of manual reconciliation while offering customers a familiar and seamless payment experience.

With Flutterwave, setting up virtual accounts is fully API-driven, which makes it ideal for developers who want a fast and reliable integration. The combination of permanent accounts for recurring payments and temporary accounts for one-time transactions gives you the flexibility to handle almost any payment scenario. Whether you’re building a subscription service, a marketplace, a wallet app, or an invoicing system, virtual accounts can streamline your entire payment process.

Ready to start building smarter payment experiences with Flutterwave virtual accounts? Create an account now.

Top comments (0)