Virtual Account Creation

Summary

Create Virtual Accounts on demands, receive funds from customers via Virtual Account Number and get notification on Virtual Accounts usage.

Dedicated Virtual Accounts (DVAs) is a service that lets you create Nigerian virtual accounts for your customers. These accounts allow your customers to receive payment from their customers.

When you create a Dedicated Virtual Account (DVA) for a customer, all bank transfers to that account will automatically be recorded as transactions from that customer.

Coverage

  • NGN

Integration steps

You can generate a VA and start receiving money in 3 easy steps:

  1. Prepare payload (payload includes: first name, last name, BVN, and email)

  2. Initiate VA creation (encrypting payload)

  3. Get instant response


Before you begin!

  • Link to the Postman collection

  • Find your keys on the Klasha Dashboard → Settings → Generate API Keys (here)

  • Klasha Dashboard, Generate API Keys page

Generate bearer token

Using the Token endpoint on the Postman collection, the token can be obtained from the headers under the header name: token. See the screenshot below for an example:

Encryption Algorithm

You need to encrypt the entire create virtual account payload that was specified on the previous point (here)

In order to encrypt the body correctly, you’d need the encryptionKey (that you can obtain following this guide here). We also provided an example in Java of the encryption algorithm in order to help you.


public static String encryptCBC(String stringToEncrypt, String encryptionkey) {
    try {
        SecureRandom sr = new SecureRandom();
        byte[] salt = new byte[8];
        sr.nextBytes(salt);
        final byte[][] keyAndIV =GenerateKeyAndIV(32, 16, 1, salt, password.getBytes(StandardCharsets.UTF_8),
                MessageDigest.getInstance("MD5"));
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyAndIV[0], "AES"), new IvParameterSpec(keyAndIV[1]));
        byte[] encryptedData = cipher.doFinal(stringToEncrypt.getBytes(StandardCharsets.UTF_8));
        byte[] prefixAndSaltAndEncryptedData = new byte[16 + encryptedData.length];
        // Copy prefix (0-th to 7-th bytes)
        System.arraycopy("Salted__".getBytes(StandardCharsets.UTF_8), 0, prefixAndSaltAndEncryptedData, 0, 8);
        // Copy salt (8-th to 15-th bytes)
        System.arraycopy(salt, 0, prefixAndSaltAndEncryptedData, 8, 8);
        // Copy encrypted data (16-th byte and onwards)
        System.arraycopy(encryptedData, 0, prefixAndSaltAndEncryptedData, 16, encryptedData.length);
        return Base64.getEncoder().encodeToString(prefixAndSaltAndEncryptedData);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}
public static byte[][] GenerateKeyAndIV(int keyLength, int ivLength, int iterations, byte[] salt, byte[] password, MessageDigest md) {
int digestLength = md.getDigestLength();
int requiredLength = (keyLength + ivLength + digestLength - 1) / digestLength * digestLength;
byte[] generatedData = new byte[requiredLength];
int generatedLength = 0;

try {
    md.reset();

    // Repeat process until sufficient data has been generated
    while (generatedLength < keyLength + ivLength) {

        // Digest data (last digest if available, password data, salt if available)
        if (generatedLength > 0)
            md.update(generatedData, generatedLength - digestLength, digestLength);
        md.update(password);
        if (salt != null)
            md.update(salt, 0, 8);
        md.digest(generatedData, generatedLength, digestLength);

        // additional rounds
        for (int i = 1; i < iterations; i++) {
            md.update(generatedData, generatedLength, digestLength);
            md.digest(generatedData, generatedLength, digestLength);
        }

        generatedLength += digestLength;
    }

    // Copy key and IV into separate byte arrays
    byte[][] result = new byte[2][];
    result[0] = Arrays.copyOfRange(generatedData, 0, keyLength);
    if (ivLength > 0)
        result[1] = Arrays.copyOfRange(generatedData, keyLength, keyLength + ivLength);

    return result;

} catch (DigestException e) {
    throw new RuntimeException(e);

} finally {
    // Clean out temporary data
    Arrays.fill(generatedData, (byte) 0);
}

}

Initiate VA creation

create a virtual account

POST

To create a virtual account using the Create virtual account API on the Postman collection, see the section below to get an example of the payload you need to send in order to create a transfer.

Serialise the payload

Encrypted payload

The result of the encryption of the payload that we specified has to be used with the Create virtual account endpoint as shown below:

Request Body

NameTypeDescription

firstName*

String

John

lastName*

String

Doe

bvn*

22222222222

email*

String

test@klasha.com

When a VA creation is initiated, processing could take a few seconds. These are sample responses you will get below:

{
    "id": 218,
    "walletId": null,
    "accountNumber": "8574578073",
    "bankName": "WEMA BANK",
    "orderRef": "fOeatZO40PEJzeiPvq8q",
    "txRef": "klasha-virtual-account-banktransfer-Steph and sons-133",
    "flwRef": "URF_G6Ie8ZP9GZ5hAZk8z6fZ",
    "userId": null,
    "businessId": 133,
    "bankCode": null,
    "enabled": true,
    "accountName": "Stephen Ojerinde",
    "email": "test@steph.com",
    "profileHash": "hwKpOI9+kZR/fFy7hLOpcQ==",
    "createdAt": "2023-07-07 00:42:45",
    "updatedAt": "2023-07-07 00:42:45"
}

Requery

GET {{url}}/wallet/virtual/v2/account/email

In case of network downtime or failures during VA creation, the virtual account can be re-queried with the user’s email. This can be fetched using the Requery with email API on the Postman collection.

Headers

NameTypeDescription

authorization*

String

{{token}}

Transaction Webhook

When payments are received from your users, we would send a webhook with the details of the transaction to your callback URL.

{
  "data": {
    "createdAt": "2023-07-14T19:28:18.585",
    "narration": "Bank Transfer",
    "destinationCurrency": "NGN",
    "sourceAmount": 2000,
    "sourceCurrency": "NGN",
    "tnxRef": "reference_322123243",
    "status": "successful",
    "destinationAmount": 2000,
    "originatorAccountName":"Stephen Oj",
    "bankName": "Access Bank",
    "originatorAccountNumber":"1234456211",
    "customer": {
      "id": 53603,
      "name": "Last Name",
      "email": "test@steph.com",
      "phone": null,
      "createdAt": "2023-07-04 20:11:05",
      "updatedAt": "2023-07-04 20:11:05"
    }
  },
  "event": "charge.completed"
}

Polling transaction status

POST {{url}}/nucleus/tnx/collection/status

To know the status of a transaction, you can fetch the transaction using the Transaction Status API on the Postman collection. Make use of the session id received from the bank. See an example below:

Request Body

NameTypeDescription

gateRef*

String

10043599483902847574821

Last updated