Retrieve your API keys from your dashboard by following the steps on our
Authentication page. Use your public API key for
all virtual account requests.
Set the following headers on every request:
{ "Authorization": "Payaza <Your public API key encoded in base 64>"}
Make sure your webhook URL is saved on the dashboard and configured to accept POST requests. See our Webhooks guide for setup instructions.
Virtual accounts come in two types. Choose based on your use case before integrating.
Dynamic (Temporary)
Reserved (Permanent)
Lifetime
15 – 480 minutes (default 30 min)
Permanent — never expires
Purpose
One-off checkout payments
Per-customer wallets, recurring top-ups
Amount validation
Configurable (exact, under, over)
Not applicable
BVN required
No
Yes — must be validated first by the user
Best for
E-commerce checkouts, one-time invoices
Wallets, savings apps, recurring billing
Always ensure you are using the correct account type for your use case. Using
a Dynamic account where a Reserved account is needed (or vice versa) will
cause integration issues that are difficult to debug.
Three banks are currently available as virtual account providers. Not all features are supported by all providers.
Bank
Bank Code
Amount Validation
Expiry Control
78 Finance Company Limited/ Bank 78
1067
✓ Yes
✓ Yes (15–480 min)
Fidelity Bank Limited
117
✗ No
✗ No
Globus Bank Limited
140
✓ Yes
✗ No
Amount validation (has_amount_validation) is only honoured by 78 Finance
and Globus Bank respectively. Passing these fields with Fidelity Bank will
have no effect.
Your server calls Create Virtual Account with account_type: "Dynamic", the customer details, amount, and expiry time.
The API returns an account_number and bank_name. Display these to your customer along with the exact amount to pay.
The customer makes a bank transfer to the virtual account from their own bank app or USSD.
Payaza receives the transfer and fires a webhook to your configured URL.
Your server calls Transaction Status Query (using account_reference) to confirm the payment, or relies on the webhook.
Dynamic accounts expire. If the customer does not pay before the account expires, you must create a new virtual account and show the updated details. Never reuse an expired account_reference.
Reserved Virtual Account flow
Validate the customer’s BVN (required for compliance before creating a
reserved account).
Your server calls Create Virtual Account with
account_type: “Reserved”, BVN, and customer details.
The API returns a permanent account_number and
bank_name. Store these against the customer record — this
account does not change.
Display the account number to the customer anywhere they need to top up or
make a payment.
When a payment arrives, Payaza fires a webhook. Use the
Get Virtual Account Status (by account number) to check
the account’s current state.
Always show the customer the exact transaction_amount_payable from the
response. If has_amount_validation is true, payments of a different amount
will be rejected by the bank.
This endpoint is available in the test environment only. Do not call it in
production. In the live environment, customers fund virtual accounts by making
real bank transfers.
Use this endpoint to simulate a customer payment during development and test your webhook and status-check logic before going live.For Dynamic accounts, pass the account_reference used at creation as initiation_transaction_reference.
For Reserved accounts, leave initiation_transaction_reference as an empty string "".
curl --request POST \ --url https://api.payaza.africa/live/merchant-collection/payaza/virtual_account/fund_test_virtual_account \ --header 'Authorization: Payaza <Your Public API key encoded in base 64>' \ --header 'Content-Type: application/json' \ --data '{ "account_name": "Payaza(Acme Checkout)", "account_number": "3340009013", "initiation_transaction_reference": "TXN-REF-20240501-001", "transaction_amount": "5000", "currency": "NGN", "source_account_number": "0123456789", "source_account_name": "John Doe", "source_bank_name": "Test Bank" }'
Step 3a — Transaction Status Query (Dynamic VAs only)
Use this endpoint to check the payment status of a Dynamic virtual account transaction. Query by the account_reference value used when creating the virtual account.
curl --request GET \ --url 'https://api.payaza.africa/live/merchant-collection/transfer_notification_controller/transaction-query?transaction_reference={transactionReference}' \ --header 'Authorization: Payaza <Your Public API key encoded in base 64>' \
There are only two possible transaction_status values: "Initialized"
(awaiting payment) and "Completed" (payment received). There is no
“Failed” status — an unpaid Dynamic account simply expires.
Step 3b — Get Virtual Account Status (Reserved VAs only)
Use this endpoint to retrieve the current status and summary of a Reserved virtual account by its account number.
curl --request GET \ --url https://api.payaza.africa/live/merchant-collection/merchant/virtual_account/detail/virtual_account/4030623702 \ --header 'Authorization: Payaza <Your Public API key encoded in base 64>'
This endpoint returns the account’s status and lifetime transaction count —
not individual transaction details. For per-payment details on a Reserved
account, rely on webhook events.
Payaza fires webhook events for all successful virtual account payments. Webhooks are the recommended way to be notified of incoming payments in real time.
Always generate a unique account_reference per virtual account creation to avoid conflicts when querying transaction status.
For Dynamic accounts, show the customer a countdown timer so they know when the account expires. Minimum expiry is 15 minutes; maximum is 480 minutes.(78 Finance Company Limited/ Bank 78)
For Reserved accounts, store the account_number and bank_name permanently in your database against the customer record — you should never need to re-create a Reserved account for the same customer.
The account_name displayed to the customer will always be prefixed with "Payaza(" by the bank (e.g. "Payaza(Acme Checkout)"). This is expected behaviour.
Webhooks are the recommended primary mechanism for payment confirmation. Use Transaction Status Query as a fallback only when a webhook is not received within your expected timeout window.
Transactions have only two terminal states: Completed (paid) and Initialized . There is no “Failed” state.
The Fund Test Virtual Account endpoint must only be called with test API Key. It will not work in the live environment and is not a real payment.