UNPKG

cbe-utils

Version:

A TypeScript utility library for CBE (Commercial Bank of Ethiopia) payment verification and transaction processing

1 lines 16.1 kB
{"version":3,"file":"index.cjs","sources":["../src/constants.ts","../src/pdf-extractor.ts","../src/transaction-parser.ts","../src/payment-verifier.ts","../src/index.ts"],"sourcesContent":["/**\n * Base URL for CBE verification service\n */\nexport const CBE_VERIFICATION_BASE_URL = 'https://apps.cbe.com.et:100';\n\n/**\n * Regular expression to validate transaction reference numbers\n */\nexport const TRANSACTION_REFERENCE_REGEX = /^[A-Z0-9]+$/;\n\n/**\n * Regular expression to validate transaction URLs\n */\nexport const TRANSACTION_URL_REGEX = /^https?:\\/\\/.+/;\n\n/**\n * Default maximum transaction age in hours\n */\nexport const DEFAULT_MAX_TRANSACTION_AGE_HOURS = 24;\n\n/**\n * Regular expression patterns for extracting transaction details from PDF text\n */\nexport const PDF_EXTRACTION_PATTERNS = {\n /** Pattern to extract reference number */\n REFERENCE_NUMBER: /Reference No\\. \\(VAT Invoice No\\)\\s*(.+)/,\n /** Pattern to extract payment date and time */\n PAYMENT_DATE_TIME: /Payment Date & Time\\s*(\\d{1,2}\\/\\d{1,2}\\/\\d{4}, \\d{1,2}:\\d{2}:\\d{2} (?:AM|PM))/,\n /** Pattern to extract total amount debited */\n TOTAL_AMOUNT: /Total amount debited from customers account\\s*([\\d,.]+\\s*ETB)/,\n /** Pattern to extract receiver information */\n RECEIVER: /Receiver\\s*(.+)/,\n /** Pattern to extract payer information */\n PAYER: /Payer\\s*(.+)/,\n} as const; ","import axios from 'axios';\nimport * as https from 'node:https';\nimport { readPdfText } from 'pdf-text-reader';\nimport type { PaymentVerificationConfig } from './types.js';\n\n/**\n * Creates an HTTPS agent with appropriate SSL configuration for CBE services\n */\nfunction createHttpsAgent(rejectUnauthorized = false): https.Agent {\n return new https.Agent({ rejectUnauthorized });\n}\n\n/**\n * Downloads PDF content from a given URL\n * @param url The URL of the PDF file to download\n * @param config Configuration options for the request\n * @returns Promise resolving to the PDF data as a blob\n */\nexport async function downloadPdf(\n url: string,\n config: PaymentVerificationConfig\n): Promise<any> {\n const httpsAgent = createHttpsAgent(config.rejectUnauthorized);\n \n try {\n const response = await axios.get(url, {\n httpsAgent,\n responseType: 'blob',\n responseEncoding: 'binary',\n timeout: 30000, // 30 second timeout\n });\n \n return response.data;\n } catch (error) {\n if (axios.isAxiosError(error)) {\n throw new Error(`Failed to download PDF: ${error.message}`);\n }\n throw new Error(`Unexpected error downloading PDF: ${error}`);\n }\n}\n\n/**\n * Extracts text content from PDF data\n * @param pdfData The PDF data as a blob\n * @returns Promise resolving to the extracted text\n */\nexport async function extractPdfText(pdfData: any): Promise<string> {\n try {\n return await readPdfText({ data: pdfData });\n } catch (error) {\n throw new Error(`Failed to extract text from PDF: ${error}`);\n }\n}\n\n/**\n * Downloads and extracts text from a PDF file at the given URL\n * @param url The URL of the PDF file\n * @param config Configuration options\n * @returns Promise resolving to the extracted text\n */\nexport async function extractTextFromPdfUrl(\n url: string,\n config: PaymentVerificationConfig\n): Promise<string> {\n const pdfData = await downloadPdf(url, config);\n return await extractPdfText(pdfData);\n} ","import dayjs from 'dayjs';\nimport type { TransactionDetails, PaymentVerificationConfig } from './types.js';\nimport { PDF_EXTRACTION_PATTERNS, DEFAULT_MAX_TRANSACTION_AGE_HOURS } from './constants.js';\n\n/**\n * Validates that a transaction is within the allowed time window\n * @param paymentDateTime The payment date and time\n * @param maxAgeHours Maximum allowed age in hours\n * @throws Error if transaction is too old\n */\nfunction validateTransactionAge(\n paymentDateTime: Date,\n maxAgeHours: number = DEFAULT_MAX_TRANSACTION_AGE_HOURS\n): void {\n const hoursDiff = dayjs().diff(dayjs(paymentDateTime), 'hour');\n if (hoursDiff > maxAgeHours) {\n throw new Error(\n `Transaction is older than ${maxAgeHours} hours (${hoursDiff} hours old), cannot verify`\n );\n }\n}\n\n/**\n * Extracts the transaction reference number from the PDF text\n * @param text The PDF text content\n * @returns The extracted reference number or null if not found\n */\nfunction extractReferenceNumber(text: string): string | null {\n const match = text.match(PDF_EXTRACTION_PATTERNS.REFERENCE_NUMBER);\n return match?.[1]?.trim() || null;\n}\n\n/**\n * Extracts the payment date and time from the PDF text\n * @param text The PDF text content\n * @returns The extracted date or null if not found\n */\nfunction extractPaymentDateTime(text: string): Date | null {\n const match = text.match(PDF_EXTRACTION_PATTERNS.PAYMENT_DATE_TIME);\n if (!match?.[1]) return null;\n \n try {\n return new Date(match[1].trim());\n } catch {\n return null;\n }\n}\n\n/**\n * Extracts the total amount debited from the PDF text\n * @param text The PDF text content\n * @returns The extracted amount or null if not found\n */\nfunction extractTotalAmount(text: string): string | null {\n const match = text.match(PDF_EXTRACTION_PATTERNS.TOTAL_AMOUNT);\n return match?.[1]?.trim() || null;\n}\n\n/**\n * Extracts the receiver information from the PDF text\n * @param text The PDF text content\n * @returns The extracted receiver info or null if not found\n */\nfunction extractReceiver(text: string): string | null {\n const match = text.match(PDF_EXTRACTION_PATTERNS.RECEIVER);\n return match?.[1]?.trim() || null;\n}\n\n/**\n * Extracts the payer information from the PDF text\n * @param text The PDF text content\n * @returns The extracted payer info or null if not found\n */\nfunction extractPayer(text: string): string | null {\n const match = text.match(PDF_EXTRACTION_PATTERNS.PAYER);\n return match?.[1]?.trim() || null;\n}\n\n/**\n * Extracts transaction details from unformatted PDF text\n * @param text The unformatted text containing transaction information\n * @param config Configuration options for validation\n * @returns An object with extracted transaction details\n * @throws Error if required fields are missing or transaction is too old\n */\nexport function extractTransactionDetails(\n text: string,\n config: PaymentVerificationConfig\n): TransactionDetails {\n const details: TransactionDetails = {\n transactionRefNumber: extractReferenceNumber(text),\n paymentDateTime: extractPaymentDateTime(text),\n totalAmountDebited: extractTotalAmount(text),\n receiver: extractReceiver(text),\n payer: extractPayer(text),\n };\n\n // Validate required fields\n if (!details.transactionRefNumber) {\n throw new Error('Transaction reference number not found in the PDF');\n }\n\n if (!details.paymentDateTime) {\n throw new Error('Payment date and time not found in the PDF');\n }\n\n // Validate transaction age\n const maxAge = config.maxTransactionAgeHours ?? DEFAULT_MAX_TRANSACTION_AGE_HOURS;\n validateTransactionAge(details.paymentDateTime, maxAge);\n\n return details;\n} ","import type { TransactionDetails, PaymentVerificationConfig } from './types.js';\nimport { \n CBE_VERIFICATION_BASE_URL, \n TRANSACTION_REFERENCE_REGEX, \n TRANSACTION_URL_REGEX,\n DEFAULT_MAX_TRANSACTION_AGE_HOURS \n} from './constants.js';\nimport { extractTextFromPdfUrl } from './pdf-extractor.js';\nimport { extractTransactionDetails } from './transaction-parser.js';\n\n/**\n * Validates input format (reference number or URL)\n * @param input The transaction reference number or URL to validate\n * @throws Error if input format is invalid\n */\nfunction validateInput(input: string): void {\n if (!input || typeof input !== 'string') {\n throw new Error('Input must be a non-empty string');\n }\n\n const isValidRef = TRANSACTION_REFERENCE_REGEX.test(input);\n const isValidUrl = TRANSACTION_URL_REGEX.test(input);\n\n if (!isValidRef && !isValidUrl) {\n throw new Error('Invalid transaction reference number or URL format');\n }\n}\n\n/**\n * Constructs the PDF URL based on input type\n * @param input Transaction reference number or URL\n * @param cbeAccountNumber CBE account number for verification\n * @returns The complete PDF URL\n */\nfunction constructPdfUrl(input: string, cbeAccountNumber: string): string {\n if (TRANSACTION_URL_REGEX.test(input)) {\n // If input is a URL, replace the last 8 characters with our account number\n return input.slice(0, -8) + cbeAccountNumber.slice(-8);\n }\n \n if (TRANSACTION_REFERENCE_REGEX.test(input)) {\n // If input is a reference number, construct the full URL\n return `${CBE_VERIFICATION_BASE_URL}/?id=${input}${cbeAccountNumber.slice(-8)}`;\n }\n\n throw new Error('Unable to construct PDF URL from input');\n}\n\n/**\n * Creates a default configuration object\n * @param cbeAccountNumber The CBE account number\n * @param overrides Optional configuration overrides\n * @returns Complete configuration object\n */\nfunction createDefaultConfig(\n cbeAccountNumber: string,\n overrides: Partial<PaymentVerificationConfig> = {}\n): PaymentVerificationConfig {\n return {\n cbeAccountNumber,\n maxTransactionAgeHours: DEFAULT_MAX_TRANSACTION_AGE_HOURS,\n rejectUnauthorized: false, // Default to false for CBE compatibility\n ...overrides,\n };\n}\n\n/**\n * Verifies a CBE direct deposit transaction by extracting and parsing PDF content\n * @param transactionRefNumberOrUrl Transaction reference number or PDF URL\n * @param config Configuration options for verification\n * @returns Promise resolving to extracted transaction details\n * @throws Error if verification fails at any stage\n */\nexport async function verifyDirectDeposit(\n transactionRefNumberOrUrl: string,\n config: PaymentVerificationConfig\n): Promise<TransactionDetails> {\n // Validate inputs\n validateInput(transactionRefNumberOrUrl);\n \n if (!config.cbeAccountNumber) {\n throw new Error('CBE account number is required for verification');\n }\n\n try {\n // Construct the PDF URL\n const pdfUrl = constructPdfUrl(transactionRefNumberOrUrl, config.cbeAccountNumber);\n \n // Extract text from PDF\n const pdfText = await extractTextFromPdfUrl(pdfUrl, config);\n \n // Parse transaction details\n const transactionDetails = extractTransactionDetails(pdfText, config);\n \n return transactionDetails;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(`Verification failed: ${error}`);\n }\n}\n\n/**\n * Convenience function to verify payment with minimal configuration\n * @param transactionRefNumberOrUrl Transaction reference number or PDF URL\n * @param cbeAccountNumber CBE account number for verification\n * @param options Optional configuration overrides\n * @returns Promise resolving to extracted transaction details\n */\nexport async function verifyPayment(\n transactionRefNumberOrUrl: string,\n cbeAccountNumber: string,\n options: Partial<PaymentVerificationConfig> = {}\n): Promise<TransactionDetails> {\n const config = createDefaultConfig(cbeAccountNumber, options);\n return verifyDirectDeposit(transactionRefNumberOrUrl, config);\n}\n\n/**\n * Validates CBE account number format\n * @param accountNumber The account number to validate\n * @returns True if valid, false otherwise\n */\nexport function isValidCbeAccountNumber(accountNumber: string): boolean {\n return typeof accountNumber === 'string' && \n accountNumber.length >= 8 && \n /^\\d+$/.test(accountNumber);\n} ","/**\n * CBE Utils - A TypeScript utility library for CBE payment verification\n */\n\n// Import main verification functions for default export\nimport { verifyDirectDeposit, verifyPayment, isValidCbeAccountNumber } from './payment-verifier.js';\n\n// Re-export main verification functions\nexport { verifyDirectDeposit, verifyPayment, isValidCbeAccountNumber } from './payment-verifier.js';\n\n// Re-export types\nexport type { TransactionDetails, PaymentVerificationConfig } from './types.js';\n\n// Re-export constants that might be useful for consumers\nexport { \n CBE_VERIFICATION_BASE_URL,\n TRANSACTION_REFERENCE_REGEX,\n TRANSACTION_URL_REGEX,\n DEFAULT_MAX_TRANSACTION_AGE_HOURS \n} from './constants.js';\n\n// Re-export utility functions for advanced usage\nexport { extractTextFromPdfUrl, downloadPdf, extractPdfText } from './pdf-extractor.js';\nexport { extractTransactionDetails } from './transaction-parser.js';\n\n/**\n * Default export for convenient importing\n */\nexport default {\n verifyDirectDeposit,\n verifyPayment,\n isValidCbeAccountNumber,\n}; "],"names":["https","readPdfText"],"mappings":"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"}