UNPKG

kafka-pub-sub

Version:

Enterprise-grade Kafka publish/subscribe library for Node.js — producer pool, batch sending, DLQ, SSL/SASL, multi-broker, and real-world industry examples.

147 lines (126 loc) 4.52 kB
/** * Financial Services — Payment Transaction Producer * * Demonstrates: * - Idempotent producer (KAFKA_IDEMPOTENT=true) for exactly-once semantics * - Partitioning by currency for ordered processing per currency pair * - SASL/SSL config (set env vars — see .env.example) * - BatchProduceEvent for high-throughput trade confirmation bursts * * Regulatory note: every payment event is stamped with a correlationId that * ties together the originating API request, Kafka message, and downstream * processing for full audit trail compliance (PCI-DSS, SOX). * * Run: * node examples/financial/paymentProducer.js */ require('dotenv').config({ path: require('path').resolve(__dirname, '../../.env') }); const { ProduceEvent, BatchProduceEvent } = require('../../index'); const TOPICS = { PAYMENT_TRANSACTION: 'payment.transaction', PAYMENT_FRAUD_ALERT: 'payment.fraud-alert', PAYMENT_SETTLED: 'payment.settled', }; const randomAmount = () => parseFloat((Math.random() * 50000).toFixed(2)); const randomCurrency = () => ['USD', 'EUR', 'GBP', 'JPY', 'SGD'][Math.floor(Math.random() * 5)]; const txId = () => `TX-${Date.now()}-${Math.random().toString(36).slice(2, 7).toUpperCase()}`; // eslint-disable-next-line no-console const log = (...args) => console.log(`[${new Date().toISOString()}]`, ...args); /** * Publishes a single high-value payment transaction. * Idempotent producer ensures the message is written exactly once * even if the network retries cause duplicate sends. */ async function publishPaymentTransaction() { const transactionId = txId(); const currency = randomCurrency(); const amount = randomAmount(); const payload = { transactionId, type: 'PAYMENT', amount, currency, sender: { accountId: 'ACCT-001', bankCode: 'BOFAUS3N' }, receiver: { accountId: 'ACCT-009', bankCode: 'CHASUS33' }, description: 'Invoice payment INV-2024-00412', riskScore: Math.random(), // 0.0 (safe) – 1.0 (high risk) initiatedAt: new Date().toISOString(), }; await ProduceEvent( TOPICS.PAYMENT_TRANSACTION, 'PAYMENT_TX', payload, { 'x-source': 'payments-api', 'x-tenant': 'finco-global', 'content-type': 'application/json', }, { partitionKey: currency, // all USD txns go to the same partition — ordered processing correlationId: transactionId, } ); log(`✓ Payment published | ${transactionId} | ${currency} ${amount}`); // Simulate fraud detection: high-risk transactions get a separate alert event if (payload.riskScore > 0.85) { await ProduceEvent( TOPICS.PAYMENT_FRAUD_ALERT, 'FRAUD_ALERT', { transactionId, riskScore: payload.riskScore, reason: 'Risk score exceeds threshold (0.85)', requiresManualReview: true, }, { 'x-source': 'fraud-detection-engine' }, { correlationId: transactionId } ); log(`⚠ Fraud alert raised for ${transactionId} (risk=${payload.riskScore.toFixed(3)})`); } return payload; } /** * Publishes a batch of end-of-day settlement confirmations. * Uses BatchProduceEvent for a single broker round-trip. */ async function publishSettlementBatch(count = 20) { log(`Publishing settlement batch (${count} records)...`); const messages = Array.from({ length: count }, () => { const id = txId(); return { topic: TOPICS.PAYMENT_SETTLED, event: 'PAYMENT_SETTLED', data: { transactionId: id, settledAmount: randomAmount(), currency: randomCurrency(), settledAt: new Date().toISOString(), clearingHouseRef: `CH-${Math.random().toString(36).slice(2, 10).toUpperCase()}`, }, headers: { 'x-source': 'settlement-engine', 'batch-run': 'EOD-DAILY' }, partitionKey: id, }; }); await BatchProduceEvent(messages, { compression: 'gzip', correlationId: `SETTLE-BATCH-${Date.now()}`, }); log(`✓ Settlement batch of ${count} records published`); } async function main() { // Publish 5 individual payment transactions for (let i = 0; i < 5; i++) { // eslint-disable-next-line no-await-in-loop await publishPaymentTransaction(); } // Publish end-of-day settlement batch await publishSettlementBatch(20); await ProduceEvent.disconnect(); await BatchProduceEvent.disconnect(); log('Done.'); } main().catch((err) => { // eslint-disable-next-line no-console console.error('Financial producer failed:', err); process.exit(1); });