How to Create NGN Virtual Bank Accounts in Node.js Using the Payscribe SDK
← Back to blog

How to Create NGN Virtual Bank Accounts in Node.js Using the Payscribe SDK

7/29/2026Engineering

Virtual bank accounts have become one of the most popular ways for African fintechs and ecommerce platforms to collect payments, because they let a customer pay with a normal bank transfer while the business still gets a fully trackable, unique account number behind the scenes. Payscribe's official Node.js/TypeScript SDK makes integrating this feature far simpler than hand-building requests against their REST API. This guide walks through installing the SDK, configuring it, and creating your first virtual account start to finish.

What You Need Before You Start

  • Node.js 18+
  • A Payscribe secret API key

Installing the SDK

npm install @payscribe/sdk

Setting Up Your Environment

Create a .env file in your project and add:

PAYSCRIBE_SECRET_KEY=ps_sk_test_YOUR_SECRET_KEY
PAYSCRIBE_ENVIRONMENT=sandbox
PORT=3000

Choose your environment: "sandbox" | "production"

Each one points to a different address on Payscribe's servers:

Note: If environment is omitted, the SDK defaults to sandbox.

Setting Up the Client

import { Payscribe } from "@payscribe/sdk";

const payscribe = new Payscribe({
  secretKey: process.env.PAYSCRIBE_SECRET_KEY!,
  environment: "sandbox"
});
Note: For internal testing against a custom endpoint, you can override the baseUrl directly:
const payscribe = new Payscribe({
  secretKey: process.env.PAYSCRIBE_SECRET_KEY!,
  baseUrl: "https://sandbox.payscribe.ng/api/v1/"
});

With the client set up, you're ready to start creating virtual accounts.

Virtual Accounts

Creating a Static (Permanent) Virtual Account

Static accounts are permanent and reusable, ideal for a customer you expect to receive repeat payments from (subscriptions, wallet funding, etc.). They're tied to an existing Payscribe customer record.

const account = await payscribe.virtualAccounts.createStatic({
  customerId: "customer-uuid",
  banks: ["9psb"],
  currency: "NGN"
});

Some partner banks Palmpay, in this case require additional identity verification fields:

const account = await payscribe.virtualAccounts.createStatic({
  customerId: "customer-uuid",
  banks: ["palmpay"],
  currency: "NGN",
  identity: {
    type: "bvn",
    number: "2233990011"
  }
});

TypeScript users get a matching input type for compile-time safety:

import type { CreateStaticVirtualAccountInput } from "@payscribe/sdk";

const body: CreateStaticVirtualAccountInput = {
  customerId: "customer-uuid",
  banks: ["9psb"],
  currency: "NGN"
};

Creating a Dynamic Virtual Account

Dynamic accounts are best suited to one-off transactions checkout flows, single orders since they expire after a set window.

const account = await payscribe.virtualAccounts.createDynamic({
  reference: "order_123",
  amount: 2500,
  amountType: "EXACT",
  description: "Payment for order_123",
  currency: "NGN",
  expiresIn: {
    duration: 1,
    type: "hours"
  },
  customer: {
    name: "Ada Lovelace",
    email: "ada@example.com",
    phone: "08099228833"
  }
});

amountType accepts one of two values: "EXACT" | "ANY"

Note: Pick EXACT when the customer must pay a fixed amount, and ANY when you want to accept whatever amount they send.
import type { CreateDynamicVirtualAccountInput } from "@payscribe/sdk";

const body: CreateDynamicVirtualAccountInput = {
  reference: "order_123",
  amount: 2500,
  amountType: "EXACT",
  expiresIn: {
    duration: 1,
    type: "hours"
  },
  customer: {
    name: "Ada Lovelace",
    email: "ada@example.com",
    phone: "08099228833"
  }
};

Checking, Activating, and Deactivating Accounts

// Get virtual account
const account = await payscribe.virtualAccounts.get("5031240100");

// Deactivate virtual account
const result = await payscribe.virtualAccounts.deactivate("5031240100");

// Activate virtual account
const result = await payscribe.virtualAccounts.activate("5031240100");

Confirming a Payment

Once a customer pays into a virtual account, you can confirm the transaction using the session ID from Payscribe:

const payment = await payscribe.virtualAccounts.confirmPayment({
  sessionId: "100004240807072606117680115283",
  amount: 5000,
  accountNumber: "5300000217"
});

If you're tracking your own transaction ID internally, pass it along too:

const payment = await payscribe.virtualAccounts.confirmPayment({
  transId: "transaction-id",
  sessionId: "100004240807072606117680115283",
  amount: 5000,
  accountNumber: "5300000217"
});

Testing a Transfer Without a Real Bank (Sandbox Only)

const simulation = await payscribe.virtualAccounts.simulateTransfer({
  reference: "test-ref",
  amount: "4500.00",
  description: "A test transfer",
  currency: "NGN",
  account: "4804760006",
  name: "Ada Lovelace",
  bank: "120001",
  senderAccountNumber: "1100000309",
  senderName: "Ada Lovelace",
  hash: "generated-hash"
});

This is a fast way to check your webhook and confirmation logic without waiting for a bank to actually move money.

SDK Reference

Full method signatures, typed inputs, and additional configuration options are available in the SDK package itself:

https://www.npmjs.com/package/@payscribe/sdk

Need More Details?

For full endpoint references, request/response schemas, and additional configuration options, check out the official API documentation: [https://docs.payscribe.co]

Need a Hand?

Questions, edge cases, or something not working as expected? Our team's got you reach out at support@payscribe.co