Skip to content

Simpaisa API Codebase Audit Report

Date: 2026-04-02 Scope: Wallet (Collection/Pay-In), Cardbackend (Card Payments), Remittance (Cross-Border), Disbursement (Pay-Out) Source: Bitbucket repos (simpaisa1 workspace) — actual source code review Reviewer: CDO Review Tech Stack: Java 17, Spring Boot 3.x, MySQL 8, Redis, Apache Kafka, HashiCorp Vault, AWS


Executive Summary

This audit examines the actual source code of Simpaisa's four core API families, superseding the earlier spec-only review (2026-03-31). The findings are significantly more severe than the spec audit suggested.

The architecture is fundamentally a microservices pattern — Spring Boot services communicating via Kafka, with MySQL for persistence and Redis for caching. The individual services are functional and process live payments. But the codebase has critical security vulnerabilities, no test coverage, no Spring Security anywhere, financial operations that lack database transaction management, and production credentials committed to Git across multiple repos.

Headline Numbers

Severity Count
P0 — Immediate Security Risk 18
P1 — Financial Integrity Risk 10
P2 — Operational Risk 18
P3 — Code Quality 14
Total 60

The Five Worst Findings

  1. Hardcoded credentials in source code — Twilio account SID/auth token (wallet), partner API username/password/system ID (remittance), MySQL/SMTP/JazzCash/Easypaisa/1Link credentials in disbursement properties files, all committed to Git
  2. SSL certificate validation disabled — 7 separate HTTP client methods in remittance accept all certificates, enabling man-in-the-middle attacks on partner bank connections
  3. AES-ECB encryption — Both wallet and card services use Cipher.getInstance("AES") which defaults to insecure ECB mode
  4. No @Transactional on financial operations — Remittance balance deductions, state updates, and settlement insertions are not wrapped in database transactions
  5. Inverted validation booleans — Card service's isValidEmail() returns true for invalid emails, isValidCountry() returns true for invalid countries

Severity Definitions

Severity Meaning
P0 — Immediate Active security vulnerability, credential exposure, or data breach risk. Requires remediation within days.
P1 — Financial Could cause incorrect money movement, duplicate payments, or settlement errors. Requires remediation within 2 weeks.
P2 — Operational Increases incident severity, slows debugging, or creates deployment risk. Remediate within 1–2 months.
P3 — Quality Technical debt that increases maintenance cost. Address during normal sprint work.

Cross-Cutting Findings (All Services)

These issues appear in every service audited.

CX-01: No Spring Security in Any Service

Severity: P0 Affects: Wallet, Card, Remittance

None of the three API families include spring-boot-starter-security. There is: - No authentication filter chain - No CSRF protection - No security headers (Content-Security-Policy, X-Frame-Options, etc.) - No authorisation framework

Authentication is handled ad-hoc: merchant ID lookup from request body (wallet), client-id header lookup (card), or nothing at all (remittance consumer).

Recommendation: Add Spring Security with a custom SecurityFilterChain. Implement API key authentication at minimum, with RSA signature verification as a second factor for payment-initiating endpoints.


CX-02: @CrossOrigin(origins = "*") on All Controllers

Severity: P0 Affects: Wallet, Card

Both the wallet and card controllers have unrestricted CORS. For server-to-server payment APIs, CORS should either be disabled entirely or restricted to known merchant domains.

File references: - wallet/.../controller/MainController.java - cardbackend/.../controller/PaymentController.java (and all other controllers)


CX-03: e.printStackTrace() Instead of Proper Logging

Severity: P2 Affects: Wallet (all catch blocks), Card (all catch blocks), Remittance (40+ instances)

Every catch block across all three codebases uses e.printStackTrace() instead of the logging framework. Stack traces go to stdout/stderr, bypass Log4j2 appenders, and are lost in containerised environments.

Recommendation: Global find-and-replace e.printStackTrace() with logger.error("Context message", e). Add a Checkstyle/PMD rule to prevent regression.


CX-04: HashMap<String, Object> Instead of Typed DTOs

Severity: P2 Affects: Wallet, Card, Remittance

