qrbank
Version:
Generate QR codes for bank transactions in Vietnam
283 lines (282 loc) • 14 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.QRBank = void 0;
const qr_pay_1 = require("./constants/qr-pay");
const crc16_1 = require("./crc16");
const qrcode_1 = __importDefault(require("qrcode"));
class QRBank {
constructor(content) {
this.isValid = true;
this.provider = new qr_pay_1.Provider();
this.consumer = new qr_pay_1.Consumer();
this.merchant = new qr_pay_1.Merchant();
this.additionalData = new qr_pay_1.AdditionalData();
this.parse(content !== null && content !== void 0 ? content : '');
}
parse(content) {
if (content.length < 4)
return this.invalid();
const crcValid = QRBank.verifyCRC(content);
if (!crcValid)
return this.invalid();
this.parseRootContent(content);
}
build() {
var _a, _b, _c, _d, _e, _f, _g;
const version = QRBank.genFieldData(qr_pay_1.FieldID.VERSION, (_a = this.version) !== null && _a !== void 0 ? _a : '01');
const initMethod = QRBank.genFieldData(qr_pay_1.FieldID.INIT_METHOD, (_b = this.initMethod) !== null && _b !== void 0 ? _b : '11');
const guid = QRBank.genFieldData(qr_pay_1.ProviderFieldID.GUID, this.provider.guid);
let providerDataContent = '';
if (this.provider.guid === qr_pay_1.QRProviderGUID.VIETQR) {
const bankBin = QRBank.genFieldData(qr_pay_1.VietQRConsumerFieldID.BANK_BIN, this.consumer.bankBin);
const bankNumber = QRBank.genFieldData(qr_pay_1.VietQRConsumerFieldID.BANK_NUMBER, this.consumer.bankNumber);
providerDataContent = bankBin + bankNumber;
}
else if (this.provider.guid === qr_pay_1.QRProviderGUID.VNPAY) {
providerDataContent = (_c = this.merchant.id) !== null && _c !== void 0 ? _c : '';
}
const provider = QRBank.genFieldData(qr_pay_1.ProviderFieldID.DATA, providerDataContent);
const service = QRBank.genFieldData(qr_pay_1.ProviderFieldID.SERVICE, this.provider.service);
const providerData = QRBank.genFieldData(this.provider.fieldId, guid + provider + service);
const category = QRBank.genFieldData(qr_pay_1.FieldID.CATEGORY, this.category);
const currency = QRBank.genFieldData(qr_pay_1.FieldID.CURRENCY, (_d = this.currency) !== null && _d !== void 0 ? _d : '704');
const amountStr = QRBank.genFieldData(qr_pay_1.FieldID.AMOUNT, this.amount);
const tipAndFeeType = QRBank.genFieldData(qr_pay_1.FieldID.TIP_AND_FEE_TYPE, this.tipAndFeeType);
const tipAndFeeAmount = QRBank.genFieldData(qr_pay_1.FieldID.TIP_AND_FEE_AMOUNT, this.tipAndFeeAmount);
const tipAndFeePercent = QRBank.genFieldData(qr_pay_1.FieldID.TIP_AND_FEE_PERCENT, this.tipAndFeePercent);
const nation = QRBank.genFieldData(qr_pay_1.FieldID.NATION, (_e = this.nation) !== null && _e !== void 0 ? _e : 'VN');
const merchantName = QRBank.genFieldData(qr_pay_1.FieldID.MERCHANT_NAME, this.merchant.name);
const city = QRBank.genFieldData(qr_pay_1.FieldID.CITY, this.city);
const zipCode = QRBank.genFieldData(qr_pay_1.FieldID.ZIP_CODE, this.zipCode);
const buildNumber = QRBank.genFieldData(qr_pay_1.AdditionalDataID.BILL_NUMBER, this.additionalData.billNumber);
const mobileNumber = QRBank.genFieldData(qr_pay_1.AdditionalDataID.MOBILE_NUMBER, this.additionalData.mobileNumber);
const storeLabel = QRBank.genFieldData(qr_pay_1.AdditionalDataID.STORE_LABEL, this.additionalData.store);
const loyaltyNumber = QRBank.genFieldData(qr_pay_1.AdditionalDataID.LOYALTY_NUMBER, this.additionalData.loyaltyNumber);
const reference = QRBank.genFieldData(qr_pay_1.AdditionalDataID.REFERENCE_LABEL, this.additionalData.reference);
const customerLabel = QRBank.genFieldData(qr_pay_1.AdditionalDataID.CUSTOMER_LABEL, this.additionalData.customerLabel);
const terminal = QRBank.genFieldData(qr_pay_1.AdditionalDataID.TERMINAL_LABEL, this.additionalData.terminal);
const purpose = QRBank.genFieldData(qr_pay_1.AdditionalDataID.PURPOSE_OF_TRANSACTION, this.additionalData.purpose);
const dataRequest = QRBank.genFieldData(qr_pay_1.AdditionalDataID.ADDITIONAL_CONSUMER_DATA_REQUEST, this.additionalData.dataRequest);
const additionalDataContent = buildNumber + mobileNumber + storeLabel + loyaltyNumber + reference + customerLabel + terminal + purpose + dataRequest;
const additionalData = QRBank.genFieldData(qr_pay_1.FieldID.ADDITIONAL_DATA, additionalDataContent);
const EVMCoContent = Object.keys((_f = this.EVMCo) !== null && _f !== void 0 ? _f : {}).sort().map(key => { var _a; return QRBank.genFieldData(key, (_a = this.EVMCo) === null || _a === void 0 ? void 0 : _a[key]); }).join('');
const unreservedContent = Object.keys((_g = this.unreserved) !== null && _g !== void 0 ? _g : {}).sort().map(key => { var _a; return QRBank.genFieldData(key, (_a = this.unreserved) === null || _a === void 0 ? void 0 : _a[key]); }).join('');
const content = `${version}${initMethod}${providerData}${category}${currency}${amountStr}${tipAndFeeType}${tipAndFeeAmount}${tipAndFeePercent}${nation}${merchantName}${city}${zipCode}${additionalData}${EVMCoContent}${unreservedContent}${qr_pay_1.FieldID.CRC}04`;
const crc = QRBank.genCRCCode(content);
return content + crc;
}
static initQRBank(options) {
const qr = new QRBank();
qr.initMethod = options.amount ? '12' : '11';
qr.provider.fieldId = qr_pay_1.FieldID.VIETQR;
qr.provider.guid = qr_pay_1.QRProviderGUID.VIETQR;
qr.provider.name = qr_pay_1.QRProvider.VIETQR;
qr.provider.service = options.service || qr_pay_1.VietQRService.BY_ACCOUNT_NUMBER;
qr.consumer.bankBin = options.bankBin;
qr.consumer.bankNumber = options.bankNumber;
qr.amount = options.amount;
qr.additionalData.purpose = options.purpose;
return qr;
}
async generateQRCode() {
const content = this.build();
return await qrcode_1.default.toDataURL(content);
}
static initVNPayQR(options) {
const qr = new QRBank();
qr.merchant.id = options.merchantId;
qr.merchant.name = options.merchantName;
qr.provider.fieldId = qr_pay_1.FieldID.VNPAYQR;
qr.provider.guid = qr_pay_1.QRProviderGUID.VNPAY;
qr.provider.name = qr_pay_1.QRProvider.VNPAY;
qr.amount = options.amount;
qr.additionalData.purpose = options.purpose;
qr.additionalData.billNumber = options.billNumber;
qr.additionalData.mobileNumber = options.mobileNumber;
qr.additionalData.store = options.store;
qr.additionalData.terminal = options.terminal;
qr.additionalData.loyaltyNumber = options.loyaltyNumber;
qr.additionalData.reference = options.reference;
qr.additionalData.customerLabel = options.customerLabel;
return qr;
}
setEVMCoField(id, value) {
if (!this.unreserved)
this.unreserved = {};
this.unreserved[id] = value;
}
setUnreservedField(id, value) {
if (!this.unreserved)
this.unreserved = {};
this.unreserved[id] = value;
}
parseRootContent(content) {
const { id, length, value, nextValue } = QRBank.sliceContent(content);
if (value.length !== length)
return this.invalid();
switch (id) {
case qr_pay_1.FieldID.VERSION:
this.version = value;
break;
case qr_pay_1.FieldID.INIT_METHOD:
this.initMethod = value;
break;
case qr_pay_1.FieldID.VIETQR:
case qr_pay_1.FieldID.VNPAYQR:
this.provider.fieldId = id;
this.parseProviderInfo(value);
break;
case qr_pay_1.FieldID.CATEGORY:
this.category = value;
break;
case qr_pay_1.FieldID.CURRENCY:
this.currency = value;
break;
case qr_pay_1.FieldID.AMOUNT:
this.amount = value;
break;
case qr_pay_1.FieldID.TIP_AND_FEE_TYPE:
this.tipAndFeeType = value;
break;
case qr_pay_1.FieldID.TIP_AND_FEE_AMOUNT:
this.tipAndFeeAmount = value;
break;
case qr_pay_1.FieldID.TIP_AND_FEE_PERCENT:
this.tipAndFeePercent = value;
break;
case qr_pay_1.FieldID.NATION:
this.nation = value;
break;
case qr_pay_1.FieldID.MERCHANT_NAME:
this.merchant.name = value;
break;
case qr_pay_1.FieldID.CITY:
this.city = value;
break;
case qr_pay_1.FieldID.ZIP_CODE:
this.zipCode = value;
break;
case qr_pay_1.FieldID.ADDITIONAL_DATA:
this.parseAdditionalData(value);
break;
case qr_pay_1.FieldID.CRC:
this.crc = value;
break;
default:
const idNum = Number(id);
if (idNum >= 65 && idNum <= 79) {
if (!this.EVMCo)
this.EVMCo = {};
this.EVMCo[id] = value;
}
else if (idNum >= 80 && idNum <= 99) {
if (!this.unreserved)
this.unreserved = {};
this.unreserved[id] = value;
}
break;
}
if (nextValue.length > 4)
this.parseRootContent(nextValue);
}
parseProviderInfo(content) {
const { id, value, nextValue } = QRBank.sliceContent(content);
switch (id) {
case qr_pay_1.ProviderFieldID.GUID:
this.provider.guid = value;
break;
case qr_pay_1.ProviderFieldID.DATA:
if (this.provider.guid === qr_pay_1.QRProviderGUID.VNPAY) {
this.provider.name = qr_pay_1.QRProvider.VNPAY;
this.merchant.id = value;
}
else if (this.provider.guid === qr_pay_1.QRProviderGUID.VIETQR) {
this.provider.name = qr_pay_1.QRProvider.VIETQR;
this.parseVietQRConsumer(value);
}
break;
case qr_pay_1.ProviderFieldID.SERVICE:
this.provider.service = value;
break;
default:
break;
}
if (nextValue.length > 4)
this.parseProviderInfo(nextValue);
}
parseVietQRConsumer(content) {
const { id, value, nextValue } = QRBank.sliceContent(content);
switch (id) {
case qr_pay_1.VietQRConsumerFieldID.BANK_BIN:
this.consumer.bankBin = value;
break;
case qr_pay_1.VietQRConsumerFieldID.BANK_NUMBER:
this.consumer.bankNumber = value;
break;
default:
break;
}
if (nextValue.length > 4)
this.parseVietQRConsumer(nextValue);
}
parseAdditionalData(content) {
const { id, value, nextValue } = QRBank.sliceContent(content);
switch (id) {
case qr_pay_1.AdditionalDataID.PURPOSE_OF_TRANSACTION:
this.additionalData.purpose = value;
break;
case qr_pay_1.AdditionalDataID.BILL_NUMBER:
this.additionalData.billNumber = value;
break;
case qr_pay_1.AdditionalDataID.MOBILE_NUMBER:
this.additionalData.mobileNumber = value;
break;
case qr_pay_1.AdditionalDataID.REFERENCE_LABEL:
this.additionalData.reference = value;
break;
case qr_pay_1.AdditionalDataID.STORE_LABEL:
this.additionalData.store = value;
break;
case qr_pay_1.AdditionalDataID.TERMINAL_LABEL:
this.additionalData.terminal = value;
break;
default:
break;
}
if (nextValue.length > 4)
this.parseAdditionalData(nextValue);
}
static verifyCRC(content) {
const checkContent = content.slice(0, -4);
const crcCode = content.slice(-4).toUpperCase();
const genCrcCode = QRBank.genCRCCode(checkContent);
return crcCode === genCrcCode;
}
static genCRCCode(content) {
const crcCode = (0, crc16_1.crc16ccitt)(content).toString(16).toUpperCase();
return `0000${crcCode}`.slice(-4);
}
static sliceContent(content) {
const id = content.slice(0, 2);
const length = Number(content.slice(2, 4));
const value = content.slice(4, 4 + length);
const nextValue = content.slice(4 + length);
return { id, length, value, nextValue };
}
invalid() {
this.isValid = false;
}
static genFieldData(id, value) {
const fieldId = id !== null && id !== void 0 ? id : '';
const fieldValue = value !== null && value !== void 0 ? value : '';
const idLen = fieldId.length;
if (idLen !== 2 || fieldValue.length <= 0)
return '';
const length = `00${fieldValue.length}`.slice(-2);
return `${fieldId}${length}${fieldValue}`;
}
}
exports.QRBank = QRBank;