Giải pháp mã nguồn mở cho thanh toán VietQR (NAPAS247 & EMVCo) trên nền tảng .NET
Danh mục 76+ ngân hàng nhúng sẵn • Render PNG / WebP / SVG • Giải mã 2 chiều • Deeplink mở 43+ App ngân hàng
Ngày 14/10/2026, nhóm phát triển VietQR Payment Kit đã đưa ra Thông báo cập nhật chính thức với các thay đổi mang tính bước ngoặt:
- Ngừng cổng API Generate (
api.vietqr.io) từ ngày 01/01/2027: Bắt buộc mọi hệ thống doanh nghiệp phải chuyển sang thư việnvietqr-coređể tạo mã QR trực tiếp trên hệ thống của mình, không còn phụ thuộc vào API mạng bên ngoài. - Ngừng tiếp nhận Custom Theme / Template mới trên cổng quản lý
my.vietqr.io.
Tuy nhiên, phía nhà cung cấp chỉ đưa ra thông báo chuyển đổi chung chung mà chưa cung cấp một bộ mã nguồn (Source Code) mở, minh bạch, hoàn chỉnh và có kiến trúc rõ ràng cho cộng đồng lập trình viên .NET.
Chính vì vậy, dự án VietQR.Core này được phát triển độc lập với mục tiêu: Cung cấp cho cộng đồng .NET một giải pháp mã nguồn mở chuẩn hóa 100% theo quy chuẩn NAPAS247 & EMVCo, hoàn toàn miễn phí, kiến trúc trong suốt và đạt hiệu năng tối đa.
Dưới đây là bảng đối chiếu giữa phương thức gọi API dịch vụ qua mạng và việc tích hợp trực tiếp thư viện VietQR.Core vào ứng dụng:
| Tiêu chí | Dịch vụ API qua mạng | Thư viện VietQR.Core (In-process) |
Ghi chú kỹ thuật |
|---|---|---|---|
| Phương thức hoạt động | Gửi HTTP request ra ngoài Internet | Tích hợp trực tiếp vào ứng dụng | Chạy trực tiếp trong mã nguồn, không phát sinh network round-trip. |
| Độ trễ xử lý (Latency) | ~100 ms (theo công bố mới của VietQR) | ~1 - 5 ms | Tạo ma trận QR trực tiếp trong RAM, không bị ảnh hưởng bởi độ trễ đường truyền. |
| Môi trường hoạt động | Cần kết nối Internet liên tục | Hoạt động Offline | Thích hợp cho mạng nội bộ (Intranet ngân hàng, Docker, On-premise). |
| Bảo mật dữ liệu | Gửi thông tin thanh toán qua API bên thứ ba | Dữ liệu xử lý nội bộ | Thông tin số tài khoản và đơn hàng được giữ trọn vẹn trong hệ thống của bạn. |
| Tùy biến mẫu & Logo | Sử dụng mẫu mặc định của cổng dịch vụ | 3 mẫu giao diện + Logo tâm | Có sẵn mẫu compact, print, qr_only và hỗ trợ nhúng CustomCenterLogo. |
| Định dạng xuất ảnh | Ưu tiên định dạng SVG | SVG, WebP và PNG | Hỗ trợ WebP tối ưu dung lượng và PNG tương thích rộng rãi. |
| Khả năng giải mã (Decode) | Chỉ tạo mã (1 chiều) | Hỗ trợ 2 chiều (Tạo & Đọc mã) | Bổ sung VietQrParser và VietQrDecoder đọc dữ liệu từ chuỗi hoặc ảnh. |
| Tối ưu chịu tải lớn | Phụ thuộc giới hạn kết nối HTTP | Hỗ trợ RenderToStreamAsync |
Ghi thẳng dữ liệu ra luồng Stream, giảm tải cấp phát mảng byte lên LOH. |
| Giấy phép & Chi phí | Theo chính sách dịch vụ bên ngoài | Mã nguồn mở (Giấy phép MIT) | Hoàn toàn miễn phí, tự do tùy biến và tích hợp vào các dự án thương mại. |
Toàn bộ ảnh mẫu được sinh tự động bằng chính thư viện VietQR.Core:
Dự án có sẵn một Web API hoàn chỉnh. Clone repo và chạy trong 10 giây:
# 1. Clone mã nguồn
git clone https://github.com/Ngochiendev/vietqr-core.git
cd vietqr-core
# 2. Khởi chạy Web API
dotnet run --project src/VietQR.ApiMở trình duyệt truy cập Swagger UI:
👉 http://localhost:5000/swagger (hoặc port hiển thị trên console).
🌐 Public Gateway / Demo URL:
https://vietqr.hiendev.io.vn
(Bạn có thể gọi trực tiếp public server này để test, hoặc thay bằng domain/localhost khi tự host)
- Tạo mã QR & Render ảnh:
GET https://vietqr.hiendev.io.vn/api/v1/vietqr-core/generate?bin=970422&accountNo=056789799999&amount=2000&description=Thanh+toan+cafe - Tạo link Deeplink mở app ngân hàng:
POST https://vietqr.hiendev.io.vn/api/v1/vietqr-core/deeplink/intents(Body JSON:{"appCode": "MB", "qrCode": "..."}) - Tra cứu danh sách ngân hàng & App hỗ trợ:
GET https://vietqr.hiendev.io.vn/api/v1/vietqr-core/bankshoặcGET https://vietqr.hiendev.io.vn/api/v1/vietqr-core/deeplink/apps
# Thư viện core: Sinh chuỗi EMVCo & render ảnh offline
dotnet add package VietQR.Core
# Thư viện mở rộng: Tạo link deeplink mở app ngân hàng
dotnet add package VietQR.Deeplink.Client👉 Hướng dẫn chi tiết: VietQR.Core README • VietQR.Deeplink.Client README
| Tên tham số | Kiểu dữ liệu | Bắt buộc | Mô tả & Ví dụ |
|---|---|---|---|
bin |
BankBin / string |
Bắt buộc | Mã BIN ngân hàng đúng 6 chữ số (VD: "970422" - MBBank, "970436" - VCB, "970415" - VietinBank). |
accountNo |
string |
Bắt buộc | Số tài khoản, số thẻ hoặc mã tài khoản định danh/ảo (Tối đa 25 ký tự). |
amount |
long? |
Tùy chọn | Số tiền thanh toán VNĐ (> 0). Bỏ trống (null) để tạo QR tĩnh (người quét tự gõ số tiền). |
description |
string? |
Tùy chọn | Nội dung thanh toán (Tối đa 95 ký tự EMVCo Tag 62.08, tự động loại bỏ dấu tiếng Việt). |
serviceType |
VietQrServiceType |
Tùy chọn | Loại hình chuyển khoản: Account (mặc định - QRIBFTTA) hoặc Card (QRIBFTTC chuyển qua thẻ). |
| Tên thuộc tính | Kiểu dữ liệu | Mặc định | Mô tả chi tiết |
|---|---|---|---|
Template |
string |
"compact" |
Mẫu thiết kế ảnh thẻ: "compact" (thẻ chuẩn), "print" (mẫu in quầy), "qr_only" (chỉ QR). |
Format |
QrImageFormat |
Png |
Định dạng file ảnh xuất ra: QrImageFormat.Png, Webp hoặc Svg. |
Size |
int |
500 |
Kích thước cạnh vuông tính bằng pixel (chỉ áp dụng cho template "qr_only"). |
Quality |
int |
100 |
Chất lượng nén ảnh (từ 1 đến 100, đặc biệt hữu ích khi xuất file WebP). |
AccountName |
string? |
null |
Tên chủ tài khoản người nhận (hiển thị nổi bật trên mẫu in ấn "print"). |
CustomCenterLogo |
byte[]? |
null |
Mảng byte ảnh logo riêng đặt ở tâm QR (thay thế cho logo VietQR mặc định). |
CenterLogoRatio |
float |
0.18f |
Tỷ lệ kích thước logo ở tâm so với mã QR (từ 0.05 đến 0.35, đảm bảo mắt đọc quét tốt). |
BillNumber |
string? |
null |
Số hóa đơn / mã đơn hàng (trích xuất đưa vào Tag 62.01). |
| Tên thuộc tính | Kiểu dữ liệu | Ý nghĩa nghiệp vụ |
|---|---|---|
BankBin |
string |
Mã BIN 6 số của ngân hàng thụ hưởng (VD: "970422"). |
BankInfo |
BankInfo? |
Thông tin ngân hàng: tên đầy đủ (Name), tên viết tắt (ShortName), logo nhúng (Logo). |
AccountNo |
string |
Số tài khoản hoặc số thẻ của người nhận tiền. |
Amount |
decimal? |
Số tiền giao dịch thanh toán (null nếu là mã QR tĩnh). |
Description |
string? |
Nội dung chuyển khoản trích xuất từ Tag 62.08. |
BillNumber |
string? |
Số hóa đơn / mã đơn hàng trích xuất từ Tag 62.01. |
ServiceType |
VietQrServiceType |
Account (chuyển tới STK) hoặc Card (chuyển tới số thẻ Napas). |
QrType |
VietQrType |
Static (QR tĩnh quét tự nhập tiền) hoặc Dynamic (QR động có sẵn tiền). |
IsValidCrc |
bool |
true nếu mã băm kiểm tra CRC16 (Tag 63) hợp lệ 100% theo chuẩn EMVCo. |
using VietQR.Core;
// 1. Tạo chuỗi mã QR Động NAPAS247 (Tài khoản MBBank, gắn sẵn số tiền)
string payload = VietQrPayload.CreateDynamic(
BankBin.Create("970422"), // MBBank
"056789799999", // Số tài khoản
amount: 2000,
description: "Thanh toan cafe");
// 2. Render ảnh thẻ thanh toán (hoàn toàn offline — logo & font nằm trong DLL)
var imageService = new QrImageService();
byte[] pngBytes = await imageService.RenderBytesAsync(payload, new QrRenderOptions
{
Template = "compact",
BankBin = "970422",
AccountNo = "056789799999",
AccountName = "VO NGOC HIEN",
Amount = 2000,
Description = "Thanh toan cafe"
});
await File.WriteAllBytesAsync("vietqr.png", pngBytes);Chuỗi EMVCo sinh ra theo mẫu chuẩn NAPAS247 (có sẵn CRC Tag 63 ở cuối):
00020101021238570010A0000007270127000697042201120567897999990208QRIBFTTA
5303704540420005802VN62180814thanh toan test6304xxxx
// Luôn truyền TƯỜNG MINH serviceType: VietQrServiceType.Card
string cardPayload = VietQrPayload.CreateDynamic(
BankBin.Create("970422"),
"9704222028493997", // Số thẻ NAPAS
amount: 1000,
serviceType: VietQrServiceType.Card, // Định danh mã dịch vụ QRIBFTTC
description: "Chuyen vao the MB");
// Render ảnh — tự động đổi nhãn "Số TK" thành "Số thẻ"
byte[] cardBytes = await imageService.RenderBytesAsync(cardPayload, new QrRenderOptions
{
Template = "compact",
BankBin = "970422",
AccountNo = "9704222028493997",
AccountName = "VO NGOC HIEN",
Amount = 1000,
ServiceType = VietQrServiceType.Card
});// (a) Phân tích từ chuỗi EMVCo — kiểm tra checksum CRC16:
Result<VietQrInfo> result = VietQrParser.Parse(payload);
if (result.IsSuccess)
{
VietQrInfo info = result.Value!;
Console.WriteLine($"Ngân hàng: {info.BankInfo?.ShortName}");
Console.WriteLine($"Số TK/Thẻ: {info.AccountNo}");
Console.WriteLine($"Số tiền: {info.Amount:N0} {info.Currency}");
Console.WriteLine($"CRC16 hợp lệ: {info.IsValidCrc}");
}
// (b) Đọc từ ảnh tải lên (PNG, WebP, JPEG, BMP):
byte[] imageBytes = await File.ReadAllBytesAsync("customer_uploaded_qr.png");
Result<VietQrInfo> decoded = VietQrDecoder.Decode(imageBytes);// WebP — dung lượng nhỏ hơn PNG khoảng 30-40%
byte[] webp = await imageService.RenderWebpBytesAsync(payload,
new QrRenderOptions { Template = "compact", Quality = 80 });
await imageService.SaveWebpAsync("vietqr.webp", payload);
// SVG vector — thuần C# (không cần Skia native), dùng cho in ấn decal khổ lớn
string svgXml = VietQrSvgRenderer.Render(payload, size: 500);
// Stream — ghi trực tiếp ra Response, không cấp phát byte[] lên LOH
await imageService.RenderToStreamAsync(payload, httpContext.Response.Body,
new QrRenderOptions { Format = QrImageFormat.Webp });Hỗ trợ tạo link mở thẳng 43+ ứng dụng ngân hàng/ví điện tử trên thiết bị di động (trong đó 7 app như MB, BIDV, TPBank, MoMo, ZaloPay... tự điền sẵn thông tin chuyển khoản):
using VietQR.Core;
using VietQR.Deeplink.Client;
// Đăng ký DI trong Program.cs: builder.Services.AddVietQrDeeplink();
// Nhận vào appCode ("MB"), shortName ("MBBank") hoặc mã BIN ("970422")
var intent = await vietQr.ToDeeplinkUrlAsync(deeplinkClient, "MB", cancellationToken);
if (intent != null)
{
Console.WriteLine($"Link mở app: {intent.Url}"); // Dẫn vào app thanh toán
}💡 Không dùng .NET / Client SDK? Bạn có thể gọi trực tiếp qua REST API (sử dụng server public
https://vietqr.hiendev.io.vn):POST https://vietqr.hiendev.io.vn/api/v1/vietqr-core/deeplink/intents Content-Type: application/json { "appCode": "MB", "qrCode": "00020101021238570010A0000007270127000697042201120567897999990208QRIBFTTA5303704540420005802VN62180814thanh toan test6304xxxx" }Response mẫu:
{ "code": "00", "desc": "success", "data": { "url": "https://vietqr.hiendev.io.vn/p/x8K2mN9...", "expiresInSeconds": 300 } }
// Program.cs
using VietQR.Core;
using VietQR.Deeplink.Client;
builder.Services.AddVietQrCore(options =>
{
options.DefaultTemplate = "compact";
});
// Tùy chọn: Đăng ký Deeplink Client nếu cần mở app ngân hàng
builder.Services.AddVietQrDeeplink();// Minimal API hoặc Controller — inject trực tiếp
app.MapGet("/api/qr", async (QrImageService qrService, string bin, string stk, long amount) =>
{
string payload = VietQrPayload.Create(BankBin.Create(bin), stk, amount);
byte[] image = await qrService.RenderBytesAsync(payload, new QrRenderOptions
{
Template = "compact",
BankBin = bin,
AccountNo = stk,
Amount = amount
});
return Results.File(image, "image/png");
});Dự án luôn hoan nghênh mọi đóng góp từ cộng đồng (Issue, Pull Request, Báo lỗi hoặc Đề xuất tính năng mới).
Vui lòng xem chi tiết quy trình cộng tác, tiêu chuẩn code và quy ước commit tại CONTRIBUTING.md.
Xin gửi lời cảm ơn chân thành đến:
- NAPAS (Công ty Cổ phần Thanh toán Quốc gia Việt Nam) cùng Sáng kiến VietQR đã xây dựng chuẩn mực thanh toán số thông minh, tiện lợi cho hàng triệu người dân và doanh nghiệp Việt Nam, đồng thời cung cấp tài liệu quy chuẩn kỹ thuật làm cơ sở để cộng đồng cùng phát triển.
- VietQR.io đã cung cấp dữ liệu danh mục ngân hàng tham khảo hữu ích và hệ thống CDN logo ngân hàng hỗ trợ cộng đồng lập trình viên Việt Nam.
- Thương hiệu:
VietQR® vàNAPAS247® là nhãn hiệu đã đăng ký thuộc quyền sở hữu của NAPAS (Công ty Cổ phần Thanh toán Quốc gia Việt Nam) và các tổ chức tương ứng. Dự án này là thư viện mã nguồn mở độc lập phi lợi nhuận, không phải sản phẩm chính thức do NAPAS hoặc vietqr.io phát hành. - Trách nhiệm giao dịch: Thư viện được cung cấp theo nguyên tắc "nguyên trạng" (AS IS) theo giấy phép MIT. Người sử dụng và đơn vị tích hợp chịu hoàn toàn trách nhiệm về tính chính xác của dữ liệu thanh toán (Số tài khoản, Mã ngân hàng, Số tiền) trước khi xuất mã QR. Tác giả không chịu trách nhiệm đối với bất kỳ sự cố chuyển nhầm, tổn thất tài chính hay gián đoạn nào phát sinh từ việc ứng dụng thư viện trong thực tế. Chi tiết xem tại DISCLAIMER.md.
Dự án được phân phối dưới giấy phép mã nguồn mở MIT License.
Bản quyền thuộc về © 2026 Ngochiendev.