API giao dịch: tra cứu tiền vào theo tài khoản ảo

Cập nhật 28/08/2026Bản Markdownllms.txtopenapi.json

Video hướng dẫn

API giao dịch: tra cứu tiền vào theo tài khoản ảo · Xem trên YouTube
Xem transcript video (9 phần)

Giao dịch không biến mất

Webhook đến chậm hoặc server từng bị lỗi không có nghĩa giao dịch biến mất. MONA Pay vẫn lưu bản ghi ACB đã báo và cho bạn kéo lại theo tài khoản ảo. Trong video này, mình gọi API danh sách, đọc cấu trúc phân trang, hiểu từng trường quan trọng, đối soát bằng transaction_code và gửi lại thông báo cho đúng một giao dịch.

Query theo số VA

Gọi GET /api/v1/acb/virtual-account/transactions với Bearer. Query bắt buộc virtual_account_number, chẳng hạn MONA0000010234. page là số trang từ một, mặc định một. limit nhận từ một đến một trăm, mặc định mười. API kiểm tra VA có thuộc tài khoản của bạn rồi mới trả dữ liệu. Lệnh đọc này không cần X-Client-Secret.

Đọc phần phân trang

Trong response, data.data là mảng giao dịch. Phần phân trang có current_page, per_page, total, last_page, start, end, has_next và has_prev. Muốn đọc tiếp, tăng page khi has_next là true. Danh sách được sắp xếp giao dịch mới trước. Đừng nhầm data bọc ngoài với mảng data bên trong; đây là nguyên nhân dễ làm code nghĩ response rỗng. per_page phản ánh giới hạn đang dùng, còn total là tổng số bản ghi tìm thấy.

Hiểu trạng thái giao dịch

Các trường trạng thái gồm transaction_status, trong đó SUCCESS nghĩa là tiền đã vào; transaction_channel là kênh ACB ghi nhận; transaction_date là thời điểm giao dịch và effective_date là ngày hiệu lực ghi sổ. debit_or_credit bằng credit là tiền vào, debit là tiền ra. Webhook hiện phát cho tiền vào, nên khi đối soát bạn lọc bản ghi credit.

Khớp đúng khoản tiền

Các trường khớp tiền gồm amount, số nguyên VND; transaction_content, nội dung khách gõ hoặc QR điền sẵn; và transaction_code, mã tham chiếu ổn định dùng làm khóa chống trùng. account_number là tài khoản ACB thật. va_prefix_cd và va_nbr là đầu số cùng số VA, có thể null nếu tiền vào thẳng tài khoản chính. attributes chứa thêm thông tin người chuyển và ngân hàng chuyển.

Đối soát từng trang

Đối soát theo từng VA, bắt đầu trang đầu với giới hạn một trăm. Mỗi bản ghi, tìm transaction_code trong bảng nội bộ. Mã đã có thì bỏ qua; mã mới thì ghi và chạy cùng hàm xử lý đơn như webhook. Đọc trang tiếp theo khi has_next là true. Hiện chưa có since_id, nên có thể dừng khi gặp dữ liệu đã có và cũ hơn mốc lần chạy trước.

Gửi lại thông báo

Muốn gửi lại thông báo, lấy trường id của bản ghi rồi gọi POST /api/v1/acb/virtual-account/transactions/{transaction_id}/retry. Request cần Bearer và X-Client-Secret. Body bắt buộc có target_type là WEBHOOK hoặc TELEGRAM; target_id có thể chỉ định một cấu hình. Nếu bỏ target_id, hệ thống gửi theo mọi cấu hình đang bật và khớp giao dịch. Payload gửi lại vẫn cùng transaction_code, nên server phải bỏ qua nếu giao dịch đã xử lý.

Sửa lỗi truy vấn

API trả bốn trăm khi số VA không tồn tại hoặc không thuộc tài khoản; lấy lại số từ endpoint danh sách VA. Bốn trăm lẻ một là token hết hạn, hãy đăng nhập lại lấy token mới. Bốn trăm hai mươi hai thường do thiếu virtual_account_number hoặc limit lớn hơn một trăm. Retry trả bốn trăm lẻ bốn thì transaction_id sai; phải dùng id của giao dịch, không dùng transaction_code.

