vietqrjs
v2.0.1
Published
Package support generate or decode VietQR code
Maintainers
Readme
Overview
This library be implemented follow the VietQR specified document from NAPAS on vietqr.net
Vietqrjs library supports some features:
- Create QR string|code from data: account number, account name, bank ID, bank name, transaction amount, description ...
- Decrypt an QR string (string read from QR code image) to get information for payment.(from v1.1.0)
Vietqrjs supports 4 service types:
Cash withdraw service (QRCASH)
Products payment service (QRPUSH)
transfer by account number (QRIBFTTA)
transfer by card number (QRIBFTTC)
Buy me a coffee
If you feel my lib is useful, buy me a coffee, thanks! MoMo Wallet transfer QR code:
Change Logs:
v2.0.0 — Breaking Changes
Removed deprecated methods from VietQRV1Builder
The following methods were deprecated in v1.1.2 and have been removed in v2.0.0:
| Removed | Use instead |
|---|---|
| getQrCodeString() | getQrString() |
| setmerchantCategoryCode(mcc) | setMerchantCategoryCode(mcc) |
crcCode is now optional in IVietQrDataV1
// v1.x
crcCode: string; // ID 63
// v2.0
crcCode?: string; // ID 63Code that accesses qrData.crcCode without a null-check may need to be updated.
Dependency changes
- Removed
country-data: country/currency validation now uses built-in ISO 3166-1 alpha-2 and ISO 4217 numeric sets — stricter validation (format-only codes like"ZZ"that aren't real country codes are now rejected). - Removed
lodash: replaced with native JS equivalents.
Other improvements (non-breaking)
- Fixed
calcCrcCheckSumpadding bug — CRC values below0x1000previously produced 3-char strings instead of 4, causing QR validation failures. - Fixed
isValidChecksumstateful regex bug — repeated calls no longer return incorrect results. - Decryptor refactored to config-driven parsing; behavior is identical.
- ESLint migrated to flat config (
eslint.config.js).
v1.1.3
- Update donation information :)
v1.1.2
- Add the 2nd parameter for VietQrV1Decryptor decrypt function to support return lean|full decrypted QR data object
- Deprecated
setmerchantCategoryCodeandgetQrCodeStringin VietQRV1Builder (removed in v2.0.0) - Update function generateQR of VietQRV1Builder class
- Update docs on README
v1.1.1
- Fixed some bugs related to function check valid CRC code of QR string in Decryptor class
v1.1.0
- Add VietQrV1Decryptor to support decrypt QR string to get Merchant/Consumer information.
- Update Unit tests for VietQRV1Builder and VietQrV1Decryptor functions
v1.0.2
- Add new service codes: QRPUSH and QRCASH
Installation
npm i --save vietqrjsInterfaces
interface IVietQrDataV1
interface IVietQrDataV1 {
version: VietQrVersion; // ID 00
initMethod: VietQrInitiateMethod; // ID 01
merchantAccInfo: IMerchantAccountInfo; // ID = 38
merchantCategoryCode?: MerchantCategoryCode | string | null | undefined; // ID 52
txnCurrency: number; // ID 53
txnAmount?: string; // ID 54
tipConvenienceIndicator?: TipOrConvenienceIndicatorType | string | null | undefined; // ID 55
convenienceFeeFixed?: StringOrNot; // ID 56
convenienceFeePercentage?: StringOrNot; // ID 57
countryCode: string; // ID 58
merchantName?: StringOrNot; // ID 59
merchantCity?: StringOrNot; // ID 60
postalCode?: StringOrNot; // ID 61
additionalData?: IAdditionalData | null | undefined; // ID 62
languageTemplate?: ILanguageTemplate | null | undefined; // ID 64
crcCode?: string; // ID 63
}interface IBasicVietQrData
interface IBasicVietQrData {
acquierId: BankBIN; // ID DVCNTT
merchantId: string; // Tài khoản/Số thẻ thụ hưởng
serviceCode?: ServiceCode;
amount?: number;
txnDescription?: string;
}interface IMerchantAccountInfo
interface IMerchantAccountInfo {
guid?: GUID | string; // ID 38 - 00 (Required Field)
beneficiaryOrg: IBeneficiaryOrganiation; // ID 38 - 01 (Required Field)
serviceCode?: ServiceCode; // ID 38 - 02 (Conditional Field)
}interface IBeneficiaryOrganiation
interface IBeneficiaryOrganiation {
acquierId: BankBIN | string; // ID 38 - 01 - 00 (Required Field)
merchantId: string; // ID 38 - 01 - 01 (Required Field)
}interface IAdditionalData
interface IAdditionalData {
billNumber?: StringOrNot; // ID 62 - 01 (Conditional Field)
mobileNumber?: StringOrNot; // ID 62 - 02 (Conditional Field)
storeLabel?: StringOrNot; // ID 62 - 03 (Optional Field)
loyaltyNumber?: StringOrNot; // ID 62 - 04 (Optional Field)
referenceLabel?: StringOrNot; // ID 62 - 05 (Conditional Field)
customerLabel?: StringOrNot; // ID 62 - 06 (Conditional Field)
terminalLabel?: StringOrNot; // ID 62 - 07 (Optional Field)
purposeOfTxn?: StringOrNot; // ID 62 - 08 (Conditional Field)
additionalConsumerDataReq?: StringOrNot; // ID 62 - 09 (Optional Field)
}interface ILanguageTemplate
interface ILanguageTemplate {
preference: string; // ID 64 - 00 (Required Field)
merchantName: string; // ID 64 - 01 (Required Field)
merchantCity?: StringOrNot; // ID 64 - 02 (Optional Field)
}interface IDecryptedQrDataOptions
interface IDecryptedQrDataOptions {
/**
* Remove all decrypted data object fields is optional and have value is empty or undefined
* @default true
*/
lean?: boolean;
}interface
interface IGenerateQROptions {
logo?: string; // path to image file or base64 image string, if null will be render vietQR logo
margin?: number; // margin of QR code and image frame. default is 4
width?: number; // width of canvas object to draw QR code // default is 250
bgColor?: string; // background color of QR code - default is '#ffffff'
color?: string; // color of QR code - default is '#000000'
errorCorrectionLevel?: 'L' | 'M' | 'Q' | 'H'; // default is 'H'
}Usage
VietQRV1Builder
Functions:
- quickBuild(data: IBasicVietQrData): VietQRV1Builder; // quick build QR string from minimum required VietQR data
- getQrString(): string; // Get the result of quickBuild or build
- build(): VietQRV1Builder; // build the QR string after setting VietQR data
- refresh(): VietQRV1Builder; // Reset builder state to defaults
- generateQR(options?: IGenerateQROptions): Promise<string>; // generate base64 QR image
- setMerchantAccountInfo(data: IMerchantAccountInfo): VietQRV1Builder;
- setMerchantName(merchantName: string): VietQRV1Builder;
- setMerchantCity(merchantCity: string): VietQRV1Builder;
- setTxnCurrency(currencyCode: number): VietQRV1Builder;
- setTxnCountry(countryCode: string): VietQRV1Builder;
- setMerchantCategoryCode(mcc: string): VietQRV1Builder;
- setAdditionalData(data: IAdditionalData): VietQRV1Builder;
- setLanguageTemplate(template: ILanguageTemplate): VietQRV1Builder;
- setTxnDescription(description: string): VietQRV1Builder;
- setTxnAmount(amount: number): VietQRV1Builder;
- setPostalCode(postalCode: string): VietQRV1Builder;
Quick generate QR string
Example:
import {VietQRV1Builder, BankBIN, ServiceCode} from 'vietqrjs';
const builder = new VietQRV1Builder();
const qrDataString = builder.quickBuild({ // IBasicVietQrData
acquierId: BankBIN.VIETINBANK, // ID DVCNTT
merchantId: '', // Account number
serviceCode: ServiceCode.BY_ACCOUNT_NUMBER,
}).getQrString();Build an QR string to render static QR code with account number
Example:
import {VietQRV1Builder, BankBIN, ServiceCode} from 'vietqrjs';
const builder = new VietQRV1Builder();
const qrDataStr = builder.setMerchantAccountInfo({
// IMerchantAccountInfo
beneficiaryOrg: { // IBeneficiaryOrganiation
acquierId: BankBIN.VIETCOMBANK,
merchantId: '03123445xxx', // account number
},
serviceCode: ServiceCode.BY_ACCOUNT_NUMBER,
})
.build()
.getQrString();Build an QR string to render static QR code with card number
Example:
import {VietQRV1Builder, BankBIN, ServiceCode} from 'vietqrjs';
const builder = new VietQRV1Builder();
const qrString = builder.setMerchantAccountInfo({
beneficiaryOrg: {
acquierId: BankBIN.ACB,
merchantId: '040812344454xxx', // card number
},
serviceCode: ServiceCode.BY_CARD_NUMBER,
})
.build()
.getQrString();Build an QR string to render QR code to cash withdrawl
Example:
import {VietQRV1Builder, BankBIN, ServiceCode} from 'vietqrjs';
const vietqr = new VietQRV1Builder();
const qrString = vietqr.setMerchantAccountInfo({
beneficiaryOrg: {
acquierId: BankBIN.ACB,
merchantId: '12345678', // ATM ID
},
serviceCode: ServiceCode.BY_CASH_WITHDRAWL_SERVICE,
})
.setMerchantName('NGUYEN VAN A')
.setMerchantCity('HA NOI')
.setTxnCurrency()
.setTxnCountry()
.setMerchantCategoryCode(MerchantCategoryCode.FINANCIAL_INSTITUTIONS_WITH_CASH_DISBURSEMENTS)
.setAdditionalData({
referenceLabel: '201901091557142283847',
terminalLabel: '00001111',
}).build()
.getQrString();
console.log(qrString);
// expect
// 00020101021138500010A000000727012200069704160108123456780206QRCASH5204601153037045802VN5912NGUYEN VAN A6006HA NOI6237052120190109155714228384707080000111163049CE4Build an QR string to render dynamic QR code with account number
Example:
import {VietQRV1Builder, BankBIN, ServiceCode} from 'vietqrjs';
const builder = new VietQRV1Builder();
const qrDataStr = builder.setMerchantAccountInfo({
beneficiaryOrg: {
acquierId: BankBIN.MB_BANK,
merchantId: '03123445xxx', // account number
},
serviceCode: ServiceCode.BY_ACCOUNT_NUMBER,
})
.setTxnAmount(86000)
.setMerchantName('Cua hang tien loi') // optional
.setMerchantCity('NANOI') // optional
.setPostalCode('10000') // optional
.setTxnDescription('Thanh toan hoa don') // optional
.setAdditionalData({ // optional
billNumber: 'B123456',
storeLabel: 'NPS124',
})
.build()
.getQrString();Build QR code from payment account information
Example:
import {VietQRV1Builder, BankBIN, ServiceCode} from 'vietqrjs';
const builder = new VietQRV1Builder();
const qrBase64Image = await builder
.quickBuild({
acquierId: BankBIN.TP_BANK, // ID DVCNTT
merchantId: '123456789', // Tài khoản/Số thẻ thụ hưởng
serviceCode: ServiceCode.BY_ACCOUNT_NUMBER,
})
.generateQR({
width: 300,
margin: 2,
color: '#555555',
bgColor: '#EEEEEE',
});
console.log(qrBase64Image);
// data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALQAAAC0CAYAAAA9zQYyAAAABmJLR0QA/wD/AP+gvaeTAAAMPElEQVR4nO3dW4xd113H8e9/7XMbe+zxJXZsx03sXhwIocADaaX2CQGCgtQAqtqCeCFISaWoPDYIXhBvSPDSopA+cpMqlAawFMEDAtqUtpBW0BYSTBMXK/ElTe144vHMOWfv9edh3NQ6idjZ3rczy7/PSxTPnH0556el/1mz9n/Z5cuXHZFEhL4vQKRJCrQkRYGWpCjQkhQFWpKiQEtSFGhJyqDsFx5++OEuruMNTzzxxP/787rXU3b8MlXPX/V8i8dffH3T91/3fpYtHxqhJSkKtCRFgZaklNbQi+rWoIvKarCymrLq8aser+75qyqrUeteb9n7Xff+us7HIo3QkhQFWpKiQEtSKtfQi9qel62q7Rq57u8vqlrjlp2/7HrKztf0d4au86ERWpKiQEtSFGhJSu0aum9Va7SqNWLXNXLdeehFTd/vstMILUlRoCUpCrQkZcfX0E2vbahaQ3Y9r1um6jx2ajRCS1IUaEmKAi1JqV1DL9u8Zds1c9n5FrU9b1z3fG3r+no0QktSFGhJigItSalcQy/bPGbdmrnpmrvr8/f980V950MjtCRFgZakKNCSFLvd9lhpum9F3XnotvtkdD0v3zeN0JIUBVqSokBLUmrX0Ms+T9l3X4m++znXPd+itvtXV72eRRqhJSkKtCRFgZaklNbQy9ZHousacqevry47ftPfefqe59YILUlRoCUpCrQkpXINXbcmLtP2niB91+hl2n5/y87X9jxy2/tMaoSWpCjQkhQFWpJS+kxh3zVz32tD2l7PXLcXXtfz0m0fv+7nqRFakqJAS1IUaElKaQ1dtUasu+9eVW3X+F3XtHXXktTV9ufR9ncejdCSFAVakqJAS1I6f6aw6eMt29qLttczV9V277qmv8Nor2+RmyjQkhQFWpJSez30sj1z1nQ/5DJt1/jLttal7c+/6usXaYSWpCjQkhQFWpLS+F7fTe993XXN23RfjrZr+Kbvv6q6a3ea/g6gEVqSokBLUhRoSUrje303XSO3vdd123trN72eetl763Xdj3uRRmhJigItSVGgJSmNz0OX6bpfcGvzutMpW89+nY0LF4h7V1m9/0eYHDnKI5/4RKXjP/GZz+AvnCM/87+EzMhOnYCT74DBW380fe8r2PV66KqfX+eBTkF+4RLX/uDTvPS1L/CyXWdmxuDgMd71q7/OKMuYFcXbOs4downX/+hx7O++iF+fYR4Z7pkQH/xZxr/xa/juXS3fSXpUclQUr1zl2qd+n62vfInzcc61zJlbgb9ykW999rPcHd7eA0DDLOPnr+XY3zzN7vVXmPg6IzbIXv8uxeee4vU/fxL8ttrktxEKdEXFU6dZOfM8Gw4bg0iIgcwD00FkcH2dk+fPsWc8Kj3O/dmE9377O4zyKViG4QR33IcM5zN48jTx1e91cEdpqV1y1P3bfdt9IJo8XxYCzz35FPd5QQyRzCNuDhGKoUFwuHKFhz72AX74Ix8Fs7c83+pgyAcuXmVlOsWAwgLO9ugSopFnOfH6OldfOMtjv/s7le6n6/XKTc9bax66Q8NswCQG3Jw1C6zm21XBdqidaJDFyItP/hXXL114y2OYGT/uxjvPX2AUDfNAYY55wAmYFRg55pGQv71aXH5Aga5gms+5umsXIQb2FcahOCIYFMEYRsg8Yjj5pZf4n9N/jcf4pmPsHQz54PlXWcmvETwS3MAcczCDwiKjwhnYkGz3ag93ubMp0BW4O/9+dB8zmzApjKM2YiU3MGMQIXOI5kDBC6dPs3Hx4puO8f5iyJGL38Nw5je+QA4Lw2x7tC9CwHzI/K672HPfqY7vcOer3Jej6337ql5Pmbp9ND756KM8cmGDe8+dJ4aCM9kWZwdTAAqDIkBwpwgjjv/0L/DAY7+NhRvjxmvrXHnot9j70nmGMWcWnCwawQ0nAkYeMq4PM57+sVP8Y7HV+zOWVY9X9vtl1JejY9P5nH8+tsbGeMSgiBz3MXuLjHmAeQBzyKLhzLnyT//A5f/6z+0XurP1+adZffkcgyLgxYBhdNxglhkBxwBz49KRO/h69uZyRcop0Lfg+ZjzzRN3EgPsn8ExH4HBIBqDGMAGDCJszNd5/i//gjib4S9fYPb5v2U7p3Ms5MyDUVhgWEC8Eel8NOCrRw6wPp/3fZs7kgJ9C7byOc8eWOXy7hUiziEy9s0DboEiOLlFBtHJs8ilr36FV778L2z86edY+e6rAMRQEC0S4nbtDZE8OI7x4l2H+BoFrj+q3JLWe9v1vU9hW6/PQuDjG877zpzFfMqlgfGtsMlmNsODYdEwtr/wvXPtTk68FlnZ3MKD42bggWFhYJHpwIHI5ngPu//kDwk3fRnsuk9I1/skVqW+HC0pYuSZ/bvY3LuHUeEczgecyle4Y2ZM5pFxbqxNMw7HwJ2XX2N16zpFtj2HbW4Ez3AziuCE6JhnPHfPYcIPvbvvW9vRFOgazs02GX/kwxSMGUe4pxhyL7s5yW7ek+/i3axyynezPzcigAdge1YjOMyySB4g+IDX9qzxhX27IOgjqUPvXg0xRoa/9CFeP3ECMLI450A+4uR8FyfjmLvngX2zwCgfEi1j4E5wIxIIDjFEwJkORnzjnqOczfO+b2nHa3yv77bnHZetBjQzfioOefA//puMKRZHmBuTOGcenHkWyIrB9p/HyQluzMOQce7MhjOyAtbvPM6eP/s0dmB/5etruq9J33uN16URuiZ3598mxqVDB3AyjO0/rOTByDPD2f7roeE3vdn+xr/l2ZDhRz/8lmGW6hToBqxPpzzzjkNEm2CxwC2Sm2Ex3FirEcEdiwE3I3Mnhoh7xua7TjL5xZ/p+xaSoUA35NngvHToMISMIot8f52/3fivG8QAjmHMMQrm2QqDj/0Ktn+tvwtPTO21HFU13Tuu7f7UVX7+vrDCx7/5bcazDbIYiHbjBwbff5NDNAgzLAZeOHYPj9+9xvp8esvXv2in7zNYRvPQHfpGyDlz1yGcAYXd9AP/wUht7kQ3ro0n/Ovxg5XCLOUU6AZt5nO+eGQ/65MJMfDGCP3GQO3b/zMbjPnO8aN8GYW5aQp0w56bbXL26EE8bMfY/cbifQfDcYtsDSc8c3AvW4XmnZvWem+7RX0/Y1im7vn/+PHHic+f4fInH2P/lXXioMBiJMRse0WdGf7BB3j49z4Fw2Hj68eXrddg3T4cVc+nEboF4d73MHrkIbYmaxCH5GQ4A6ZhzJX772Pt0d+E4bDvy0ySGs20wYy9D36I2bFjXD3992QvnmO8skr+wI+y75d/jnD4UN9XmCwFui3BGL3/Jzj4k+/FN7ewLIPxGIKVv1ZuWed7rCzqe15zUdU9Yha1vfai698ve32ZunvuVL1e1dCSFAVakqJAS1Iq19B1a5y2+0xU/f2u+yuXWbZ9EKteX9XXL1JvO5GbKNCSFAVaktJ4X45FbT/z1vbe4Ttt/XHbzww23TelaRqhJSkKtCRFgZaklM5Dt10jd90/uu1+1nVr4qbndZetD0fbNEJLUhRoSYoCLUmpvZZjUdfrc8vUXXtQ9ztA12snlr1GbnteWyO0JEWBlqQo0JKUznvbLVq29chN13hd19BNr9Vo+/VN0wgtSVGgJSkKtCSl97Ucfe9z13d/6jJNz5t3XTN33atQI7QkRYGWpCjQkpTazxQuWvZnBBd1/Yxc3/O2O+0Zwao0QktSFGhJigItSam81/eirtcudN1XokzTNftO3/u8TNvz3hqhJSkKtCRFgZaklK7lqFrzdN1no2lV573bnldu+5nGtj/fus9Aai2H3NYUaEmKAi1Jqb0eumlN79GybH04+l4/3nWNX/X31ZdD5CYKtCRFgZakNN7brqqma/Sm+y/XnWduux911fPV3few7HxVNf1+aISWpCjQkhQFWpJSuYZe1HZfimXbW7yupvdwWfbvIIuq1vhlr1+kEVqSokBLUhRoSUrtGrprbfeeK9P1M5Z1dd2Lr0zb8/AaoSUpCrQkRYGWpCx9Dd32PHDXfTmqHm9R2/2iq+r69ZqHltuKAi1JUaAlKbVr6Lb7cDQ979x277m+9zavqu9eemXHV18Oua0p0JIUBVqSUrmGXva1CXXncbveF3DZ+lc3ret9ETVCS1IUaEmKAi1JaXyfQpE+aYSWpCjQkhQFWpKiQEtSFGhJigItSVGgJSn/B11+FYw2v18iAAAAAElFTkSuQmCCVietQrV1Decryptor (supported from v1.1.0)
Decrypt QR string to get information for payment.
Check CRC checksum of QR string
isValidChecksum(qrString: string): boolean;
Example:
import { VietQrV1Decryptor } from 'vietqrjs';
const decryptor = new VietQrV1Decryptor();
const isValid = decryptor.isValidChecksum('00020101021238500010A000000727012200069704030108123456780206QRCASH5204601153037045802VN5915NGUYEN HUU HUAN6005HANOI6105100006237052120190109155714228384707080000111164260002en0107shop vn0205Hanoi63047611');
console.log(isValid) // falseDecrypt QR string to get information for payment
decrypt(qrString: string, options?: IDecryptedQrDataOptions): IVietQrDataV1
Example:
import { VietQrV1Decryptor } from 'vietqrjs';
const decryptor = new VietQrV1Decryptor();
// decrypt QR string and get full fields QR data
const qrData = decryptor.decrypt(
'00020101021238500010A000000727012200069704030108123456780206QRCASH5204601153037045802VN5915NGUYEN HUU HUAN6005HANOI6105100006237052120190109155714228384707080000111164260002en0107shop vn0205Hanoi630476DA',
{ lean: false },
);
console.log(qrData);
// {
// version: '01',
// initMethod: '12',
// merchantAccInfo: {
// guid: 'A000000727',
// beneficiaryOrg: {
// acquierId: '970403',
// merchantId: '12345678'
// },
// serviceCode: 'QRCASH'
// },
// merchantCategoryCode: '6011',
// txnCurrency: 704,
// txnAmount: undefined,
// tipConvenienceIndicator: undefined,
// convenienceFeeFixed: undefined,
// convenienceFeePercentage: undefined,
// countryCode: 'VN',
// merchantName: 'NGUYEN HUU HUAN',
// merchantCity: 'HANOI',
// postalCode: '10000',
// additionalData: {
// billNumber: undefined,
// mobileNumber: undefined,
// storeLabel: undefined,
// loyaltyNumber: undefined,
// referenceLabel: '201901091557142283847',
// customerLabel: undefined,
// terminalLabel: '00001111',
// purposeOfTxn: undefined,
// additionalConsumerDataReq: undefined
// },
// languageTemplate: {
// preference: 'en',
// merchantName: 'shop vn',
// merchantCity: 'Hanoi',
// },
// crcCode: '76DA'
// }
const leanQrData = decryptor.decrypt(
'00020101021238500010A000000727012200069704030108123456780206QRCASH5204601153037045802VN5915NGUYEN HUU HUAN6005HANOI6105100006237052120190109155714228384707080000111164260002en0107shop vn0205Hanoi630476DA',
);
console.log(leanQrData);
// {
// version: '01',
// initMethod: '12',
// merchantAccInfo: {
// guid: 'A000000727',
// beneficiaryOrg: {
// acquierId: '970403',
// merchantId: '12345678'
// },
// serviceCode: 'QRCASH'
// },
// merchantCategoryCode: '6011',
// txnCurrency: 704,
// countryCode: 'VN',
// merchantName: 'NGUYEN HUU HUAN',
// merchantCity: 'HANOI',
// postalCode: '10000',
// additionalData: {
// referenceLabel: '201901091557142283847',
// terminalLabel: '00001111',
// },
// languageTemplate: {
// preference: 'en',
// merchantName: 'shop vn',
// merchantCity: 'Hanoi',
// },
// crcCode: '76DA'
// }