Plugin WooCommerce

Plugin BankOpenAPI for WooCommerce thêm phương thức "Chuyển khoản ngân hàng (VietQR)" vào cửa hàng. Khách đặt hàng, quét mã QR bằng app ngân hàng; tiền về là đơn tự chuyển sang "Đang xử lý", không cần ai mở app ngân hàng đối chiếu. Tiền chuyển thẳng vào tài khoản của bạn, BankOpenAPI không giữ tiền.

Tải plugin (bản 0.1.0, file zip)

Yêu cầu

WordPress6.5 trở lên (đã thử tới 7.1)
WooCommerce8.5 trở lên (đã thử tới 11.2), checkout cổ điển hoặc Checkout block, có HPOS
PHP7.4 trở lên (đã thử 7.4 và 8.3)
Tiền tệ cửa hàngĐồng Việt Nam (VND); tiền tệ khác thì phương thức tự ẩn
Tổng đơnTừ 1.000 ₫ đến 500.000.000 ₫
HTTPSNên có để nhận webhook. Website chỉ có http vẫn xác nhận được đơn, chậm hơn vài giây (plugin tự hỏi trạng thái).

Cài đặt

  1. Tải file zip của plugin.
  2. Trong trang quản trị WordPress: Plugin → Cài mới → Tải plugin lên, chọn file zip, bấm "Cài đặt", rồi "Kích hoạt".
  3. Vào WooCommerce → Cài đặt → Thanh toán → BankOpenAPI.

Kết nối tài khoản

Bấm "Kết nối BankOpenAPI" ở chế độ Live hoặc Sandbox. Trình duyệt chuyển sang app BankOpenAPI: đăng nhập (chưa có tài khoản thì đăng ký), xem quyền plugin xin rồi bấm "Cho phép kết nối". Bạn được đưa về trang cài đặt, plugin đã có API key và tự đăng ký webhook. Không phải sao chép khóa nào.

Kết nối tạo một API key quyền "Thu tiền" tên "WooCommerce – tên miền của bạn". Khóa này chỉ tạo và xem yêu cầu thanh toán, đọc danh sách tài khoản ngân hàng nhận tiền và quản lý webhook của chính nó; không đọc được lịch sử giao dịch, số dư, người chuyển tiền, không đổi được cài đặt hay khóa khác. Bấm "Ngắt kết nối" trong plugin là khóa bị thu hồi và webhook bị gỡ; cũng thu hồi được ở trang API key.

Website không mở được trang kết nối (vd. trang quản trị nằm sau VPN)? Mở mục "Nhập API key thủ công": tạo khóa quyền "Thu tiền" ở trang API key rồi dán vào. Hoặc khai báo trong wp-config.php, khi đó ô nhập trong cài đặt bị khóa:

wp-config.php
// wp-config.php, trước dòng "That's all, stop editing!"
define( 'BANKOPENAPI_LIVE_API_KEY', 'boa_live_...' );
define( 'BANKOPENAPI_TEST_API_KEY', 'boa_test_...' );

Tài khoản nhận tiền

Khách chuyển thẳng vào tài khoản ngân hàng bạn đã liên kết ở trang Tài khoản ngân hàng (liên kết tài khoản thật cần nạp lần đầu từ 5.000 ₫). Có nhiều tài khoản thì chọn tài khoản nhận tiền trong thẻ "Tài khoản nhận tiền" của cài đặt; mặc định là tài khoản liên kết gần nhất. Chưa có tài khoản nào thì phương thức chuyển khoản tự ẩn ở trang thanh toán, để khách không đặt hàng rồi mới gặp lỗi.

Thử bằng Sandbox

  1. Ở thẻ "Tài khoản BankOpenAPI", bấm "Kết nối BankOpenAPI" ở dòng Sandbox. Chưa có tài khoản ngân hàng thử thì BankOpenAPI tạo sẵn một tài khoản.
  2. Chọn chế độ Sandbox, bấm "Lưu thay đổi".
  3. Đăng nhập bằng tài khoản quản lý shop, đặt thử một đơn với phương thức chuyển khoản.
  4. Trang đơn hàng hiện nút "Giả lập thanh toán" và "Giả lập chuyển thiếu" (chỉ người quản lý shop thấy). Bấm là hộp đổi sang "Đã nhận thanh toán", đơn sang "Đang xử lý" như khi tiền thật về.

Ở Sandbox, hộp thanh toán không hiện mã QR thật vì tài khoản thử không có ở ngân hàng nào. Xong thì chọn lại chế độ Live.

Khách thanh toán thế nào