Mở docs và đối chiếu

Response đầy đủ, bảng giải nghĩa và code đọc mọi trang nằm tại monapay.vn/docs/api/giao-dich. Bạn đăng ký tại my.monapay.vn, mở mục Giao dịch để so một bản ghi dashboard với kết quả API thực tế trước khi đặt tác vụ đối soát. Nếu còn nhầm ID, số VA hoặc phân trang, gọi tổng đài một chín không không, sáu ba sáu, sáu bốn tám để đội MONA hỗ trợ.

Mọi giao dịch ACB báo về đều được MONA Pay lưu lại, kể cả khi webhook tới server bạn thất bại. Truy vấn bằng GET /api/v1/acb/virtual-account/transactions?virtual_account_number=<số VA>&page=1&limit=100 (Bearer token, limit tối đa 100). Mỗi bản ghi có transaction_code là mã tham chiếu ổn định của ngân hàng, dùng làm khoá chống trùng khi đối soát. Giao dịch nào lỡ không tới được server thì gọi endpoint retry để MONA Pay gửi lại webhook hoặc tin Telegram cho đúng giao dịch đó.

GET /api/v1/acb/virtual-account/transactions

Cần Bearer. Hệ thống tìm VA theo số, kiểm tra VA thuộc tài khoản của bạn rồi mới trả giao dịch.

Tham số (query) Kiểu Bắt buộc Ghi chú
virtual_account_number string có Số VA, ví dụ MONA0000010234
page integer ≥1 không Mặc định 1
limit integer 1-100 không Mặc định 10
Terminal
curl "https://api.monapay.vn/api/v1/acb/virtual-account/transactions?virtual_account_number=MONA0000010234&page=1&limit=100" \
  -H "Authorization: Bearer $MONA_TOKEN"

Response 200:

JSON
{
  "success": true,
  "message": "Success",
  "data": {
    "data": [
      {
        "id": "0190d0e1-...",
        "acb_callback_request_id": "0190d0e0-...",
        "transaction_status": "SUCCESS",
        "transaction_channel": "IBFT",
        "transaction_date": "2026-08-28T10:45:12",
        "effective_date": "2026-08-28T10:45:12",
        "debit_or_credit": "credit",
        "amount": 2500000,
        "transaction_content": "Thanh toan DH10234",
        "transaction_code": "FT26240001234",
        "account_number": "123456789",
        "va_prefix_cd": "MONA",
        "va_nbr": "MONA0000010234",
        "attributes": {
          "remitter_name": "NGUYEN VAN A",
          "remitter_account_number": "9876543210",
          "issuer_bank_name": "Vietcombank",
          "reference_number": "FT26240001234"
        }
      }
    ],
    "current_page": 1,
    "per_page": 100,
    "total": 1,
    "last_page": 1,
    "start": 1,
    "end": 1,
    "has_next": false,
    "has_prev": false
  }
}

Ý nghĩa từng trường

Trường Ý nghĩa
id ID giao dịch trong MONA Pay, dùng cho endpoint retry
transaction_status Trạng thái ACB trả về (SUCCESS là tiền đã vào)
transaction_channel Kênh giao dịch theo ACB (chuyển khoản nhanh, QR…)
transaction_date Thời điểm giao dịch, giờ Việt Nam
effective_date Ngày hiệu lực ghi sổ
debit_or_credit credit = tiền vào, debit = tiền ra. Webhook hiện chỉ bắn cho tiền vào
amount Số tiền VND, số nguyên
transaction_content Nội dung chuyển khoản khách gõ (hoặc QR điền sẵn)
transaction_code Mã tham chiếu ngân hàng, ổn định qua mọi lần gửi lại, khoá chống trùng
account_number Số tài khoản ACB thật nhận tiền
va_prefix_cd, va_nbr Đầu số và số VA khớp giao dịch, null nếu tiền vào thẳng tài khoản chính
attributes Thông tin phụ ACB gửi kèm: tên và số tài khoản người chuyển, ngân hàng chuyển, số tham chiếu, các trường custom1..custom10 nếu có

Ngoài ra, cùng bản ghi này là thứ webhook gửi sang bạn dưới dạng rút gọn (xem Định dạng payload).

