Order VAs API quick start

Create per-order VAs and receive exact-amount payments via webhook with SePay API. Supports BIDV, Sacombank, Vietcombank, VietinBank.

||

An order VA is a virtual account number dedicated to a single order. The customer transfers to that VA, SePay matches the transaction to the order by its exact amount and sends a webhook to your system.

Try it without a real bank account

The Sandbox gives you a simulated account to run the whole flow: create an order, pay it, receive the webhook.

Supported Banks

BIDV, Sacombank, Vietcombank and VietinBank accounts linked to your SePay company all create order VAs through the same endpoint, but differ in the account type, the required parameters and how they take the money. Read the section for the bank you are integrating.

BIDV

Account typeEnterprise
Amount (amount)Optional
Order code (order_code)6-50 chars, ^[a-zA-Z0-9]+$ (letters and digits only, no spaces or special characters)
Partial paymentYes
BIDV allows open orders

Omit amount and the VA takes any sum. BIDV is the only bank that supports this kind of order.

When the customer pays less than amount, the order moves to Partially and the VA keeps accepting payments until the total is reached.

A BIDV enterprise account can also set its own VA holder name through va_holder_name, but the feature has to be enabled first. Contact SePay if you need it.

Sacombank

Account typePersonal or household business
VA prefix (va_prefix)Required
Amount (amount)Required
Order code (order_code)6-50 chars, ^[a-zA-Z0-9]+$ (letters and digits only, no spaces or special characters)
Partial paymentNo

Every Sacombank order must be tied to a VA prefix. Call VA Prefixes to fetch an active va_prefix before creating the order. The merchant must be activated, otherwise the call returns 400.

Sacombank takes the exact amount only

Orders only transition from Pending to Paid or Cancelled; there is no Partially state.

Vietcombank

Account typeEnterprise or household business
Terminal ID (tid)Required
Amount (amount)Required
Order code (order_code)6-15 chars, ^[a-zA-Z0-9]+$ (letters and digits only, no spaces or special characters)
Partial paymentNo
Integrating Vietcombank

Every order needs a tid

Every Vietcombank order must be tied to a specific terminal via the tid parameter. If you don't have a terminal yet, see how to add a terminal for a Vietcombank enterprise/household business account. Call List Terminals to fetch a valid tid before creating an order. tid must be the raw Vietcombank-issued Terminal ID (e.g. 20933557), not the SePay xid UUID.

Exact amount only

Orders only transition from Pending to Paid or Cancelled; there is no Partially state. Note also that order_code is capped at 15 characters, shorter than the other banks.

VietinBank

Account typeEnterprise
Amount (amount)Required
Order code (order_code)6-50 chars, ^[a-zA-Z0-9]+$ (letters and digits only, no spaces or special characters)
Partial paymentYes (overpayment rejected)
RefundsYes
VietinBank needs identified-account collection registered

A VietinBank enterprise account must be registered for identified-account collection with VietinBank before it can create orders. An unregistered account returns 422 vietinbank_va_order_not_enabled. If you hit this error, contact SePay for support.

A VietinBank order always carries an amount, but a customer paying less is still recorded: the order moves to Partially and the VA keeps accepting until the total is reached. A VA that has expired, is already paid, or was cancelled takes no further money.

A transfer above the VA amount is not matched: the money still lands in the real account, but the order does not move to Paid. Collecting in instalments on VietinBank means each instalment must be at or below the VA amount.

What VietinBank collected can be refunded through the API.

Statuses

Order Status
Rendering diagram...
VA Status
Rendering diagram...

An order also carries its own refund_status field, null until the first refund is issued. It only takes another value on a bank that supports refunds.

Order Refund Status
Rendering diagram...

Unlike status, this field is recomputed from all of the order's refunds, so it can walk back. See what each value means.

Endpoints

GET
https://userapi.sepay.vn/v2/bank-accounts/{ba_xid}/orders
POST
https://userapi.sepay.vn/v2/bank-accounts/{ba_xid}/orders
GET
https://userapi.sepay.vn/v2/bank-accounts/{ba_xid}/orders/{order_xid}
DELETE
https://userapi.sepay.vn/v2/bank-accounts/{ba_xid}/orders/{order_xid}
POST
https://userapi.sepay.vn/v2/bank-accounts/{ba_xid}/orders/{order_xid}/va
DELETE
https://userapi.sepay.vn/v2/bank-accounts/{ba_xid}/orders/{order_xid}/va/{va_number}
GET
https://userapi.sepay.vn/v2/bank-accounts/{ba_xid}/prefixes
GET
https://userapi.sepay.vn/v2/bank-accounts/{ba_xid}/prefixes/{va_prefix}
GET
https://userapi.sepay.vn/v2/bank-accounts/{ba_xid}/terminals

Payment Verification Flow for Order VAs

order_code is the thread tying your own order to the order on SePay. Your website generates the code when the customer checks out, passes it into the create-order call, and the webhook returns it in the code field so you can reconcile. Omit it and SePay generates one, but then you have to store the returned code yourself to match back.

SePay normalises order_code to upper case before storing it. Sending dh20250001 is accepted, but the order is stored as DH20250001, and that is what the response and the webhook return. If your system uses lower-case codes, compare them case-insensitively, or store the value from the response rather than the one you sent.