Chọn trong cài đặt (thẻ "Đơn hàng", mục "Khách thanh toán ở đâu"):

  • Ngay trên trang đơn hàng của shop (mặc định): đặt hàng xong, trang "Đơn hàng đã nhận" hiện mã QR, ngân hàng, số tài khoản, số tiền, nội dung chuyển khoản kèm nút sao chép và đồng hồ đếm ngược. Tiền về là hộp tự đổi sang "Đã nhận thanh toán", không cần tải lại trang.
  • Trên trang thanh toán của BankOpenAPI: khách được chuyển sang trang thanh toán dựng sẵn, trả xong tự quay về trang đơn hàng của shop. Hợp khi theme hoặc plugin dựng trang thanh toán làm mất hộp QR.

Mỗi đơn có mã QR và nội dung chuyển khoản riêng (dạng TT7K3M9Q). Mã hết hạn sau thời gian giữ hàng của WooCommerce (WooCommerce → Cài đặt → Sản phẩm → Kho hàng), đúng lúc WooCommerce hủy đơn chưa trả; đổi được trong cài đặt. Email đơn đang chờ có link mở lại trang thanh toán cho khách lỡ đóng trang.

Trạng thái đơn

Tình huốngTrạng thái đơnGhi chú đơn
Vừa đặt, chờ chuyển khoảnChờ thanh toán (pending)ID yêu cầu thanh toán, nội dung chuyển khoản
Nhận đủ tiềnĐang xử lý (hoặc Đã hoàn thành với sản phẩm ảo, tải về; chọn được trong cài đặt)Số tiền đã nhận, mã giao dịch
Chuyển thiếuTạm giữ (on-hold)Đã nhận bao nhiêu, còn thiếu bao nhiêu; hộp thanh toán hiện số còn thiếu và mã QR mới
Chuyển thừaNhư nhận đủSố tiền thừa để bạn tự hoàn lại nếu cần
Tiền về sau khi đơn đã hủyTạm giữ, không tự mở lại đơnEmail báo quản trị viên kiểm kho rồi xử lý tay
Hết thời hạn mà chưa trảWooCommerce tự hủy đơn theo thời gian giữ hàngMã QR của đơn cũng bị hủy

Giao diện

Hộp thanh toán nằm thẳng trong trang (không phải iframe) nên tự theo font, màu chữ và kiểu nút của theme. Thẻ "Giao diện trang đơn hàng" trong cài đặt chỉnh bố cục (tự động hoặc xếp dọc), màu nhấn, đếm ngược, lời báo thành công, kèm khung xem trước dùng chính theme của bạn: chọn trạng thái (đang chờ, chuyển thiếu, đã thanh toán, hết hạn) và cỡ màn hình máy tính hoặc điện thoại.

Muốn chỉnh thêm, đè biến CSS trong Giao diện → Tùy biến → CSS bổ sung:

CSS bổ sung
/* Giao diện → Tùy biến → CSS bổ sung */
.boa-pay {
  --boa-accent: #f5b700;   /* màu biểu tượng đang chờ */
  --boa-radius: 12px;      /* bo góc */
  --boa-qr-size: 260px;    /* cỡ mã QR */
}
BiếnChỉnh gìMặc định
--boa-accentMàu biểu tượng đang chờMàu chính của theme (theme.json), không có thì màu chữ
--boa-radiusBo góc hộp, khung QR, nút8px
--boa-gapKhoảng cách giữa các phần1.25rem
--boa-qr-sizeCỡ mã QR220px
--boa-successMàu biểu tượng đã thanh toán#2f855a
--boa-warningMàu thông báo chuyển thiếu#b45309

Mã QR luôn nằm trên nền trắng, có lề trắng và cỡ cố định dù theme dùng nền tối hay bo góc ảnh, để app ngân hàng quét được. Sửa cả HTML: chépwp-content/plugins/bankopenapi/templates/payment-box.php vào wp-content/themes/<theme của bạn>/bankopenapi/payment-box.php rồi sửa bản chép; giữ các thuộc tính data-boa-* để hộp tự cập nhật trạng thái.

Trang cảm ơn dựng bằng page builder (Elementor, FunnelKit, CartFlows…) không hiện hộp QR? Thêm shortcode [bankopenapi_payment] vào trang đó, hoặc chọn "Trên trang thanh toán của BankOpenAPI".

Cho dev: hook và sự kiện

HookLoạiDùng để
bankopenapi_payment_request_argsfilterSửa dữ liệu gửi đi khi tạo yêu cầu thanh toán (mô tả, thời hạn). Số tiền luôn lấy từ tổng đơn, không đổi được.
bankopenapi_payment_box_argsfilterSửa biến đưa vào template hộp thanh toán.
bankopenapi_payment_box_stringsfilterSửa mọi câu chữ của hộp thanh toán.
bankopenapi_payment_box_before, bankopenapi_payment_box_afteractionChèn nội dung trước hoặc sau hộp thanh toán.
bankopenapi_order_paidactionChạy sau khi đơn được xác nhận đã thanh toán; nhận đơn và yêu cầu thanh toán.
functions.php
// functions.php của theme con, hoặc một plugin nhỏ của bạn

