Virtual Accounts

Learn how to use a virtual account.

Virtual accounts are generated account details (account number and bank) that allow Flutterwave merchants to receive payments from customers via bank transfer. Virtual accounts are currently only available in naira.

🚧

Virtual Cards and Virtual Accounts are Unrelated

Virtual accounts allow you to receive payments into your Flutterwave wallet via bank transfer, while virtual cards allow you to make online payments.

This feature is open to all flutterwave merchants and is supported in all countries.

Types of Virtual Accounts

There are two types of virtual accounts that Flutterwave allows you to create.

  1. Dynamic or Temporary Virtual Account.
  2. Static or Permanent Virtual Account.

Dynamic Virtual Account

Dynamic virtual accounts are temporary, single-use accounts typically created to collect payments for one-time transactions. These accounts are valid only for a short period and expire after they are used. They are ideal for situations where immediate payment collection is required and the reconciliation of transactions needs to be seamless.

📘

Virtual Account Expiration

By default, the virtual account expires after one hour after creation. A new account can then be generated or re-assigned to your customer.

Creating a Dynamic Virtual Account

To create a virtual account, send a request to the create virtual account endpoint, specifying an amount greater than zero and the customer's email.

The response will have a response_code of 02 and a response_message of "Transaction in progress", indicating that the account number has been successfully created, and we're ready to accept payments for you. The response body will also contain the bank name and account number. You can display these details to your customer to facilitate payment via bank transfer

curl --request POST 'https://api.flutterwave.com/v3/virtual-account-numbers' \
  --header 'Authorization: Bearer YOUR_SECRET_KEY' \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "email":"[email protected]",
    "amount": 100,
    "tx_ref":"apex_tx_ref-002201",
    "phonenumber":"08100000000",
    "firstname":"Flutterwave",
    "lastname":"Developers",
    "narration":"Kids Foundation"
}'

{
    "status": "success",
    "message": "Virtual account created",
    "data": {
        "response_code": "02",
        "response_message": "Transaction in progress",
        "flw_ref": "FLW-d3f049dad861484a9e1504309e07f615",
        "order_ref": "URF_1726598678168_8608035",
        "account_number": "9949162075",
        "frequency": "N/A",
        "bank_name": "WEMA BANK",
        "created_at": "2024-09-17 18:44:38",
        "expiry_date": "2024-09-17 19:44:38",
        "note": "Please make a bank transfer to testing functions FLW",
        "amount": "101.40"
    }
}

📘

Account Name Format

Whenever the virtual account holder information is resolved, the account name will be displayed as: Flutterwave/{narration}.

For example: Flutterwave/Kids Foundation


Once the customer completes a payment, we will send a webhook to notify you of the transaction status.

Possible Error / Issues

If your transaction fee preferences are set to require customers to cover the fees, ensure that the additional fees are included in the amount the customer needs to transfer. Otherwise, the payment will fail, and the customer will be refunded.

In cases where the fee was not added on creating the virtual account with this preference set. we will send a webhook indicating the under or overpayment.

{
  "event": "charge.completed",
  "data": {
    "id": 1549484859,
    "tx_ref": "apex_tx_ref-002201",
    "flw_ref": "000030240917194839337261344044",
    "device_fingerprint": "N/A",
    "amount": 100,
    "currency": "NGN",
    "charged_amount": 101.4,
    "app_fee": 1.4,
    "merchant_fee": 0,
    "processor_response": "NGN100was underpaid and refunded",
    "auth_model": "AUTH",
    "ip": "::ffff:172.16.2.18",
    "narration": "testing functions",
    "status": "failed",
    "payment_type": "bank_transfer",
    "created_at": "2024-09-17T18:49:30.000Z",
    "account_id": 47337,
    "customer": {
      "id": 979921436,
      "name": "Flutterwave Developers",
      "phone_number": "08100000000",
      "email": "[email protected]",
      "created_at": "2024-09-17T17:07:39.000Z"
    }
  },
  "event.type": "BANK_TRANSFER_TRANSACTION"
}

Static Virtual Accounts

Static Virtual Accounts can be useful if you're building an app with a wallet system. You can issue a virtual account number for each customer to use in funding their wallet:

  1. You generate a virtual account for the customer with our API and provide them with the details.
  2. The customer transfers to their assigned account.
  3. We send you a webhook notifying you that we've received the payment.
  4. You credit the needed funds to the customer's wallet on your platform.

🚧

BVN is Required for Static Virtual Account

When generating a static virtual account, you'll need to provide a BVN. If you've created a static virtual account without a BVN in the past, you'll need to set a BVN. You'll find more details in our announcement.

Creating a Static Virtual Account

You can generate a virtual account with this endpoint, which is supported in our backend SDKs. You will need to specify the email to be associated with the account number (the customer's email). If you're creating a static virtual number, their bvn will be required.

curl --request POST 'https://api.flutterwave.com/v3/virtual-account-numbers' \
  --header 'Authorization: Bearer YOUR_SECRET_KEY' \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "email":"[email protected]",
    "tx_ref":"apex_tx_ref-002201",
    "phonenumber":"08100000000",
    "is_permanent": true,
    "firstname":"Flutterwave",
    "lastname":"Developers",
    "narration":"Kids Foundation",
    "bvn": "1234567890"
}'

{
    "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 Hefa Cust No.20 FLW",
        "amount": "0.00"
    }
}
{
  "status": "error",
  "message": "BVN is required for static account number",
  "data": null
}
// Install with: composer require flutterwavedev/flutterwave-v3

$flw = new \Flutterwave\Rave(getenv('FLW_SECRET_KEY')); // Set `PUBLIC_KEY` as an environment variable
$virtualAccountsService = new \Flutterwave\VirtualAccount();
$details = [
    "email" => "[email protected]",
    "tx_ref" =>"apex_tx_ref-002201",
    "phonenumber" => "08100000000",
    "is_permanent" => true,
    "firstname" =>"Flutterwave",
    "lastname" => "Developers",
    "narration" =>"Kids Foundation",
    "bvn" => "1234567890"
];
$response = $virtualAccountsService->createVirtualAccount($details);
# Install with: pip install rave_python

import os
from rave_python import Rave

rave = Rave(os.getenv("FLW_PUBLIC_KEY"), os.getenv("FLW_SECRET_KEY"))
res = rave.VirtualAccount.create({
    "email": "[email protected]",
  	"tx_ref":"apex_tx_ref-002201",
    "phonenumber":"08100000000",
    "is_permanent": true,
    "firstname":"Flutterwave",
    "lastname":"Developers",
    "narration":"Kids Foundation",
    "bvn": "1234567890"
})
print(res)

There are more options available when generating static virtual accounts. You can also set the following:

ParametersDefinition
amountThe amount to be collected per transaction.
frequencyThe number of times the account can be used before expiring.
narrationThe name should be shown when the account details are fetched.
tx_refThe transaction reference that should be returned whenever a transfer is made into the account.

Additionally, you can create virtual accounts in bulk and retrieve their details at any time. For more details, see the API reference.

Virtual Account Webhooks

When ever a transfer is made to the virtual account, we'll send you a webhook that 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": "[email protected]",
      "created_at": "2024-09-17T20:38:43.000Z"
    }
  },
  "event.type": "BANK_TRANSFER_TRANSACTION"
}

Now, your webhook handler can handle the event and credit the customer's wallet. For help setting up webhooks, see our guide on webhooks.