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¶
- 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
- SSL certificate validation disabled — 7 separate HTTP client methods in remittance accept all certificates, enabling man-in-the-middle attacks on partner bank connections
- AES-ECB encryption — Both wallet and card services use
Cipher.getInstance("AES")which defaults to insecure ECB mode - No
@Transactionalon financial operations — Remittance balance deductions, state updates, and settlement insertions are not wrapped in database transactions - 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 117BankOfAsiaService.finalize()— line 191FaysalBankService.initiate()— line 457FaysalBankService.finalize()— line 590TrustBankService.finalize()— line 231RemittanceService.markRemittanceSuccess()— line 327MerchantBalanceService.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 |