Skip to main content

Virtual Accounts (NGN)

Payments received by the merchant into their account are referred to as collections/payins. This section will show how they are supported by the 54Pay platform.

Our Virtual Account Generation APIs let you create Static or Dynamic virtual accounts for receiving payments. A Static virtual account does not expire and can accept any amount, making it ideal for recurring or flexible payments. A Dynamic virtual account is valid for only 30 minutes and can only receive the exact amount specified at creation, making it suitable for time-sensitive or one-off transactions. Currently, these APIs allow for funds collection using Virtual Accounts limited to Nigeria. Kindly visit here to learn more about request headers. Below are the sample requests, responses and base urls.

Authentication

Requires authentication using your Merchant Public Key encoded in Base64 format.

Header:

x-api-key: {{Base64(Merchant Public Key)}}

Generate Static Account (POST)

https://va.dev.mypaygate.co/api/v1/account/static/generate

Request Headers

HeaderTypeRequiredDescription
x-api-keystringYesBase64-encoded Merchant Public Key
Content-TypestringYesMust be application/json

Request Body

FieldTypeRequiredDescription
referencestringYesUnique merchant reference for this account
emailstringYesCustomer email address
currencystringYesCurrency code (e.g., "NGN")
sourcestringYesSource identifier (e.g., "API")
firstNamestringNoCustomer first name
lastNamestringNoCustomer last name
webhookUrlstringNoCustom webhook URL for payment notifications. If empty, notifications are sent to the dashboard-configured webhook URL
curl --location ‘https://va.dev.mypaygate.co/api/v1/account/static/generate' --header 'x-api-key: {{base64PublicKey}}' --data-raw '{
"reference": "PG-STC-17866544767382",
"email": "example@youremail.com",
"currency": "NGN",
"source": "API",
"firstName": "Example Company", //optional
"lastName": "Nigeria Limited", //optional,
"webhookUrl": "https://webhook.site/708c7e7f-e111-4d25-af9f-e6ec97e92f58"
}

Generate Dynamic Account (POST)

https://va.dev.mypaygate.co/api/v1/account/dynamic/generate

Request Headers

HeaderTypeRequiredDescription
x-api-keystringYesBase64-encoded Merchant Public Key
Content-TypestringYesMust be application/json

Request Body

FieldTypeRequiredDescription
creationRefstringYesUnique merchant reference for this account
firstNamestringYesCustomer first name
lastNamestringYesCustomer last name
amountnumberYesExact amount to be received
emailstringYesCustomer email address
currencystringYesCurrency code (e.g., "NGN")
sourcestringYesSource identifier (e.g., "API")
webhookUrlstringNoCustom webhook URL for payment notifications. If empty, notifications are sent to the dashboard-configured webhook URL
curl --location 'https://va.dev.mypaygate.co/api/v1/account/dynamic/generate' --header 'x-api-key: {{BASE64(MERCHANT_PUBLIC_KEY)
}}' --data-raw '{
"creationRef": "PG-STC-17678798676395",
"firstName": "Trevor",
"lastName": "Merryweather",
"amount": 10000,
"email": "example@youremail.com",
"currency": "NGN",
"source": "API",
"webhookUrl": "https://webhook.site/708c7e7f-e111-4d25-af9f-e6ec97e92f58"
}'

Response Fields

FieldTypeDescription
successbooleanIndicates if the request was successful
messagestringResponse message
responseData.accountCreatedReferencestringAccount creation reference
responseData.partnerRefstringPartner reference identifier
responseData.channelstringPayment channel type
responseData.bankNamestringName of the issuing bank
responseData.bankCodestringBank code
responseData.accountNamestringVirtual account name
responseData.accountNumberstringVirtual account number for receiving payments
responseData.accountStatusstringCurrent account status
responseData.accountTypestringAccount type ("Dynamic")
responseData.expirystringValidity time in minutes
responseData.amountnumberTotal amount including fees
responseData.feenumberTransaction fee
responseData.amountExpectednumberExact amount required for successful payment
responseData.dateCreatedstringAccount creation timestamp
responseData.dateExpiredstringAccount expiry timestamp

Important Notes

  • Exact Amount Requirement: For dynamic virtual accounts, the customer must pay the exact amountExpected value down to the decimal. Any deviation will result in transaction failure.
  • 30-Minute Validity: The account expires 30 minutes after creation.
  • Single-Use: Dynamic accounts are designed for one-time transactions.