All three services accept HashMap<String, Object> as request bodies. There are no typed request/response DTOs anywhere. This means: - No compile-time type safety - No automatic OpenAPI schema generation - No Bean Validation (@Valid, @NotNull, @Size) - Field names scattered as string constants


CX-05: No API Versioning

Severity: P1 Affects: Wallet (/wallets/transaction/*), Card (/payments, /capture), Remittance (/kafka-send)

No URL versioning, no header versioning, no content negotiation. Any breaking change affects all consumers simultaneously.


CX-06: No Rate Limiting

Severity: P1 Affects: Wallet, Card, Remittance

No rate limiting or throttling exists in any service. No Bucket4j, no Spring Cloud Gateway rate limiter, no custom implementation. Combined with the lack of authentication, these APIs are fully open to abuse.


CX-07: No OpenAPI/Swagger Documentation

Severity: P2 Affects: Wallet, Card, Remittance

No springdoc-openapi or springfox dependency in any service. No static OpenAPI YAML/JSON specs. No machine-readable API contract exists.


CX-08: No Database Migration Tooling

Severity: P2 Affects: Wallet, Card, Remittance

No Flyway or Liquibase in any service. Schema changes are unversioned and managed externally via stored procedures. Deployments are fragile and non-reproducible.


CX-09: No Global Exception Handler

Severity: P2 Affects: Wallet, Card, Remittance

No @ControllerAdvice or @ExceptionHandler in any service. Each controller has its own try-catch with inconsistent behaviour — some return 200 OK with empty body, some return 401, some return 201.


CX-10: No Correlation IDs or Distributed Tracing

Severity: P2 Affects: Wallet, Card, Remittance

No MDC-based request tracing, no X-Request-Id propagation into log context, no Spring Cloud Sleuth or Micrometer Tracing. For a payment platform processing transactions across multiple microservices and partner banks, end-to-end tracing is essential.


CX-11: Redis Used Everywhere (Not SurrealDB)

Severity: P3 (Note) Affects: Wallet (3 Redis instances), Card (2 Redis instances), Remittance (1 Redis instance)

All three services use Redis (Jedis/Lettuce/Redisson) for caching, locking, and session management. The organisation's stated direction of SurrealDB (in-memory) has not been adopted in any production service.


CX-12: No Test Suite

Severity: P2 Affects: Wallet, Card, Remittance

Zero unit tests, zero integration tests across all three codebases. No src/test directories with any test files. No test dependencies (JUnit, Mockito) in active use.


Wallet (Collection/Pay-In) — Specific Findings

Repo: simpaisa1/wallet Purpose: Mobile wallet pay-in (Easypaisa, JazzCash, Alfa, HBL Konnect, JSBL Zindagi)

W-01: Hardcoded Twilio Credentials

Severity: P0 — IMMEDIATE

Live Twilio credentials committed to source control:

File: wallet/.../utility/Utility.java (lines 395-397)
ACCOUNT_SID = "AC37679a043db3b432320f26dfb8f42d1a"
AUTH_TOKEN = "0fe360cd24d6767063f6fc574e84d6d8"
FROM_NUMBER = "+18587629352"

Action: Rotate these credentials immediately. Move to AWS Parameter Store or Vault. Add .gitignore patterns and pre-commit hooks to prevent future credential commits.


W-02: PEM Private Keys in Source Control

Severity: P0

Private key files checked into src/main/resources/: - hbl_konnect_prv_pcks8.pem - private_key_pkcs8.pem

Action: Remove from repo history (BFG Repo-Cleaner or git filter-branch). Rotate the keys. Move to Vault.


W-03: AES-ECB Encryption

Severity: P0

AESEncryptionDecryption.java uses Cipher.getInstance("AES") which defaults to AES/ECB/PKCS5Padding. ECB mode is deterministic — identical plaintext blocks produce identical ciphertext blocks, leaking data patterns.

Recommendation: Migrate to AES/GCM/NoPadding with random 12-byte IV per encryption operation.


W-04: Idempotency Fails Open

Severity: P1

The @IdempotentAnnonation aspect has a catch block (line 83-86) that falls through to pjp.proceed() on Redis failure. If Redis is down, idempotency is silently disabled and duplicate payments can be created.

Additionally, idempotency is optional — if Request-Id header is absent, the request proceeds without deduplication.


W-05: Silent Exception Swallowing

Severity: P1

Controller catch blocks catch Exception, call e.printStackTrace(), and return whatever is in the response variable (potentially empty) with HTTP 200 OK. Callers cannot distinguish success from failure.


W-06: No Webhook Retry or Signing

Severity: P1

Merchant postback (MerchantPostbackService) sends a single POST request. If the merchant's server is down, the postback is lost. No retry mechanism, no exponential backoff, no webhook signature for authenticity verification.

RabbitMQ queue constants exist (POSTBACK_QUEUE, POSTBACK_EXCHANGE) suggesting a queue-based approach was planned but never implemented.


W-07: Signature Logged to stdout

Severity: P2

SignatureValidation.validateSignature() calls System.out.println("request signature = " + signature) — leaking cryptographic signatures to stdout in production.


W-08: Three Separate Redis Instances

Severity: P3

The wallet service connects to three different Redis instances (primary, secondary, ternary) using three different client libraries (Jedis, Lettuce, Redisson). This creates operational complexity and triple the failure surface area.


Cardbackend (Card Payments) — Specific Findings

Repo: simpaisa1/cardbackend Purpose: Card payment processing via Alfalah MasterCard Gateway and Safepay

C-01: AES-ECB Encryption

Severity: P0 — IMMEDIATE

Same as W-03. AESEncryptionDecryption.java line 41: Cipher.getInstance("AES") defaults to insecure ECB mode. For a card payment service handling PAN data, this is a PCI-DSS blocker.


C-02: Inverted Validation Booleans

Severity: P0

Two critical validation methods return the opposite of what their names suggest:

// Utility.java line 639 — returns true when email is INVALID
public static boolean isValidEmail(String email) {
    return !EMAIL_PATTERN.matcher(email.trim()).matches();
}

// Utility.java line 632 — returns true when country is INVALID
public static boolean isValidCountry(String country) {
    return !COUNTRY_PATTERN.matcher(country.trim()).matches();
}

These are called in customerValidation() where isValidEmail() returning true triggers the "invalid email" error — meaning valid emails are rejected and invalid emails are accepted.


C-03: Unauthenticated Safepay Endpoints

Severity: P0

SafepayRedirectionController exposes /fetch-details and /redirection with zero authentication — no client-id check, no signature, no IP whitelist. Anyone can call these endpoints.


C-04: IP Security Filter Disabled

Severity: P0

IpSecurityFilter.doFilter() passes all requests through unconditionally. The validation logic is commented out. FilterConfig is also commented out. The filter exists but does nothing.


C-05: Raw PAN Through Application Layer

Severity: P1

AlfalahMasterCardThreeDService methods accept String cardNumber and String cvv as plain parameters. The application handles raw PAN data directly rather than using tokenised references from the acquirer gateway. This significantly increases PCI-DSS scope.

Card data is also stored in Redis (MASTERCARD_CARDDATA cache, 15-minute TTL) without confirmed TLS or ACL configuration.


C-06: InputValidationImpl.validatePaymentBody() Is a No-Op

Severity: P1

The payment body validation method returns an empty error set — it validates nothing. This means payment requests pass through without any field-level validation.

// InputValidationImpl.java lines 16-19
public Set<String> validatePaymentBody(Map<String, Object> body) {
    Set<String> errors = new HashSet<>();
    return errors;
}

C-07: Private Key Fetched Per Request

Severity: P2

SignatureUtil.getPemPrivateKeyVault() fetches from AWS Secrets Manager on every single request. No local caching of the key. This is both a performance bottleneck and a cost concern at scale.


C-08: Merchant Cache with Infinite TTL

Severity: P2

Merchant data is cached in Redis with no expiry. If a merchant is deactivated, the cache continues serving status=true indefinitely. No eviction mechanism exists.


C-09: Snowflake ID Generator Hardcoded workerId=1

Severity: P2

MainService.java line 62: long workerId = 1L. If multiple instances run (which they will in production), ID collisions are possible.


Remittance (Cross-Border) — Specific Findings

Repo: simpaisa1/sp-remittance-consumer + sp-remittance-scheduler-retry Purpose: Cross-border remittance processing (Pakistan, Bangladesh corridors) Partners: Bank of Asia, Faysal Bank, Trust Bank, 1Link

R-01: SSL Certificate Validation Disabled

Severity: P0 — IMMEDIATE

HttpClientService contains seven separate methods that create a TrustManager accepting all certificates and use NoopHostnameVerifier. This disables TLS verification on connections to partner banks.

File: sp-remittance-consumer/.../utility/HttpClientService.java
Methods: trustBankPostRequest(), postRequestTrustWithUrlConnection(),
         postRequestTrustWithHttpClient(), postRequestQueryParamWithHttpClient(),
         postRequestQueryParamWithConnection(), getRequestQueryParamWithHttpClient(),
         getRequestQueryParamWithConnection()

This means a network attacker between Simpaisa and any partner bank can intercept and modify remittance instructions (amounts, beneficiary accounts) in transit.

Action: Remove all trust-all patterns. Use proper CA certificates. If partners have self-signed certs, import them into a dedicated truststore.


R-02: Hardcoded Credentials in Source

Severity: P0

RSAEncryptor.java contains hardcoded partner API credentials:

private static final String UserName = "ComPlex_API";
private static final String Password = "7NuPOqs6jDVQFH";
private static final String SystemId = "53A5342A-64DD-44DE-A19F-EDC68E5F9019";

Plus a hardcoded RSA public key in the same file.


R-03: MD5 Used for Security Tokens

Severity: P0

FaysalBankService.generateMD5Hash() creates authentication tokens using MD5: token = MD5(date + time + SECRET_KEY + stan). MD5 is cryptographically broken and should never be used for security-sensitive operations.


R-04: Hardcoded IP Address

Severity: P0

HttpClientService.callSoapApi() line 87 contains a hardcoded IP: http://13.215.165.235:8080. This is a direct reference to a specific server, bypassing DNS, load balancing, and any infrastructure flexibility.


R-05: No @Transactional on Financial Operations

Severity: P1 — CRITICAL FINANCIAL RISK

Balance deductions, remittance state updates, and settlement insertions are not wrapped in database transactions. No @Transactional annotation appears anywhere in the codebase. This means:

  • A balance deduction could succeed while the remittance state update fails
  • A settlement record could be inserted while the balance update fails
  • Partial states are possible and unrecoverable without manual intervention

R-06: double Used for Money

Severity: P1

Financial amounts use double throughout (merchantSettlement.getAmount(), remittance.getDeductedAmount()). Floating-point arithmetic causes rounding errors. For a service processing real cross-border remittances, this can cause settlement discrepancies.

DecimalFormat("#.##") is used to truncate amounts to 2 decimal places for postbacks, which silently loses precision.

Recommendation: Migrate all monetary fields to BigDecimal with explicit rounding modes.


R-07: finally { return } Anti-Pattern in Financial Methods

Severity: P1

Multiple financial-critical methods use finally { return response; } which suppresses exceptions:

  • BankOfAsiaService.initiate() — line 117
  • BankOfAsiaService.finalize() — line 191
  • FaysalBankService.initiate() — line 457
  • FaysalBankService.finalize() — line 590
  • TrustBankService.finalize() — line 231
  • RemittanceService.markRemittanceSuccess() — line 327
  • MerchantBalanceService.lockMerchantBalancePostRequest() — line 70

If an exception occurs during a remittance, it is silently swallowed and the method returns whatever partial state exists.


R-08: No Kafka Dead Letter Topic

Severity: P1

The Kafka error handler's exhaustion callback is empty (lines 38-40 of AcknowledgmentConfig.java). When all retries fail, the message is silently dropped. No Dead Letter Topic is configured. Failed remittance messages are lost.


R-09: No Idempotency on Kafka Consumer

Severity: P1

No deduplication check exists before processing a Kafka message. With Kafka's at-least-once delivery, the same remittance could be processed twice. The inConsumer flag is set after processing, creating a race window for duplicates.


R-10: Retry Scheduler Only Supports Bank of Asia

Severity: P1

The retry scheduler's ChannelFactory only has an implementation for Bank of Asia. All other partners (Faysal Bank, Trust Bank, 1Link) default to the Bank of Asia channel, meaning retry attempts for those partners will send requests to the wrong bank's API.


R-11: RSA/ECB/PKCS1Padding

Severity: P2

Utility.encryptRSA() uses RSA/ECB/PKCS1Padding which is vulnerable to Bleichenbacher padding oracle attacks. Should use RSA/ECB/OAEPWithSHA-256AndMGF1Padding.


R-12: PII in Logs

Severity: P2

Full request/response bodies including account numbers, beneficiary names, addresses, IBAN numbers, and phone numbers are logged at INFO level. SOAP XML requests contain complete customer PII. This violates data protection principles and increases the blast radius of any log access breach.


Disbursement (Pay-Out) — Specific Findings

Repos: simpaisa1/disbursement-gateway, simpaisa1/disbusrment-scheduler, simpaisa1/disbursement-scheduler-sunshine Purpose: Domestic PK payouts via 1Link IBFT, Easypaisa, JazzCash, HBL Note: Main API repo (disbursmentApi) was inaccessible via SSH — only gateway and schedulers audited

D-01: Production Credentials in Committed Properties

Severity: P0 — IMMEDIATE

application-prd.properties (even though commented out) contains real production credentials visible in version control: - MySQL: DisbursmentBuil / Msgg45G%%37y%7 - SMTP password: Bhdg456#@jks - JazzCash client ID/secret - Easypaisa client ID/secret - 1Link client ID/secret - SSL keystore password: changeit

Action: Rotate all credentials immediately. Purge from Git history with BFG Repo-Cleaner.


D-02: Chromedriver Binaries in Repo (23 MB)

Severity: P0

Both scheduler repos contain chromedriver (Linux) and chromedriver.exe (Windows) — 23 MB of binaries. These were used for a legacy Selenium hack to automate login to the Easypaisa web portal. The commented-out code in EasypaisaService.java (lines 491-531) reveals hardcoded Easypaisa MSISDN (923332227227) and PIN (02610).

The hack has been replaced by a proper REST login, but the binaries, Selenium dependencies (selenium-chrome-driver:4.12.0, selenium-api:3.141.59 — version mismatch), and commented-out code remain.

Action: Remove binaries, Selenium deps from pom.xml, and all commented-out browser automation code. Purge from Git history.


D-03: IP Filter Logic Bug

Severity: P0

IpFilter.java reads X-Forwarded-For into a variable (line 105) but then checks getRemoteAddr() instead (line 111). Behind a load balancer, getRemoteAddr() returns the load balancer's IP, not the client's. This means the IP whitelist either: - Blocks all legitimate traffic (if the LB IP isn't whitelisted), or - Allows all traffic through (if the LB IP is whitelisted)


D-04: Hardcoded IP Whitelist (30+ IPs)

Severity: P1

IpFilter.java (lines 30-91) contains ~30 hardcoded partner IPs (ByteDance, Sunshine, dLocal, Thunes, Gamify, BC Game, CODA, etc.). Adding a new partner requires a code change, rebuild, and redeployment.

Recommendation: Externalise to a database table or AWS Parameter Store.


D-05: Gateway on Spring Boot 2.2.6 / Java 8

Severity: P1

The disbursement-gateway runs Spring Boot 2.2.6 with Java 8 — both end-of-life. The gateway uses Netflix Zuul (also in maintenance mode). The schedulers run Spring Boot 3.x / Java 17, creating an inconsistent runtime environment.

Recommendation: Migrate to Spring Cloud Gateway on Spring Boot 3.x / Java 17.


D-06: HBL Pass-Through Proxy

Severity: P1

The gateway profile -dcb/-wa proxies requests directly to https://paymentapi.hbl.com/OpenAPIRest with no authentication, transformation, rate limiting, or logging of its own. Any request reaching the gateway on that path goes straight to HBL's production API.


D-07: AES-ECB Encryption (Again)

Severity: P0

AESEncryptionDecryption.java uses Cipher.getInstance("AES") — same insecure ECB mode as wallet and card services. Note: a separate EncryptionUtils.java correctly uses AES/CBC/PKCS5Padding, but the older utility is still in the codebase and may still be called.


D-08: RSA/ECB/PKCS1Padding

Severity: P2

RSAEncryption.java uses PKCS1 v1.5 padding, vulnerable to Bleichenbacher attacks. Should use OAEP padding.


D-09: No Row-Level Locking on Disbursement Fetch

Severity: P1

The scheduler fetches up to 1,000 published disbursements with a simple SELECT ... WHERE state = 'published' LIMIT 1000. There is no SELECT ... FOR UPDATE SKIP LOCKED. If multiple scheduler instances run, they will process the same disbursements simultaneously.


D-10: Sunshine Scheduler Is a Full Code Fork

Severity: P2

disbursement-scheduler-sunshine is a near-identical copy of disbusrment-scheduler with minor differences (no payout limit functionality). Every bug fix and feature must be applied to both repos. This should be consolidated into one repo with configuration-driven behaviour.


D-11: publickey.der in Source Control

Severity: P2

A 294-byte DER-encoded RSA public key (Easypaisa) is committed to both scheduler repos. Cryptographic material should be managed via Vault.


D-12: Hardcoded Merchant Exclusion in SQL

Severity: P3

DisbursementRepository.java line 25 contains m.merchantId not in (2000055,2000194) — a production workaround baked into source code.


D-13: Enum Bug — Monthly vs Daily

Severity: P3

Responses.MONTHLY_LIMIT_EXCEEDED("0098", "Daily-limit-exceeded") — the enum name says "Monthly" but the message says "Daily".


Remediation Roadmap

Phase 0 — Emergency (This Week)

# Action Services
1 Rotate hardcoded Twilio credentials (W-01) Wallet
2 Rotate hardcoded partner API credentials (R-02) Remittance
3 Rotate production credentials in properties files (D-01) Disbursement
4 Remove PEM keys from Git history (W-02) Wallet
5 Remove chromedriver binaries and Selenium code (D-02) Disbursement
6 Fix SSL certificate validation — remove trust-all (R-01) Remittance
7 Fix inverted validation booleans (C-02) Card
8 Authenticate Safepay endpoints (C-03) Card
9 Fix IP filter logic bug — use X-Forwarded-For (D-03) Disbursement
10 Purge all credentials from Git history (BFG Repo-Cleaner) All affected

Phase 1 — Security Hardening (Weeks 1–4)

# Action Services
11 Replace AES-ECB with AES-GCM (W-03, C-01, D-07) Wallet, Card, Disbursement
12 Add Spring Security with API key auth (CX-01) All
13 Remove @CrossOrigin(origins="*") (CX-02) Wallet, Card
14 Enable IP security filter (C-04) Card
15 Externalise IP whitelist to DB/config (D-04) Disbursement
16 Replace MD5 token generation (R-03) Remittance
17 Add @Transactional to financial operations (R-05) Remittance
18 Migrate monetary fields to BigDecimal (R-06) Remittance
19 Fix no-op payment validation (C-06) Card
20 Upgrade RSA padding to OAEP (R-11, D-08) Remittance, Disbursement

Phase 2 — Reliability (Weeks 4–8)

# Action Services
21 Add @ControllerAdvice global exception handler (CX-09) All
22 Replace e.printStackTrace() with logger (CX-03) All
23 Add Kafka Dead Letter Topic (R-08) Remittance
24 Make idempotency mandatory, fix fail-open (W-04, R-09) Wallet, Remittance
25 Add row-level locking on disbursement fetch (D-09) Disbursement
26 Add correlation IDs / distributed tracing (CX-10) All
27 Add webhook retry with exponential backoff (W-06) Wallet
28 Fix retry scheduler partner routing (R-10) Remittance
29 Remove finally { return } anti-pattern (R-07) Remittance
30 Add authentication/rate-limiting to HBL proxy (D-06) Disbursement

Phase 3 — API Quality (Weeks 8–16)

# Action Services
31 Introduce typed DTOs replacing HashMap (CX-04) All
32 Add API versioning /api/v1/ (CX-05) All
33 Add rate limiting (CX-06) All
34 Add springdoc-openapi for documentation (CX-07) All
35 Add Flyway database migrations (CX-08) All
36 Add unit and integration test suites (CX-12) All
37 Mask PII in logs (R-12) Remittance, Disbursement
38 Consolidate scheduler forks into single repo (D-10) Disbursement
39 Upgrade gateway to Spring Cloud Gateway / Boot 3.x (D-05) Disbursement
40 Implement SurrealDB migration plan (CX-11) All

Appendix A: Repository Inventory

Repo Project Purpose Size Tech
wallet Collection Mobile wallet pay-in (PK) 107.5 MB Spring Boot 3.1.1, MySQL, Redis×3, RabbitMQ (planned)
cardbackend Card Card payments (Alfalah, Safepay) 5.1 MB Spring Boot 3.5.12, MySQL, Redis×2, AWS Secrets Manager
sp-remittance-consumer Remittance Cross-border remittance processing 5.8 MB Spring Boot 3.1.0, MySQL, Redis, Kafka, Vault, gRPC
sp-remittance-scheduler-retry Remittance Retry scheduler 109.6 MB Spring Boot 3.1.0, MySQL, Redis, Kafka
verify-consumer Remittance Verification consumer 574.8 KB Empty — no source code
verify-producer Remittance Verification producer 145.3 KB Empty — no source code
1bill Collection Bill payment integration 152.7 MB Spring Boot, MySQL
card-redirection-app Card 3DS redirect handler 94.4 KB Spring Boot, MySQL
auto-void-scheduler Card Auto-void scheduler 321.8 KB Spring Boot, MySQL
sp-card-refund-reversal Card Card refund/reversal 869.5 KB Spring Boot, MySQL

| disbursement-gateway | Disbursement | Zuul API gateway (mTLS, IP filter, routing) | 1.0 MB | Spring Boot 2.2.6, Java 8, Netflix Zuul | | disbusrment-scheduler | Disbursement | Main disbursement batch processor | 84.6 MB | Spring Boot 3.x, MySQL, Redis, Vault | | disbursement-scheduler-sunshine | Disbursement | Sunshine partner variant (code fork) | 88.4 MB | Spring Boot 3.x, MySQL, Redis, Vault | | disbursmentApi | Disbursement | Main disbursement API | 90.8 MB | Not accessible — SSH key lacks access |

Appendix B: Dependency Version Mismatches

Service Spring Boot Parent Concerning Dependencies
Wallet 3.1.1 Log4j2 2.6.3 (mismatched), Spring Actuator 3.0.6, mixed starter versions
Card 3.5.12
Remittance 3.1.0 spring.main.allow-circular-references = true
Disbursement Gateway 2.2.6 Java 8 (EOL), Netflix Zuul (maintenance mode)
Disbursement Scheduler 3.x Selenium 4.12.0 + Selenium API 3.141.59 (version mismatch, unused)

Appendix C: Comparison with Spec-Only Audit (2026-03-31)

Finding Area Spec Audit Assessment Code Reality
Idempotency "Not documented" Exists but optional, fails open on Redis failure
Authentication "RSA signing for some products" No Spring Security anywhere, some endpoints completely unauthenticated
Error handling "No standard format" Worse — exceptions silently swallowed, 200 OK returned on failure
Caching "SurrealDB in-memory" Redis everywhere, no SurrealDB adoption
Webhooks "Not documented" Implemented but no retry, no signing
Encryption "AES encryption for cards" AES-ECB (insecure), plus hardcoded credentials and disabled SSL
Database "MySQL with proper layers" Raw JDBC, no ORM, no migrations, no transactions in remittance
Gateway Not assessed Spring Boot 2.2.6 / Java 8 (EOL), Zuul (maintenance mode), IP filter bug
Disbursement "Solid architecture" Production credentials in Git, chromedriver binaries, no row locking, forked scheduler repos