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

# Pay with transfer

> Show a dedicated virtual account, wait for the customer to transfer, and confirm with a webhook.

Pay with transfer issues a one-time virtual account for this payment. The customer transfers the exact amount from their bank app. Credit happens when the inbound transfer is confirmed.

Email on initialize is required for this channel.

## Request an account

After initialize, from checkout (access code, not the secret key):

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST https://server.staging.vestrapay.app/v1/payment/checkout/charge/bank-transfer \
  -H "x-access-code: ACCESS_CODE"
```

There is no request body. The account is on `data`:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "status": "success",
  "data": {
    "status": "awaiting_transfer",
    "reference": "VPY_TXN_ACME001",
    "accountNumber": "0012345678",
    "bankName": "Guaranty Trust Bank",
    "accountName": "Acme Stores Limited",
    "expiresAt": "2026-09-08T10:15:00.000Z",
    "expiresInSeconds": 600
  }
}
```

Show bank name, account number, account name, and a countdown from `expiresInSeconds`. Repeating the call while the account is valid returns the same account.

## Confirm the transfer

The customer pays at their bank.

1. Subscribe to `payment.completed` and `payment.failed`.
2. Optionally listen to `checkout.awaiting_transfer`, then `checkout.completed`.
3. [Verify](/payments/verify) before fulfilling.

Wrong amount: checkout can emit `amount_mismatch` while the window is open. Keep the same account on screen and ask them to send the exact total.

## Test mode

On test, POST `/v1/payment/checkout/charge/bank-transfer/simulate` with the access code:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST https://server.staging.vestrapay.app/v1/payment/checkout/charge/bank-transfer/simulate \
  -H "x-access-code: ACCESS_CODE" \
  -H "Content-Type: application/json" \
  -d '{ "scenario": "success" }'
```

Allowed scenarios: `success`, `failed`, `abandoned`. Rejected on live.

When GTBank sandbox is the active transfer rail, `success` creates a sandbox virtual account and funds it so the payment completes through the same webhook path as production.