Simulate Funding (POST)

https://va.dev.mypaygate.co/api/v1/account/funding/simulate

Request Body

FieldTypeRequiredDescription
accountReferencestringYesReference of the virtual account to fund
amountstringYesAmount to credit to the account
curl --location 'https://va.dev.mypaygate.co/api/v1/account/funding/simulate' --data '{
"accountReference": "PG-STC-17866544767382",
"amount": 10180.00
}'

Important Notes

  • This endpoint is available only in the sandbox environment for testing purposes.
  • Use this to simulate customer payments.

Transaction Status Query (GET)

https://va.dev.mypaygate.co/api/v1/transaction/requery?reference=PG-STC-17678798676394

curl --location 'https://va.dev.mypaygate.co/api/v1/transaction/requery?reference=PG-STC-32322442424&x-api-key={{Base64(MerchantPublicKey)}}'

Sample Webhooks

{
"merchantReference": "PG-STC-4002865275",
"transactionReference": "PG-SAC|20251021221444|881D669889|1761084884636",
"transactionStatus": "COMPLETED",
"transactionFee": 100,
"amountReceived": 1417,
"initiatedDate": "2025-10-21 22:14:44.594314619",
"currentStatusDate": "2025-10-21 22:14:44.59433262",
"receivedFrom": {
"accountNumber": "8888878786",
"accountName": "Demo Funding",
"bankCode": "999999",
"bankName": "54Pay Bank PLC"
},
"status": "Funds Received",
"channel": "Bank Transfer",
"currencyCode": "NGN"
}

Webhook Security Headers

Our virtual accounts webhook requests include the following security headers for verification:

FieldDescription
sourceIpIP address from which the webhook originates
transactionHashHMAC-SHA512 hash of the webhook payload for verification

Verifying Webhook Authenticity

To ensure webhook security, you must verify the transactionHash header against the payload received. This prevents unauthorized or tampered webhook requests.

Step 1: Sort Payload Keys

The payload must be sorted alphabetically by keys before hashing. Use a TreeMap or equivalent to ensure consistent key ordering.

Java Example:

private String generatePayloadHash(JsonObject payload) {
try {
String key = "{{merchant_secret_key}}";
Type treeMapType = new TypeToken<TreeMap<String, Object>>() {}.getType();
Map<String, Object> payloadMap = gson.fromJson(payload, treeMapType);
return HashingUtil.hashPayload(key, gson.toJson(payloadMap));
} catch (Exception e) {
return "Error";
}
}

Step 2: Generate HMAC-SHA512 Hash

Use your Merchant Secret Key to generate an HMAC-SHA512 hash of the sorted JSON payload.

Java Example:

public static String hashPayload(String key, String payload) {
try {
// Create a secret key object
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "HmacSHA512");

// Create an HMAC-SHA512 instance and initialize it with the secret key
Mac mac = Mac.getInstance("HmacSHA512");
mac.init(keySpec);

// Generate the HMAC-SHA512 hash
byte[] hashBytes = mac.doFinal(payload.getBytes());

// Convert the hash bytes to hexadecimal format
StringBuilder hexHash = new StringBuilder();
for (byte b : hashBytes) {
String hex = String.format("%02x", b);
hexHash.append(hex);
}

// Return hashed value
return hexHash.toString();

} catch (NoSuchAlgorithmException | InvalidKeyException e) {
e.printStackTrace();
}
return null;
}

Step 3: Compare Hashes

Compare the generated hash with the value in the transactionHash header. If they match, the webhook is authentic.

Verification Example:

String receivedHash = request.getHeader("transactionHash");
String computedHash = generatePayloadHash(webhookPayload);

if (receivedHash != null && receivedHash.equals(computedHash)) {
// Webhook is authentic, process the payment
processPayment(webhookPayload);
} else {
// Invalid webhook, reject the request
throw new SecurityException("Invalid webhook signature");
}

Important Notes

  • Specify a custom webhookUrl in the account creation request, or Configure a default webhook URL in your merchant dashboard
  • Ensure your webhook endpoint can receive POST requests and returns a 200 OK response
  • Always verify the transactionHash header before processing webhook data
  • Optionally, whitelist the sourceIp for additional security

Base URLs