McTaba Labs logo
Software EngineeringThe WhiteboardProjectsEventsMasterclassesWebinarsLearn
Book Call

500+ developers already in the community

Get answers, find accountability partners, and stay ahead

Join Discord →
McTaba Labs logo

Educating 1 million students in tech by 2030.

Programs

  • HighSchool Graduates Launchpad
  • AI & Business Automation
  • Corporate Training
  • McTaba Story & Mission
  • Full-Stack Curriculum
  • Course Pricing & Plans
  • Frequently Asked Questions

Resources

  • Join Discord Community
  • Tech Events in Nairobi
  • Masterclasses
  • Free Resources
  • Learn
  • Campus
  • Africa Tech Talent Report
  • Partner With Us
  • Start a Project
  • Our Work

Contact Us

  • 124 Manyani East Road, Lavington, Nairobi
  • +254727795603
  • Book a Discovery Call

Office Hours:

Mon-Fri: 9am - 2pm EAT

Sat: 10am - 12pm EAT

© 2027 McTaba Labs - A product of Edtech Industries. All rights reserved.

Privacy PolicyTerms of ServiceRefund Policy
  1. Learn
  2. Paystack for African Developers
  3. Paystack Dedicated Virtual Accounts: Complete Implementation
Bonaventure OgetoBy Bonaventure Ogeto|Updated July 20, 2026

Paystack Dedicated Virtual Accounts: Complete Implementation

Create a Paystack Dedicated Virtual Account by POSTing to /dedicated_account with a customer code, preferred_bank, and optional BVN. Paystack returns a permanent bank account number at a partner bank. When anyone transfers to that account, Paystack fires a dedicatedaccount.assign.success or charge.success webhook with the deposit details. Use this for wallet top-ups, marketplace deposits, and any flow where customers need a permanent account number.

In this article

  1. 1. What Is a Dedicated Virtual Account?
  2. 2. Creating a Dedicated Virtual Account
  3. 3. Handling Deposits via Webhooks
  4. 4. Displaying Account Details to Customers
  5. 5. BVN and KYC Requirements
  6. 6. Deactivating and Managing DVAs
  7. 7. Stay Up to Date

What Is a Dedicated Virtual Account?

A Dedicated Virtual Account (DVA) is a bank account number that Paystack creates at a partner bank (like Wema Bank or Providus Bank) and assigns to a specific customer on your platform. Unlike the temporary accounts generated during bank transfer checkout, DVAs are permanent. The customer can use the same account number for every deposit.

When someone transfers money to a DVA, the funds arrive in your Paystack balance, and Paystack tells you (via webhook) which customer the account belongs to and how much was deposited. Your code then credits the customer's internal balance, activates a service, or triggers whatever fulfillment logic you need.

DVAs are the foundation of wallet-based applications, marketplace deposit systems, and savings platforms built on Paystack.

Creating a Dedicated Virtual Account

First, create a Paystack customer (if you have not already). Then create the DVA for that customer.

// Step 1: Create a Paystack customer
async function createCustomer(email, firstName, lastName, phone) {
  var response = await fetch('https://api.paystack.co/customer', {
    method: 'POST',
    headers: {
      Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      email: email,
      first_name: firstName,
      last_name: lastName,
      phone: phone,
    }),
  });

  var data = await response.json();
  return data.data.customer_code; // e.g., 'CUS_xxxxxxxx'
}

// Step 2: Create a DVA for the customer
async function createDVA(customerCode, preferredBank) {
  var response = await fetch('https://api.paystack.co/dedicated_account', {
    method: 'POST',
    headers: {
      Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      customer: customerCode,
      preferred_bank: preferredBank, // e.g., 'wema-bank' or 'providus-bank'
    }),
  });

  var data = await response.json();

  if (data.status) {
    return {
      accountName: data.data.account_name,
      accountNumber: data.data.account_number,
      bankName: data.data.bank.name,
    };
  }

  throw new Error('Failed to create DVA: ' + data.message);
}

// Full flow
var customerCode = await createCustomer('ade@example.com', 'Ade', 'Ogunleye', '08012345678');
var dva = await createDVA(customerCode, 'wema-bank');
console.log('Account: ' + dva.accountNumber + ' at ' + dva.bankName);
// Show this to the customer: "Transfer to 0123456789 at Wema Bank"

