A robust Node.js microservice for payment processing simulation built with Express.js, TypeScript, and comprehensive testing. This service demonstrates modern microservice architecture patterns including error handling, validation, logging, and asynchronous processing.
- 🚀 RESTful API with Express.js and TypeScript
- 🔒 Input Validation using Joi schemas
- 📊 Comprehensive Error Handling with custom error classes
- 📝 Structured Logging with Winston
- 💾 Dual Persistence (in-memory and file-based JSON)
- ⚡ Asynchronous Payment Processing with realistic simulation
- 🧪 Full Test Coverage with Jest and Supertest
- 📖 API Documentation with Swagger/OpenAPI
- 🛡️ Security Middleware with Helmet and CORS
- 🔄 Graceful Shutdown handling
- 📡 Event-Driven Architecture with mock event bus for service communication
The payment microservice includes a comprehensive event-driven architecture that emits events for all payment lifecycle changes. This enables loose coupling between services and supports real-time notifications, analytics, and integrations.
The system emits the following payment events:
payment.created- When a new payment is createdpayment.updated- When payment data is updatedpayment.processing- When payment processing beginspayment.completed- When a payment is successfully completedpayment.failed- When a payment failspayment.cancelled- When a payment is cancelledpayment.refunded- When a payment is refundedpayment.deleted- When a payment is deleted
Each event follows a consistent structure:
interface BasePaymentEvent {
eventId: string; // Unique event identifier
eventType: PaymentEventType; // Type of event
timestamp: Date; // When the event occurred
paymentId: string; // ID of the related payment
version: string; // Event schema version
source: string; // Source service identifier
data: { // Event-specific payload
// ... event-specific data
};
}The service includes a mock event bus that simulates real message broker functionality:
- Event Publishing: Automatically emits events for payment lifecycle changes
- Event Subscription: Supports multiple subscribers per event type
- Error Handling: Gracefully handles subscriber errors with retry logic
- Event History: Maintains configurable event history for debugging
- External Service Simulation: Includes sample handlers for analytics, notifications, accounting, fraud detection, and monitoring services
The mock event bus includes handlers that simulate how external services would consume payment events:
- Analytics Service: Tracks payment metrics and statistics
- Notification Service: Sends notifications to customers and merchants
- Accounting Service: Updates financial records and reconciliation
- Fraud Detection Service: Analyzes payments for potential fraud
- Monitoring Service: Tracks system health and performance metrics
const eventBus = getEventBus({
enableHistory: true, // Track event history
maxHistorySize: 1000, // Maximum events to keep
enableLogging: true, // Log event processing
retryAttempts: 3, // Retry failed handlers
retryDelayMs: 1000 // Delay between retries
});// Subscribe to payment completion events
const subscriptionId = eventBus.subscribe(
PaymentEventType.PAYMENT_COMPLETED,
async (event) => {
console.log(`Payment ${event.paymentId} completed!`);
// Process the event (send notification, update analytics, etc.)
}
);
// Create a payment (automatically emits payment.created event)
const payment = await paymentService.createPayment(paymentRequest);
// Update payment status (automatically emits payment.completed event)
await paymentService.updatePayment(payment.id, { status: 'completed' });
// Unsubscribe when done
eventBus.unsubscribe(PaymentEventType.PAYMENT_COMPLETED, subscriptionId);In a production environment, the mock event bus can be replaced with real message brokers:
- Apache Kafka - High-throughput distributed streaming
- RabbitMQ - Reliable message queuing
- AWS SQS/SNS - Cloud-native messaging
- Google Pub/Sub - Scalable messaging service
- Azure Service Bus - Enterprise messaging
Run the event bus demonstration:
npm run build
node dist/demo/eventBusDemo.jsThis script shows:
- Event emission during payment lifecycle
- Multiple event handlers processing events
- Event history tracking
- Custom event subscriptions
- Error handling in event processing
src/
├── types/ # TypeScript interfaces and types
├── services/ # Business logic and persistence
├── routes/ # Express route handlers
├── middleware/ # Custom middleware (validation, error handling)
├── utils/ # Utility functions (validation, logging)
├── config/ # Configuration files (Swagger)
├── __tests__/ # Test files
└── data/ # JSON persistence files
- Runtime: Node.js 18+
- Framework: Express.js
- Language: TypeScript
- Validation: Joi
- Testing: Jest + Supertest
- Logging: Winston
- Documentation: Swagger/OpenAPI
- Security: Helmet, CORS
- Node.js 18.0.0 or higher
- npm or yarn
-
Clone the repository
git clone <repository-url> cd payment-microservice
-
Install dependencies
npm install
-
Build the project
npm run build
-
Start the server
npm start
The server will start on
http://localhost:3667
For development with hot reloading:
npm run devRun the test suite:
# Run all tests
npm test
# Run tests in watch mode
npm run test:watch
# Run tests with coverage
npm run test:coveragehttp://localhost:3667/api/v1
Visit http://localhost:3667/api-docs for the interactive Swagger UI documentation.
GET /health
POST /api/v1/payments
Content-Type: application/json
{
"amount": 99.99,
"currency": "USD",
"paymentMethod": "credit_card",
"description": "Payment for services",
"customerId": "123e4567-e89b-12d3-a456-426614174000",
"merchantId": "987fcdeb-51a2-43d1-b789-123456789abc",
"metadata": {
"orderId": "ORD-123"
}
}Response:
{
"success": true,
"data": {
"id": "payment-uuid",
"amount": 99.99,
"currency": "USD",
"status": "pending",
"paymentMethod": "credit_card",
"description": "Payment for services",
"customerId": "123e4567-e89b-12d3-a456-426614174000",
"merchantId": "987fcdeb-51a2-43d1-b789-123456789abc",
"metadata": {
"orderId": "ORD-123"
},
"createdAt": "2024-01-01T12:00:00.000Z",
"updatedAt": "2024-01-01T12:00:00.000Z"
},
"timestamp": "2024-01-01T12:00:00.000Z"
}GET /api/v1/payments/{id}GET /api/v1/payments?page=1&limit=10&status=pending¤cy=USD&customerId=123e4567-e89b-12d3-a456-426614174000PUT /api/v1/payments/{id}
Content-Type: application/json
{
"status": "processing",
"transactionId": "TXN-123456"
}PATCH /api/v1/payments/{id}
Content-Type: application/json
{
"status": "completed"
}DELETE /api/v1/payments/{id}GET /api/v1/payments/statsResponse:
{
"success": true,
"data": {
"totalPayments": 150,
"totalAmount": 25000.50,
"averageAmount": 166.67,
"statusCounts": {
"pending": 10,
"processing": 5,
"completed": 120,
"failed": 10,
"cancelled": 3,
"refunded": 2
},
"currencyCounts": {
"USD": 100,
"EUR": 30,
"GBP": 20
},
"methodCounts": {
"credit_card": 80,
"debit_card": 40,
"bank_transfer": 20,
"digital_wallet": 8,
"cryptocurrency": 2
}
},
"timestamp": "2024-01-01T12:00:00.000Z"
}The service simulates realistic payment processing with:
- Asynchronous Processing: Payments are processed in the background
- Status Transitions: Enforced state machine for payment status changes
- Failure Simulation: 15% failure rate with various failure scenarios
- Processing Delays: 1-4 second random delays to simulate real processing
- Business Rules: Amount limits, currency validation, duplicate detection
pending → processing → completed
↓ ↓ ↓
cancelled failed refunded
credit_carddebit_cardbank_transferdigital_walletcryptocurrency
- USD, EUR, GBP, JPY, CAD, AUD
The service provides comprehensive error handling with standardized error responses:
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed: [details]",
"details": {
"field": "amount",
"message": "Amount must be a positive number"
}
},
"timestamp": "2024-01-01T12:00:00.000Z"
}VALIDATION_ERROR(400): Input validation failedPAYMENT_NOT_FOUND(404): Payment with given ID not foundINVALID_STATUS_TRANSITION(400): Invalid payment status changePAYMENT_PROCESSING_ERROR(422): Payment processing failedINSUFFICIENT_FUNDS(422): Insufficient funds for paymentDUPLICATE_PAYMENT(409): Payment with same transaction ID existsINTERNAL_SERVER_ERROR(500): Unexpected server error
| Variable | Default | Description |
|---|---|---|
PORT |
3667 | Server port |
HOST |
0.0.0.0 | Server host |
NODE_ENV |
development | Environment |
LOG_LEVEL |
info | Logging level |
API_BASE_URL |
http://localhost:3667 | Base URL for API docs |
CORS_ORIGIN |
* | CORS origin |
Logs are written to:
logs/combined.log- All logslogs/error.log- Error logs only- Console output (in development)
src/__tests__/
├── setup.ts # Test configuration
├── services/
│ └── paymentService.test.ts # Service layer tests
└── routes/
└── paymentRoutes.test.ts # API endpoint tests
# Run all tests
npm test
# Run tests in watch mode
npm run test:watch
# Run tests with coverage report
npm run test:coverage
# Run specific test file
npm test paymentService.test.tsThe test suite covers:
- ✅ Payment creation and validation
- ✅ Payment retrieval and filtering
- ✅ Payment updates and status transitions
- ✅ Payment deletion rules
- ✅ Error handling and edge cases
- ✅ API endpoint integration tests
- ✅ Business logic validation
| Script | Description |
|---|---|
npm run build |
Compile TypeScript to JavaScript |
npm start |
Start the production server |
npm run dev |
Start development server with hot reload |
npm test |
Run the test suite |
npm run test:watch |
Run tests in watch mode |
npm run test:coverage |
Run tests with coverage report |
npm run lint |
Run ESLint |
npm run lint:fix |
Fix ESLint errors automatically |
The service supports two persistence modes:
- Fast and ephemeral
- Data lost on restart
- Used automatically in test environment
- Persistent across restarts
- Data stored in
./data/payments.json - Automatic backup and recovery
- Helmet.js: Security headers
- CORS: Cross-origin resource sharing
- Input Validation: Joi schema validation
- Error Sanitization: Safe error responses
- Rate Limiting: Built-in Express rate limiting
- Async Processing: Non-blocking payment processing
- Pagination: Efficient data retrieval
- Logging: Structured logging with rotation
- Memory Management: Efficient in-memory storage
- Error Recovery: Graceful error handling
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
For support and questions:
- Create an issue in the repository
Built with ❤️ using Node.js, Express, and TypeScript