@iso8583/toolkit
v1.0.0
Published
A TypeScript-based ISO8583 TCP server toolkit for building and handling financial transaction messages using the ISO8583 protocol. Includes message parsing, building, and TCP transport support. Compatible with NestJS and other Node.js frameworks.
Maintainers
Readme
@iso8583/toolkit
A TypeScript-based ISO8583 TCP/TLS server toolkit for building and handling financial transaction messages using the ISO8583 protocol. Includes message parsing, building, and secure transport support with both traditional and JSON-friendly APIs. Compatible with NestJS and other Node.js frameworks.
✨ Features
- Dual API Support: Traditional ISO8583 format and developer-friendly JSON format
- TCP/TLS Server: Full-featured server for handling ISO8583 messages over TCP and TLS
- Secure Transport: TLS support with configurable certificates and security options
- Message Parser: Parse ISO8583 messages from Buffer format to JSON or internal format
- Message Builder: Build ISO8583 messages from JSON or internal format to Buffer
- Type Safety: Comprehensive TypeScript types and interfaces
- Field Validation: Built-in validation for all ISO8583 field types
- Configuration Validation: Automatic validation of field definitions and configuration
- Error Handling: Standardized error classes for better debugging
- Helper Functions: Pre-built functions for common message types (echo, financial, etc.)
- Event System: Rich event handling for monitoring and debugging
- Statistics: Built-in connection and message tracking
- Resource Management: Proper cleanup and resource management
- NestJS Integration: Seamless integration with NestJS applications
- Comprehensive Tests: Full test suite covering all functionality including edge cases
🚀 Quick Start
Installation
npm install @iso8583/toolkitBasic Usage (Node.js)
Traditional ISO8583 Mode
import {
createDefaultServer,
FieldType,
CommonMTI,
ResponseCode
} from '@iso8583/toolkit';
// Create server with traditional ISO8583 handling
const server = createDefaultServer(
{ port: 5000, host: 'localhost' },
{
async onMessage(message, socket) {
console.log('Received message:', message.mti);
// Create response with same fields + response code
const responseFields = new Map(message.fields);
responseFields.set(39, ResponseCode.APPROVED); // Response code
return {
mti: '0810', // Echo response
bitmap: '', // Will be auto-generated
fields: responseFields,
buffer: Buffer.alloc(0) // Will be auto-generated
};
},
onConnect(socket) {
console.log('Client connected:', socket.remoteAddress);
},
onDisconnect(socket) {
console.log('Client disconnected:', socket.remoteAddress);
}
}
);
// Start the server
await server.start();
console.log('ISO8583 server started on port 5000');JSON Mode (Recommended)
import {
createJsonServer,
createEchoRequest,
createEchoResponse,
createFinancialRequest,
createFinancialResponse,
CommonMTI,
ResponseCode
} from '@iso8583/toolkit';
// Create server with JSON mode enabled
const server = createJsonServer(
{ port: 5000, host: 'localhost', enableJsonMode: true },
{
async onMessage(message, socket) {
console.log('Received JSON message:', message.mti);
// Handle different message types
switch (message.mti) {
case CommonMTI.ECHO_REQUEST:
return createEchoResponse(message);
case CommonMTI.FINANCIAL_REQUEST:
// Process financial transaction
const isApproved = await processTransaction(message);
return createFinancialResponse(
message,
isApproved ? ResponseCode.APPROVED : ResponseCode.DECLINED
);
default:
return createEchoResponse(message, {
RESPONSE_CODE: ResponseCode.INVALID_MTI
});
}
}
}
);
await server.start();
console.log('ISO8583 JSON server started on port 5000');TLS Server Mode
import {
Iso8583TlsServer,
Iso8583Parser,
Iso8583Builder,
FieldType,
CommonMTI,
ResponseCode
} from '@iso8583/toolkit';
import fs from 'fs';
// TLS configuration
const tlsConfig = {
enabled: true,
port: 5001, // Explicit TLS port (optional, defaults to TCP port + 1)
cert: fs.readFileSync('./certs/server.crt'),
key: fs.readFileSync('./certs/server.key'),
ca: fs.readFileSync('./certs/ca.crt'), // Optional CA certificate
requestCert: true, // Request client certificates
rejectUnauthorized: false, // Allow self-signed certs in development
ciphers: 'TLS_AES_128_GCM_SHA256',
minVersion: 'TLSv1.2',
maxVersion: 'TLSv1.3'
};
// Create parser and builder
const parser = new Iso8583Parser({
fieldDefinitions: DEFAULT_FIELD_DEFINITIONS,
encoding: 'ascii',
includeRaw: false,
enableJsonMode: true
});
const builder = new Iso8583Builder({
fieldDefinitions: DEFAULT_FIELD_DEFINITIONS,
encoding: 'ascii',
enableJsonMode: true
});
// Create TLS server
const tlsServer = new Iso8583TlsServer(
{
port: 5000,
host: 'localhost',
enableJsonMode: true,
tls: tlsConfig
},
{
async onMessage(message, socket) {
console.log('Received secure message:', message.mti);
return createEchoResponse(message);
},
onSecureConnection(socket) {
console.log('Secure connection established:', socket.getProtocol());
},
onConnect(socket) {
console.log('Client connected:', socket.remoteAddress);
},
onDisconnect(socket) {
console.log('Client disconnected:', socket.remoteAddress);
}
},
parser,
builder
);
// Start both TCP and TLS servers
await tlsServer.start();
console.log('ISO8583 TLS server started on ports 5000 (TCP) and 5001 (TLS)');Advanced Usage
Custom Field Definitions
import {
Iso8583Server,
Iso8583Parser,
Iso8583Builder,
FieldType
} from '@iso8583/toolkit';
// Define custom field definitions
const customFieldDefinitions = [
{ id: 2, name: 'PAN', type: FieldType.LLVAR, maxLength: 19, required: true },
{ id: 3, name: 'PROC_CODE', type: FieldType.NUMERIC, maxLength: 6, required: true },
{ id: 4, name: 'AMOUNT', type: FieldType.AMOUNT, maxLength: 12, required: true },
{ id: 7, name: 'TRANSMISSION_DATE_TIME', type: FieldType.NUMERIC, maxLength: 10, required: true },
{ id: 11, name: 'STAN', type: FieldType.NUMERIC, maxLength: 6, required: true },
{ id: 39, name: 'RESPONSE_CODE', type: FieldType.ALPHANUMERIC, maxLength: 2, required: false },
{ id: 62, name: 'RESERVED_PRIVATE', type: FieldType.LLLVAR, maxLength: 255, required: false },
// Add more fields as needed...
];
// Create parser and builder with custom definitions
const parser = new Iso8583Parser({
fieldDefinitions: customFieldDefinitions,
encoding: 'ascii',
includeRaw: false,
enableJsonMode: true
});
const builder = new Iso8583Builder({
fieldDefinitions: customFieldDefinitions,
encoding: 'ascii',
enableJsonMode: true
});
// Create server with custom components
const server = new Iso8583Server(
{ port: 5000, enableJsonMode: true },
{
async onMessage(message, socket) {
// Handle messages with custom field definitions
return createEchoResponse(message);
}
},
parser,
builder
);Message Validation and Error Handling
import {
createJsonConverter,
createEchoRequest,
ResponseCode,
Iso8583ValidationError,
Iso8583ParsingError,
Iso8583BuildingError
} from '@iso8583/toolkit';
const converter = createJsonConverter();
// Validate message before processing
const message = createEchoRequest({
PAN: '1234567890123456',
PROC_CODE: '000000',
AMOUNT: '000000012500'
});
const validation = converter.validateJsonMessage(message);
if (!validation.isValid) {
console.error('Validation errors:', validation.errors);
console.warn('Validation warnings:', validation.warnings);
return;
}
// Handle specific error types
try {
const isoMessage = converter.toIso8583(message);
console.log('Converted message:', isoMessage);
} catch (error) {
if (error instanceof Iso8583ValidationError) {
console.error('Validation error:', error.message, 'Field:', error.field);
} else if (error instanceof Iso8583ParsingError) {
console.error('Parsing error:', error.message, 'Field:', error.field);
} else if (error instanceof Iso8583BuildingError) {
console.error('Building error:', error.message, 'Field:', error.field);
} else {
console.error('Unknown error:', error);
}
}
// Convert to ISO8583 format
// (This is now handled in the try-catch block above)Broadcasting Messages
import { createJsonServer, createEchoRequest } from '@iso8583/toolkit';
const server = createJsonServer(
{ port: 5000, enableJsonMode: true },
{
async onMessage(message, socket) {
// Broadcast message to all connected clients
const broadcastMessage = createEchoRequest({
PAN: '1234567890123456',
PROC_CODE: '000000'
});
const sentCount = server.broadcastJson(broadcastMessage);
console.log(`Broadcasted message to ${sentCount} clients`);
return createEchoResponse(message);
}
}
);🏗️ NestJS Integration
Basic NestJS Service
// iso8583.service.ts
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import {
createJsonServer,
createEchoRequest,
createEchoResponse,
createFinancialRequest,
createFinancialResponse,
CommonMTI,
ResponseCode,
type Iso8583JsonMessage,
type Iso8583JsonResponse
} from '@iso8583/toolkit';
@Injectable()
export class Iso8583Service implements OnModuleInit, OnModuleDestroy {
private server: any;
async onModuleInit() {
this.server = createJsonServer(
{ port: 5000, enableJsonMode: true },
{
onMessage: this.handleMessage.bind(this),
onConnect: this.handleConnect.bind(this),
onDisconnect: this.handleDisconnect.bind(this),
onError: this.handleError.bind(this)
}
);
await this.server.start();
console.log('ISO8583 server started on port 5000');
}
async onModuleDestroy() {
if (this.server) {
await this.server.stop();
}
}
private async handleMessage(
message: Iso8583JsonMessage,
socket: any
): Promise<Iso8583JsonResponse | void> {
console.log('Received message:', message.mti);
switch (message.mti) {
case CommonMTI.ECHO_REQUEST:
return createEchoResponse(message);
case CommonMTI.FINANCIAL_REQUEST:
return await this.processFinancialTransaction(message);
default:
return createEchoResponse(message, {
RESPONSE_CODE: ResponseCode.INVALID_MTI
});
}
}
private async processFinancialTransaction(
message: Iso8583JsonMessage
): Promise<Iso8583JsonResponse> {
// Implement your business logic here
const isApproved = await this.validateTransaction(message);
return createFinancialResponse(
message,
isApproved ? ResponseCode.APPROVED : ResponseCode.DECLINED
);
}
private handleConnect(socket: any) {
console.log('Client connected:', socket.remoteAddress);
}
private handleDisconnect(socket: any) {
console.log('Client disconnected:', socket.remoteAddress);
}
private handleError(error: Error, socket?: any) {
console.error('ISO8583 server error:', error);
}
// Public methods for external use
getStats() {
return this.server.getStats();
}
getConnections() {
return this.server.getConnections();
}
broadcastMessage(message: Iso8583JsonMessage) {
return this.server.broadcastJson(message);
}
}NestJS Module
// iso8583.module.ts
import { Module } from '@nestjs/common';
import { Iso8583Service } from './iso8583.service';
import { Iso8583Controller } from './iso8583.controller';
@Module({
providers: [Iso8583Service],
controllers: [Iso8583Controller],
exports: [Iso8583Service]
})
export class Iso8583Module {}NestJS Controller
// iso8583.controller.ts
import { Controller, Get, Post, Body } from '@nestjs/common';
import { Iso8583Service } from './iso8583.service';
import { createEchoRequest } from '@iso8583/toolkit';
@Controller('iso8583')
export class Iso8583Controller {
constructor(private readonly iso8583Service: Iso8583Service) {}
@Get('stats')
getStats() {
return this.iso8583Service.getStats();
}
@Get('connections')
getConnections() {
return this.iso8583Service.getConnections();
}
@Post('broadcast')
broadcastMessage(@Body() message: any) {
const echoMessage = createEchoRequest(message);
const sentCount = this.iso8583Service.broadcastMessage(echoMessage);
return { sentCount, message: echoMessage };
}
}NestJS Application
// app.module.ts
import { Module } from '@nestjs/common';
import { Iso8583Module } from './iso8583/iso8583.module';
@Module({
imports: [Iso8583Module],
})
export class AppModule {}// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
console.log('NestJS application started on port 3000');
}
bootstrap();📋 API Reference
Core Classes
Iso8583Server
Main server class for handling TCP connections and ISO8583 messages.
const server = new Iso8583Server(config, handlers, parser, builder);Methods:
start(): Start the serverstop(): Stop the servergetStats(): Get server statisticsgetConnections(): Get active connectionsbroadcast(mti, fields): Broadcast message to all clientsbroadcastJson(jsonMessage): Broadcast JSON message to all clientssendMessage(socket, mti, fields): Send message to specific clientsendJsonMessage(socket, jsonMessage): Send JSON message to specific clientcleanup(): Cleanup resourcesdestroy(): Destroy server and cleanup resources
Iso8583TlsServer
Main server class for handling TCP and TLS connections and ISO8583 messages.
const tlsServer = new Iso8583TlsServer(config, handlers, parser, builder);Methods:
start(): Start both TCP and TLS serversstop(): Stop both serversgetStats(): Get server statistics including secure connectionsgetConnections(): Get all active connectionsgetSecureConnections(): Get only TLS connectionsbroadcast(mti, fields): Broadcast message to all clientsbroadcastJson(jsonMessage): Broadcast JSON message to all clientssendMessage(socket, mti, fields): Send message to specific clientsendJsonMessage(socket, jsonMessage): Send JSON message to specific clientisTlsEnabled(): Check if TLS is enabledgetTlsConfig(): Get TLS configurationcleanup(): Cleanup resourcesdestroy(): Destroy server and cleanup resources
Iso8583Parser
Parse ISO8583 messages from Buffer format.
const parser = new Iso8583Parser(config);Methods:
parse(buffer): Parse Buffer to internal formatparseToJson(buffer): Parse Buffer to JSON format (JSON mode only)validate(message): Validate parsed messageenableJsonMode(): Enable JSON mode after construction
Iso8583Builder
Build ISO8583 messages to Buffer format.
const builder = new Iso8583Builder(config);Methods:
build(mti, fields): Build message from internal formatbuildFromJson(jsonMessage): Build message from JSON (JSON mode only)validate(mti, fields): Validate message before buildingvalidateJson(jsonMessage): Validate JSON message (JSON mode only)createResponse(request, responseMti, responseFields): Create response from requestcreateResponseFromJson(request, responseMti, responseFields, responseCode): Create JSON responseenableJsonMode(): Enable JSON mode after construction
Iso8583JsonConverter
Convert between JSON and internal ISO8583 formats.
const converter = new Iso8583JsonConverter(fieldDefinitions);Methods:
toIso8583(jsonMessage): Convert JSON to internal formattoJson(isoMessage): Convert internal format to JSONtoIso8583Response(jsonResponse): Convert JSON response to internal formattoJsonResponse(isoResponse): Convert internal response to JSONvalidateJsonMessage(jsonMessage): Validate JSON messagecreateResponse(request, responseMti, responseFields, responseCode): Create JSON response
Helper Functions
Message Creation
import {
createEchoRequest,
createEchoResponse,
createFinancialRequest,
createFinancialResponse
} from '@iso8583/toolkit';
// Create echo request
const echoRequest = createEchoRequest({
PAN: '1234567890123456',
PROC_CODE: '000000'
});
// Create echo response
const echoResponse = createEchoResponse(echoRequest);
// Create financial request
const financialRequest = createFinancialRequest({
PAN: '1234567890123456',
PROC_CODE: '000000',
AMOUNT: '000000012500'
});
// Create financial response
const financialResponse = createFinancialResponse(
financialRequest,
ResponseCode.APPROVED
);Utility Functions
import {
createDefaultParser,
createDefaultBuilder,
createDefaultServer,
createJsonParser,
createJsonBuilder,
createJsonServer,
createJsonConverter
} from '@iso8583/toolkit';
// Traditional mode
const parser = createDefaultParser();
const builder = createDefaultBuilder();
const server = createDefaultServer(config, handlers);
// JSON mode
const jsonParser = createJsonParser();
const jsonBuilder = createJsonBuilder();
const jsonServer = createJsonServer(config, handlers);
const converter = createJsonConverter();Error Classes
import {
Iso8583Error,
Iso8583ValidationError,
Iso8583ParsingError,
Iso8583BuildingError
} from '@iso8583/toolkit';
// Base error class
Iso8583Error
// Specific error types
Iso8583ValidationError // For validation errors
Iso8583ParsingError // For parsing errors
Iso8583BuildingError // For building errorsConstants
import {
CommonMTI,
ResponseCode,
FieldType,
DEFAULT_FIELD_DEFINITIONS
} from '@iso8583/toolkit';
// Common MTIs
CommonMTI.ECHO_REQUEST // '0800'
CommonMTI.ECHO_RESPONSE // '0810'
CommonMTI.FINANCIAL_REQUEST // '0200'
CommonMTI.FINANCIAL_RESPONSE // '0210'
// Response codes
ResponseCode.APPROVED // '00'
ResponseCode.DECLINED // '05'
ResponseCode.INSUFFICIENT_FUNDS // '51'
ResponseCode.SYSTEM_ERROR // '96'
// Field types
FieldType.NUMERIC
FieldType.ALPHA
FieldType.ALPHANUMERIC
FieldType.BINARY
FieldType.LLVAR
FieldType.LLLVAR
FieldType.DATE
FieldType.TIME
FieldType.AMOUNT🧪 Testing
# Run tests
npm test
# Run tests in watch mode
npm run test:watch
# Run tests with coverage
npm run test:coverage
# Run specific test files
npm test -- test/tls-server.test.ts
npm test -- test/critical-bug-fixes.test.ts🏗️ Development
# Install dependencies
npm install
# Build the project
npm run build
# Run in development mode (watch for changes)
npm run dev
# Clean build artifacts
npm run clean📁 Project Structure
iso8583-toolkit/
├── src/
│ ├── index.ts # Main entry point
│ ├── types.ts # TypeScript type definitions
│ ├── parser.ts # ISO8583 message parser
│ ├── builder.ts # ISO8583 message builder
│ ├── server.ts # TCP server implementation
│ ├── tls-server.ts # TLS server implementation
│ └── json-converter.ts # JSON conversion utilities
├── test/
│ ├── server.test.ts # TCP server test suite
│ ├── tls-server.test.ts # TLS server test suite
│ ├── critical-bug-fixes.test.ts # Critical bug fixes verification
│ ├── parser.test.ts # Parser test suite
│ ├── builder.test.ts # Builder test suite
│ ├── json-converter.test.ts # JSON converter test suite
│ └── certs/ # Test certificates
├── dist/ # Built output
├── package.json # Package configuration
├── tsconfig.json # TypeScript configuration
├── tsup.config.ts # Build configuration
├── vitest.config.ts # Test configuration
├── BUGS_AND_IMPROVEMENTS.md # Bug analysis and improvements
└── README.md # This file🤝 Contributing
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🆘 Support
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Documentation: GitHub Wiki
📈 Changelog
v1.1.0
- TLS Server Support: Added full TLS server implementation with configurable certificates
- Critical Bug Fixes: Fixed bitmap generation crash with empty fields and LLLVAR field handling
- Configuration Validation: Added automatic validation of field definitions and configuration
- Error Handling: Implemented standardized error classes for better debugging
- Resource Management: Added proper cleanup and resource management methods
- Enhanced Testing: Comprehensive test suite including edge cases and TLS functionality
- Documentation: Updated documentation with TLS examples and error handling
v1.0.0
- Initial release
- Traditional ISO8583 mode support
- JSON mode support for developer-friendly API
- TCP server implementation
- Message parsing and building
- Field validation
- Helper functions for common message types
- NestJS integration examples
- Comprehensive test suite
- TypeScript support with full type safety
