WordPress · chuyển khoản ACB

Tích hợp cổng thanh toán vào WordPress:
chuyển khoản ACB tự xác nhận

MONA Pay đưa thông báo tiền vào tài khoản ACB tới thẳng website WordPress của anh chị bằng webhook có chữ ký. Web nhận gói tin, khớp đúng người trả, rồi tự mở khoá học, xác nhận cọc, gia hạn hội viên. Tiền không qua MONA Pay, dán 1 đoạn PHP là chạy, miễn phí hoàn toàn, không giới hạn giao dịch.

Đủ kiểu web, chung một chỗ kẹt

Web WordPress nào đang thu tiền chuyển khoản bằng tay?

Trong 14.000+ dự án tụi em làm từ 2016, WordPress chiếm phần lớn. Web nào có thu tiền là có người ngồi dò sao kê, bất kể bán gì.

1

Web bán khoá học, tài liệu, thành viên trả phí

Học viên chuyển khoản xong nhắn "em chuyển rồi", admin mở app ngân hàng dò, rồi vào WordPress mở quyền bằng tay. Khuya nhắn thì sáng mới có khoá. Học viên đóng tiền lúc đang hứng, chờ 8 tiếng là nguội.

2

Web đặt cọc: tour, phòng, sự kiện, dịch vụ

Cọc chưa xác nhận thì chỗ chưa giữ. Nhân viên phải nhìn app ngân hàng và bảng đặt chỗ cùng lúc, hai người cùng cọc một suất là rối. Cuối ngày kế toán ngồi khớp lại từng dòng sao kê với từng đơn cọc.

3

Form đăng ký thu phí, gia hạn dịch vụ

Khách điền form xong, chuyển tiền, rồi email chứng từ. Bộ phận thu tiền tìm email, tìm sao kê, đối chiếu tên người chuyển với tên trong form. Tên viết không dấu, chuyển từ tài khoản người thân, là mất dấu.

4

Nhiều khách chuyển cùng số tiền

Học phí cùng giá, cọc cùng giá. Ba giao dịch 1.990.000 đ trong 10 phút, nội dung thì "ck", "chuyen tien", "hoc phi". Khớp nhầm là mở nhầm khoá cho người chưa đóng và khoá nhầm người đã đóng.

Cách MONA Pay làm

Web WordPress nhận tiền tự động qua 4 bước

Chính website khanhhung.academy và mona.host của tụi em đã thu học phí, thu tiền hosting bằng đúng luồng này nhiều năm trước khi mở public.

Trang thanh toán hiện mã QR hoặc số tài khoản ảo riêng

Web sinh mã VietQR đã điền sẵn số tiền và nội dung (mã học viên, mã đơn cọc, mã hội viên). Muốn khách khỏi gõ gì cả thì cấp mỗi khách 1 số tài khoản ảo riêng do ACB cấp từ đầu số của anh chị. Cả hai cách đều làm bằng API hoặc trong dashboard.

Khách chuyển, tiền vào thẳng tài khoản ACB

Không ví trung gian, không giữ tiền, không chờ đối soát cuối ngày. ACB thấy tiền vào là báo cho MONA Pay biết giao dịch đó ngay.

MONA Pay gửi webhook có chữ ký về web

Gói tin JSON 7 trường kèm chữ ký HMAC-SHA256 và mốc thời gian. Web kiểm chữ ký, từ chối gói lệch giờ quá 5 phút, kiểm trùng bằng mã giao dịch. Web trả 200 trong 10 giây là MONA Pay ghi nhận đã giao.

Web tự làm phần việc của mình

Mở khoá học, đổi trạng thái cọc, gia hạn hội viên, gửi email, bật quyền xem nội dung. Cùng một gói tin, mỗi loại web bắt lấy làm việc khác nhau, nên đoạn code mẫu bên dưới phát ra 1 sự kiện WordPress để anh chị nối vào bất cứ thứ gì.

Làm ngay hôm nay

Dán 1 đoạn PHP, web WordPress có địa chỉ nhận webhook

Đoạn code bên dưới tạo địa chỉ https://web-cua-anh-chi.vn/wp-json/monapay/v1/webhook, kiểm chữ ký HMAC, chặn gói lệch giờ quá 5 phút, chặn xử lý trùng, rồi phát sự kiện monapay_tien_vao. Cuối file có ví dụ bắt sự kiện để mở khoá học cho học viên; đặt cọc hay hội viên thì đổi vài dòng trong hàm đó. Web làm tại MONA thì tụi em nối sẵn phần này lúc bàn giao, khỏi tự dán.

<?php
// mona-pay-webhook.php: bỏ vào wp-content/mu-plugins/
// Nhận webhook MONA Pay, kiểm chữ ký, rồi phát sự kiện 'monapay_tien_vao' cho phần còn lại của web dùng
add_action('rest_api_init', function () {
    register_rest_route('monapay/v1', '/webhook', [
        'methods'             => 'POST',
        'callback'            => 'monapay_nhan_webhook',
        'permission_callback' => '__return_true', // tự xác thực bằng HMAC bên dưới
    ]);
});