Get weekly developer tips

Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.

No spam. Unsubscribe anytime.

Handling Deposits via Webhooks

When someone transfers to a DVA, Paystack fires a webhook. The exact event type may be charge.success with a dedicated_account channel or a dedicated account-specific event. Your webhook handler credits the customer balance:

app.post('/webhooks/paystack', async function(req, res) {
  res.sendStatus(200);

  var event = req.body;

  if (event.event === 'charge.success') {
    var data = event.data;
    var customerCode = data.customer.customer_code;
    var amount = data.amount; // In kobo
    var reference = data.reference;

    // Find the customer in your database
    var user = await db.users.findByPaystackCode(customerCode);

    if (user) {
      // Credit the customer's wallet
      await db.wallets.credit(user.id, amount, {
        source: 'dva_deposit',
        reference: reference,
      });

      console.log('Credited ' + amount + ' kobo to user ' + user.id);
    } else {
      console.log('Deposit from unknown customer: ' + customerCode);
      // Log for manual review
    }
  }
});

Make your deposit handler idempotent. If the same webhook arrives twice (Paystack retries on failure), do not credit the customer twice. Check if the reference has already been processed before crediting.

Displaying Account Details to Customers

After creating a DVA, store the account details in your database and display them to the customer whenever they want to deposit:

// Store DVA details after creation
await db.users.update(user.id, {
  dva_account_number: dva.accountNumber,
  dva_bank_name: dva.bankName,
  dva_account_name: dva.accountName,
});

// Display in your app
app.get('/api/my-account-details', async function(req, res) {
  var user = await db.users.findById(req.userId);

  res.json({
    accountNumber: user.dva_account_number,
    bankName: user.dva_bank_name,
    accountName: user.dva_account_name,
    instructions: 'Transfer any amount to this account to top up your wallet.',
  });
});

Include clear instructions: "Transfer to [Account Number] at [Bank Name]. Your wallet will be credited automatically." Add a copy button for the account number so the customer can easily paste it into their banking app.

BVN and KYC Requirements

Some DVA configurations require the customer's BVN (Bank Verification Number) for compliance. If required, you validate the customer's identity with Paystack before creating the DVA:

// Validate customer identity
async function validateCustomer(customerCode, bvn, bankCode, accountNumber) {
  var response = await fetch('https://api.paystack.co/customer/' + customerCode + '/identification', {
    method: 'POST',
    headers: {
      Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      type: 'bvn',
      value: bvn,
      country: 'NG',
      bank_code: bankCode,
      account_number: accountNumber,
    }),
  });

  var data = await response.json();
  return data;
}

BVN validation is asynchronous. Paystack verifies the BVN with NIBSS and notifies you via webhook when validation is complete. Only create the DVA after successful validation.

The KYC requirements depend on your Paystack account type, the partner bank, and regulatory requirements. Check with Paystack for the current requirements for your specific setup.

Deactivating and Managing DVAs

You can deactivate a DVA when a customer closes their account or when you need to stop accepting deposits for a specific customer:

// Deactivate a DVA
async function deactivateDVA(dedicatedAccountId) {
  var response = await fetch(
    'https://api.paystack.co/dedicated_account/' + dedicatedAccountId,
    {
      method: 'DELETE',
      headers: {
        Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
      },
    }
  );

  return response.json();
}

// List all DVAs
async function listDVAs() {
  var response = await fetch('https://api.paystack.co/dedicated_account', {
    headers: {
      Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
    },
  });

  var data = await response.json();
  return data.data;
}

After deactivation, transfers to that account number will be rejected. Make sure to communicate this to the customer and remove the account details from their profile in your app.

For reconciliation patterns and handling mismatched deposits, see reconciling DVA deposits. For assigning DVAs during user registration, see assigning DVAs at signup.

Stay Up to Date

Paystack expands DVA availability and features periodically. We update these guides when things change.

Sign up for the McTaba newsletter to stay informed about Paystack DVA updates.

