Bonaventure OgetoBy Bonaventure Ogeto|

Paystack Dedicated Account Deposit Not Reflecting

Paystack DVA deposits can take 5 to 30 minutes to reflect depending on the sending bank. If the deposit is not reflecting, check these in order: confirm the customer sent to the correct account number and bank, verify the amount meets the minimum threshold (usually 100 NGN), check your webhook endpoint is receiving dedicatedaccount.assign.success events, and verify your handler is processing the payload correctly. Most "missing" deposits are either bank delays, wrong account numbers, or webhook handlers that crash silently.

What Happens When a DVA Deposit Arrives

Dedicated Virtual Accounts (DVAs) are bank account numbers that Paystack assigns to your customers. When someone transfers money to their DVA, the flow goes like this:

  1. The customer initiates a transfer from their bank app to the DVA number you showed them.
  2. The customer's bank processes the transfer. Depending on whether it is same-bank or interbank, this takes anywhere from a few seconds to 30 minutes.
  3. The receiving bank (Wema Bank or Titan Trust Bank, depending on your Paystack setup) receives the funds and notifies Paystack.
  4. Paystack matches the deposit to the correct DVA, creates a transaction record, and sends a webhook event to your server.
  5. Your webhook handler processes the event, credits the customer's balance in your system, and returns HTTP 200.

A "deposit not reflecting" means the chain broke somewhere. Either the money has not arrived at the receiving bank yet (bank delay), it went to the wrong account (customer error), Paystack could not deliver the webhook (your endpoint is down or misconfigured), or your code received the webhook but did not process it correctly.

Bank Processing Delays Are the Most Common Cause

Before debugging your code, check whether enough time has passed. Nigerian interbank transfers go through the NIP (NIBSS Instant Payment) system, which is usually fast but not guaranteed instant.

Typical processing times:

  • Same bank (e.g., Wema to Wema): usually under 2 minutes
  • Different bank to Wema/Titan: 2 to 15 minutes normally, up to 30 minutes during peak periods
  • During bank system maintenance or NIP downtime: can take hours, or the transfer may fail and reverse

If a customer contacts you saying their deposit has not reflected and it has been less than 30 minutes, ask them to wait. If it has been over an hour, start investigating.

To check whether Paystack received the deposit, log into your Paystack dashboard, switch to the correct mode (Test or Live), and look at the Transactions page. Filter by "bank_transfer" channel. If the deposit shows there but not in your system, the problem is on your side (webhook or handler). If it does not show in the dashboard either, the money has not reached Paystack yet, which means it is a bank processing issue.

Customer Sent to the Wrong Account

Customers get account details wrong more often than you would think. They transpose digits, send to the wrong bank, or use an old account number from a previous provider. There is also the case where a customer sends from a different bank than expected, which can cause routing issues.

Common mistakes:

  • Typed the account number wrong (even one digit off sends money to a different account or fails)
  • Selected the wrong bank. Paystack DVAs are on Wema Bank or Titan Trust Bank. If the customer sends to "Paystack" or "GTBank," the money goes nowhere useful.
  • Used an old or deactivated DVA number. If you have re-created DVAs or the customer's account was deactivated and reassigned, the old number will not work.

Your UI should display the account details as clearly as possible. Show the bank name, account number, and account name. Make the account number copyable so customers do not have to type it manually.

// When displaying DVA details to the customer
function displayDVADetails(dva) {
  return {
    bankName: dva.bank.name,        // "Wema Bank"
    accountNumber: dva.account_number, // "7654321098"
    accountName: dva.account_name,     // "PAYSTACK-TITAN / Customer Name"
  };
}

// In your frontend, make the account number easy to copy
// 

If you suspect the customer sent to the wrong account, ask them for their bank's transfer receipt or confirmation message. It will show the destination account number and bank. Compare that against the DVA you assigned to them.

Amount Below Minimum Threshold

Paystack DVAs may have a minimum deposit amount. Transfers below this amount can be held or rejected by the receiving bank. The typical minimum is 100 NGN, but this can vary depending on your Paystack configuration and the partner bank.

If a customer sends 50 NGN to their DVA, the transfer might succeed at the bank level (the money leaves their account) but Paystack may not create a transaction for it. The funds end up in a holding state that requires manual intervention from Paystack support.

Set clear minimum deposit amounts in your UI and validate on the client side before the customer initiates a transfer.

// Validate deposit amount before showing DVA details
const MINIMUM_DEPOSIT_NGN = 100;

function validateDepositAmount(amountNgn) {
  if (amountNgn < MINIMUM_DEPOSIT_NGN) {
    return {
      valid: false,
      message: `Minimum deposit is NGN ${MINIMUM_DEPOSIT_NGN}. Please enter a higher amount.`,
    };
  }
  return { valid: true };
}

Webhook Not Configured or Not Receiving Events

This is the second most common cause after bank delays. The deposit arrives at Paystack (you can see it in the dashboard), but your system never updates because your webhook endpoint is broken.

