1- console . log ( 'Fraud Detection Service' ) ;
1+ import { connect , NatsConnection , Subscription , JSONCodec } from 'nats' ;
2+
3+ // Type definitions based on AsyncAPI spec
4+ interface FraudAlert {
5+ alertId : string ;
6+ transactionId : string ;
7+ alertTime : string ;
8+ severity : string ;
9+ details : string ;
10+ }
11+
12+ interface TransactionEvaluated {
13+ transactionId : string ;
14+ evaluationTime : string ;
15+ isFraudulent : boolean ;
16+ riskScore : number ;
17+ }
18+
19+ interface TransactionReview {
20+ transactionId : string ;
21+ reviewTime : string ;
22+ reviewOutcome : string ;
23+ reviewerId : string ;
24+ }
25+
26+ // Channel addresses from AsyncAPI spec
27+ const CHANNELS = {
28+ FRAUD_ALERT : 'fraud.alert' ,
29+ TRANSACTION_EVALUATED : 'transaction.evaluated' ,
30+ TRANSACTION_REVIEW : 'transaction.review' ,
31+ } ;
32+
33+ class FraudDetectionService {
34+ private nc : NatsConnection | null = null ;
35+ private subscriptions : Subscription [ ] = [ ] ;
36+ private jc = JSONCodec ( ) ;
37+
38+ async connect ( natsUrl : string = 'nats://localhost:4222' ) : Promise < void > {
39+ console . log ( `Connecting to NATS at ${ natsUrl } ...` ) ;
40+ this . nc = await connect ( { servers : natsUrl } ) ;
41+ console . log ( `Connected to NATS server: ${ this . nc . getServer ( ) } ` ) ;
42+ }
43+
44+ async disconnect ( ) : Promise < void > {
45+ for ( const sub of this . subscriptions ) {
46+ sub . unsubscribe ( ) ;
47+ }
48+ if ( this . nc ) {
49+ await this . nc . drain ( ) ;
50+ console . log ( 'Disconnected from NATS' ) ;
51+ }
52+ }
53+
54+ // Operation: sendFraudAlert
55+ async sendFraudAlert ( alert : FraudAlert ) : Promise < void > {
56+ if ( ! this . nc ) throw new Error ( 'Not connected to NATS' ) ;
57+
58+ console . log ( `Publishing fraud alert for transaction ${ alert . transactionId } ` ) ;
59+ this . nc . publish ( CHANNELS . FRAUD_ALERT , this . jc . encode ( alert ) ) ;
60+ }
61+
62+ // Operation: receiveTransactionEvaluated
63+ async subscribeToTransactionEvaluated (
64+ handler : ( data : TransactionEvaluated ) => Promise < void >
65+ ) : Promise < void > {
66+ if ( ! this . nc ) throw new Error ( 'Not connected to NATS' ) ;
67+
68+ console . log ( `Subscribing to ${ CHANNELS . TRANSACTION_EVALUATED } ...` ) ;
69+ const sub = this . nc . subscribe ( CHANNELS . TRANSACTION_EVALUATED ) ;
70+ this . subscriptions . push ( sub ) ;
71+
72+ ( async ( ) => {
73+ for await ( const msg of sub ) {
74+ try {
75+ const data = this . jc . decode ( msg . data ) as TransactionEvaluated ;
76+ console . log ( `Received transaction evaluation for ${ data . transactionId } ` ) ;
77+ await handler ( data ) ;
78+ } catch ( err ) {
79+ console . error ( 'Error processing transaction evaluated message:' , err ) ;
80+ }
81+ }
82+ } ) ( ) ;
83+ }
84+
85+ // Operation: receiveTransactionReview
86+ async subscribeToTransactionReview (
87+ handler : ( data : TransactionReview ) => Promise < void >
88+ ) : Promise < void > {
89+ if ( ! this . nc ) throw new Error ( 'Not connected to NATS' ) ;
90+
91+ console . log ( `Subscribing to ${ CHANNELS . TRANSACTION_REVIEW } ...` ) ;
92+ const sub = this . nc . subscribe ( CHANNELS . TRANSACTION_REVIEW ) ;
93+ this . subscriptions . push ( sub ) ;
94+
95+ ( async ( ) => {
96+ for await ( const msg of sub ) {
97+ try {
98+ const data = this . jc . decode ( msg . data ) as TransactionReview ;
99+ console . log ( `Received transaction review for ${ data . transactionId } ` ) ;
100+ await handler ( data ) ;
101+ } catch ( err ) {
102+ console . error ( 'Error processing transaction review message:' , err ) ;
103+ }
104+ }
105+ } ) ( ) ;
106+ }
107+ }
108+
109+ // Main application logic
110+ async function main ( ) {
111+ const service = new FraudDetectionService ( ) ;
112+
113+ const natsUrl = process . env . NATS_URL || 'nats://localhost:4222' ;
114+
115+ try {
116+ await service . connect ( natsUrl ) ;
117+
118+ // Handle transaction evaluations
119+ await service . subscribeToTransactionEvaluated ( async ( data ) => {
120+ console . log ( 'Processing transaction evaluation:' , JSON . stringify ( data , null , 2 ) ) ;
121+
122+ // Business logic: If transaction is flagged as fraudulent, send an alert
123+ if ( data . isFraudulent || data . riskScore > 0.7 ) {
124+ const alert : FraudAlert = {
125+ alertId : `ALERT-${ Date . now ( ) } ` ,
126+ transactionId : data . transactionId ,
127+ alertTime : new Date ( ) . toISOString ( ) ,
128+ severity : data . riskScore > 0.9 ? 'CRITICAL' : data . riskScore > 0.7 ? 'HIGH' : 'MEDIUM' ,
129+ details : `Transaction flagged with risk score: ${ data . riskScore } ` ,
130+ } ;
131+ await service . sendFraudAlert ( alert ) ;
132+ }
133+ } ) ;
134+
135+ // Handle manual transaction reviews
136+ await service . subscribeToTransactionReview ( async ( data ) => {
137+ console . log ( 'Processing transaction review:' , JSON . stringify ( data , null , 2 ) ) ;
138+
139+ // Business logic: Log review outcomes
140+ if ( data . reviewOutcome === 'Declined' ) {
141+ console . log ( `Transaction ${ data . transactionId } was declined by reviewer ${ data . reviewerId } ` ) ;
142+ } else if ( data . reviewOutcome === 'Escalated' ) {
143+ const alert : FraudAlert = {
144+ alertId : `ALERT-ESC-${ Date . now ( ) } ` ,
145+ transactionId : data . transactionId ,
146+ alertTime : new Date ( ) . toISOString ( ) ,
147+ severity : 'HIGH' ,
148+ details : `Transaction escalated by reviewer ${ data . reviewerId } ` ,
149+ } ;
150+ await service . sendFraudAlert ( alert ) ;
151+ }
152+ } ) ;
153+
154+ console . log ( 'Fraud Detection Service is running. Press Ctrl+C to exit.' ) ;
155+
156+ // Keep the service running
157+ process . on ( 'SIGINT' , async ( ) => {
158+ console . log ( '\nShutting down...' ) ;
159+ await service . disconnect ( ) ;
160+ process . exit ( 0 ) ;
161+ } ) ;
162+
163+ process . on ( 'SIGTERM' , async ( ) => {
164+ console . log ( '\nShutting down...' ) ;
165+ await service . disconnect ( ) ;
166+ process . exit ( 0 ) ;
167+ } ) ;
168+
169+ } catch ( err ) {
170+ console . error ( 'Failed to start Fraud Detection Service:' , err ) ;
171+ process . exit ( 1 ) ;
172+ }
173+ }
174+
175+ main ( ) ;
0 commit comments