Bank Transfers and USSD Payments in Nigeria: A Developer's Guide
Bank transfers in Nigeria work through virtual account numbers generated at checkout. The customer opens their banking app (or uses USSD), transfers to the virtual account, and the payment gateway confirms the transaction via webhook. USSD payments use bank-specific shortcodes like *737# (GTBank), *901# (Access Bank), *770# (Fidelity), and *894# (First Bank). The customer dials the code, follows prompts on their phone keypad, and the bank processes the payment without internet. Both methods are supported by Paystack and Flutterwave as standard payment channels.
Why Bank Transfers Dominate Nigerian Online Payments
In most of the world, online payments mean credit or debit cards. In Nigeria, bank transfers are king. This is not a temporary trend or a quirk. It reflects how the Nigerian banking system actually works and what customers trust.
Several factors drive this:
- Card friction: Nigerian debit card payments require 3D Secure authentication (an OTP sent via SMS). This adds a step, sometimes fails when the OTP is delayed, and frustrates customers. Bank transfers skip this friction entirely. The customer opens their banking app, enters the amount and account number, and confirms with their existing bank PIN or biometric.
- Trust: Many Nigerians are cautious about entering card details on websites. Bank transfers feel more familiar and controlled because the customer initiates the payment from their own banking app, not from a form on someone else's website.
- Account penetration: While not every Nigerian has a card set up for online transactions, almost every banked Nigerian has a bank app or access to USSD banking. The Central Bank of Nigeria's financial inclusion drive has pushed bank account ownership well above card-for-online-use penetration.
- Speed and reliability: The Nigeria Inter-Bank Settlement System (NIBSS) processes instant bank transfers. NIP (NIBSS Instant Payment) transactions settle in real time. For customers, this means transfers feel instant. For merchants, confirmation arrives within seconds to minutes.
For developers, the practical implication is clear: if your checkout only accepts cards, you are losing a significant share of potential customers. Bank transfer support is not a nice-to-have feature. It is baseline functionality for any serious Nigerian e-commerce or fintech product.
How Bank Transfer Payments Work (Technical Flow)
When a customer selects "bank transfer" at checkout on a Paystack or Flutterwave-powered site, the following happens behind the scenes:
- Virtual account creation: The payment gateway generates a temporary virtual account number. This is a real bank account number, typically at a partner bank (Wema Bank for Paystack, or Wema/VFD Microfinance Bank for Flutterwave). The account is unique to this transaction and expires after a set period (usually 30 to 60 minutes).
- Display to customer: The checkout page shows the customer the virtual account number, the bank name, and the exact amount to transfer. It also usually shows a countdown timer for the expiration.
- Customer transfers: The customer opens their banking app (GTBank, Access, First Bank, Zenith, UBA, Kuda, OPay, or any other Nigerian bank) and initiates a transfer to the displayed account number for the exact amount.
- Settlement: The bank processes the transfer through NIBSS. The money lands in the virtual account.
- Webhook notification: The payment gateway detects the incoming transfer, matches it to the pending transaction, and sends a webhook to your server confirming the payment. This is where you update your order status from "pending" to "paid."
- Funds settlement to merchant: Paystack and Flutterwave settle funds to your main bank account on the next business day (T+1), minus their transaction fee.
Key technical considerations:
- The virtual account number is temporary. If the customer does not transfer within the expiration window, the account is deactivated and the transaction expires. Your webhook handler will receive a failure event.
- Amount matching matters. If the customer transfers the wrong amount (even by one naira), the payment may not match and will not confirm automatically. Both Paystack and Flutterwave handle this, but it can cause support tickets.
- Bank transfers are asynchronous. Unlike card payments that confirm within seconds, transfers can take anywhere from 10 seconds to 3 minutes, depending on the customer's bank and network conditions. Your frontend needs a polling mechanism or real-time update (via WebSocket or Server-Sent Events) to show the customer when their payment confirms.
USSD Payments: Reaching Customers Without Internet
USSD (Unstructured Supplementary Service Data) is a communication protocol that works on every GSM phone, from the latest iPhone to a 10-year-old Nokia. It does not require internet access, only cellular network coverage. In Nigeria, every major bank offers USSD banking through a dedicated shortcode:
- *737# - GTBank (Guaranty Trust Bank)
- *901# - Access Bank
- *894# - First Bank
- *770# - Fidelity Bank
- *822# - Sterling Bank
- *919# - UBA (United Bank for Africa)
- *945# - Stanbic IBTC
- *326# - Ecobank
- *966# - Zenith Bank
How USSD payment works for e-commerce:
- Your checkout initializes a transaction with Paystack or Flutterwave and includes "ussd" as a payment channel.
- The checkout page shows the customer a code to dial and a payment reference. For example: "Dial *737*50*amount*ref# on your phone."
- The customer dials the code on their phone, selects "pay" from the USSD menu, and enters their PIN.
- The bank processes the payment and notifies the payment gateway.
- The gateway sends a webhook to your server confirming the payment.
USSD is slower than bank transfers. The customer has to navigate multiple text-based menus, enter their PIN, and wait for each step to process. A USSD payment that takes 30 seconds on a bank transfer can take 2 to 3 minutes via USSD. Your frontend needs to accommodate this with clear waiting indicators and a longer timeout before showing a failure message.
When to offer USSD: USSD matters most when your product serves a broad Nigerian audience, including people in areas with poor internet connectivity, older demographics who are more comfortable with phone keypads than apps, or customers on feature phones. If your audience is primarily urban, app-savvy users in Lagos, bank transfers and cards may be sufficient. But adding USSD costs nothing extra through Paystack and Flutterwave, so there is little reason to exclude it.
Integration Patterns: Paystack and Flutterwave
Both Paystack and Flutterwave treat bank transfers and USSD as payment "channels" that you enable when initializing a transaction. The core integration is the same as for card payments; you just specify which channels to offer.
With Paystack: When initializing a transaction, include a "channels" array in your request body. To offer bank transfers, include "bank_transfer". To offer USSD, include "ussd". You can combine them: ["card", "bank_transfer", "ussd"]. The Paystack checkout page will display only the methods you have enabled.
With Flutterwave: The approach is similar. When using the Flutterwave Standard (redirect) or inline checkout, you specify payment_options in the initialization payload. Include "banktransfer" and "ussd" alongside "card" as needed.
Webhook handling for both methods: The webhook payload for bank transfer and USSD payments looks the same as for card payments, with the same verification flow. The key difference is timing. Card payments trigger a webhook almost instantly. Bank transfer and USSD webhooks can arrive 10 seconds to several minutes after the customer initiates payment. Your webhook handler must be resilient to this delay.
Practical patterns for your backend:
- Store the transaction reference and expected amount in your database when you initialize the payment.
- When the webhook arrives, verify the signature, check the reference against your database, confirm the amount matches, and update the order status.
- Implement a polling endpoint that your frontend can call every 5 to 10 seconds to check order status while the customer waits for their transfer or USSD payment to confirm.
- Set a timeout. If the payment has not confirmed within 30 to 60 minutes, mark the transaction as expired and let the customer retry.
Testing Bank Transfers and USSD in Sandbox
Both Paystack and Flutterwave provide test environments where you can simulate bank transfers and USSD payments without real money moving.
Paystack test mode: Use your test secret key (sk_test_...) to initialize transactions. When the checkout shows a bank transfer option, the sandbox generates a virtual account that you can "pay" using the test dashboard's simulation tools. USSD in test mode shows a USSD code, but since you cannot actually dial it from a sandbox, Paystack provides a way to simulate the completion on their test dashboard.
Flutterwave test mode: Use your test keys (FLWSECK_TEST-...) and the staging API URL. Flutterwave's sandbox simulates bank transfer confirmations. You can trigger success or failure scenarios from the sandbox interface.
What to test:
- Successful bank transfer payment: confirm that your webhook handler processes the confirmation correctly, updates the order status, and shows the customer a success page.
- Expired transfer: let the virtual account expire without paying. Confirm that your system handles the failure webhook and updates the order status accordingly.
- Wrong amount transfer: if possible in the sandbox, simulate a transfer with the wrong amount and verify your system's response.
- Duplicate webhooks: send the same webhook payload twice and verify that your idempotency logic prevents double-processing.
- USSD timeout: simulate a USSD payment that the customer starts but does not complete. Verify that the timeout and failure handling work.
For testing on real devices before going live, you can make small test transactions (NGN 50 to 100) using live keys. This catches issues that the sandbox cannot simulate, like actual bank processing delays and real OTP delivery times.
Building These Skills
Understanding bank transfers and USSD is foundational for any developer building products for the Nigerian market. These are not edge-case payment methods here. They are the primary way money moves online.
If you want structured training that covers payment integration alongside the full stack of skills you need (frontend, backend, databases, deployment), McTaba Academy offers courses designed for the African market.
Start here: Tech Foundations (NGN 3,500 to 6,000; exchange rates fluctuate, check current price at checkout) covers the fundamentals every developer needs before writing their first integration.
Full payment training: Full-Stack Software & AI Engineering (NGN 140,000 to 220,000; exchange rates fluctuate, check current price at checkout) includes a dedicated payment integration module covering Paystack and Flutterwave from initialization through webhook handling and production deployment.
Or create a free account to explore the platform first.
The official documentation from Paystack and Flutterwave is also excellent. Both have dedicated sections on bank transfer and USSD integration with sample code in multiple languages.
Key Takeaways
- ✓Bank transfers are the most popular online payment method in Nigeria. They work by generating a temporary virtual account number at checkout that the customer transfers to from their bank app.
- ✓USSD payments work without internet access. The customer dials a bank-specific shortcode (like *737# for GTBank or *901# for Access Bank) and follows text-based prompts. This reaches customers that card and app-based payments cannot.
- ✓Both methods are asynchronous. Payment confirmation can take anywhere from 10 seconds to several minutes. Your backend must handle pending states and process webhook notifications when the payment confirms.
- ✓Paystack and Flutterwave both support bank transfers and USSD as built-in payment channels. You do not need separate integrations for each method.
Frequently Asked Questions
- Are bank transfers safe for e-commerce in Nigeria?
- Yes. Bank transfers processed through Paystack and Flutterwave use temporary virtual accounts that expire after the transaction window. The customer transfers from their own banking app with their own security (PIN, biometric, or password). The payment gateway handles reconciliation and sends a verified webhook to your server. Fraud risk is lower than card payments because there are no card details to steal.
- How long does a bank transfer take to confirm?
- Most bank transfers in Nigeria confirm within 10 to 60 seconds thanks to the NIBSS Instant Payment (NIP) system. Occasionally, transfers from certain banks can take up to 3 to 5 minutes during peak hours or if the originating bank has processing delays. Your checkout should show a waiting screen and poll for confirmation.
- Can USSD payments work on smartphones?
- Yes. USSD works on any phone with a SIM card, including smartphones. The customer dials the shortcode from their phone's dialer. On smartphones, it opens a USSD session overlay. The key advantage is that USSD works without internet, so it reaches customers in areas with no data connectivity or those who have exhausted their data plans.
- Do I need separate Paystack or Flutterwave integration for bank transfers and USSD?
- No. Bank transfers and USSD are payment channels within the same integration. When you initialize a transaction, you specify which channels to enable (card, bank_transfer, ussd). The checkout page shows the enabled options. Your webhook handler processes all methods the same way. One integration covers all payment types.
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