function monapay_nhan_webhook(WP_REST_Request $req) {
    $secret    = defined('MONA_WEBHOOK_SECRET') ? MONA_WEBHOOK_SECRET : ''; // khai báo trong wp-config.php
    $raw       = $req->get_body();
    $timestamp = $req->get_header('X-Mona-Timestamp') ?: '';
    $signature = $req->get_header('X-Mona-Signature') ?: '';

    // 1. Chặn replay: lệch quá 5 phút thì từ chối
    if (abs(time() - (int) $timestamp) > 300) {
        return new WP_REST_Response(['success' => false, 'message' => 'timestamp qua han'], 400);
    }
    // 2. Kiểm chữ ký HMAC-SHA256(secret, "<timestamp>.<raw_body>")
    $expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $raw, $secret);
    if (!hash_equals($expected, $signature)) {
        return new WP_REST_Response(['success' => false, 'message' => 'sai chu ky'], 401);
    }

    $data = json_decode($raw, true);

    // 3. Chống trùng bằng transaction_code (không đổi qua mọi lần gửi lại)
    $key = 'monapay_tx_' . sanitize_key($data['transaction_code']);
    if (get_option($key)) {
        return new WP_REST_Response(['success' => true, 'message' => 'da xu ly'], 200);
    }
    add_option($key, wp_json_encode($data), '', 'no');

    // 4. Phát sự kiện: phần nào của web cần biết "có tiền vào" thì bắt sự kiện này
    do_action('monapay_tien_vao', $data);

    return new WP_REST_Response(['success' => true], 200);
}

// Ví dụ: web bán khoá học, nội dung chuyển khoản "HV88" = học viên có ID 88 → mở khoá
add_action('monapay_tien_vao', function ($data) {
    if (preg_match('/HV\s?(\d+)/i', (string) $data['description'], $m) && (int) $data['amount'] >= 1990000) {
        update_user_meta((int) $m[1], 'da_thanh_toan', current_time('mysql'));
        wp_mail(get_userdata((int) $m[1])->user_email, 'Đã nhận học phí', 'Khoá học của anh chị đã mở, vào học ngay.');
    }
});
# Giả lập MONA Pay gọi vào web WordPress để test tại chỗ
SECRET='secret_hmac_dat_trong_dashboard'
URL='https://web-cua-anh-chi.vn/wp-json/monapay/v1/webhook'
TS=$(date +%s)
BODY='{"amount":1990000,"description":"HV88 hoc phi","transfer_date":"10:30:00 28/08/2026","transaction_code":"FT26240001234","account_number":"1234567890","bank_name":"ACB","type":"income"}'

