> ## Documentation Index
> Fetch the complete documentation index at: https://docs.payluk.ng/llms.txt
> Use this file to discover all available pages before exploring further.

# Simulate bank transfers with the Payluk Test Bank

> Generate a virtual account on staging, pay it from the Payluk Test Bank, and watch the customer's wallet credit through the same settlement path production uses.

Virtual accounts on staging are issued by a sandbox bank that has no way to send
money into them. That leaves a gap in testing: you can generate an account, but
you cannot pay it, so a deposit can be started and never finished.

The **Payluk Test Bank** closes that gap. It is a simulated bank that issues the
account and gives you a funded float to pay it from. Paying an account drives the
same settlement your live traffic runs: the same webhook, the same verification,
the same wallet credit.

<Note>
  The test bank exists only on staging. On production, virtual accounts are issued
  by the real bank and every one of the routes below returns `404`.
</Note>

## Before you start

You need:

* A `sk_test_` secret key from your Payluk dashboard.
* A [merchant customer](/api-reference/merchant-customers/create-merchant-customer)
  whose country is Nigeria. Virtual accounts are Nigerian-only.

A BVN on the customer makes no difference here. Dedicated accounts are a Paystack
product, so they are only issued while Paystack is the active gateway; under Safe
Haven every merchant customer gets the temporary account the test bank serves.

## 1. Generate a virtual account

Call [Generate virtual account](/api-reference/payments/generate-virtual-account)
with the customer's id in the `customer-id` header:

```bash theme={null}
curl -X POST https://staging.api.payluk.ng/v1/payment/virtual-account \
  -H "Authorization: Bearer sk_test_replace_me" \
  -H "customer-id: 665f1b2c9a1e4d0012ab3c01"
```

On staging the account comes back from the test bank:

```json theme={null}
{
  "status": 200,
  "message": "Virtual account generated successfully",
  "data": {
    "accountNumber": "9912345678",
    "bankCode": "999999",
    "amount": 100,
    "accountName": "PAYLUK/ADA EZE",
    "bank": "Payluk Test Bank",
    "expiresIn": "in 24 hours",
    "dedicated": false
  }
}
```

Two fields identify a simulated account: `bank` reads `Payluk Test Bank`, and
`accountNumber` always begins with `99`. The account is valid for 24 hours and
accepts `amount` (₦100) or more.

<Tip>
  Calling this endpoint again for the same customer within 24 hours returns the
  same account rather than issuing a new one.
</Tip>

## 2. Pay the account from the test bank

Open the test bank in a browser:

```
https://staging.live.payluk.ng/v1/checkout/test-bank
```

<Warning>
  The test bank is served by the checkout host (`staging.live.payluk.ng`), not by
  the merchant API host (`staging.api.payluk.ng`). Requesting it from the API host
  returns `404`.
</Warning>

Then:

1. Pick a source account under **From**. Three are provided, funded with
   ₦1,000,000, ₦250,000, and ₦100. The last one is there so you can test an
   insufficient-funds rejection.
2. Paste the `accountNumber` from step 1 into **To account number**. The name
   resolves under the field once it matches an issued account, and the amount the
   account expects appears beneath **Amount**.
3. Enter an amount and select **Send money**.

The receipt names the session id and confirms that the credit notification was
delivered.

<Tip>
  Drained a source account? Select **Reset balances** to restore the float.
</Tip>

## 3. Confirm the wallet credited

The transfer credits the customer's wallet through the same path a real deposit
takes. Read the balance back with
[Get customer wallet](/api-reference/merchant-customers/get-customer-wallet):

```bash theme={null}
curl https://staging.api.payluk.ng/v1/wallet \
  -H "Authorization: Bearer sk_test_replace_me" \
  -H "customer-id: 665f1b2c9a1e4d0012ab3c01"
```

The deposit also appears in
[Get payment history](/api-reference/payments/get-payment-history) filtered to
`?type=transfer`, with status `success`.

<Note>
  A wallet top-up does not send a webhook to your callback URL. Payluk sends
  webhooks for escrow events, not for deposits. Poll the wallet or the payment
  history instead.
</Note>

Once the wallet is funded, pay an escrow from it exactly as you would in
production. See step 4 of the [quickstart](/quickstart).

## What is and is not simulated

<CardGroup cols={2}>
  <Card title="Simulated on staging" icon="flask">
    Virtual account issuance, the inbound transfer, the credit notification, and
    the wallet settlement that follows.
  </Card>

  <Card title="Not simulated" icon="building-columns">
    Payouts, bank lists, and account name enquiry against real banks. Those still
    run against the provider's sandbox.
  </Card>
</CardGroup>

## Troubleshooting

<AccordionGroup>
  <Accordion title="The account number does not resolve in the test bank">
    The test bank only knows accounts it issued. Check that `bank` in the
    generate response read `Payluk Test Bank` and that the number begins with
    `99`. An account issued more than 24 hours ago has expired: generate a new
    one.
  </Accordion>

  <Accordion title="The transfer succeeded but the wallet did not credit">
    The receipt reports whether the credit notification was delivered. If it
    reports a failure, the money moved inside the test bank but the notification
    did not reach Payluk. Retry the transfer, and if it keeps failing, send the
    session id from the receipt to your Payluk contact.
  </Accordion>

  <Accordion title="Every test bank route returns 404">
    You are either on production, where the test bank does not exist, or you are
    calling the merchant API host rather than the checkout host. Use
    `https://staging.live.payluk.ng`.
  </Accordion>
</AccordionGroup>

## Paying an escrow checkout directly

If your customers pay through the [Inline Checkout SDK](/sdk/introduction)
rather than from a wallet balance, you do not need any of the steps above. On
staging the checkout window shows a test bank account and a **Pay from Payluk
Test Bank** button, which opens the bank with the account and amount already
filled in. The window settles and closes on its own once the transfer lands.