// Đổi mô tả hiện trên trang thanh toán, rút thời hạn còn 30 phút
add_filter( 'bankopenapi_payment_request_args', function ( $args, $order ) {
    $args['description']      = 'Đơn #' . $order->get_order_number() . ' – Shop ABC';
    $args['expiresInMinutes'] = 30;
    return $args;
}, 10, 2 );

// Sửa câu chữ của hộp thanh toán
add_filter( 'bankopenapi_payment_box_strings', function ( $strings ) {
    $strings['title'] = 'Chuyển khoản để hoàn tất đơn hàng';
    return $strings;
} );

// Việc riêng sau khi đơn được xác nhận đã thanh toán
add_action( 'bankopenapi_order_paid', function ( $order, $payment_request ) {
    // vd. gửi tin cho kho, ghi sổ kế toán
}, 10, 2 );

Trên trang đơn hàng, hộp thanh toán phát sự kiện bankopenapi:paid (đã nhận đủ) và bankopenapi:changed (chuyển thiếu, hết hạn) trên document:

document.addEventListener('bankopenapi:paid', (event) => {
  // event.detail: { orderId, status }
  window.dataLayer?.push({ event: 'purchase_paid', order_id: event.detail.orderId });
});

Webhook và tường lửa

Plugin nhận webhook tại https://<website của bạn>/wp-json/bankopenapi/v1/webhook, kiểm chữ ký theo chuẩn Standard Webhooks, rồi hỏi lại BankOpenAPI trạng thái của đơn trước khi cập nhật (không tin số liệu trong webhook). Webhook của plugin chỉ nhận các đơn do chính plugin tạo, nên nhiều shop dùng chung một tài khoản BankOpenAPI không nhận lẫn đơn của nhau.

Webhook gửi đi không có dải IP cố định. Tường lửa, plugin bảo mật hay chế độ chống DDoS chặn webhook thì cho phép theo đường dẫn /wp-json/bankopenapi/v1/webhook. Bị chặn thì đơn vẫn được xác nhận: trang đơn hàng đang mở tự hỏi trạng thái mỗi vài giây, và mỗi 5 phút plugin kiểm các đơn đang chờ trong 24 giờ qua.

Bảo mật và dữ liệu

  • Khi khách đặt hàng, plugin gửi cho BankOpenAPI: số tiền, số đơn, một dòng mô tả ("Đơn #… tại tên shop"), thời hạn thanh toán và link trang đơn hàng. Không gửi tên, email, số điện thoại hay địa chỉ của khách.
  • API key không bao giờ hiện trong mã nguồn trang, không gửi xuống trình duyệt, không ghi vào log; trong cài đặt chỉ hiện 4 ký tự cuối.
  • Trang trạng thái đơn chỉ trả lời khi có đúng mã đơn (order key) như trang "Đơn hàng đã nhận" của WooCommerce, và chỉ trả trạng thái, không trả thông tin khách.
  • Gỡ plugin thì cài đặt và khóa đã lưu bị xóa; lịch sử thanh toán trên đơn hàng được giữ.

Xử lý sự cố

Không thấy phương thức chuyển khoản ở trang thanh toán. Kiểm theo thứ tự: phương thức đã bật; tiền tệ cửa hàng là VND; đã kết nối đúng chế độ đang chạy; đã có tài khoản ngân hàng nhận tiền; tổng đơn trong khoảng 1.000 ₫ đến 500.000.000 ₫.

Trang đơn hàng không hiện mã QR. Trang cảm ơn bị page builder thay thì thêm shortcode [bankopenapi_payment], hoặc chọn trang thanh toán của BankOpenAPI.

Đơn không tự chuyển trạng thái. Bấm "Kiểm tra lại" ở thẻ "Tài khoản BankOpenAPI" để xem webhook đã đăng ký chưa. Bật "Debug log" trong thẻ Nâng cao rồi xem WooCommerce → Trạng thái → Nhật ký, nguồn bankopenapi.

Dùng plugin cache (LiteSpeed Cache, WP Rocket…). Mã QR vẽ sẵn phía server nên luôn hiện; nếu bật "trì hoãn JavaScript", thêm bankopenapi vào danh sách loại trừ để trạng thái tự cập nhật ngay khi khách chưa chạm vào trang.

Vẫn vướng thì gửi email tới teams@bankopenapi.com kèm tên miền website và thời điểm đặt đơn.

Bắt đầu với BankOpenAPI

Đăng ký tài khoản, liên kết tài khoản ngân hàng và nhận webhook báo có đầu tiên. Cần tư vấn thì nhắn chúng tôi.