# Webhook thanh toán: cách tích hợp cho web, phần mềm Việt Nam

> Webhook thanh toán giúp web không bỏ sót chuyển khoản: xác thực HMAC, chống trùng, tự động đối soát. Xem thêm code PHP, Node, Python thực tế từ MONA Pay.

URL: https://mona.software/webhook-thanh-toan-la-gi-cach-tich-hop/
Ngày đăng: 2026-09-03T12:43:39+07:00
Cập nhật: 2026-10-07T09:41:43+07:00
Tác giả: monamedia

**Webhook thanh toán** là yêu cầu HTTP POST được hệ thống nhận tiền gửi sang web hoặc phần mềm ngay khi ngân hàng ghi nhận giao dịch. Làm sao để tích hợp webhook mà không ghi trùng đơn? Đầu nhận phải kiểm dấu thời gian, xác thực HMAC trên raw body, trả HTTP 200 rồi đưa dữ liệu vào hàng đợi. Làm đúng thứ tự này, đội dev có một luồng tự động xác nhận chuyển khoản theo thời gian thực và còn đường đối soát khi máy chủ gián đoạn. Tụi em từng làm việc này. Từ năm 2022, kiến trúc đó đã nhận biến động ACB cho phần mềm MONA; tụi em sau đó mới đóng gói thành MONA Pay và mở công khai ngày 29/08/2026. Phần dưới đi thẳng vào payload 7 trường, code PHP, Node, Python, 5 lỗi tụi em đã gặp, retry, đối soát 15 phút và cách thử sandbox không tốn tiền.

## Webhook thanh toán là gì dưới góc nhìn của đội dev MONA?

Một khách chuyển khoản xong, phần mềm bán hàng cần biết tiền đã tới mà không chờ người mở ứng dụng ngân hàng. Webhook thanh toán làm đúng việc đó: nguồn phát chủ động đẩy một sự kiện tới URL do đội dev khai báo, thay vì đầu nhận liên tục hỏi xem có giao dịch mới chưa. Với **webhook thanh toán**, sự kiện mang số tiền, nội dung, thời gian, mã giao dịch và tài khoản nhận. Hiểu vậy là đủ.