Things that break webhook delivery:

  • No webhook URL set in the Paystack dashboard. Go to Settings > API Keys & Webhooks and confirm a URL is configured.
  • Webhook URL points to a development or staging server that is down.
  • Your SSL certificate expired. Paystack requires a valid HTTPS endpoint.
  • Your server is returning a non-200 status code. Maybe a middleware (CSRF protection, authentication) is blocking the request before it reaches your handler.
  • Your handler crashes before processing the DVA event. It handles charge.success but throws an error on dedicatedaccount events.
const crypto = require('crypto');
const express = require('express');
const app = express();

// You MUST use raw body for signature verification
app.post('/webhooks/paystack', express.raw({ type: 'application/json' }), (req, res) => {
  // Always return 200 first
  res.sendStatus(200);

  // Verify signature
  const hash = crypto
    .createHmac('sha512', process.env.PAYSTACK_SECRET_KEY)
    .update(req.body)
    .digest('hex');

  if (hash !== req.headers['x-paystack-signature']) {
    console.error('Invalid webhook signature');
    return;
  }

  const event = JSON.parse(req.body.toString());

  // Handle DVA deposit events
  if (event.event === 'charge.success' && event.data.channel === 'dedicated_nuban') {
    console.log('DVA deposit received:');
    console.log('  Amount:', event.data.amount / 100, 'NGN');
    console.log('  Customer:', event.data.customer.email);
    console.log('  Reference:', event.data.reference);
    console.log('  Account:', event.data.authorization.receiver_bank_account_number);

    // Process the deposit - credit user balance, send notification, etc.
    processDVADeposit(event.data).catch(err => {
      console.error('Failed to process DVA deposit:', err);
      // Log for manual review. Do not throw - you already sent 200.
    });
  }
});

async function processDVADeposit(data) {
  // Your business logic here
  // 1. Find the customer by email or DVA account number
  // 2. Verify the transaction via API (belt and suspenders)
  // 3. Credit their balance
  // 4. Send them a notification
}

Notice that the DVA deposit comes as a charge.success event with channel: "dedicated_nuban". Some developers set up webhook handlers that only look for card payments and ignore bank transfer events entirely. Check your event filtering logic.

Checking for Missed Deposits via the API

Do not rely solely on webhooks. Build a reconciliation check that queries Paystack directly. If a webhook was missed, this catches it.

const axios = require('axios');

async function checkRecentDVADeposits() {
  const now = new Date();
  const oneHourAgo = new Date(now.getTime() - 60 * 60 * 1000);

  try {
    const response = await axios.get('https://api.paystack.co/transaction', {
      headers: {
        Authorization: `Bearer ${process.env.PAYSTACK_SECRET_KEY}`,
      },
      params: {
        channel: 'dedicated_nuban',
        status: 'success',
        from: oneHourAgo.toISOString(),
        to: now.toISOString(),
        perPage: 100,
      },
    });

    const transactions = response.data.data;
    console.log(`Found ${transactions.length} DVA deposits in the last hour`);

    for (const txn of transactions) {
      // Check if this transaction exists in your database
      const exists = await checkTransactionInDB(txn.reference);

      if (!exists) {
        console.warn(`MISSED DEPOSIT: ${txn.reference} - ${txn.amount / 100} NGN`);
        // Process it now
        await processDVADeposit(txn);
      }
    }
  } catch (error) {
    console.error('Reconciliation check failed:', error.response?.data || error.message);
  }
}

async function checkTransactionInDB(reference) {
  // Query your database for this reference
  // Return true if found, false if missing
  return false; // Placeholder
}

// Run every 15 minutes
setInterval(checkRecentDVADeposits, 15 * 60 * 1000);

This is not a replacement for webhooks. Webhooks give you near-real-time updates. The reconciliation job is a safety net that catches anything the webhook flow missed. Run it every 15 to 30 minutes and alert your team when it finds discrepancies.

What to Tell the Customer While You Investigate

Customers who send money and do not see it reflected get anxious fast. Here is how to handle the communication.

If the deposit is less than 30 minutes old:

"Your transfer is being processed. Bank transfers can take up to 30 minutes to reflect. You will receive a notification once it is confirmed."

If the deposit is more than 30 minutes old:

"We are investigating your deposit. Please share your transfer receipt or confirmation message so we can trace the payment. We will update you within [your SLA, e.g., 2 hours]."

If the deposit went to the wrong account:

"It looks like the transfer was sent to a different account number than the one assigned to you. Please contact your bank to initiate a reversal. Your correct account details are: [bank name], [account number]."

Build an internal tool that lets your support team look up a customer's DVA, check the Paystack dashboard for deposits to that account, and cross-reference with your database. This saves everyone time.

// Internal support tool: check DVA status
async function lookupCustomerDVA(customerEmail) {
  // 1. Find the customer's DVA
  const customerResponse = await axios.get(
    `https://api.paystack.co/dedicated_account?customer=${customerEmail}`,
    {
      headers: { Authorization: `Bearer ${process.env.PAYSTACK_SECRET_KEY}` },
    }
  );

  const accounts = customerResponse.data.data;
  console.log('Customer DVAs:', accounts.map(a => ({
    bank: a.bank.name,
    accountNumber: a.account_number,
    accountName: a.account_name,
    active: a.active,
  })));

  // 2. Check recent transactions for this customer
  const txnResponse = await axios.get('https://api.paystack.co/transaction', {
    headers: { Authorization: `Bearer ${process.env.PAYSTACK_SECRET_KEY}` },
    params: {
      customer: customerEmail,
      channel: 'dedicated_nuban',
      perPage: 10,
    },
  });

  console.log('Recent deposits:', txnResponse.data.data.map(t => ({
    reference: t.reference,
    amount: t.amount / 100,
    status: t.status,
    date: t.created_at,
  })));
}