Key Takeaways

  • ✓A Dedicated Virtual Account (DVA) is a permanent bank account number assigned to a specific customer on your platform.
  • ✓When anyone transfers to a DVA, the funds go to your Paystack balance, and you receive a webhook identifying the customer and amount.
  • ✓DVAs are available for NGN transactions through partner banks like Wema Bank and Providus Bank.
  • ✓Creating a DVA requires a Paystack customer code. You can optionally provide the customer BVN for additional verification.
  • ✓DVAs are ideal for wallet top-ups, marketplace deposits, savings platforms, and any flow where customers deposit money repeatedly.
  • ✓You must handle the dedicatedaccount webhook events to credit customer balances and reconcile deposits.

Frequently Asked Questions

Can a customer have multiple DVAs?
A customer can have DVAs at different partner banks. Whether you can create multiple DVAs at the same bank for the same customer depends on Paystack and the partner bank policies.
Is there a cost to create a DVA?
Check with Paystack for current pricing. DVA creation and maintenance may have associated costs that differ from standard transaction fees.
What happens if someone transfers to a DVA and the customer account is closed?
If you have deactivated the DVA, the transfer should be rejected. If the DVA is still active but the customer no longer has an account on your platform, you will still receive the webhook. Handle this edge case by flagging the deposit for manual review rather than silently discarding it.
Can DVAs accept transfers from any bank?
Yes. DVAs are regular bank account numbers at the partner bank. Any Nigerian bank can transfer to them using standard inter-bank transfers (NIP). The source bank does not matter.
How quickly are DVA deposits confirmed?
DVA deposits follow standard inter-bank transfer timing. Most transfers are confirmed within seconds to a few minutes. Paystack fires the webhook as soon as the deposit is confirmed.

Learn Paystack Integration

KES 2,999

The definitive guide to building payment systems with Paystack — from your first transaction to production-grade payment infrastructure across Africa. 9 modules, 47 lessons. Free preview available.

Start Paystack Course

Not ready? Create Free Account

Keep reading

Accepting Payments with Paystack: Complete Guide

Everything you need to accept your first payment with Paystack: choosing between Initialize Transaction and the Charge API, handling amounts in kobo, attaching metadata, setting callback URLs, and designing transaction references that keep your system clean.

Read more

Paystack Metadata: Passing Custom Data Through a Transaction

Paystack metadata lets you attach arbitrary JSON data to a transaction that flows through the entire lifecycle: from initialization to verification to webhooks. This guide shows you what to put in metadata, how to structure it for different use cases, and the limits you need to stay within.

Read more

Paystack Product API for Simple Storefronts

The Paystack Product API lets you create products with names, prices, and descriptions, then generate payment links for each one. For simple storefronts selling a handful of products, this is faster than building a full e-commerce backend. This guide covers creating products, linking them to payment pages, and knowing when to outgrow this approach.

Read more

Building a Wallet Top-Up Flow on Paystack

A step-by-step guide to building wallet top-up flows with Paystack. Initialize payment, handle webhooks, credit wallets safely, enforce minimum amounts, and prevent double-crediting in production.

Read more

Queueing Paystack Webhook Work with Laravel Queues

Handle Paystack webhooks in Laravel by verifying the signature in a controller, dispatching a queued job, and returning 200 immediately. The queue worker processes payment events in the background with automatic retries.

Read more

Explore related topics

M-Pesa & African Stack

M-Pesa Daraja API tutorials, USSD development, and WhatsApp Business API guides. The African Stack that global bootcamps do not teach.

Learn to Code in Kenya

Everything a Kenyan needs to know before writing their first line of code. Career paths, real costs, hardware requirements, and honest answers about what it takes.

Learning to Code

Roadmaps, language guides, and practical advice for learning to code and launching a programming career.

Breaking Into Tech

Practical, no-nonsense guides for anyone looking to start or change their career in tech. From choosing your first language to acing interviews.

The African Stack: Skills for This Market

The skills that make Kenyan developers hard to replace: M-Pesa, USSD, WhatsApp, fintech, and building for the African market.

Bonaventure Ogeto

Bonaventure Ogeto

Founder, McTaba Labs

Bonaventure is the founder of McTaba Labs, a coding bootcamp in Nairobi, Kenya. He has spent years building products for the African market and mentoring the next generation of developers across the continent.