UNPKG

qrbank

Version:

Generate QR codes for bank transactions in Vietnam

283 lines (282 loc) 14 kB
"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;