[![](https://i.ytimg.com/vi/DsTVp6v9u4Y/hqdefault.jpg)Phát video: Tích hợp webhook thanh toán: nhận tiền vào ACB trong 5 phút.](https://www.youtube.com/watch?v=DsTVp6v9u4Y)

Video hướng dẫn: Tích hợp webhook thanh toán MONA Pay trong 5 phút. Tài liệu chi tiết và các bước làm theo tại [monapay.vn](https://monapay.vn/docs/webhooks/tich-hop-webhook).

MONA Pay là API ngân hàng và dịch vụ xác nhận thanh toán tự động của The MONA Group, giúp doanh nghiệp Việt Nam nhận và xác nhận tiền chuyển khoản theo thời gian thực qua tài khoản ảo (VA), VietQR, webhook và Telegram, thiết kế để cả lập trình viên lẫn AI agent tích hợp trong vài phút. Tiền đi thẳng vào tài khoản ngân hàng đứng tên doanh nghiệp, còn MONA Pay nhận tin báo có rồi chuyển tiếp sự kiện. Hệ thống không cần mật khẩu ngân hàng và không có quyền rút tiền. Ngày 29/08/2026, tụi em mở MONA Pay công khai sau hơn 4 năm dùng luồng nhận báo có ACB trong phần mềm, và hơn 14.000 dự án của cả tập đoàn cho tụi em đủ ca thực tế để chốt các lớp phòng vệ.

![Tài liệu webhook thanh toán và API dành cho lập trình viên](https://mona.software/wp-content/uploads/2026/09/tai-lieu-webhook-api-mona-pay-mona.webp) 

Tài liệu MONA Pay tách riêng phần nhận webhook thanh toán, bảo mật, gửi lại và đối soát.

Khác biệt này ảnh hưởng trực tiếp tới kiến trúc web. Khi nhân viên đọc sao kê, trạng thái đơn nằm trong đầu một người; khi webhook thanh toán đi vào phần mềm, trạng thái nằm trong cơ sở dữ liệu và mọi khâu sau đó dùng chung một bản ghi. Đội dev MONA đã áp dụng cách thứ hai cho phần mềm giao khách từ năm 2022, trước lúc MONA Pay có tên và có bảng giá công khai. Anh chị đang xây [phần mềm quản lý đơn hàng](https://mona.software/phan-mem-quan-ly-don-hang/) hoặc đọc bài [API ngân hàng cho phần mềm bán hàng](https://mona.software/api-ngan-hang-cho-phan-mem-ban-hang/) sẽ thấy đây là lớp dữ liệu nối tiền với nghiệp vụ, nên kiến trúc cần được chọn đúng từ đầu. Mốc vận hành từ năm 2022 cũng là lý do tụi em ưu tiên log và đối soát thay vì chỉ nhìn một lần callback thành công.

| Thành phần | Việc phải làm | Dữ kiện cần giữ | Điều kiện đạt |
| --- | --- | --- | --- |
| Ngân hàng và MONA Pay | Ghi nhận tiền vào, tạo sự kiện | Mã giao dịch, số tiền, tài khoản nhận | Giao dịch xuất hiện trong dashboard |
| Sender webhook thanh toán | POST payload tới URL đã khai báo | Raw body, timestamp, chữ ký | Nhận 200, 201 hoặc 202 trong 10 giây |
| Receiver webhook thanh toán | Kiểm thời gian và HMAC | `transaction_code` UNIQUE | Không nhận gói sai chữ ký hoặc trùng mã |
| Hàng đợi nghiệp vụ | Khớp đơn, gửi email, cập nhật kho | Trạng thái xử lý và log lỗi | Chạy độc lập sau phản hồi HTTP |

## Kiến trúc webhook thanh toán chuẩn bắt đầu bằng raw body

Tụi em hay vẽ luồng này thành 5 ô: ngân hàng ghi nhận tiền, MONA Pay lưu giao dịch, webhook thanh toán POST tới endpoint, endpoint xác minh, hàng đợi xử lý đơn. Dấu thời gian trong `X-Mona-Timestamp` phải được kiểm trước để loại gói lệch quá 300 giây. Tiếp theo, máy chủ lấy đúng raw body từng byte và tính HMAC-SHA256 bằng secret riêng của cấu hình. Sau khi chữ ký khớp, endpoint trả 200 trong giới hạn 10 giây.

Thứ tự đó rất đáng giữ, vì tác vụ khớp đơn có thể đụng cơ sở dữ liệu, kho, CRM và dịch vụ gửi mail trong cùng một lượt. Chỉ một API phía sau chậm 12 giây là MONA Pay ghi nhận `TIMEOUT`, dù tiền đã vào và chữ ký đã đúng. Trả lời trước giúp kênh giao nhận sự kiện kết thúc nhanh; hàng đợi mới chịu trách nhiệm chạy lại tác vụ nghiệp vụ. Đừng đảo chỗ.

![Wizard nhập URL HTTPS nhận webhook thanh toán MONA Pay](https://mona.software/wp-content/uploads/2026/09/wizard-webhook-url-mona-pay-mona.webp) 

Bước đầu của wizard yêu cầu URL đích cuối, tránh redirect làm lần gửi bị đánh dấu thất bại.

Payload mặc định có 7 trường và giữ cấu trúc phẳng để hệ thống cũ cũng đọc được. `amount` là số nguyên VND; `description` giữ nội dung chuyển khoản; `transfer_date` dùng giờ Việt Nam; `transaction_code` là khóa chống trùng; `account_number` là VA hoặc tài khoản nhận; `bank_name` cho biết ngân hàng; `type` hiện mang giá trị `income`. Code cần bỏ qua trường lạ trong tương lai, nhưng phải từ chối kiểu dữ liệu sai ở 7 trường đang dùng để tạo ra ranh giới dễ kiểm.

| Trường | Ví dụ | Dùng để làm gì | Quy tắc đầu nhận |
| --- | --- | --- | --- |
| `amount` | `2500000` | So với số tiền đơn | Số nguyên VND, không có phần thập phân |
| `description` | `DH10234` | Khớp mã khi không dùng VA riêng | Chuẩn hóa để tìm, vẫn lưu bản gốc |
| `transfer_date` | `10:30:00 28/08/2026` | Đối chiếu thời điểm ngân hàng báo | Không đưa thẳng vào `new Date()` |
| `transaction_code` | `FT26240001234` | Idempotency | Đặt UNIQUE ở cơ sở dữ liệu |
| `account_number` | `MONA0000010234` | Nhận diện VA hoặc đơn | Lưu dạng chuỗi |
| `bank_name` | `ACB` | Chọn bộ quy tắc theo ngân hàng | Không hard-code một giá trị duy nhất |
| `type` | `income` | Phân loại tiền vào | Bỏ qua loại chưa hỗ trợ |

Trong dự án theo [mô hình MVC](https://mona.software/mo-hinh-mvc/), receiver chỉ làm phần xác minh và chuyển payload sang service; controller mỏng giúp kiểm thử lỗi rõ hơn. Phần lưu giao dịch nên có migration tạo chỉ mục UNIQUE, đọc thêm cách tổ chức dữ liệu ở bài [SQL là gì](https://mona.software/sql-la-gi/). Còn tác vụ đổi trạng thái đơn phải gọi cùng một hàm cho cả webhook thanh toán lẫn đối soát, để hai đường dữ liệu chỉ có một nguồn xử lý.

## Tích hợp webhook thanh toán và xác thực HMAC bằng PHP, Node, Python

Ba đoạn dưới giữ cùng một thứ tự: đọc raw body, kiểm cửa sổ 5 phút, xác thực webhook HMAC bằng so sánh thời gian cố định, trả 200 rồi mới chuyển dữ liệu webhook thanh toán sang hàng đợi. Tụi em giữ cửa sổ đó ở 300 giây và đặt trần phản hồi 10 giây cho cả ba SDK. Secret nằm trong biến môi trường `MONA_WEBHOOK_SECRET`, tách khỏi `client_secret` dùng để gọi API MONA Pay vì hai khóa có công việc khác nhau. Đừng lưu chúng trong Git.

![Wizard chọn xác thực webhook HMAC SHA256 và secret](https://mona.software/wp-content/uploads/2026/09/wizard-webhook-hmac-sha256-mona-pay-mona.webp) 

Secret HMAC thuộc riêng cấu hình webhook thanh toán và phải nằm ngoài mã nguồn của dự án.

### Mẫu PHP thuần kiểm HMAC trên raw body

PHP thuần đọc trực tiếp `php://input`, nên đoạn receiver khá ngắn nhưng raw body phải nguyên vẹn. Sau phản hồi, `fastcgi_finish_request()` đóng kết nối với sender trước khi ứng dụng đưa giao dịch sang worker. Ở WordPress hoặc Laravel, vẫn lấy body gốc từ request thay vì mã hóa lại mảng đã parse.

```php
<?php
$secret = getenv('MONA_WEBHOOK_SECRET');
$raw = file_get_contents('php://input');
$timestamp = $_SERVER['HTTP_X_MONA_TIMESTAMP'] ?? '';
$signature = $_SERVER['HTTP_X_MONA_SIGNATURE'] ?? '';

if (abs(time() - (int) $timestamp) > 300) {
    http_response_code(400); exit('timestamp qua han');
}

$expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $raw, $secret);
if (!hash_equals($expected, $signature)) {
    http_response_code(401); exit('sai chu ky');
}

http_response_code(200); echo 'OK';
if (function_exists('fastcgi_finish_request')) fastcgi_finish_request();
$payload = json_decode($raw, true);
queue_transaction($payload);
```

### Mẫu Node dùng SDK @monapay/node 0.4.0

SDK Node nhận raw body của webhook thanh toán dưới dạng Buffer và kiểm hai header bằng hàm `verifyWebhook`. Route này phải đặt `express.raw()` trước middleware `express.json()`, nếu không body đã bị biến thành object. Dot notation giữ nguyên để dev copy vào Express rồi đọc tiếp. Ở route production, tụi em còn đặt cảnh báo khi thời gian phản hồi p95 vượt 1 giây.

```javascript
import express from 'express';
import { verifyWebhook } from '@monapay/node';

const app = express();

app.post('/webhooks/monapay', express.raw({ type: 'application/json' }), async (req, res) => {
  const result = verifyWebhook({
    rawBody: req.body,
    headers: req.headers,
    secret: process.env.MONA_WEBHOOK_SECRET
  });

  if (!result.ok) return res.status(401).send(result.reason);
  res.sendStatus(200);

  await queue.add('monapay', {
    transactionCode: result.payload.transaction_code,
    payload: result.payload
  });
});
```

### Mẫu Python dùng gói monapay trên PyPI

FastAPI đọc bytes của webhook thanh toán bằng `await request.body()`, sau đó thư viện `monapay` trả kết quả đã kiểm timestamp và chữ ký. `BackgroundTasks` đủ cho ví dụ ngắn; hệ thống có tải nên chuyển sang Redis, RabbitMQ hoặc hàng đợi đang có. Phần worker vẫn dựa vào UNIQUE `transaction_code`.

```python
import os
from fastapi import BackgroundTasks, FastAPI, Request, Response
from monapay import verify_webhook

app = FastAPI()

@app.post('/webhooks/monapay')
async def monapay_webhook(request: Request, tasks: BackgroundTasks):
    raw_body = await request.body()
    result = verify_webhook(
        raw_body,
        request.headers,
        os.environ['MONA_WEBHOOK_SECRET']
    )
    if not result.ok:
        return Response(content=result.reason, status_code=401)
    tasks.add_task(process_transaction, result.payload)
    return Response(status_code=200)
```

Đội dev đang làm [lập trình web app](https://mona.software/dich-vu-lap-trinh-web-app/) cho khách có thể giữ ba mẫu trên làm contract test webhook thanh toán. Tạo một raw body cố định, ký bằng secret test, rồi chạy ca chữ ký đúng, chữ ký sai, timestamp quá hạn và cùng `transaction_code` gửi hai lần. Bài [kiểm thử phần mềm](https://mona.software/kiem-thu-phan-mem/) giải thích sâu hơn cách tách dữ liệu test khỏi môi trường thật. Bốn ca này phải xanh trước khi nối tiền.

## Năm lỗi thật khi dev nối webhook thanh toán và cách sửa

Từ năm 2022, tụi em đã gặp lại đúng 5 lỗi ở các đầu nhận tiền vào phần mềm: parse JSON rồi ký lại, thiếu UNIQUE cho mã giao dịch, URL còn redirect 301, xử lý nặng trước phản hồi 200 và giờ máy chủ bị lệch. Mỗi lỗi nhìn riêng đều nhỏ; đặt vào đường tiền, nó biến thành đơn treo, đơn ghi hai lần hoặc log đỏ hàng loạt. Đội nhận bàn giao thường chỉ thấy triệu chứng sau cùng. Sửa từ gốc.

![Wizard chọn định dạng JSON cho webhook chuyển khoản](https://mona.software/wp-content/uploads/2026/09/wizard-webhook-dinh-dang-payload-mona-pay-mona.webp) 

Định dạng JSON tiện cho framework hiện đại, nhưng chữ ký vẫn tính trên chuỗi body nguyên bản.

Lỗi parse trước khi ký xuất hiện nhiều ở Express, Next.js và Laravel vì middleware chạy sẵn. JSON được parse rồi serialize lại có thể đổi thứ tự khóa, khoảng trắng hoặc cách escape Unicode, nên HMAC khác dù dữ liệu nhìn bằng mắt vẫn giống nhau. URL redirect cũng dễ lọt qua lúc test bằng trình duyệt, vì trình duyệt tự đi theo 301; sender webhook thanh toán không đi tiếp và gắn nhãn `HTTP_3XX`. Browser không phải sender, vì vậy thấy nhãn gì phải sửa đúng lớp đó. Tụi em kiểm từng nhãn ngay trong lịch sử gửi trước khi đụng tới code nghiệp vụ.

| Lỗi tụi em đã gặp | Dấu hiệu trong log | Nguyên nhân | Cách sửa dứt điểm |
| --- | --- | --- | --- |
| Parse JSON rồi ký lại | 401, mọi chữ ký đều sai | Raw body đã đổi byte | Lấy body bytes trước middleware JSON |
| Không UNIQUE `transaction_code` | Một khoản làm đơn chạy hai lần | Gửi lại được xem như giao dịch mới | Ràng buộc UNIQUE và insert idempotent |
| URL redirect 301 | `HTTP_3XX` | Thiếu HTTPS, www hoặc dấu gạch cuối | Khai URL đích cuối trả thẳng 200 |
| Làm việc nặng trước 200 | `TIMEOUT` sau 10 giây | Khớp đơn, gửi mail chạy đồng bộ | Trả 200 rồi đẩy vào hàng đợi |
| Giờ máy chủ lệch | 400 timestamp quá hạn | NTP tắt hoặc đồng hồ container sai | Bật NTP, cảnh báo khi lệch trên 60 giây |

Ngày 03/09/2026, tụi em đóng vai chủ một tiệm hoa mới đăng ký rồi đi hết wizard webhook thanh toán 5 bước, nối Telegram và bật 2FA để kiểm lại đường người dùng. Một chi tiết đáng nhớ là URL nhìn đúng trong ô nhập vẫn có thể trả 301 nếu máy chủ ép thêm dấu gạch cuối; phải bấm Gửi thử và nhìn chính log mới biết. Secret HMAC được wizard sinh, còn API key nằm ở màn hình khác và chỉ hiện phần bí mật một lần. Ca kiểm đó giúp tài liệu phản ánh đúng thứ dev sẽ bấm.

The MONA Group làm dự án từ năm 2016, đã làm hơn 14.000 dự án, nên tụi em xem [bảo trì phần mềm](https://mona.software/bao-tri-phan-mem-la-gi/) là một phần của thiết kế ban đầu. Webhook thanh toán không kết thúc ở ngày nhận được payload đầu tiên. Endpoint cần log raw body, header, mã phản hồi, thời gian xử lý và trạng thái worker tối thiểu 30 ngày để khi đơn sai còn lần lại được, thay vì debug bằng trí nhớ.

## Retry webhook thanh toán và đối soát 15 phút giữ đơn không bị sót

Webhook thanh toán là kênh thời gian thực, còn đối soát là lưới đỡ. MONA Pay ghi mỗi lần gửi với mã HTTP, `duration_ms`, payload, phần đầu body phản hồi và nhãn như `HTTP_4XX`, `HTTP_5XX`, `TIMEOUT`, `SSL`, `DNS` hoặc `CONNECTION`. Lần lỗi bấm gửi lại được từ dashboard hoặc gọi API gửi lại theo mã giao dịch. Vì vậy, đối soát định kỳ vẫn phải có ngay từ đầu.

![Lịch sử giao dịch dùng đối soát webhook chuyển khoản](https://mona.software/wp-content/uploads/2026/09/lich-su-giao-dich-mona-pay-mona.webp) 

Mã giao dịch chung giúp webhook thanh toán, lần gửi lại và đối soát gặp nhau ở một bản ghi.

Luồng bán hàng giao ngay, vé hoặc mở khóa học nên chạy đối soát mỗi 15 phút; phần mềm quản lý ít gấp hơn có thể chạy mỗi giờ, kế toán vẫn chốt đủ vào 23:30. Tác vụ gọi `GET /api/v1/acb/virtual-account/transactions` theo từng VA, lấy tối đa 100 giao dịch mỗi trang và dừng khi cả trang không còn mã mới. Mỗi khoản thiếu được đưa vào đúng hàm worker mà webhook thanh toán đang dùng, không viết hai bộ nghiệp vụ.

Chống trùng nằm ở cơ sở dữ liệu, không nằm trong bộ nhớ tiến trình. Hai worker chạy cùng lúc đều có thể đọc thấy “chưa tồn tại”, nên câu lệnh insert cần UNIQUE `transaction_code` và xử lý xung đột như một kết quả bình thường. Với MySQL dùng `INSERT IGNORE`; PostgreSQL dùng `ON CONFLICT DO NOTHING`. Sau đó cơ sở dữ liệu mới chốt lượt bằng cách khóa hoặc cập nhật đơn theo transaction.

Một quy tắc tụi em giữ khá cứng: webhook thanh toán, retry và đối soát đều gọi một hàm `process_transaction()`, rồi hàm này tự kiểm idempotency trước khi chạm đơn hàng. Cách làm hợp với [luồng công việc](https://mona.software/workflow-la-gi/) có nhiều tác vụ nối nhau và giảm số chỗ cần sửa khi nghiệp vụ đổi. Nếu tiền đã vào dashboard nhưng đơn chưa đổi trạng thái, đội vận hành nhìn được bước nào gãy thay vì chuyển bill qua nhóm chat để đoán. Log phải trả lời được.

### Đưa một endpoint thật vào bàn kiểm

Anh chị đã tốn thời gian viết route, dựng bảng giao dịch, cấu hình SSL và nối hàng đợi; đừng đợi tới lúc 800 khoản tiền trong tháng mới phát hiện URL đang redirect hoặc cùng một mã bị ghi hai lần. Gửi MONA endpoint staging, tụi em cùng bắn payload ký HMAC, xem log dưới 10 giây và chạy lại một khoản qua đối soát 15 phút.

[Tạo MONA Pay và thử webhook thanh toán](https://monapay.vn/) [Gọi 1900 636 648](tel:1900636648)

## Wizard 5 bước rút ngắn cấu hình webhook thanh toán

Dashboard chia cấu hình webhook thanh toán thành 5 bước rõ ràng: đặt tên và URL HTTPS, chọn phạm vi mọi tài khoản hoặc một VA, chọn HMAC-SHA256, chọn JSON hoặc dạng form, rồi kiểm lại trước khi lưu. Tụi em đã bấm Gửi thử, mở Lịch sử gửi và xác nhận mã 200 cùng thời gian phản hồi dưới 1 giây trên chính luồng này. Nhãn `HTTP_3XX` buộc sửa URL đích; nhãn `TIMEOUT` buộc tách việc nặng khỏi request, vì wizard không che lỗi và dev nhìn được từng bước.

![API key và lệnh MCP cho AI agent tích hợp webhook](https://mona.software/wp-content/uploads/2026/09/api-key-client-secret-mona-pay-mona.webp) 

Màn hình API Keys đưa sẵn client credentials để agent cấu hình webhook thanh toán đúng luồng.

Luồng AI agent làm webhook thanh toán bắt đầu tại **my.monapay.vn → API Keys → Tạo key**. Màn hình hiện sẵn `client_id`, `client_secret` và lệnh `claude mcp add monapay -e MONAPAY_CLIENT_ID=… -e MONAPAY_CLIENT_SECRET=… -- npx -y monapay-mcp`. Bản MCP 0.4.0 có 34 tools, gồm cả cấu hình webhook, Telegram, email và giao dịch sandbox; agent tự gọi `POST /api/v1/oauth/token` với client credentials để lấy token, không cầm username hoặc mật khẩu. Cách này vẫn chạy khi tài khoản bật 2FA.

Sau khi có token, agent nối ngân hàng bằng 2 lượt OTP ngay trong khung chat: bắt đầu đăng ký VA, hỏi mã ngân hàng gửi tới điện thoại, xác minh, đăng ký nhận thông báo rồi hỏi OTP lần hai. Agent không tự đoán mã và không lưu mật khẩu ngân hàng. Khi ngân hàng đã nối, `POST /api/v1/sandbox/transactions` tạo giao dịch giả để phát qua webhook thanh toán, Telegram và email mà không tính hạn mức gói. Thử sạch trước.

![Dashboard MONA Pay theo dõi bước tích hợp webhook](https://mona.software/wp-content/uploads/2026/09/tong-quan-dashboard-mona-pay-mona.webp) 

Danh sách kích hoạt cho thấy ngân hàng, webhook thanh toán, Telegram và API key đã hoàn tất hay chưa.

Ba prompt ngắn dưới đây đủ giao việc webhook thanh toán ban đầu cho agent, rồi dev review pull request như mọi thay đổi liên quan tiền. Prompt một: *“Đọc monapay.vn/llms-full.txt, tạo endpoint POST /webhooks/monapay, kiểm HMAC trên raw body, từ chối timestamp lệch 300 giây và viết test chữ ký đúng, sai.”* Prompt hai: *“Đặt transaction_code UNIQUE, trả 200 trước khi chạy nghiệp vụ, bỏ qua DUMMY123 và ghi log amount không khớp đơn.”* Prompt ba: *“Tạo cron đối soát 15 phút, lấy 100 giao dịch mỗi trang, dùng chung process_transaction với webhook và báo số khoản được bổ sung.”* Tài liệu máy đọc nằm ở [trang AI agent MONA Pay](https://monapay.vn/ai-agent).

## Webhook thanh toán, polling và Telegram hợp với ba việc khác nhau

Webhook thanh toán đẩy sự kiện ngay khi tiền vào, nên nó hợp với đổi trạng thái đơn, giữ chỗ, mở quyền và phát lệnh xuống kho. Polling chủ động hỏi API theo lịch 15 phút hoặc 1 giờ, chậm hơn nhưng kiểm soát được nhịp tải và rất hữu ích cho đối soát. Telegram báo cho con người trong nhóm hoặc topic, không thay cho bản ghi cơ sở dữ liệu vì không kênh nào ôm hết việc. Ba kênh đứng cạnh nhau.

![Telegram nhận thông báo tiền vào theo nhóm và topic](https://mona.software/wp-content/uploads/2026/09/thiet-lap-telegram-nhom-topic-mona-pay-mona.webp) 

Telegram giúp người vận hành nhìn thấy tiền vào, còn webhook thanh toán cập nhật phần mềm theo quy tắc.

Tụi em thường cấu hình cả ba: webhook thanh toán chạy thời gian thực, cron đối soát mỗi 15 phút, Telegram báo cho nhóm vận hành biết. Khi endpoint lỗi 8 phút trong một lần deploy, Telegram vẫn giúp người trực thấy khoản tiền, còn cron bổ sung bản ghi thiếu sau khi máy chủ sống lại. Một kênh hỏng không làm cả hệ thống mù và tạo ra dự phòng có mục đích.

| Kênh | Độ trễ | Điểm mạnh | Chỗ phải bù | Dùng cho |
| --- | --- | --- | --- | --- |
| Webhook thanh toán | Gần thời gian thực | Đẩy đúng sự kiện, ít request thừa | Cần endpoint sống và xử lý idempotent | Đổi trạng thái đơn, mở quyền |
| Polling API | Theo lịch 15 phút hoặc 1 giờ | Tự kéo lại dữ liệu khi receiver gián đoạn | Tốn request, có độ trễ | Đối soát và tác vụ bù |
| Telegram | Gần thời gian thực | Nhóm vận hành thấy ngay, có topic | Con người vẫn phải đọc | Cảnh báo, kiểm tra nhanh |

Hệ thống chỉ thu vài khoản mỗi tháng và chưa có nghiệp vụ sau báo có thì Telegram là điểm bắt đầu gọn. Web bán hàng có kho, CRM và email xác nhận cần webhook thanh toán làm kênh chính. Dù chọn cách nào, phần mềm production vẫn cần polling đối soát vì mạng, SSL và lúc deploy luôn có xác suất gián đoạn. Tụi em không khuyên kéo thả no-code thành đường tiền chính; một node sai cấu hình đủ làm luồng đứng, trong khi đơn vẫn phát sinh. Giao production cho code có log và người chịu trách nhiệm.

## Phí đường tiền và lộ trình nhiều ngân hàng cần nói rõ từ đầu

Webhook thanh toán của MONA Pay được thiết kế cho nhiều ngân hàng, nối nhiều tài khoản ở nhiều ngân hàng về một dashboard; ACB đang chạy trực tiếp vì tụi em đã dùng thật với ACB từ năm 2022, các ngân hàng khác đang nối thêm theo lộ trình. Bảng trạng thái được cập nhật tại [monapay.vn/ngan-hang](https://monapay.vn/ngan-hang). Tiền vẫn vào thẳng tài khoản của anh chị, còn MONA Pay chỉ nhận tin báo.

![Bảng giá MONA Pay theo số khoản tiền vào](https://mona.software/wp-content/uploads/2026/09/bang-gia-mona-pay-500-giao-dich-mona.webp) 

Gói miễn phí có 500 khoản mỗi tháng, các gói sau tăng theo số khoản chứ không theo doanh thu.

Chữ “miễn phí” của webhook thanh toán chỉ có nghĩa khi đặt cạnh phí của từng đường tiền. Biểu phí thương mại điện tử VietinBank hiệu lực 22/04/2026 ghi thẻ nội địa NAPAS nối trực tiếp ở mức 0,5% tới 0,7%, nối gián tiếp 1,09% cộng 1.818 đồng mỗi giao dịch; thẻ Visa hoặc Mastercard phát hành trong nước là 1,818% tới 2,182% cộng 4.200 đồng. PayPal công bố ngày 28/05/2026 mức 4,40% cộng 0,30 USD, phí rút 60.000 đồng mỗi lần và đổi tiền thêm 4%. Chuyển khoản tự báo có qua MONA Pay là 0% cộng 0 đồng trên từng khoản, và phép tính phải ghi nguồn.

| Đường tiền | Phí công bố | Khoản cố định | Nguồn, ngày kiểm |
| --- | --- | --- | --- |
| Thẻ nội địa NAPAS qua cổng ngân hàng | 0,5% đến 0,7% trực tiếp; 1,09% gián tiếp | Gián tiếp cộng 1.818 đồng mỗi giao dịch | [VietinBank](https://www.vietinbank.vn/ca-nhan/cong-cu-tien-ich/bieu-phi-va-bieu-mau/dich-vu-chap-nhan-thanh-toan-the-40-html), hiệu lực 22/04/2026, chưa VAT |
| Visa, Mastercard phát hành trong nước | 1,818% đến 2,182% | 4.200 đồng mỗi giao dịch | VietinBank, hiệu lực 22/04/2026, chưa VAT |
| PayPal | 4,40% cộng 0,30 USD | Rút 60.000 đồng mỗi lần, đổi tiền 4% | [PayPal Việt Nam](https://www.paypal.com/vn/webapps/mpp/merchant-fees), 28/05/2026 |
| Chuyển khoản tự báo có qua MONA Pay | **0% cộng 0 đồng** mỗi khoản | Gói 0 đồng có 500 khoản mỗi tháng | [Bảng giá MONA Pay](https://monapay.vn/bang-gia), 03/09/2026; [NAPAS và VietQR](https://baochinhphu.vn/napas-gop-phan-tao-cuoc-cach-mang-ve-phi-10224020517542487.htm) |

![Biểu đồ phí thẻ và chuyển khoản tự báo có](https://mona.software/wp-content/uploads/2026/09/so-sanh-phi-cong-thanh-toan-the-paypal-chuyen-khoan-mona.webp) 

Với 200 triệu đồng và 800 khoản, lựa chọn đường tiền tạo chênh lệch hàng triệu đồng mỗi tháng.

Lấy một phần mềm SaaS thu 200 triệu đồng mỗi tháng qua 800 hóa đơn, trung bình 250.000 đồng một khoản. Nếu toàn bộ đi qua thẻ phát hành trong nước ở mức thấp 1,818%, riêng phần trăm là 3.636.000 đồng, cộng 3.360.000 đồng phí cố định, tổng gần 7 triệu đồng và chưa VAT. Chuyển khoản tự báo có kèm webhook thanh toán dùng gói 99.000 đồng cho 2.000 khoản, chênh khoảng 6,9 triệu đồng mỗi tháng và hơn 82 triệu đồng một năm.

Gói Miễn phí có 500 khoản webhook thanh toán mỗi tháng; các mức tiếp theo là 99.000 đồng cho 2.000 khoản, 299.000 đồng cho 10.000, 799.000 đồng cho 50.000 và 1.990.000 đồng cho 300.000, trả năm bằng tiền 10 tháng. Khách đang làm web, phần mềm hoặc hosting tại MONA được miễn phí hoàn toàn trong thời gian còn là khách hàng. Tụi em vẫn giữ cổng thẻ cho khách cần thẻ hoặc bán ra nước ngoài, còn chuyển khoản nội địa đi đường VietQR để giảm phí. Chọn theo người trả tiền.

## Webhook thanh toán đi vào bán lẻ, F&B, giáo dục và SaaS

Cùng một payload webhook thanh toán 7 trường, mỗi ngành lại có quy tắc khớp tiền khác nhau. Một shop chạy 50 đơn mỗi ngày, một trung tâm có 300 học viên và một SaaS gia hạn theo 12 tháng vẫn dùng chung receiver nếu contract không đổi. Bán lẻ dùng VA hoặc mã đơn để đổi trạng thái và phát lệnh đóng gói; F&B gắn khoản cọc với bàn hoặc tiệc; giáo dục gắn VA với học viên và kỳ thu; SaaS gắn mã giao dịch với hóa đơn gia hạn. Receiver chung nhưng worker khác, vì nghiệp vụ quyết định phần sau. Tụi em giữ contract chung để sửa lớp bảo mật một lần mà không đụng quy tắc riêng của từng ngành.

![Tạo tài khoản ảo để khớp giao dịch theo khách](https://mona.software/wp-content/uploads/2026/09/dang-ky-tai-khoan-ao-acb-mona-pay-mona.webp) 

VA riêng giúp phần mềm biết tiền thuộc đơn hoặc khách nào mà không phải đoán nội dung.

Với [phần mềm quản lý bán hàng](https://mona.software/phan-mem-quan-ly-ban-hang/), worker webhook thanh toán phải đưa đơn sai số tiền sang danh sách kiểm tra, không tự đánh dấu đã thanh toán. Trong [phần mềm quản lý nhà hàng](https://mona.software/phan-mem-quan-ly-nha-hang/), tiền cọc về có thể giữ bàn, nhưng việc khách đổi lịch vẫn để người quản lý duyệt. Một [phần mềm thu học phí](https://mona.software/phan-mem-thu-hoc-phi/) cần tách học viên, kỳ thu và khoản thừa; còn mô hình [SaaS](https://mona.software/saas-la-gi/) phải ngăn một giao dịch gia hạn hai lần. Máy chỉ chạy quy tắc đã viết.

| Ngành | Khóa khớp | Tác vụ sau báo có | Ca cần người duyệt |
| --- | --- | --- | --- |
| Bán lẻ | Mã đơn hoặc VA của đơn | Đổi trạng thái, gửi kho | Thiếu hoặc thừa tiền |
| F&B | Mã đặt bàn, mã tiệc | Giữ chỗ, báo thu ngân | Đổi lịch sau khi cọc |
| Giáo dục | VA học viên và kỳ thu | Ghi học phí, mở quyền học | Chuyển sai kỳ hoặc chuyển gộp |
| SaaS | Mã hóa đơn | Gia hạn tài khoản | Một khoản trả nhiều hóa đơn |

Tụi em xem webhook thanh toán là một module trong hệ thống lớn hơn, giống cách [CRM](https://mona.software/crm-la-gi/) giữ hồ sơ khách và [ERP](https://mona.software/phan-mem-erp-la-gi/) giữ luồng vận hành. Giao dịch đã xác minh tạo ra sự kiện sạch, còn phần mềm quyết định đổi đơn, cộng công nợ, mở quyền hay chờ người duyệt. Trường hợp chỉ cần nhóm biết tiền vào, Telegram đủ dùng và anh chị chưa cần thuê dev. Trường hợp đã có nhiều trạng thái đơn, hãy tích hợp vào code gốc.

Gán mỗi đơn một số tài khoản ảo giúp webhook khớp đơn chắc hơn đọc nội dung chuyển khoản; cách làm có trong bài [tài khoản ngân hàng ảo là gì](https://mona.software/tai-khoan-ngan-hang-ao-la-gi/).

## Câu hỏi thường gặp và bước kiểm cuối trước khi nhận tiền thật

### Webhook thanh toán là gì nếu phần mềm đang dùng polling?

Nếu đội kỹ thuật còn hỏi webhook là gì, hãy nhìn theo hướng gửi dữ liệu: polling là đầu nhận hỏi API theo lịch, webhook thanh toán là nguồn phát chủ động gửi sự kiện khi có thay đổi. Anh chị giữ polling làm đối soát 15 phút và thêm webhook làm kênh thời gian thực, không cần bỏ một trong hai. Cùng một hàm xử lý giao dịch sẽ nhận dữ liệu từ cả hai đường.

### Xác thực webhook HMAC có thay cho đối soát không?

Không. HMAC chứng minh payload webhook thanh toán tới từ bên biết secret và body không bị sửa trên đường truyền; đối soát xác nhận bản ghi của hệ thống anh chị đủ so với sổ MONA Pay. Với giao dịch giá trị lớn, worker còn phải so amount, account_number và trạng thái đơn trước khi giao hàng, vì hai lớp này giải hai rủi ro riêng.

### Webhook thanh toán gửi lại có làm đơn chạy hai lần không?

Không, nếu bảng giao dịch đặt UNIQUE cho `transaction_code` và worker xử lý idempotent. Lần gửi lại giữ nguyên mã, nên insert xung đột được xem là giao dịch đã nhận.

### AI agent có cần mật khẩu ngân hàng để tích hợp webhook thanh toán không?

Không cần. Agent dùng `client_id` và `client_secret` để đổi lấy token, còn 2 mã OTP nối ngân hàng do chủ tài khoản đọc từ điện thoại và nhập ngay trong phiên chat. Sandbox tạo giao dịch giả sau khi nối VA, không tốn tiền và không tính vào hạn mức. Mật khẩu ngân hàng không đi qua MONA Pay.

![Bật xác thực hai lớp trước khi vận hành webhook](https://mona.software/wp-content/uploads/2026/09/xac-thuc-hai-lop-totp-mona-pay-mona.webp) 

TOTP bảo vệ dashboard, còn HMAC bảo vệ từng sự kiện webhook thanh toán gửi sang phần mềm.

Trước khi nhận tiền thật, đội dev chạy đủ 8 mục cho webhook thanh toán:

1. HTTPS hợp lệ trên đúng domain production.
2. URL đích cuối không redirect.
3. Raw body chưa bị middleware parse.
4. Timestamp lệch dưới 300 giây.
5. HMAC được so sánh timing-safe.
6. Endpoint trả HTTP 200 dưới 1 giây.
7. `transaction_code` có ràng buộc UNIQUE.
8. Cron đối soát chạy mỗi 15 phút.

Sau đó bắn `DUMMY123`, tạo một giao dịch sandbox 10.000 đồng, kiểm log và gửi lại cùng payload để chắc worker không chạy trùng. Bài [framework là gì](https://mona.software/framework-la-gi/) hay [ngôn ngữ lập trình là gì](https://mona.software/ngon-ngu-lap-trinh-la-gi/) không thay cho bài kiểm này; contract phải chạy trên chính ứng dụng sắp nhận tiền. Test rồi mới mở.

### Chốt webhook thanh toán trước khi 800 hóa đơn chạy qua

Một redirect 301, một middleware parse JSON sớm hoặc một bảng thiếu UNIQUE đều đủ làm luồng webhook thanh toán sai trong đêm triển khai; phép tính ở trên cho thấy 800 khoản đã tạo chênh gần 6,9 triệu đồng mỗi tháng, nên phần tiết kiệm chỉ có ý nghĩa khi endpoint chạy chắc. Tụi em có thể ngồi cùng đội dev kiểm HMAC, retry và đối soát trên staging.

Tạo MONA Pay, lấy key và bắn sandbox ngay hôm nay. Khách đang dùng web, phần mềm hoặc hosting của MONA được gán gói miễn phí hoàn toàn; đội kỹ thuật cần người rà cùng thì gọi 1900 636 648.

[Tạo tài khoản và bắn sandbox](https://monapay.vn/) [Gọi 1900 636 648](tel:1900636648)

Tụi em đã dùng webhook nhận tiền cho phần mềm MONA từ năm 2022, rồi ngày 03/09/2026 tự đi lại trọn wizard 5 bước như một tài khoản mới để kiểm từng chỗ dev sẽ gặp. Một **webhook thanh toán** đáng đưa vào production phải xác minh raw body, chặn replay 5 phút, phản hồi trong 10 giây, chống trùng bằng `transaction_code` và có đối soát 15 phút. Anh chị xem code gốc tại [hướng dẫn tích hợp webhook](https://monapay.vn/docs/webhooks/tich-hop-webhook), đọc riêng [bảo mật HMAC-SHA256](https://monapay.vn/docs/webhooks/bao-mat) cùng phần gửi lại và xử lý lỗi; cần rà kiến trúc phần mềm hiện có, gọi 1900 636 648 hoặc viết tới info@themona.global. Anh chị đã sẵn sàng bắn giao dịch sandbox đầu tiên chưa?

## Câu hỏi thường gặp

### Webhook thanh toán là gì nếu phần mềm đang dùng polling?

Nếu đội kỹ thuật còn hỏi webhook là gì, hãy nhìn theo hướng gửi dữ liệu: polling là đầu nhận hỏi API theo lịch, webhook thanh toán là nguồn phát chủ động gửi sự kiện khi có thay đổi. Anh chị giữ polling làm đối soát 15 phút và thêm webhook làm kênh thời gian thực, không cần bỏ một trong hai. Cùng một hàm xử lý giao dịch sẽ nhận dữ liệu từ cả hai đường.

### Xác thực webhook HMAC có thay cho đối soát không?

Không. HMAC chứng minh payload webhook thanh toán tới từ bên biết secret và body không bị sửa trên đường truyền; đối soát xác nhận bản ghi của hệ thống anh chị đủ so với sổ MONA Pay. Với giao dịch giá trị lớn, worker còn phải so amount, account_number và trạng thái đơn trước khi giao hàng, vì hai lớp này giải hai rủi ro riêng.

### Webhook thanh toán gửi lại có làm đơn chạy hai lần không?

Không, nếu bảng giao dịch đặt UNIQUE cho transaction_code và worker xử lý idempotent. Lần gửi lại giữ nguyên mã, nên insert xung đột được xem là giao dịch đã nhận.

### AI agent có cần mật khẩu ngân hàng để tích hợp webhook thanh toán không?

Không cần. Agent dùng client_id và client_secret để đổi lấy token, còn 2 mã OTP nối ngân hàng do chủ tài khoản đọc từ điện thoại và nhập ngay trong phiên chat. Sandbox tạo giao dịch giả sau khi nối VA, không tốn tiền và không tính vào hạn mức. Mật khẩu ngân hàng không đi qua MONA Pay. TOTP bảo vệ dashboard, còn HMAC bảo vệ từng sự kiện webhook thanh toán gửi sang phần mềm. Trước khi nhận tiền thật, đội dev chạy đủ 8 mục cho webhook thanh toán: HTTPS hợp lệ trên đúng domain production. URL đích cuối không redirect. Raw body chưa bị middleware parse. Timestamp lệch dưới 300 giây. HMAC được so sánh timing-safe. Endpoint trả HTTP 200 dưới 1 giây. transaction_code có ràng buộc UNIQUE. Cron đối soát chạy mỗi 15 phút. Sau đó bắn DUMMY123, tạo một giao dịch sandbox 10.000 đồng, kiểm log và gửi lại cùng payload để chắc worker không chạy trùng. Bài framework là gì hay ngôn ngữ lập trình là gì không thay cho bài kiểm này; contract phải chạy trên chính ứng dụng sắp nhận tiền. Test rồi mới mở.