SIG=$(printf '%s.%s' "$TS" "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')

curl -X POST "$URL" \
  -H 'Content-Type: application/json' \
  -H "X-Mona-Timestamp: $TS" \
  -H "X-Mona-Signature: sha256=$SIG" \
  --data "$BODY"
# Kỳ vọng: {"success":true}, gọi lần 2 cùng BODY → {"success":true,"message":"da xu ly"} (không xử lý trùng)
// monapay-wp.js: dịch vụ Node nhận webhook rồi gọi WordPress REST API cập nhật dữ liệu
const express = require('express');
const crypto  = require('crypto');

const app    = express();
const SECRET = process.env.MONA_WEBHOOK_SECRET; // secret HMAC trong dashboard MONA Pay
const WP_URL = 'https://web-cua-anh-chi.vn/wp-json/wp/v2';
const WP_AUTH = 'Basic ' + Buffer.from(process.env.WP_USER + ':' + process.env.WP_APP_PASSWORD).toString('base64'); // Application Password của WordPress

app.post('/webhook/monapay', express.raw({ type: 'application/json' }), async (req, res) => {
  const timestamp = req.header('X-Mona-Timestamp') || '';
  const signature = req.header('X-Mona-Signature') || '';
  const rawBody   = req.body.toString('utf8');

  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return res.status(400).send('timestamp qua han');
  const expected = 'sha256=' + crypto.createHmac('sha256', SECRET).update(timestamp + '.' + rawBody).digest('hex');
  const hopLe = signature.length === expected.length && crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
  if (!hopLe) return res.status(401).send('sai chu ky');

  res.status(200).json({ success: true }); // trả 200 trước, xử lý sau

  const data = JSON.parse(rawBody);
  const m = /HV\s?(\d+)/i.exec(data.description || '');
  if (!m) return;
  // Ghi meta cho user (cần plugin/đoạn code expose meta 'da_thanh_toan' qua REST)
  await fetch(WP_URL + '/users/' + m[1], {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', Authorization: WP_AUTH },
    body: JSON.stringify({ meta: { da_thanh_toan: data.transfer_date } }),
  });
});

app.listen(3000, () => console.log('Webhook MONA Pay cho WordPress chạy ở cổng 3000'));

3 việc trong dashboard MONA Pay sau khi dán code: (1) mục Webhooks, thêm địa chỉ trên, chọn xác thực HMAC_SHA256 và đặt secret trùng với MONA_WEBHOOK_SECRET; (2) chọn webhook nghe mọi tài khoản hay chỉ 1 tài khoản ảo; (3) bấm gửi thử, xem kết quả trong Lịch sử: mã HTTP, thời gian phản hồi, nhãn lỗi nếu có. Chi tiết tại tài liệu tích hợp webhook.

Chữ ký trên từng gói tin

Header X-Mona-Signature là HMAC-SHA256 trên mốc thời gian và nguyên văn gói tin. Ai không có secret thì không giả được thông báo "đã nhận tiền" để lừa web mở khoá.

Kiểm trùng bằng mã giao dịch

transaction_code giữ nguyên qua mọi lần gửi lại. Web ghi lại mã đã xử lý, gặp lại là bỏ qua, nên gửi lại 3 lần cũng chỉ mở khoá 1 lần.

Lịch sử từng lần gửi

Mỗi lần MONA Pay gọi web đều có log: mã HTTP, mili giây phản hồi, nhãn lỗi (TIMEOUT, SSL, DNS, HTTP_5XX). Web lỗi thì thấy ngay, bấm gửi lại từ dashboard.

MONA Pay là cổng thanh toán và API ngân hà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.

Hệ thống MONA tự xây để thu tiền cho chính mình, rồi từ 2022 tới nay hơn 6.000 khách hàng mới của MONA đã thu tiền qua đây trong web, phần mềm MONA giao. Năm 2026 mở cho mọi doanh nghiệp, miễn phí hoàn toàn. The MONA Group thành lập 2016, 14.000+ dự án, 85% khách quay lại, tổng đài 1900 636 648 có người trực trong giờ làm việc. Đội làm web WordPress lâu năm nên phần nối vào web là việc tụi em làm mỗi ngày.

Hỏi đáp

Chủ web WordPress hay hỏi tụi em

Web WordPress không dùng WooCommerce có dùng được không?

Được. MONA Pay chỉ cần 1 địa chỉ trên web để gửi webhook tới. Đoạn PHP mẫu trên trang này tạo địa chỉ đó và phát sự kiện monapay_tien_vao, phần còn lại anh chị nối vào bất kỳ plugin hay code nào đang chạy: khoá học, thành viên, đặt lịch, form đăng ký thu phí. Bán hàng bằng WooCommerce thì xem trang riêng cho WooCommerce.

Tiền có nằm ở MONA Pay lúc nào không?

Không. Tiền đi thẳng từ ngân hàng khách vào tài khoản ACB của anh chị. MONA Pay chỉ đọc thông báo giao dịch từ ngân hàng rồi báo về web. Vì vậy không có chuyện rút tiền về, không có phí giữ tiền, và sao kê ngân hàng của anh chị vẫn là nguồn sự thật cuối cùng.

Web dùng cache, CDN, Cloudflare có chặn webhook không?

Địa chỉ /wp-json/monapay/v1/webhook là request POST nên cache trang thường không đụng tới. Nếu anh chị bật tường lửa chặn POST lạ, cho phép địa chỉ máy chủ gửi webhook của MONA Pay là 103.168.55.14 (kiểm ngày 28/08/2026, có đổi tụi em cập nhật ở trang địa chỉ IP trong tài liệu).

Tôi không có dev, ai làm phần code cho tôi?

Web làm tại MONA thì đội kỹ thuật tụi em nối sẵn lúc bàn giao, không tính thêm. Web làm nơi khác thì anh chị gửi đoạn code mẫu trên trang này cho người đang quản trị web, thường mất 30 phút; hoặc gọi 1900 636 648 để tụi em báo giá phần nối.

Cần chuẩn bị gì trước khi bắt đầu?

Tài khoản ACB đứng tên anh chị hoặc công ty, số điện thoại đăng ký với ACB để nhận 2 lần OTP khi nối, website có HTTPS. Đăng ký tại my.monapay.vn là dùng ngay, không cần ai duyệt.

Miễn phí tới mức nào?

MONA Pay miễn phí hoàn toàn, không giới hạn giao dịch, đủ mọi tính năng. Web bán khoá học vài chục hay vài nghìn đơn một tháng đều không tốn đồng nào, chi tiết tại trang bảng giá.

Web WordPress của anh chị nhận tiền tự động từ tuần này

Đăng ký tài khoản, nối ACB bằng 2 lần OTP, dán webhook. Web làm tại MONA thì tụi em nối sẵn.

Gọi tổng đài MONA, có người trực

1900 636 648

[email protected]