curl --request POST \
--url https://staging.api.payluk.ng/v1/payment/virtual-account \
--header 'Authorization: Bearer <token>' \
--header 'customer-id: <customer-id>'import requests
url = "https://staging.api.payluk.ng/v1/payment/virtual-account"
headers = {
"customer-id": "<customer-id>",
"Authorization": "Bearer <token>"
}
response = requests.post(url, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'customer-id': '<customer-id>', Authorization: 'Bearer <token>'}
};
fetch('https://staging.api.payluk.ng/v1/payment/virtual-account', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://staging.api.payluk.ng/v1/payment/virtual-account",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"customer-id: <customer-id>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://staging.api.payluk.ng/v1/payment/virtual-account"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("customer-id", "<customer-id>")
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://staging.api.payluk.ng/v1/payment/virtual-account")
.header("customer-id", "<customer-id>")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://staging.api.payluk.ng/v1/payment/virtual-account")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["customer-id"] = '<customer-id>'
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"status": 410,
"message": "Virtual accounts are no longer supported. CBN rules require all inflows to come in as a direct escrow payment — collect them with the Payluk Checkout SDK instead.",
"data": null
}{
"status": 429,
"message": "Too many request"
}Generate virtual account (deprecated)
Deprecated — this endpoint no longer issues virtual accounts.
CBN rules require every inflow to arrive as a direct escrow payment. Money paid into a virtual account lands in a wallet with no escrow behind it and nothing recording what it was for, so the account can no longer be offered.
Collect payments with the Payluk Checkout SDK instead: it opens an escrow first and settles the payment against it.
Every call now returns 410 Gone, whatever the customer’s country, gateway or BVN status. The path is kept so an existing integration is told what happened rather than receiving a 404, which would read as a bad path.
What to use instead
- Create an escrow with Create escrow.
- Collect payment for it with the Checkout SDK, or server-side with Create payment intent.
- Confirm it with Verify payment.
curl --request POST \
--url https://staging.api.payluk.ng/v1/payment/virtual-account \
--header 'Authorization: Bearer <token>' \
--header 'customer-id: <customer-id>'import requests
url = "https://staging.api.payluk.ng/v1/payment/virtual-account"
headers = {
"customer-id": "<customer-id>",
"Authorization": "Bearer <token>"
}
response = requests.post(url, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'customer-id': '<customer-id>', Authorization: 'Bearer <token>'}
};
fetch('https://staging.api.payluk.ng/v1/payment/virtual-account', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://staging.api.payluk.ng/v1/payment/virtual-account",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"customer-id: <customer-id>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://staging.api.payluk.ng/v1/payment/virtual-account"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("customer-id", "<customer-id>")
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://staging.api.payluk.ng/v1/payment/virtual-account")
.header("customer-id", "<customer-id>")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://staging.api.payluk.ng/v1/payment/virtual-account")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["customer-id"] = '<customer-id>'
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"status": 410,
"message": "Virtual accounts are no longer supported. CBN rules require all inflows to come in as a direct escrow payment — collect them with the Payluk Checkout SDK instead.",
"data": null
}{
"status": 429,
"message": "Too many request"
}410 Gone.CBN rules require every inflow to arrive as a direct escrow payment. Money paid into a virtual
account lands in a wallet with no escrow behind it and nothing recording what it was for.Collect payments with the Payluk Checkout SDK instead — it opens an escrow
first and settles the payment against it.Authorizations
Your secret key as a Bearer token. The key prefix selects the environment: sk_test_... (staging) or sk_live_... (production); a key on the wrong host is refused with 403 Unauthorized Access. Each key is limited to 10 requests per minute (429 beyond that) and, on production, to the IP addresses on your dashboard allowlist when one is configured.
Headers
The merchant customer this request acts on behalf of.
Response
Gone. Virtual accounts are no longer issued.