@@ -206,8 +206,38 @@ console.log('Mock LHDN Public Key:', MOCK_PUBLIC_KEY);
206206// Mock citizen database - simulates LHDN's citizen income records
207207// Key: IC number (without hyphens), Value: income and name data
208208const mockCitizensData = {
209+ // Existing profiles
209210 '030520012185' : { income : 7000 , name : 'HAR SZE HAO' } , // B40_LOW category
210- '030322016289' : { income : 2350 , name : 'PANG ZHAN HUANG' } // B40_HIGH category
211+ '030322016289' : { income : 2350 , name : 'PANG ZHAN HUANG' } , // B40_HIGH category
212+
213+ // B40 Income Range (Below RM4,850)
214+ '950101011234' : { income : 1500 , name : 'NURUL AISYAH BINTI AHMAD' } ,
215+ '880215025678' : { income : 2800 , name : 'LIM WEI JIAN' } ,
216+ '920308031122' : { income : 3200 , name : 'KUMAR A/L RAJAN' } ,
217+ '850420043344' : { income : 4500 , name : 'TAN MEI LING' } ,
218+
219+ // M40-M1 Range (RM4,851 - RM7,100)
220+ '910512055566' : { income : 5000 , name : 'FARAH BINTI IBRAHIM' } ,
221+ '870625067788' : { income : 5800 , name : 'WONG KAR WAI' } ,
222+ '930710079900' : { income : 6500 , name : 'SITI NURHALIZA BINTI TARUDIN' } ,
223+ '840822081234' : { income : 7000 , name : 'RAJESH A/L KUMAR' } ,
224+
225+ // M40-M2 Range (RM7,101 - RM10,970)
226+ '960905095567' : { income : 7500 , name : 'CHUA YEN LING' } ,
227+ '890118107889' : { income : 8900 , name : 'MUHAMMAD HAFIZ BIN ZAINUDIN' } ,
228+ '920203129012' : { income : 9800 , name : 'LEE CHONG WEI' } ,
229+ '861114143345' : { income : 10500 , name : 'ANITA A/P SUPPIAH' } ,
230+
231+ // T20 Range (Above RM10,970)
232+ '940425155678' : { income : 12000 , name : 'DATO\' TAN BOON HUAT' } ,
233+ '810507167890' : { income : 15000 , name : 'DR. AMINAH BINTI KASSIM' } ,
234+ '970618179123' : { income : 18500 , name : 'VINCENT CHIN YONG SENG' } ,
235+ '831022181456' : { income : 22000 , name : 'DATIN LATIFAH BINTI ABDULLAH' } ,
236+
237+ // Edge cases for testing
238+ '000101010001' : { income : 4850 , name : 'BOUNDARY TEST B40-M40' } , // Exact B40/M40 boundary
239+ '000202020002' : { income : 7100 , name : 'BOUNDARY TEST M40-M1-M2' } , // Exact M40-M1/M40-M2 boundary
240+ '000303030003' : { income : 10970 , name : 'BOUNDARY TEST M40-T20' } // Exact M40/T20 boundary
211241} ;
212242
213243/**
@@ -239,8 +269,8 @@ function signData(data, privateKey) {
239269 * $ref: '#/components/schemas/HealthResponse'
240270 */
241271app . get ( '/health' , ( req , res ) => {
242- res . json ( {
243- status : 'OK' ,
272+ res . json ( {
273+ status : 'OK' ,
244274 service : 'Mock LHDN API' ,
245275 timestamp : new Date ( ) . toISOString ( ) ,
246276 public_key : MOCK_PUBLIC_KEY
@@ -295,10 +325,10 @@ app.get('/health', (req, res) => {
295325app . post ( '/api/verify-income' , ( req , res ) => {
296326 try {
297327 const { ic } = req . body ;
298-
328+
299329 // Input validation - ensure IC number is provided
300330 if ( ! ic ) {
301- return res . status ( 400 ) . json ( {
331+ return res . status ( 400 ) . json ( {
302332 error : 'IC number is required' ,
303333 code : 'MISSING_IC'
304334 } ) ;
@@ -307,13 +337,13 @@ app.post('/api/verify-income', (req, res) => {
307337 // Clean IC number by removing hyphens for database lookup
308338 // Converts "030520-01-2185" to "030520012185"
309339 const cleanIC = ic . replace ( / - / g, '' ) ;
310-
340+
311341 // Look up citizen data in mock database
312342 const citizenData = mockCitizensData [ cleanIC ] ;
313-
343+
314344 // Return 404 if citizen not found in database
315345 if ( ! citizenData ) {
316- return res . status ( 404 ) . json ( {
346+ return res . status ( 404 ) . json ( {
317347 error : 'Citizen not found in LHDN database' ,
318348 code : 'CITIZEN_NOT_FOUND'
319349 } ) ;
@@ -332,7 +362,7 @@ app.post('/api/verify-income', (req, res) => {
332362 // Generate digital signature for data integrity
333363 // This proves the data came from LHDN and hasn't been tampered with
334364 const signature = signData ( responseData , MOCK_PRIVATE_KEY ) ;
335-
365+
336366 // Combine response data with signature and public key
337367 const finalResponse = {
338368 ...responseData ,
@@ -341,12 +371,12 @@ app.post('/api/verify-income', (req, res) => {
341371 } ;
342372
343373 console . log ( `✅ Income verified for IC: ${ ic } , Income: RM${ citizenData . income } ` ) ;
344-
374+
345375 res . json ( finalResponse ) ;
346376
347377 } catch ( error ) {
348378 console . error ( 'Error in verify-income:' , error ) ;
349- res . status ( 500 ) . json ( {
379+ res . status ( 500 ) . json ( {
350380 error : 'Internal server error' ,
351381 code : 'INTERNAL_ERROR'
352382 } ) ;
@@ -381,11 +411,11 @@ app.get('/api/test-ics', (req, res) => {
381411 // Provides both raw and formatted IC numbers for convenience
382412 const testICs = Object . keys ( mockCitizensData ) . map ( ic => ( {
383413 ic : ic , // Raw IC without formatting
384- formatted_ic : `${ ic . slice ( 0 , 6 ) } -${ ic . slice ( 6 , 8 ) } -${ ic . slice ( 8 ) } ` , // Formatted with hyphens
414+ formatted_ic : `${ ic . slice ( 0 , 6 ) } -${ ic . slice ( 6 , 8 ) } -${ ic . slice ( 8 ) } ` , // Formatted with hyphens
385415 income : mockCitizensData [ ic ] . income ,
386416 name : mockCitizensData [ ic ] . name
387417 } ) ) ;
388-
418+
389419 res . json ( {
390420 message : 'Available test IC numbers' ,
391421 test_citizens : testICs
@@ -429,20 +459,20 @@ app.get('/api/test-ics', (req, res) => {
429459app . post ( '/api/verify-signature' , ( req , res ) => {
430460 try {
431461 const { data, signature } = req . body ;
432-
462+
433463 // Generate expected signature using the same algorithm and private key
434464 const expectedSignature = signData ( data , MOCK_PRIVATE_KEY ) ;
435-
465+
436466 // Compare signatures - constant time comparison would be better for security
437467 const isValid = signature === expectedSignature ;
438-
468+
439469 res . json ( {
440470 is_valid : isValid ,
441471 message : isValid ? 'Signature is valid' : 'Signature is invalid'
442472 } ) ;
443-
473+
444474 } catch ( error ) {
445- res . status ( 400 ) . json ( {
475+ res . status ( 400 ) . json ( {
446476 error : 'Invalid signature verification request' ,
447477 code : 'INVALID_REQUEST'
448478 } ) ;
@@ -465,10 +495,10 @@ app.use((err, req, res, next) => {
465495 details : err . message
466496 } ) ;
467497 }
468-
498+
469499 // Generic error handler for unexpected errors
470500 console . error ( 'Unhandled error:' , err ) ;
471- res . status ( 500 ) . json ( {
501+ res . status ( 500 ) . json ( {
472502 error : 'Something went wrong!' ,
473503 code : 'UNHANDLED_ERROR'
474504 } ) ;
0 commit comments