cbe-utils
Version:
A TypeScript utility library for CBE (Commercial Bank of Ethiopia) payment verification and transaction processing
193 lines (192 loc) • 7.1 kB
JavaScript
;
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
const axios = require("axios");
const https = require("node:https");
const pdfTextReader = require("pdf-text-reader");
const dayjs = require("dayjs");
function _interopNamespaceDefault(e) {
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
if (e) {
for (const k in e) {
if (k !== "default") {
const d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: () => e[k]
});
}
}
}
n.default = e;
return Object.freeze(n);
}
const https__namespace = /* @__PURE__ */ _interopNamespaceDefault(https);
const CBE_VERIFICATION_BASE_URL = "https://apps.cbe.com.et:100";
const TRANSACTION_REFERENCE_REGEX = /^[A-Z0-9]+$/;
const TRANSACTION_URL_REGEX = /^https?:\/\/.+/;
const DEFAULT_MAX_TRANSACTION_AGE_HOURS = 24;
const PDF_EXTRACTION_PATTERNS = {
/** Pattern to extract reference number */
REFERENCE_NUMBER: /Reference No\. \(VAT Invoice No\)\s*(.+)/,
/** Pattern to extract payment date and time */
PAYMENT_DATE_TIME: /Payment Date & Time\s*(\d{1,2}\/\d{1,2}\/\d{4}, \d{1,2}:\d{2}:\d{2} (?:AM|PM))/,
/** Pattern to extract total amount debited */
TOTAL_AMOUNT: /Total amount debited from customers account\s*([\d,.]+\s*ETB)/,
/** Pattern to extract receiver information */
RECEIVER: /Receiver\s*(.+)/,
/** Pattern to extract payer information */
PAYER: /Payer\s*(.+)/
};
function createHttpsAgent(rejectUnauthorized = false) {
return new https__namespace.Agent({ rejectUnauthorized });
}
async function downloadPdf(url, config) {
const httpsAgent = createHttpsAgent(config.rejectUnauthorized);
try {
const response = await axios.get(url, {
httpsAgent,
responseType: "blob",
responseEncoding: "binary",
timeout: 3e4
// 30 second timeout
});
return response.data;
} catch (error) {
if (axios.isAxiosError(error)) {
throw new Error(`Failed to download PDF: ${error.message}`);
}
throw new Error(`Unexpected error downloading PDF: ${error}`);
}
}
async function extractPdfText(pdfData) {
try {
return await pdfTextReader.readPdfText({ data: pdfData });
} catch (error) {
throw new Error(`Failed to extract text from PDF: ${error}`);
}
}
async function extractTextFromPdfUrl(url, config) {
const pdfData = await downloadPdf(url, config);
return await extractPdfText(pdfData);
}
function validateTransactionAge(paymentDateTime, maxAgeHours = DEFAULT_MAX_TRANSACTION_AGE_HOURS) {
const hoursDiff = dayjs().diff(dayjs(paymentDateTime), "hour");
if (hoursDiff > maxAgeHours) {
throw new Error(
`Transaction is older than ${maxAgeHours} hours (${hoursDiff} hours old), cannot verify`
);
}
}
function extractReferenceNumber(text) {
const match = text.match(PDF_EXTRACTION_PATTERNS.REFERENCE_NUMBER);
return match?.[1]?.trim() || null;
}
function extractPaymentDateTime(text) {
const match = text.match(PDF_EXTRACTION_PATTERNS.PAYMENT_DATE_TIME);
if (!match?.[1]) return null;
try {
return new Date(match[1].trim());
} catch {
return null;
}
}
function extractTotalAmount(text) {
const match = text.match(PDF_EXTRACTION_PATTERNS.TOTAL_AMOUNT);
return match?.[1]?.trim() || null;
}
function extractReceiver(text) {
const match = text.match(PDF_EXTRACTION_PATTERNS.RECEIVER);
return match?.[1]?.trim() || null;
}
function extractPayer(text) {
const match = text.match(PDF_EXTRACTION_PATTERNS.PAYER);
return match?.[1]?.trim() || null;
}
function extractTransactionDetails(text, config) {
const details = {
transactionRefNumber: extractReferenceNumber(text),
paymentDateTime: extractPaymentDateTime(text),
totalAmountDebited: extractTotalAmount(text),
receiver: extractReceiver(text),
payer: extractPayer(text)
};
if (!details.transactionRefNumber) {
throw new Error("Transaction reference number not found in the PDF");
}
if (!details.paymentDateTime) {
throw new Error("Payment date and time not found in the PDF");
}
const maxAge = config.maxTransactionAgeHours ?? DEFAULT_MAX_TRANSACTION_AGE_HOURS;
validateTransactionAge(details.paymentDateTime, maxAge);
return details;
}
function validateInput(input) {
if (!input || typeof input !== "string") {
throw new Error("Input must be a non-empty string");
}
const isValidRef = TRANSACTION_REFERENCE_REGEX.test(input);
const isValidUrl = TRANSACTION_URL_REGEX.test(input);
if (!isValidRef && !isValidUrl) {
throw new Error("Invalid transaction reference number or URL format");
}
}
function constructPdfUrl(input, cbeAccountNumber) {
if (TRANSACTION_URL_REGEX.test(input)) {
return input.slice(0, -8) + cbeAccountNumber.slice(-8);
}
if (TRANSACTION_REFERENCE_REGEX.test(input)) {
return `${CBE_VERIFICATION_BASE_URL}/?id=${input}${cbeAccountNumber.slice(-8)}`;
}
throw new Error("Unable to construct PDF URL from input");
}
function createDefaultConfig(cbeAccountNumber, overrides = {}) {
return {
cbeAccountNumber,
maxTransactionAgeHours: DEFAULT_MAX_TRANSACTION_AGE_HOURS,
rejectUnauthorized: false,
// Default to false for CBE compatibility
...overrides
};
}
async function verifyDirectDeposit(transactionRefNumberOrUrl, config) {
validateInput(transactionRefNumberOrUrl);
if (!config.cbeAccountNumber) {
throw new Error("CBE account number is required for verification");
}
try {
const pdfUrl = constructPdfUrl(transactionRefNumberOrUrl, config.cbeAccountNumber);
const pdfText = await extractTextFromPdfUrl(pdfUrl, config);
const transactionDetails = extractTransactionDetails(pdfText, config);
return transactionDetails;
} catch (error) {
if (error instanceof Error) {
throw error;
}
throw new Error(`Verification failed: ${error}`);
}
}
async function verifyPayment(transactionRefNumberOrUrl, cbeAccountNumber, options = {}) {
const config = createDefaultConfig(cbeAccountNumber, options);
return verifyDirectDeposit(transactionRefNumberOrUrl, config);
}
function isValidCbeAccountNumber(accountNumber) {
return typeof accountNumber === "string" && accountNumber.length >= 8 && /^\d+$/.test(accountNumber);
}
const index = {
verifyDirectDeposit,
verifyPayment,
isValidCbeAccountNumber
};
exports.CBE_VERIFICATION_BASE_URL = CBE_VERIFICATION_BASE_URL;
exports.DEFAULT_MAX_TRANSACTION_AGE_HOURS = DEFAULT_MAX_TRANSACTION_AGE_HOURS;
exports.TRANSACTION_REFERENCE_REGEX = TRANSACTION_REFERENCE_REGEX;
exports.TRANSACTION_URL_REGEX = TRANSACTION_URL_REGEX;
exports.default = index;
exports.downloadPdf = downloadPdf;
exports.extractPdfText = extractPdfText;
exports.extractTextFromPdfUrl = extractTextFromPdfUrl;
exports.extractTransactionDetails = extractTransactionDetails;
exports.isValidCbeAccountNumber = isValidCbeAccountNumber;
exports.verifyDirectDeposit = verifyDirectDeposit;
exports.verifyPayment = verifyPayment;
//# sourceMappingURL=index.cjs.map