Preventing Missing Deposits Going Forward

Once you fix the immediate issue, put these safeguards in place to prevent it from happening again.

  1. Webhook monitoring. Set up alerts for when your webhook endpoint returns errors or becomes unreachable. If your endpoint goes down for even 10 minutes, you could miss dozens of deposits during peak hours.
  2. Reconciliation job. Run the API-based check from the previous section on a schedule. Alert your team when discrepancies are found.
  3. Webhook logging. Log every incoming webhook payload to a separate store (a log file, a dedicated database table, or a service like Datadog). This gives you an audit trail and helps you debug when something goes wrong.
  4. Clear UI. Display DVA details prominently with a copy button. Show the bank name in bold. Some customers confuse Wema Bank with other banks that have similar names.
  5. Status page for customers. Show deposit status in your app: "Waiting for deposit," "Processing," "Confirmed." This reduces support tickets from customers who just need reassurance that the system is working.

The combination of reliable webhooks, a reconciliation safety net, and clear customer communication handles the vast majority of DVA deposit issues. The remaining edge cases (NIP downtime, bank system failures) are outside your control, but at least you will know about them quickly and can communicate proactively.

How to Verify Your Fix

After fixing the issue, confirm the full deposit flow works end to end.

  1. Create a test DVA in sandbox mode (or use an existing one).
  2. Simulate a deposit. In sandbox, you can use the Paystack dashboard to trigger a test deposit event.
  3. Check your webhook endpoint logs. You should see the incoming event with the correct payload.
  4. Check your database. The deposit should be recorded and the customer's balance updated.
  5. Query the Paystack Transaction API and confirm the transaction appears there too.
  6. Run your reconciliation job manually and confirm it finds zero discrepancies.

If you are testing in live mode, make a small real deposit (100 NGN) to your own DVA. Confirm it reflects in both the Paystack dashboard and your system. Then check that the amount, reference, and customer details all match.

Key Takeaways

  • Bank transfer processing times vary. Interbank transfers can take 5 to 30 minutes. Same-bank transfers are usually faster but not instant.
  • The most common cause of "missing" deposits is the customer sending to the wrong account number or the wrong bank. Always display the full account details clearly in your UI.
  • Paystack sends a dedicatedaccount.assign.success webhook event when a deposit is confirmed. If your webhook handler is broken, deposits will show in the Paystack dashboard but not in your system.
  • Amounts below the minimum threshold (typically 100 NGN) may be rejected or held. Check your DVA configuration for minimum deposit amounts.
  • Always build a reconciliation mechanism. Compare your database records against the Paystack Transaction List API at least once daily to catch any missed deposits.
  • Your webhook handler must return HTTP 200 immediately. If it takes too long or crashes, Paystack will retry, and you may miss events if retries also fail.

Frequently Asked Questions

How long should a Paystack DVA deposit take to reflect?
Most deposits reflect within 5 to 15 minutes. Same-bank transfers (Wema to Wema) are usually faster, often under 2 minutes. Interbank transfers go through NIP and can take up to 30 minutes during peak periods. If a deposit has not reflected after 1 hour, start investigating.
The deposit shows in the Paystack dashboard but not in my system. What is wrong?
Your webhook handler is either not receiving the event or not processing it correctly. Check that your webhook URL is configured in the Paystack dashboard, your endpoint is reachable and returning HTTP 200, and your handler processes dedicatedaccount/charge.success events with the dedicated_nuban channel. Add logging to trace exactly where the flow breaks.
Can a customer send to a Paystack DVA from any bank in Nigeria?
Yes. Customers can transfer from any Nigerian bank to a Paystack DVA. The DVA is a real bank account on Wema Bank or Titan Trust Bank. Any bank that supports NIP transfers can send to it. The transfer goes through the standard interbank clearing system.
What happens if a customer sends less than the minimum amount?
Deposits below the minimum threshold (usually 100 NGN) may not trigger a transaction in Paystack. The money may be held at the bank level. Contact Paystack support to resolve these cases. To prevent it, set a clear minimum deposit amount in your UI and validate before showing the DVA details.
How do I handle deposits that arrive after my webhook endpoint was down?
Paystack retries failed webhook deliveries several times over a period of hours. If all retries fail, those deposits will only show in the Paystack dashboard and API. Build a reconciliation job that queries the Transaction List API periodically and processes any deposits that are not in your database. Run it at least every 30 minutes.

Ready to build real-world apps?

Join the McTaba Labs full-stack marathon (4 months full-time · 6 months part-time). Learn M-Pesa, USSD, and WhatsApp engineering while shipping 8 production apps.

Apply to the McTaba Marathon