Đối soát bằng API

Hiện chưa có tham số since_id; đối soát theo cách sau (cron mỗi 15-30 phút hoặc cuối ngày):

  1. Với mỗi VA đang dùng, gọi trang 1 với limit=100, đọc tiếp khi has_next là true.
  2. Với mỗi bản ghi, tra transaction_code trong bảng giao dịch của bạn. Có rồi thì bỏ qua, chưa có thì ghi thêm và xử lý đơn như khi nhận webhook.
  3. Dừng khi gặp transaction_code đã có và transaction_date cũ hơn mốc đối soát lần trước (danh sách sắp xếp mới trước).

Chi tiết và code mẫu ở Đối soát giao dịch.

PHP: đọc hết các trang

PHP
<?php
function monaGet(string $url): array {
    $ctx = stream_context_create(['http' => ['header' => 'Authorization: Bearer ' . getenv('MONA_TOKEN') . "\r\n", 'ignore_errors' => true]]);
    return json_decode(file_get_contents($url, false, $ctx), true) ?? [];
}
$va = 'MONA0000010234'; $page = 1;
do {
    $res = monaGet("https://api.monapay.vn/api/v1/acb/virtual-account/transactions?virtual_account_number=$va&page=$page&limit=100");
    if (empty($res['success'])) throw new RuntimeException($res['message'] ?? 'Lỗi truy vấn');
    foreach ($res['data']['data'] as $tx) {
        if ($tx['debit_or_credit'] !== 'credit') continue;
        // INSERT ... ON DUPLICATE KEY (UNIQUE transaction_code) → chỉ ghi bản chưa có
        ghiNhanGiaoDich($tx['transaction_code'], $tx['amount'], $tx['transaction_content'], $tx['transaction_date']);
    }
    $page++;
} while (!empty($res['data']['has_next']));

Node

Code
async function* monaTransactions(va) {
  for (let page = 1; ; page++) {
    const r = await fetch(`https://api.monapay.vn/api/v1/acb/virtual-account/transactions?virtual_account_number=${va}&page=${page}&limit=100`, {
      headers: { Authorization: `Bearer ${process.env.MONA_TOKEN}` },
    });
    const j = await r.json();
    if (!j.success) throw new Error(j.message);
    yield* j.data.data;
    if (!j.data.has_next) break;
  }
}
for await (const tx of monaTransactions('MONA0000010234')) {
  if (tx.debit_or_credit !== 'credit') continue;
  await db.upsertByTransactionCode(tx.transaction_code, tx); // UNIQUE(transaction_code)
}

POST /api/v1/acb/virtual-account/transactions/{transaction_id}/retry

Gửi lại thông báo cho một giao dịch cụ thể (server bạn lúc đó bị lỗi, hoặc cấu hình webhook sai rồi sửa lại). Cần Bearer + X-Client-Secret. Gửi lại tự động theo lịch đang triển khai, hiện là thao tác chủ động này hoặc nút “Gửi lại” trong dashboard.

Trường Kiểu Bắt buộc Ghi chú
target_type WEBHOOK / TELEGRAM có Kênh cần gửi lại
target_id uuid không ID cấu hình webhook hoặc Telegram cụ thể. Bỏ trống = gửi lại theo mọi cấu hình đang bật khớp giao dịch
Terminal
curl -X POST https://api.monapay.vn/api/v1/acb/virtual-account/transactions/0190d0e1-.../retry \
  -H "Authorization: Bearer $MONA_TOKEN" -H "X-Client-Secret: $MONA_SECRET" \
  -H 'Content-Type: application/json' -d '{"target_type":"WEBHOOK"}'

Payload gửi lại y hệt lần đầu, cùng transaction_code, nên server bạn phải chống trùng (xem Gửi lại và xử lý lỗi).

Lỗi thường gặp

HTTP Nguyên nhân Cách xử lý
400 Số VA không tồn tại hoặc không thuộc tài khoản Lấy đúng số VA từ GET /api/v1/acb/{bank_account_id}/virtual-account/retrieve
401 Token hết hạn Login lại
422 limit > 100 hoặc thiếu virtual_account_number Sửa tham số
404 (retry) transaction_id không tồn tại Lấy id từ danh sách giao dịch