Virtual accounts
Give a customer a permanent 10-digit account number. Money they transfer to it is credited to your Gafiapay balance.
Generate a virtual account
Create a new virtual account number for your customer.
Send one of bvn, nin or businessNumber. If you send more than one, the API uses businessNumber first, then bvn, then nin. If you send none, your own business's verified BVN, NIN or CAC number is used.
Headers
x-api-key string required x-signature string required x-timestamp string required Content-Type string required application/jsonBody
name string required email string required bvn string one ID number nin string one ID number businessNumber string one ID number RC1234567 or BN1234567. Use it when the customer is a business.Errors
| Status | Example message | When |
|---|---|---|
400 | bvn must be 11 digits | An ID number is in the wrong format. |
400 | Reason from the bank | The bank couldn't verify the ID number you sent. |
500 | Reason in error | We couldn't reach the bank. Try again later. |
curl -X POST 'https://api.gafiapay.com/api/v1/external/account/generate' \
-H 'Content-Type: application/json' \
-H 'x-api-key: your_api_key' \
-H 'x-timestamp: 1678901234567' \
-H 'x-signature: generated_signature' \
-d '{
"name": "John Doe",
"email": "john@example.com",
"bvn": "22345678901"
}'const crypto = require('crypto');
const body = JSON.stringify({
name: 'John Doe',
email: 'john@example.com',
bvn: '22345678901'
});
const timestamp = Date.now().toString();
const signature = crypto
.createHmac('sha256', process.env.GAFIAPAY_SECRET_KEY)
.update(body + timestamp)
.digest('hex');
const res = await fetch('https://api.gafiapay.com/api/v1/external/account/generate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': process.env.GAFIAPAY_API_KEY,
'x-timestamp': timestamp,
'x-signature': signature,
},
body, // send the exact string you signed
});
const data = await res.json();import hashlib, hmac, json, os, time, requests
body = json.dumps({
"name": "John Doe",
"email": "john@example.com",
"bvn": "22345678901"
}, separators=(",", ":")) # compact, no spaces
timestamp = str(int(time.time() * 1000))
signature = hmac.new(
os.environ["GAFIAPAY_SECRET_KEY"].encode(),
(body + timestamp).encode(),
hashlib.sha256,
).hexdigest()
res = requests.post(
"https://api.gafiapay.com/api/v1/external/account/generate",
data=body, # data=, not json=, so the signed string is sent as is
headers={
"Content-Type": "application/json",
"x-api-key": os.environ["GAFIAPAY_API_KEY"],
"x-timestamp": timestamp,
"x-signature": signature,
},
)
data = res.json()<?php
$body = json_encode([
'name' => 'John Doe',
'email' => 'john@example.com',
'bvn' => '22345678901'
], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
$timestamp = (string) round(microtime(true) * 1000);
$signature = hash_hmac('sha256', $body . $timestamp, getenv('GAFIAPAY_SECRET_KEY'));
$ch = curl_init('https://api.gafiapay.com/api/v1/external/account/generate');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => $body,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'x-api-key: ' . getenv('GAFIAPAY_API_KEY'),
'x-timestamp: ' . $timestamp,
'x-signature: ' . $signature,
],
]);
$data = json_decode(curl_exec($ch), true);require 'net/http'
require 'openssl'
require 'json'
body = {
name: 'John Doe',
email: 'john@example.com',
bvn: '22345678901'
}.to_json
timestamp = (Time.now.to_f * 1000).to_i.to_s
signature = OpenSSL::HMAC.hexdigest('sha256', ENV['GAFIAPAY_SECRET_KEY'], body + timestamp)
uri = URI('https://api.gafiapay.com/api/v1/external/account/generate')
req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req['x-api-key'] = ENV['GAFIAPAY_API_KEY']
req['x-timestamp'] = timestamp
req['x-signature'] = signature
req.body = body
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body)import java.net.URI;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
import java.util.HexFormat;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
// Compact JSON, sent exactly as signed
String body = "{\"name\":\"John Doe\",\"email\":\"john@example.com\",\"bvn\":\"22345678901\"}";
String timestamp = String.valueOf(System.currentTimeMillis());
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(System.getenv("GAFIAPAY_SECRET_KEY").getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
String signature = HexFormat.of().formatHex(mac.doFinal((body + timestamp).getBytes(StandardCharsets.UTF_8)));
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.gafiapay.com/api/v1/external/account/generate"))
.header("Content-Type", "application/json")
.header("x-api-key", System.getenv("GAFIAPAY_API_KEY"))
.header("x-timestamp", timestamp)
.header("x-signature", signature)
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());{
"status": "success",
"data": {
"bankName": "Palmpay",
"accountNumber": "1234567890",
"accountName": "John Doe",
"userName": "John Doe"
}
}{
"status": "fail",
"message": "bvn must be 11 digits"
}{
"status": "fail",
"error": "Error connecting to the bank"
}Notes
Account reuse
Each email gets one permanent account on your business. If you call this endpoint again with an email that already has one, you won't get a second account: the existing one is updated and returned. The account number can change when this happens, so always save the number from the latest response.
Account numbers
Account numbers are 10 digits. Show your customer the accountNumber, bankName and accountName from the response so they can transfer to it.
ID numbers
Temporary accounts follow the same rules for bvn, nin and businessNumber.