Skip to main content
Virtual-account (VA) and bank-transfer payments break two assumptions that hold everywhere else: the user pays outside the checkout session, and the amount that arrives is whatever the user typed. That combination produces the questions on this page. Confirming Payment Results explains the two-stage result these methods report and why a long-pending order is normal. Timeouts & When a Result Is Final covers the timeout side. This page is about the account number itself and about amounts that do not match.

What is a virtual account, and how does the flow differ from a redirect payment?

On a VA payment Waffo returns a bank account number generated for that order instead of sending the user to a payment page. The user then transfers to that number from their own banking app, ATM, or branch — entirely outside your session and outside Waffo’s. Consequences you have to build for: Related payment-method types on the checkout include VA (virtual account) and the over-the-counter types, where the user pays in cash at a counter. The customization parameters for showing only VA methods are documented in Checkout customization.

How long is a VA number valid, and can I control it?

VA is one of the few method families where the expiry can actually be set on the order rather than being fixed by the payment method. Where a VA method supports it:
  • The expiry window is capped at a small number of days; the exact range a row accepts is shown in the Order Expiry Customization column on Service → Payin (see Payin Agreements & Transaction Limits).
  • If you do not specify one, the method’s default applies.
Every other method family generally uses its own default expiry, which you cannot override; the published guidance on orderExpiredAt covers that. The Merchant Portal’s PayIn page shows the expiry behaviour per payment method.

Is the VA number unique to my order? Could someone else’s payment land on it?

Within the order’s validity window the account number is unique — it belongs to that order and nothing else. After the order has expired, that guarantee ends. Months later the same account number can appear again, issued for a different order. So:
  • Never treat a VA number as a durable identifier for a customer, an order, or an invoice.
  • Never store it and reconcile against it after the order has expired.
  • Never show an expired VA number to a user again, and never let a user pay against a number you handed out for a previous order — reissue by creating a new order.
Match on your own order identifiers instead. The VA number is a payment instruction with a short shelf life, not a key.

The user typed the wrong amount, or used the number after it expired. Is my order dead?

No. Both cases are rejected at the bank, before anything reaches your order. Because the bank validates the account number and the amount before accepting the transfer, an incorrect attempt usually cannot even be submitted. The order status is not affected, and the user can simply try again with the correct amount for as long as the order is still valid. The practical support answer: “your transfer was not taken; the order is still open; transfer the exact amount shown before the deadline.”

The user paid more than the order amount. What happens?

On bank-transfer methods that report the amount actually received, an overpayment produces a successful order whose received amount is larger than the amount you requested. The result notification carries both figures, so the difference is visible to you. What that means:
  • The order is payable and fulfillable — do not treat it as an exception that blocks delivery.
  • The surplus is not something the payment method reverses on its own.
  • Returning the surplus to the user is a merchant action. Depending on the method it may not be refundable through the API at all, in which case it is settled outside the payment.
Reconcile on the received amount, not on the amount you requested, or your ledger will drift by exactly the surplus.

The user paid less than the order amount. Can I still fulfil?

Only if they top it up in time. An underpayment is reported as a distinct outcome — the notification says the amount received was insufficient and carries the shortfall — and it resolves in one of two ways: So an underpaid order is not immediately failed, and it is not immediately payable either. Hold fulfilment while it is short. When it ends up failed, the amount already transferred has to be returned to the user — again a merchant action, outside the original payment on methods that cannot refund a failed bank transfer through the API. Two rules that save the most trouble:
  1. Never fulfil on the underpayment notification. Wait for the final state.
  2. Never assume the shortfall notification is the end of the story — the user may still complete.

Is this the same as stablecoin overpayment and underpayment?

The shape is the same, the handling is not. Both are cases of “the amount that arrived is not the amount requested”, and both exist because the payer controls the amount. But the resolution paths differ enough that you should not reuse one code path for both. The stablecoin rules are in Stablecoin Exceptions & Refunds. Read that page rather than inferring from this one, and vice versa.

Can I refund a bank-transfer order through the API?

Not always — and this is worth checking before you accept the method rather than after your first return request. Several bank-transfer payment methods are not refundable through the refund API at all: the money did not come in over a reversible rail, so there is nothing to reverse. Where the API can refund, some methods need additional user details (bank account information) because the money is returned by transfer rather than reversed on the original payment. Do not decide which case you are in from the payment method’s name in your own code — required fields are configuration, not a property of the method you can infer. Submit the refund and branch on the response. The full rules are in Refund Limits by Payment Method and Refund FAQ.

Do over-the-counter payments work the same way?

Broadly yes — the user pays outside the session, the result arrives in two stages, and a long pending period is normal. The difference is what you hand the user: instead of an account number, over-the-counter methods return a payment code the user presents at a counter, and a set of instructions describing where and how to pay. Two practical points:
  • The instructions are text and a code, not an image or a link you can rely on rendering. Put them somewhere durable — an email is the usual answer, because the user will pay hours or days later.
  • The order behaves like a VA order for everything on this page: do not cancel early, do not treat elapsed time as failure, wait for the final notification.

Should I cancel a bank-transfer order that has been pending for hours?

No. For VA, bank-transfer and over-the-counter payments a long pending period is the normal case, because the user pays on their own schedule. Cancelling early creates the worst failure mode there is: the order is closed on your side while the money is still on its way. Wait for the final notification, or poll /api/v1/order/inquiry. See Confirming Payment Results for the long-pending case and Timeouts & When a Result Is Final for what a timeout does and does not tell you.

What should I show the user after creating a VA or over-the-counter order?

Everything they need to complete the transfer, and nothing that expires silently:
  1. The account number or payment code exactly as returned, in a form the user can copy.
  2. The exact amount, including currency and decimals — an amount mismatch is rejected by the bank, so precision is not cosmetic.
  3. The deadline, in the user’s own terms (“pay before …”), not as a raw timestamp.
  4. A note that the payment is confirmed after the transfer is received, not immediately.
Send the same details by email or another durable channel as well. The user will close your page and pay later — that is the whole point of the method — and a payment instruction that exists only on a page they navigated away from becomes a support ticket.

Where do I look next?