Payment verification flow for order VAs
Rendering diagram...

Refund Flow

Once an order is Paid or Partially, you send what it collected back through the Refunds API. Only VietinBank enterprise accounts that SePay has enabled for refunds can use it today.

Order Refund Flow
Rendering diagram...

A refund does not change the order's status or paid_amount; the order carries a separate refund_status field instead. See Refund an order for the parameters, the state diagram and the error table.


Integration Steps

Prerequisites

  • Create an API Token
  • A bank account linked on SePay:
    • BIDV: enterprise account
    • Sacombank: personal or business account, merchant activated
    • Vietcombank: enterprise or household business account with at least one terminal (see how to add a terminal, or fetch the list via List Terminals)
    • VietinBank: enterprise account registered for identified-account collection with VietinBank
  • Get the account UUID from Bank Accounts API

Fetch VA Prefixes (Sacombank) or Terminals (Vietcombank)

BIDV does not need this step. Skip to the next step.

Create Order

cURL
1
2
3
4
curl -X POST "https://userapi.sepay.vn/v2/bank-accounts/{ba_uuid}/orders" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-d '{"amount": 500000, "order_code": "DH20250001", "with_qrcode": "1"}'
Response 201
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"status": "success",
"message": "Order created successfully",
"data": {
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678902",
"order_code": "DH20250001",
"va_number": "963NQDORD1234567890AB",
"va_holder_name": "CONG TY CP TECH VINA",
"amount": 500000,
"status": "Pending",
"bank_name": "BIDV",
"account_holder_name": "CONG TY CP TECH VINA",
"account_number": "1234567890",
"expired_at": null,
"qr_code": "data:image/png;base64,...",
"qr_code_url": "https://vietqr.app/img?acc=963NQDORD1234567890AB&bank=BIDV&amount=500000&template=compact"
}
}

Display to Customer

From the response, show the customer:

  • Account number: va_number (VA number to transfer to)
  • Amount: amount
  • QR code: qr_code or qr_code_url
  • Expiry: expired_at (if set)

Receive Payment Notification

When the customer transfers successfully, SePay sends a webhook to your configured URL. The transaction will contain a code field matching the order's order_code.

See webhook configuration for details.

Check Order Status

Bash
1
2
curl -X GET "https://userapi.sepay.vn/v2/bank-accounts/{ba_uuid}/orders/{order_uuid}" \
-H "Authorization: Bearer YOUR_API_TOKEN"

Order statuses:

  • Pending: awaiting payment
  • Paid: fully paid
  • Partially: partially paid (BIDV and VietinBank)
  • Cancelled: cancelled

Key Parameters

ParameterRequiredDescription
va_prefixYes (Sacombank)VA prefix. Not used for BIDV, Vietcombank or VietinBank.
tidYes (Vietcombank)Raw Vietcombank-issued Terminal ID. Not used for BIDV, Sacombank or VietinBank.
order_codeNoOrder code, 6-50 chars (Vietcombank max 15), matching ^[a-zA-Z0-9]+$, meaning letters and digits only. SePay normalises it to upper case. Auto-generated if omitted, as 10 upper-case characters.
amountYes (Sacombank, Vietcombank, VietinBank)Amount (VND). Only BIDV allows it to be optional.

See all parameters


Partial Payments

A partial payment is a transfer smaller than the order's amount that SePay still records, instead of matching only an exact-amount transfer. The order collects gradually across several transfers until the total is reached.

A first transfer below amount moves the order to Partially, and the paid_amount field holds the running total. The VA keeps accepting, each transfer adding to paid_amount. Once paid_amount reaches amount, the order becomes Paid. You can also create an extra VA on an order in Pending or Partially to collect the remainder.

Compared to a full payment

Full paymentPartial payment
Amount the customer transfersExactly amountLess than amount
Order status after the first transferPaidPartially
VA after the first transferMoves to Paid, stops acceptingKeeps accepting until amount is reached
Supported banksBIDV, Sacombank, Vietcombank, VietinBankBIDV, VietinBank

Bank support

BIDV and VietinBank record underpayments: the order sits at Partially until the full amount is collected.

VietinBank adds one constraint in the other direction: a transfer larger than the VA amount is not matched. The money still lands in the real account, but the order does not move. So when collecting in instalments on VietinBank, each instalment must be at or below the VA amount.

To collect the remainder with an extra VA, work the figure out yourself and pass amount. Omitting amount gives the VA the parent order's amount, not the outstanding remainder, on BIDV too. On a BIDV open order (amount null) an extra VA defaults to an amount of 0.

Sacombank and Vietcombank do not support Partially: their orders move straight from Pending to Paid on payment. They can still take an extra VA on a Pending order, but that VA is a parallel payment channel for the same amount, not a way to collect a remainder.

A BIDV open order is a different mechanism

An open order omits amount, so there is no figure to measure against: the VA takes any sum and no remainder ever arises. A partial payment is the opposite: amount is fixed up front and the order tracks paid_amount until it is met.

See Create VA API


Cancel Order or VA

Only Pending orders and Unpaid VAs can be cancelled. Response: 204 No Content.

Cancel Order | Cancel VA


See Also