---
updatedAt: 2025-07-14T17:02:16.000Z
agentTools:
  projectIndex: https://transactpay.readme.io/llms.txt
---

# Accept Bank Transfers with Virtual Accounts

### 📘 Use Case

A utility or subscription service wants to assign unique virtual account numbers to users so payments via bank transfer can be automatically reconciled.

***

### Why Use Virtual Accounts?

* Customer pays to a dedicated account (e.g. Wema/Providus)
* Easy auto-matching of payments to customers/orders
* Ideal for recurring billing or pay-per-use systems

***

### Prerequisites

* Live or Test TransactPay account
* API credentials (Secret key and RSA public key)
* Active virtual account service on your merchant profile
* Customer ID or reference in your DB

***

### Step-by-Step Implementation

***

### Step 1: Setup Backend

#### Node.js

Install dependencies:

```bash
npm install axios node-rsa dotenv
```

Create `.env`:

```
TRANSACTPAY_SECRET_KEY=your_secret_key
```

***

### Step 2: Prepare Request Payload

Virtual account requests may require:

```json
{
  "customer_name": "John Doe",
  "reference": "INV-123456",
  "currency": "NGN"
}
```

***

### Step 3: Node.js Code to Generate Virtual Account

```node
const axios = require("axios");
const NodeRSA = require("node-rsa");
const fs = require("fs");
require("dotenv").config();

const pubKey = fs.readFileSync("./transactpay_pub.pem", "utf8");
const rsa = new NodeRSA(pubKey);
rsa.setOptions({ encryptionScheme: "pkcs1" });

const payload = {
  customer_name: "John Doe",
  reference: "INV-123456",
  currency: "NGN",
};

const encrypted = rsa.encrypt(JSON.stringify(payload), "base64");

axios
  .post("https://api.transactpay.io/virtual-account/create", {
    data: encrypted,
  }, {
    headers: {
      Authorization: `Bearer ${process.env.TRANSACTPAY_SECRET_KEY}`,
    },
  })
  .then((res) => {
    console.log("Virtual Account:", res.data.data);
  })
  .catch((err) => {
    console.error("Error:", err.response?.data || err.message);
  });
```
```python
import json, requests, os
from dotenv import load_dotenv
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.asymmetric import padding

load_dotenv()
key = os.getenv("TRANSACTPAY_SECRET_KEY")

with open("transactpay_pub.pem", "rb") as key_file:
    pub_key = serialization.load_pem_public_key(key_file.read())

payload = {
    "customer_name": "John Doe",
    "reference": "INV-123456",
    "currency": "NGN"
}

encrypted = pub_key.encrypt(json.dumps(payload).encode(), padding.PKCS1v15())

res = requests.post(
    "https://api.transactpay.io/virtual-account/create",
    headers={"Authorization": f"Bearer {key}"},
    json={"data": encrypted.hex()}
)

print("Response:", res.json())
```

<br />

***

### Sample Response

```json
{
  "status": true,
  "message": "Account created",
  "data": {
    "account_number": "1234567890",
    "bank_name": "Wema Bank",
    "reference": "INV-123456"
  }
}
```

***

### Step 4: Display Account Details to Customer

On your frontend or dashboard:

> “Pay NGN 25,000 to the Wema Bank account below. Your invoice will be confirmed automatically.”

***

### Step 5: Listen for Webhook Updates

TransactPay sends webhook events like `virtual_account.payment_received` to notify you when a deposit is made.

Sample webhook handling logic:

* Match `reference` or `account_number`
* Mark invoice/order as “Paid”
* Send confirmation email or SMS

***

### Security Notes

* Never expose your secret key on frontend
* Store reference IDs securely
* Log unexpected webhooks

***

### Use Case Variants

* Create 1 account per customer (for subscriptions)
* Create 1 account per order (for pay-per-use)
* Expire accounts after X days if needed