TΓΌbingen β Reconciliation Engine is a backend-focused banking reconciliation service built with Java Spring Boot, designed to match and reconcile financial transactions between two independent sources (e.g. Core Banking vs Switch / Payment Provider).
The system demonstrates real-world banking reconciliation logic, using an efficient Two-Pointer algorithm to achieve linear-time matching on large, ordered transaction datasets.
This project is intentionally designed as a clean, production-style backend service, suitable for banking, fintech, and payment systems.
In real banking systems, transactions are processed by multiple parties:
- Core Banking System
- Payment Switch (ATM, EDC, QR, ISO8583)
- External Clearing or Settlement Providers
Due to network delays, retries, duplicates, or partial failures, transaction records between systems often do not perfectly align.
Reconciliation ensures:
- Financial consistency
- Accurate settlement
- Early detection of missing, duplicate, or mismatched transactions
This project simulates that exact scenario in a simplified but realistic way.
- Reconcile transactions between Source A and Source B
- Efficient Two-Pointer matching algorithm (O(n))
- Tolerance-based matching (timestamp & amount)
- Detection of:
- Matched transactions
- Missing transactions
- Amount mismatches
- Duplicates
- Batch-style reconciliation jobs
- RESTful APIs for ingestion, execution, and reporting
- Backend-first design (frontend optional)
ββββββββββββββββββββββββββ
β Client β
β (curl / Postman / UI) β
βββββββββββββ¬βββββββββββββ
β REST
βΌ
ββββββββββββββββββββββββββ
β Spring Boot API β
β Tubingen Recon Engine β
βββββββββββββ¬βββββββββββββ
β
βββββββββββββββββββββββββΌββββββββββββββββββββββββ
β β β
βΌ βΌ βΌ
ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ
β Ingest β β Ingest β β Ingest β
β Service β β Reconcil β β Reconcil β
β (CSV / Logs) β β (Two Ptrs) β β (CSV / Logs) β
ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ
β β β
βΌ βΌ βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββ
β PostgreSQL / H2 β
β transaction_record | recon_job | β
β recon_match | audit β
ββββββββββββββββββββββββββββββββββββββββββββββββββ
Represents a normalized transaction from any source.
Key attributes:
- Source (A or B)
- Transaction ID / STAN
- Amount & currency
- Timestamp (UTC)
- Raw payload (for audit)
Represents a single reconciliation execution between two datasets.
Stores the outcome of matching:
- MATCHED
- MISSING_IN_SOURCE_A
- MISSING_IN_SOURCE_B
- AMOUNT_MISMATCH
- DUPLICATE
The engine uses a Hybrid Approach combining HashMap indexing and Two-Pointer technique.
Before matching, each source list is scanned for duplicate Transaction IDs.
- If a duplicate ID is found within the same source, it is immediately marked as
DUPLICATE. - These records are removed from the matching pool to prevent false positives.
- Source B is indexed into a HashMap for O(1) lookup.
- Source A is iterated to find exact ID matches in the map.
- Matches are recorded as
MATCHEDorAMOUNT_MISMATCH. - Unmatched records are saved for the next pass.
- Remaining records from both sources are sorted by timestamp.
- A Two-Pointer algorithm traverses both lists to find matches based on:
- Time proximity (within configurable tolerance)
- Amount equality (within configurable tolerance)
- Records that remain unmatched are marked as
MISSING_IN_SOURCE_AorMISSING_IN_SOURCE_B.
This approach ensures that exact ID matches are prioritized over fuzzy matches, which is critical for accuracy in banking systems.
Upload transactions for a specific source.
- Endpoint:
POST /api/ingest - Body:
List<TransactionRecord>
Creates and runs a reconciliation job with configurable tolerances.
- Endpoint:
POST /api/reconcile/start - Body:
ReconciliationRequest(specifies job parameters)
Retrieves the status and summary of a reconciliation job.
- Endpoint:
GET /api/recon/{jobId}/status
Retrieves the detailed results (matches, mismatches, missing) for a job.
- Endpoint:
GET /api/recon/{jobId}/results
curl -X POST http://localhost:8080/api/ingest \
-H "Content-Type: application/json" \
-d '[
{
"source": "A",
"transactionId": "TXN1001",
"amount": 150.00,
"timestamp": "2023-10-27T10:00:00Z"
},
{
"source": "A",
"transactionId": "TXN1002",
"amount": 200.50,
"timestamp": "2023-10-27T10:05:00Z"
}
]'curl -X POST http://localhost:8080/api/ingest \
-H "Content-Type: application/json" \
-d '[
{
"source": "B",
"transactionId": "TXN1001",
"amount": 150.00,
"timestamp": "2025-10-27T10:00:05Z"
},
{
"source": "B",
"transactionId": "TXN1003",
"amount": 300.00,
"timestamp": "2025-10-27T10:10:00Z"
}
]'This triggers the two-pointer algorithm.
curl -X POST http://localhost:8080/api/reconcile/start \
-H "Content-Type: application/json" \
-d '{
"jobName": "Daily Reconciliation",
"sourceA": "A",
"sourceB": "B",
"timestampToleranceMillis": 10000,
"amountTolerance": 0.01
}'This will return a
jobId. Let's assume it's123e4567-e89b-12d3-a456-426614174000.
curl http://localhost:8080/api/recon/123e4567-e89b-12d3-a456-426614174000/statusExpected Response:
{
"jobId": "123e4567-e89b-12d3-a456-426614174000",
"status": "COMPLETED",
"summary": {
"matched": 1,
"missingInSourceB": 1,
"missingInSourceA": 1,
"amountMismatch": 0,
"duplicates": 0
}
}curl http://localhost:8080/api/recon/123e4567-e89b-12d3-a456-426614174000/resultsExpected Response:
[
{
"type": "MATCHED",
"transactionA": { "transactionId": "TXN1001", "amount": 150.00},
"transactionB": { "transactionId": "TXN1001", "amount": 150.00}
},
{
"type": "MISSING_IN_SOURCE_B",
"transactionA": { "transactionId": "TXN1002"},
"transactionB": null
},
{
"type": "MISSING_IN_SOURCE_A",
"transactionA": null,
"transactionB": { "transactionId": "TXN1003"}
}
]- Java 17+
- Spring Boot
- Spring Web
- Spring Data JPA
- H2 (local) / PostgreSQL (production)
- Maven
- Docker (optional)
tubingen-reconciliation-engine/
βββ src/main/java/com/pswied/tubingen/
β βββ controller/
β βββ service/
β βββ model/
β βββ repository/
β βββ config/
βββ src/test/
βββ sample-data/
βββ docs/
βββ Dockerfile
βββ pom.xml
βββ README.md
./mvnw spring-boot:run
http://localhost:8080
- Designed to integrate with JWT / OAuth2
- Input validation & file size limits
- Audit logging for traceability
- Stateless API suitable for horizontal scaling
- CSV upload support (Source A / Source B)
- Async job execution
- React dashboard (optional)
- ISO8583 / ISO20022 mock adapters
- Kubernetes deployment example
- Metrics & monitoring (Actuator + Prometheus)
Slamet Widodo (Wied)
Software Engineer / Engineering Manager
Banking Β· Middleware Β· Distributed Systems
This project is provided for educational and portfolio purposes.