UNPKG

paynow-decoder

Version:

A decoder for PayNow QR codes and payment information

100 lines (99 loc) 4.69 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PayNowDecoder = void 0; class PayNowDecoder { static decode(payload) { const fields = this.parseFields(payload); return this.extractPayNowData(fields); } static parseFields(payload) { const fields = []; let position = 0; while (position < payload.length) { const id = payload.substr(position, 2); position += 2; const lengthStr = payload.substr(position, 2); const length = parseInt(lengthStr, 10); position += 2; const value = payload.substr(position, length); position += length; // Handle nested fields for merchant account info and additional data if (id === this.MERCHANT_ACCOUNT_INFO || id === this.ADDITIONAL_DATA) { const nestedFields = this.parseFields(value); fields.push({ id, length, value, nestedFields }); } else { fields.push({ id, length, value }); } } return fields; } static extractPayNowData(fields) { const merchantInfoField = fields.find((f) => f.id === this.MERCHANT_ACCOUNT_INFO); const additionalDataField = fields.find((f) => f.id === this.ADDITIONAL_DATA); const transactionAmount = this.extractFieldValue(fields, this.TRANSACTION_AMOUNT); return { payloadFormat: this.extractFieldValue(fields, this.PAYLOAD_FORMAT_INDICATOR), pointOfInitiation: this.extractFieldValue(fields, this.POINT_OF_INITIATION_METHOD), merchantAccountInfo: this.parseMerchantAccountInfo((merchantInfoField === null || merchantInfoField === void 0 ? void 0 : merchantInfoField.nestedFields) || []), merchantCategoryCode: this.extractFieldValue(fields, this.MERCHANT_CATEGORY_CODE), currencyCode: this.extractFieldValue(fields, this.TRANSACTION_CURRENCY), transactionAmount: transactionAmount ? parseFloat(transactionAmount) : undefined, countryCode: this.extractFieldValue(fields, this.COUNTRY_CODE), merchantName: this.extractFieldValue(fields, this.MERCHANT_NAME), merchantCity: this.extractFieldValue(fields, this.MERCHANT_CITY), additionalData: this.parseAdditionalData((additionalDataField === null || additionalDataField === void 0 ? void 0 : additionalDataField.nestedFields) || []), crc: this.extractFieldValue(fields, this.CRC), }; } static extractFieldValue(fields, id) { const field = fields.find((f) => f.id === id); return field === null || field === void 0 ? void 0 : field.value; } static parseMerchantAccountInfo(nestedFields) { const identifier = this.extractFieldValue(nestedFields, this.MERCHANT_IDENTIFIER); const rawProxyType = this.extractFieldValue(nestedFields, this.MERCHANT_PROXY_TYPE); const proxyType = (rawProxyType && { "0": "mobile", "2": "uen", "3": "vpa", }[rawProxyType]) || undefined; const amountEditableStr = this.extractFieldValue(nestedFields, this.MERCHANT_AMOUNT_EDITABLE); return { identifier: identifier, proxyType: proxyType, proxyValue: this.extractFieldValue(nestedFields, this.MERCHANT_PROXY_VALUE), amountEditable: amountEditableStr === "1", expiryDate: this.extractFieldValue(nestedFields, this.MERCHANT_EXPIRY_DATE), }; } static parseAdditionalData(nestedFields) { const referenceNumber = this.extractFieldValue(nestedFields, this.REFERENCE_NUMBER); return referenceNumber ? { referenceNumber } : undefined; } } exports.PayNowDecoder = PayNowDecoder; // Root level IDs PayNowDecoder.PAYLOAD_FORMAT_INDICATOR = "00"; PayNowDecoder.POINT_OF_INITIATION_METHOD = "01"; PayNowDecoder.MERCHANT_ACCOUNT_INFO = "26"; PayNowDecoder.MERCHANT_CATEGORY_CODE = "52"; PayNowDecoder.TRANSACTION_CURRENCY = "53"; PayNowDecoder.TRANSACTION_AMOUNT = "54"; PayNowDecoder.COUNTRY_CODE = "58"; PayNowDecoder.MERCHANT_NAME = "59"; PayNowDecoder.MERCHANT_CITY = "60"; PayNowDecoder.ADDITIONAL_DATA = "62"; PayNowDecoder.CRC = "63"; // Merchant Account Info nested IDs PayNowDecoder.MERCHANT_IDENTIFIER = "00"; PayNowDecoder.MERCHANT_PROXY_TYPE = "01"; PayNowDecoder.MERCHANT_PROXY_VALUE = "02"; PayNowDecoder.MERCHANT_AMOUNT_EDITABLE = "03"; PayNowDecoder.MERCHANT_EXPIRY_DATE = "04"; // Additional Data nested IDs PayNowDecoder.REFERENCE_NUMBER = "01";