Skip to content

Simpaisa vs Open Banking UK: Gap Analysis & Recommendations

Date: 2026-04-03 Baseline: Open Banking UK Read/Write API v4.0 Subject: Simpaisa GitBook API Docs (Pakistan Pay-Ins, Pay-Outs, Remittances, Cards) Markets: Pakistan, Bangladesh, Nepal Reviewer: CDO Review Status: Updated with source code findings (supersedes spec-only assessment of 2026-04-01)


Note on this revision: The original gap analysis (2026-04-01) was written from GitBook API documentation only. This revision incorporates findings from a full source code audit (Codebase-Audit.md, 2026-04-02) covering Wallet, Cardbackend, Remittance, and Disbursement repos. In almost every dimension, the actual codebase gap is significantly worse than the spec-only assessment suggested. Severity ratings have been adjusted accordingly.


Why Open Banking UK as the Baseline

Open Banking UK is the most mature, well-documented payment API standard globally. It was built for regulated financial services with real money movement, not just data access. The patterns it establishes for authentication, error handling, idempotency, signing, pagination, and lifecycle management are directly applicable to Simpaisa's use case.

That said, Simpaisa operates in South Asian markets (Pakistan, Bangladesh, Nepal) where the payment rails are fundamentally different from UK banking. Mobile wallets (Easypaisa, JazzCash, bKash) are more dominant than bank accounts. OTP-based authentication is standard. Real-time settlement through RAAST or 1Link IBFT differs from Faster Payments. The comparison must be adapted, not copied blindly.

The goal: adopt Open Banking's design patterns and conventions while keeping Simpaisa's domain-specific flows (wallet OTP, tokenisation, multi-channel routing, cross-border FX).


Gap Analysis: 15 Dimensions

1. Authentication & Authorisation

Dimension Open Banking UK Simpaisa Current (Code Reality) Gap Priority
Protocol OAuth 2.0 + OIDC with FAPI profile No Spring Security in any service. No authentication filter chain, no CSRF protection, no security headers. Auth is ad-hoc: merchant ID from request body (wallet), client-id header lookup (card), or nothing at all (remittance consumer). CRITICAL — WORSE THAN ASSESSED P0
Token management Bearer tokens with expires_in, refresh tokens with expiry claims No token management. No OAuth. No bearer tokens anywhere in code. CRITICAL P0
Client auth Client Credentials Grant for app-level, Auth Code for user-level merchantId in body/path. Some endpoints completely unauthenticated — Safepay /fetch-details and /redirection have zero auth checks (C-03). IP security filter exists in Card but is disabled/commented out (C-04). Disbursement IP filter has a logic bug reading getRemoteAddr() instead of X-Forwarded-For (D-03). CRITICAL — WORSE THAN ASSESSED P0
Consent Intent-based consent model (consent resources, PSU authorisation) Not applicable (B2B merchant API, not consumer-facing) N/A
CORS Not applicable (server-to-server) @CrossOrigin(origins = "*") on all controllers in Wallet and Card — wide open to browser-based abuse (CX-02) CRITICAL P0

Assessment change: The spec audit rated authentication as CRITICAL/P1. Code reality is P0 — immediate security risk. There is literally no security framework in place. The gap is not "different auth mechanism" — it is "no auth mechanism" on multiple endpoints.

Recommendation for Simpaisa:

Full OAuth 2.0 with FAPI is overkill for a B2B merchant API. But the current state (no Spring Security anywhere, unauthenticated endpoints, disabled IP filters) requires emergency remediation before any standards alignment.

Immediate (before Open Banking alignment): - Add spring-boot-starter-security to all services - Implement SecurityFilterChain with API key authentication as minimum - Authenticate all endpoints — close the unauthenticated Safepay endpoints (C-03) - Fix the disbursement IP filter to read X-Forwarded-For (D-03) - Enable the Card IP security filter (C-04) - Remove @CrossOrigin(origins = "*") from all controllers

Then adopt: - OAuth 2.0 Client Credentials Grant for obtaining access tokens (merchantId + secret -> bearer token) - RSA request signing standardised across ALL products - Mutual TLS standardised across ALL products

Skip: - OIDC, FAPI profile, consent model (these are for consumer-facing open banking, not B2B) - Authorisation Code Grant (no PSU redirect needed in B2B)

Tailoring for South Asia: - Keep OTP-based payment authorisation for wallet Pay-Ins (this IS the consent mechanism for mobile wallets in Pakistan) - Document OTP as the equivalent of Strong Customer Authentication (SCA) in the South Asian context


2. Error Response Schema

Dimension Open Banking UK Simpaisa Current (Code Reality) Gap Priority
Structure {"Errors":[{"ErrorCode":"...","Message":"...","Path":"...","Url":"..."}]} No @ControllerAdvice or @ExceptionHandler in any service (CX-09). Each controller has its own try-catch with inconsistent behaviour — some return 200 OK with empty body, some return 401, some return 201. No standard error schema exists in code. CRITICAL — WORSE THAN ASSESSED P0
Exception handling Structured error propagation Exceptions silently swallowed. Controller catch blocks catch Exception, call e.printStackTrace(), and return whatever partial state exists with HTTP 200 OK (W-05). Remittance financial methods use finally { return response; } which suppresses all exceptions (R-07). Callers cannot distinguish success from failure. CRITICAL — WORSE THAN ASSESSED P0
Error codes ISO 20022 reason codes + OBL proprietary codes Custom codes per product (0000-9999). Card service's validatePaymentBody() is a no-op — returns empty error set, validates nothing (C-06). Inverted validation booleans in Card — isValidEmail() returns true for invalid emails (C-02). CRITICAL P0
Field path Path field pointing to the problematic request field Not present. No typed DTOs exist to even reference field paths. HIGH P2
Documentation link Url field linking to error documentation Not present MEDIUM P3

Assessment change: The spec audit rated error handling as CRITICAL/P1. Code reality is P0. The problem is not just "inconsistent format" — it is that errors are actively hidden from callers. A payment can fail and the caller receives HTTP 200 with an empty or partial response.

Recommendation for Simpaisa:

Before adopting Open Banking error formats, the codebase needs fundamental error handling:

Immediate: - Add @ControllerAdvice with @ExceptionHandler in every service - Remove all finally { return } anti-patterns in financial methods (R-07) - Replace e.printStackTrace() with logger.error() across all services (CX-03, 40+ instances in Remittance alone) - Fix inverted validation booleans in Card (C-02) - Implement actual validation in Card's validatePaymentBody() (C-06)

Then adopt the Open Banking error array pattern:

{
  "errors": [
    {
      "code": "INVALID_ACCOUNT",
      "message": "Beneficiary account number is invalid",
      "path": "request.beneficiaryAccountNo",
      "reference": "https://simpaisa.gitbook.io/simpaisa-docs/errors/INVALID_ACCOUNT"
    }
  ],
  "traceId": "uuid-for-support-debugging"
}

Tailoring: - Use Simpaisa's existing numeric codes (0001, 0008, etc.) as the code field, but add a human-readable constant alongside: {"code": "0008", "name": "INVALID_ACCOUNT", ...} - Add traceId (not in Open Banking but essential for cross-border debugging across multiple payment rails) - The path field maps directly and should be adopted as-is


3. HTTP Status Codes

Dimension Open Banking UK Simpaisa Current (Code Reality) Gap Priority
Success 200 OK, 201 Created, 204 No Content Confirmed: returns 200 for everything including failures. Controller catch blocks return HTTP 200 with empty body on exception (W-05). CRITICAL — CONFIRMED P0
Client errors 400, 401, 403, 405, 406, 415 Confirmed: 200 with error code (or empty body). No validation framework to produce 400 errors — HashMap<String, Object> request bodies have no Bean Validation. Card's validation is a no-op (C-06). CRITICAL — CONFIRMED P0
Rate limiting 429 with Retry-After header Confirmed: no rate limiting exists in any service (CX-06). No Bucket4j, no Spring Cloud Gateway rate limiter, no custom implementation. CRITICAL P1
Server errors 500, 503 (also for deprecated versions) Exceptions caught and swallowed — 500 never returned even when server fails. CRITICAL P0
400 vs 404 400 for missing resource IDs, 404 for undefined endpoints No differentiation. Everything is 200. CRITICAL P1

Assessment change: The spec audit suspected "200 for everything" and rated it CRITICAL/P1. Code confirms this and reveals it is even worse — exceptions produce 200 with empty/partial bodies, making it impossible for clients to detect failures.

Recommendation for Simpaisa:

Open Banking's HTTP status code usage should be adopted directly, but this requires the @ControllerAdvice and typed DTO work first. Without typed request objects, the framework cannot generate 400 responses automatically.

Adopt immediately (once @ControllerAdvice is in place): - 201 for successful resource creation (payment initiation) - 200 for successful queries - 400 for validation errors - 401 for auth failures - 429 for rate limiting with Retry-After - 500 for server errors

Tailoring: None needed. HTTP status codes are universal.


4. Idempotency

Dimension Open Banking UK Simpaisa Current (Code Reality) Gap Priority
Header x-idempotency-key (max 40 chars) Request-Id (UUID, 36 chars) on Wallet via @IdempotentAnnonation aspect. Optional — if header is absent, request proceeds without deduplication (W-04). HIGH P1
Scope POST requests only, 24-hour window Wallet verify call only. No idempotency in Remittance Kafka consumer (R-09) — with at-least-once delivery, duplicates possible. No idempotency in Disbursement. CRITICAL P0
Failure mode Deterministic (idempotency always enforced) Fails open. If Redis is down, catch block (line 83-86) falls through to pjp.proceed() — idempotency silently disabled, duplicate payments can be created (W-04). CRITICAL — WORSE THAN ASSESSED P0
Body verification MAY verify body unchanged via signature Not implemented MEDIUM P2
Timing TPP must wait >= 1 second before retry Not documented or enforced LOW P3

Assessment change: The spec audit rated idempotency as "not documented" / CRITICAL/P1. Code reveals it partially exists but fails open — a Redis outage silently disables duplicate payment protection. This is a financial integrity risk.

Recommendation for Simpaisa:

Immediate: - Make idempotency mandatory on all payment-initiating endpoints (fail closed, not open) - Fix the Redis failure path — if idempotency store is unavailable, reject the request (don't process it) - Add idempotency check to Remittance Kafka consumer before processing (R-09) - Add row-level locking on Disbursement scheduler fetch to prevent duplicate processing (D-09)

Then adopt: - Rename Request-Id to x-idempotency-key for industry alignment - Keep UUID format (Simpaisa's 36-char UUID is within Open Banking's 40-char limit) - Extend to all POST endpoints across all products - 24-hour deduplication window


5. Request/Response Signing

Dimension Open Banking UK Simpaisa Current (Code Reality) Gap Priority
Algorithm PS256 (RSA-PSS with SHA-256) via detached JWS RSA 2048-bit in some services. But Remittance uses RSA/ECB/PKCS1Padding which is vulnerable to Bleichenbacher padding oracle attacks (R-11, D-08). HIGH — WORSE THAN ASSESSED P1
Format Detached JWS in x-jws-signature header signature field in request/response body. Signatures logged to stdoutSystem.out.println("request signature = " + signature) in Wallet (W-07), leaking cryptographic material in production. CRITICAL P0
Non-repudiation Full JOSE header with issuer, timestamp, trust anchor Basic RSA signature with no metadata MEDIUM P2
Pay-Ins Signed (per spec) NO signing at all on Wallet endpoints HIGH P1
Key management Trust anchor chain, certificate rotation Private keys committed to source control — PEM files in src/main/resources/ (W-02). Hardcoded RSA public key in Remittance (R-02). Card fetches private key from AWS Secrets Manager per request with no caching (C-07). CRITICAL P0

Assessment change: The spec audit rated signing as MEDIUM/P2. Code reveals critical key management failures — private keys in Git, signatures in stdout, vulnerable padding modes.

Recommendation for Simpaisa:

Immediate (before standards alignment): - Remove PEM private keys from source control and Git history (W-02) — use BFG Repo-Cleaner - Stop logging signatures to stdout (W-07) - Migrate from PKCS1 v1.5 padding to OAEP (R-11, D-08) - Cache private keys from AWS Secrets Manager instead of fetching per request (C-07)

Then adopt: - Move signature from request body to x-jws-signature header (cleaner separation of data and auth) - Add signing to Pay-Ins (currently unsigned, which is a security gap) - Manage all keys via HashiCorp Vault (already in some services)

Defer: - Full JOSE header with trust anchor chain (overkill for B2B, useful when Simpaisa opens to third-party TPPs) - PS256 algorithm migration (Simpaisa's SHA-256 RSA is acceptable once padding is fixed)


6. Pagination

Dimension Open Banking UK Simpaisa Current (Code Reality) Gap Priority
Mechanism HATEOAS links: Links.Self, Links.Next, Links.Prev Unknown from code audit. No evidence of pagination in the controllers reviewed. HashMap<String, Object> responses make structured pagination unlikely. HIGH P2
Page size Min 25, max 1000 per page Not implemented HIGH P2
Metadata Meta.TotalPages Not present MEDIUM P3

Assessment change: Unchanged — pagination was undocumented in specs and unconfirmed in code.

Recommendation for Simpaisa:

Adopt the HATEOAS link pattern for pagination. It's self-documenting and doesn't require merchants to construct URLs.

{
  "data": [...],
  "links": {
    "self": "/merchants/123/disbursements?page=2",
    "next": "/merchants/123/disbursements?page=3",
    "prev": "/merchants/123/disbursements?page=1"
  },
  "meta": {
    "totalPages": 47,
    "totalCount": 1150
  }
}

Tailoring: For high-volume transaction lists (disbursements, remittances), use cursor-based pagination instead of page numbers. This performs better on the MySQL databases Simpaisa uses. The Disbursement scheduler currently fetches up to 1,000 records with a simple SELECT ... WHERE state = 'published' LIMIT 1000 with no locking (D-09) — any pagination implementation must also address row-level locking.


7. Versioning

Dimension Open Banking UK Simpaisa Current (Code Reality) Gap Priority
Format /v[major].[minor]/ in URL path Confirmed: no versioning anywhere (CX-05). Wallet uses /wallets/transaction/*, Card uses /payments, /capture, Remittance uses /kafka-send. No URL versioning, no header versioning, no content negotiation. CRITICAL — CONFIRMED P1
Deprecation 503 for deprecated versions Not possible (no versions exist) HIGH P2
Migration payload-version header for schema migration Not present MEDIUM P3

Assessment change: The spec audit noted inconsistent versioning (/v2/ for Pay-Ins, version: 3.0 header elsewhere). Code confirms no versioning exists at all — those version indicators were in the GitBook docs only, not implemented in code.

Recommendation for Simpaisa:

Adopt URL path versioning: /v1/, /v2/. Drop the version header approach (harder to route, harder to cache). This must be implemented alongside the URI restructuring (see Dimension 10).

Tailoring: Use major versions only (/v1/, /v2/). Open Banking's v4.0 minor versioning adds complexity Simpaisa doesn't need yet.


8. Correlation & Tracing

Dimension Open Banking UK Simpaisa Current (Code Reality) Gap Priority
Correlation ID x-fapi-interaction-id (UUID, echoed in response) Confirmed: not present (CX-10). No MDC-based request tracing, no X-Request-Id propagation into log context, no Spring Cloud Sleuth or Micrometer Tracing. CRITICAL — CONFIRMED P1
Request tracing Interaction ID links request -> response -> webhook Not present. Logging uses e.printStackTrace() to stdout (CX-03), bypassing Log4j2 appenders entirely. In containerised environments, these stack traces are lost. CRITICAL — WORSE THAN ASSESSED P0

Assessment change: The spec audit rated this HIGH/P1. Code confirms it and reveals the logging infrastructure is fundamentally broken — e.printStackTrace() in every catch block across all services means distributed tracing cannot work even if correlation IDs were added.

Recommendation for Simpaisa:

Immediate (before correlation IDs): - Replace all e.printStackTrace() with logger.error("Context message", e) across all services (40+ instances in Remittance alone) - Stop logging signatures, PII, and full request/response bodies at INFO level (R-12, W-07)

Then adopt: - x-request-id header (simpler naming than FAPI) - Echo it in every response and every webhook payload - Propagate into MDC for structured logging - Store in OpenSearch for tracing


9. Payment Lifecycle States

Dimension Open Banking UK Simpaisa Current (Code Reality) Gap Priority
Standard ISO 20022 PaymentStatusCode (13 states) Custom states per product (6 for Pay-Outs, 9 for Remittances). Disbursement has an enum bug — MONTHLY_LIMIT_EXCEEDED message says "Daily-limit-exceeded" (D-13). MEDIUM P2
Naming RCVD, PDNG, ACTC, ACSP, ACSC, RJCT, etc. published, in_review, on_hold, stuck, disbursed, rejected, etc. MEDIUM P2
Transitions Documented in spec Listed but no transition diagram. Code reveals unrecoverable partial states — Remittance has no @Transactional on financial operations (R-05), so balance deductions can succeed while state updates fail, creating orphaned states that require manual intervention. CRITICAL — WORSE THAN ASSESSED P0

Assessment change: The spec audit rated lifecycle states as MEDIUM/P2. Code reveals a P0 issue — the absence of database transactions means the state machine can reach states that are not defined in the model and cannot be recovered automatically.

Recommendation for Simpaisa:

Immediate: - Add @Transactional to all financial operations (R-05) — this is a prerequisite for any reliable state machine - Fix the enum naming bug (D-13)

Then adopt: - State transition diagrams (Open Banking documents which transitions are valid) - StatusUpdateDateTime field on every resource (when did the state last change?) - Webhook notifications for ALL state changes (Simpaisa's Remittances only sends postbacks for 4 of 9 states) - Don't rename states to ISO 20022 codes — Simpaisa's human-readable names are better for developer experience

Tailoring: Map Simpaisa states to ISO 20022 equivalents in documentation for banks/regulators who need it, but keep the human-readable names as primary.


10. Resource URI Structure

Dimension Open Banking UK Simpaisa Current (Code Reality) Gap Priority
Pattern /open-banking/v4.0/pisp/domestic-payments/{id} Confirmed: 3+ different patterns. Wallet: /wallets/transaction/*, Card: /payments, /capture, Remittance: /kafka-send. No consistent hierarchy, no versioning prefix. HIGH P2
Hierarchy resource-group / resource / resource-id / sub-resource Flat (no sub-resources). Remittance endpoint is literally /kafka-send — implementation detail exposed as API contract. HIGH P2

Recommendation for Simpaisa:

Adopt a consistent pattern across all products:

/api/v1/payin/{merchantId}/transactions/{action}
/api/v1/payout/{merchantId}/disbursements/{action}
/api/v1/remittance/{merchantId}/transfers/{action}
/api/v1/cards/{merchantId}/payments/{action}

Tailoring: Use product names that match Simpaisa's domain (payin, payout, remittance) not Open Banking's (pisp, aisp, cbpii).


11. Webhook / Event Notification

Dimension Open Banking UK Simpaisa Current (Code Reality) Gap Priority
Format JWS-signed event notifications Unsigned JSON POST — confirmed in code. No HMAC, no JWS, no signature of any kind on webhook payloads (W-06). CRITICAL P0
Retry Documented policy No retry. MerchantPostbackService sends a single POST request. If the merchant's server is down, the postback is lost. RabbitMQ queue constants exist (POSTBACK_QUEUE, POSTBACK_EXCHANGE) suggesting a queue-based retry was planned but never implemented (W-06). CRITICAL — CONFIRMED P0
Verification Signature verification by recipient Not possible (unsigned) CRITICAL P0
Event types Typed event resources Inferred from payload content HIGH P2
Dead letter Failed events tracked and retryable Kafka dead letter topic handler is empty — when all retries fail, remittance messages are silently dropped (R-08). CRITICAL P0

Assessment change: The spec audit rated webhooks as CRITICAL/P1. Code confirms and reveals additional severity — not only are webhooks unsigned and unretried, but the Kafka error handler silently drops failed messages.

Recommendation for Simpaisa:

Immediate: - Implement Kafka Dead Letter Topic — failed messages must not be silently dropped (R-08) - Add webhook retry with exponential backoff (3 retries at 1min, 5min, 30min)

Then adopt: - HMAC-SHA256 signature on webhook payloads - X-Simpaisa-Signature header with HMAC of raw body using shared secret - Event type field: "event": "payment.completed", "event": "disbursement.rejected"

Tailoring: HMAC-SHA256 instead of JWS (simpler for merchants to implement, sufficient for B2B webhooks).


12. Date/Time Formats

Dimension Open Banking UK Simpaisa Current (Code Reality) Gap Priority
Format ISO-8601 with timezone required Mixed formats (2023-06-08 16:09:56.0 in callbacks). Remittance uses DecimalFormat("#.##") to truncate amounts (R-06) and double for money — date/time handling likely equally inconsistent. MEDIUM P2
Timezone Mandatory in all payloads Not specified MEDIUM P2

Recommendation: Adopt ISO-8601 with timezone: 2023-06-08T16:09:56+05:00 (PKT). The current format without timezone and with .0 suffix is non-standard.


13. Supplementary / Extensibility

Dimension Open Banking UK Simpaisa Current (Code Reality) Gap Priority
Extension model SupplementaryData empty object, ASPSP-defined No extension model. However, HashMap<String, Object> everywhere (CX-04) means the entire request/response is effectively untyped and extensible — but in the worst possible way (no schema, no validation, no documentation). MEDIUM P3

Recommendation: The HashMap<String, Object> pattern must be replaced with typed DTOs first (CX-04). Once typed models exist, add a metadata object for market-specific fields. For example, Pakistan requires CNIC for certain transactions, Bangladesh might require different KYC fields. A metadata object keeps the core schema clean while allowing regional extensions.


14. Security (TLS/mTLS/Encryption)

Dimension Open Banking UK Simpaisa Current (Code Reality) Gap Priority
TLS version TLS 1.2 mutual authentication SSL certificate validation disabled in Remittance — 7 separate methods create TrustManager accepting all certificates with NoopHostnameVerifier (R-01). This enables man-in-the-middle attacks on partner bank connections (Bank of Asia, Faysal Bank, Trust Bank, 1Link). CRITICAL — CATASTROPHIC P0
Certificate X.509 with RSA >= 2048-bit, SHA-2 signed Private keys committed to Git (W-02, D-01). Hardcoded credentials in source (W-01, R-02, D-01). Disbursement SSL keystore password is changeit (D-01). CRITICAL — WORSE THAN ASSESSED P0
mTLS Mandatory Only Disbursement gateway (and even that runs on EOL Spring Boot 2.2.6 / Java 8 with Netflix Zuul). Not present in Wallet, Card, or Remittance. CRITICAL P0
Encryption AES-256 or equivalent AES-ECB mode across Wallet, Card, and Disbursement (W-03, C-01, D-07). ECB is deterministic — identical plaintext blocks produce identical ciphertext. For card payment PAN data, this is a PCI-DSS blocker. Disbursement has a separate EncryptionUtils.java that correctly uses AES/CBC, but the insecure utility coexists and may still be called. CRITICAL — PCI BLOCKER P0
Hashing SHA-256+ Remittance uses MD5 for security tokens (R-03): token = MD5(date + time + SECRET_KEY + stan). MD5 is cryptographically broken. CRITICAL P0
PAN handling Tokenised references, minimal PCI scope Card service handles raw PAN through application layerString cardNumber and String cvv as plain parameters (C-05). Card data stored in Redis with 15-minute TTL without confirmed TLS or ACL. Significantly increases PCI-DSS scope. CRITICAL — PCI BLOCKER P0

Assessment change: The spec audit rated security as MEDIUM-HIGH. Code reveals multiple P0 security vulnerabilities: disabled SSL, AES-ECB on card data, MD5 for auth tokens, private keys in Git, raw PAN handling, and hardcoded credentials. This dimension alone has more P0 findings than the entire original assessment.

Immediate actions: 1. Fix SSL certificate validation — remove all trust-all patterns in Remittance (R-01) 2. Replace AES-ECB with AES-GCM across all services (W-03, C-01, D-07) 3. Replace MD5 token generation with HMAC-SHA256 (R-03) 4. Rotate and remove all hardcoded credentials from source and Git history (W-01, R-02, D-01) 5. Remove PEM private keys from repos (W-02) 6. Review PAN handling for PCI-DSS compliance (C-05)

Recommendation: Standardise mTLS across all products. Migrate Disbursement gateway to Spring Boot 3.x / Spring Cloud Gateway (D-05).


15. Settlement & Reconciliation

Dimension Open Banking UK Simpaisa Current (Code Reality) Gap Priority
Expected settlement ExpectedSettlementDateTime in response Not present. Additionally, settlement operations lack database transactions (R-05) — a settlement record can be inserted while the balance update fails. CRITICAL — WORSE THAN ASSESSED P0
Expected execution ExpectedExecutionDateTime in response Not present HIGH P1
Charges Charges array in payment response Not present MEDIUM P2
Refund reference Refund object with account details Refund API incomplete HIGH P1
Money precision Decimal precision in all financial fields double used for money throughout Remittance (R-06). DecimalFormat("#.##") truncates to 2 decimal places, silently losing precision. Settlement discrepancies are inevitable. CRITICAL P0
Concurrent processing Safe concurrent access to settlement data No row-level locking on Disbursement fetch (D-09) — multiple scheduler instances process the same disbursements simultaneously. Retry scheduler only supports Bank of Asia, defaulting all other partners to wrong bank's API (R-10). CRITICAL P0

Assessment change: The spec audit rated settlement as HIGH/P1. Code reveals P0 financial integrity risks — floating-point money, no database transactions on settlement operations, no row-level locking, and a retry scheduler that routes failed transactions to the wrong partner bank.

Immediate: - Migrate all monetary fields to BigDecimal with explicit rounding modes (R-06) - Add @Transactional to settlement operations (R-05) - Add SELECT ... FOR UPDATE SKIP LOCKED to disbursement scheduler (D-09) - Fix retry scheduler partner routing (R-10)

Then adopt: - expectedExecutionDateTime — when the payment will be submitted to the rail - expectedSettlementDateTime — when the beneficiary will receive funds - charges array — breakdown of fees applied

Tailoring for South Asia: - Settlement timing varies wildly: Easypaisa is near-instant, 1Link IBFT is real-time, bank transfers may be T+1 - Document per-channel settlement expectations - For cross-border remittances: include both sending and receiving settlement times


Summary: What to Adopt, Adapt, and Skip

Adopt Directly (universal best practices)

  1. Standard HTTP status codes (200, 201, 400, 401, 429, 500)
  2. x-idempotency-key header on all POST endpoints (24-hour window, fail closed)
  3. Error array format with code, message, path, reference URL
  4. x-request-id correlation header (echoed in responses and webhooks)
  5. ISO-8601 with timezone for all date/time fields
  6. HATEOAS pagination links with metadata
  7. URL path versioning (/v1/, /v2/)
  8. Retry-After header on 429 responses
  9. Webhook signature verification (HMAC-SHA256)
  10. expectedSettlementDateTime in payment responses

Adopt — But Requires Foundational Remediation First (code reality blockers)

These Open Banking patterns cannot be implemented until the underlying codebase issues are resolved:

Open Banking Pattern Blocker in Code Remediation Required First
OAuth 2.0 Client Credentials No Spring Security in any service (CX-01) Add spring-boot-starter-security, implement SecurityFilterChain
Standard error schema Exceptions silently swallowed, 200 OK on failure (W-05, R-07, CX-09) Add @ControllerAdvice, remove finally { return }, fix inverted validators
Request/response signing Private keys in Git, signatures in stdout (W-02, W-07) Purge keys from history, stop logging signatures, fix RSA padding
Typed API contracts (OpenAPI) HashMap<String, Object> everywhere (CX-04) Introduce typed DTOs with Bean Validation
Idempotency enforcement Fails open on Redis failure (W-04) Make mandatory, fail closed
Settlement timing double for money, no @Transactional (R-05, R-06) Migrate to BigDecimal, add transaction management
mTLS everywhere SSL validation disabled in Remittance (R-01), gateway on EOL stack (D-05) Fix trust-all patterns, upgrade gateway

Adapt for South Asian Markets

  1. Auth: OAuth 2.0 Client Credentials (not full FAPI/OIDC, B2B not consumer)
  2. Signing: RSA or JWS (not PS256 mandated, keep RSA-SHA256 compatibility for regional banks — but fix PKCS1 padding first)
  3. Payment states: Keep human-readable names, map to ISO 20022 in docs for regulators
  4. Consent: OTP-based wallet authorisation IS the SCA equivalent, document it as such
  5. URI structure: Use Simpaisa product names (payin/payout/remittance), not OB group names
  6. Metadata extension: Add metadata object for market-specific fields (CNIC for Pakistan, NID for Bangladesh)
  7. Settlement timing: Document per-channel (Easypaisa vs 1Link vs bKash) since it varies by rail
  8. Webhook events: Include channel-specific events (OTP sent, wallet approval pending, AML review)

Skip (not applicable to B2B merchant API)

  1. OIDC / Authorisation Code Grant / Hybrid flow (consumer-facing, not B2B)
  2. Consent resource model (PSU authorisation via redirect, not needed)
  3. CIBA (backchannel auth for consumer banking)
  4. FAPI profile (regulatory requirement for UK open banking, not South Asian markets)
  5. ISO 20022 status codes as primary identifiers (keep as secondary mapping)
  6. JWE encryption (mTLS + RSA signing is sufficient for B2B)
  7. Trust anchor chain in JWS headers (needed for multi-party open banking, not single-provider)
  8. 503 for deprecated versions (Simpaisa should use proper deprecation headers instead)

Implementation Roadmap (Revised — Aligned with Code Reality)

Timeline adjustment: The original roadmap (2026-04-01) assumed a starting point of "inconsistent but functional APIs." The code audit reveals the starting point is fundamentally insecure and unreliable. A new Phase 0 (emergency security remediation) is required before any Open Banking alignment work can begin. Overall timeline extends from 16 weeks to 28–32 weeks.

Phase 0: Emergency Security Remediation (Weeks 1–2)

Goal: Close active security vulnerabilities. No Open Banking alignment — just stop the bleeding.

This phase maps directly to the Codebase Audit Phase 0 remediation items.

# Action Source Finding Services
1 Rotate all hardcoded credentials (Twilio, partner APIs, MySQL, SMTP, JazzCash, Easypaisa, 1Link) W-01, R-02, D-01 All
2 Remove private keys from Git history (BFG Repo-Cleaner) W-02 Wallet
3 Fix SSL certificate validation — remove all trust-all patterns R-01 Remittance
4 Fix inverted validation booleans C-02 Card
5 Authenticate Safepay endpoints C-03 Card
6 Enable IP security filter / fix IP filter X-Forwarded-For bug C-04, D-03 Card, Disbursement
7 Remove chromedriver binaries and Selenium code D-02 Disbursement
8 Purge all credentials from Git history All credential findings All

Phase 1: Security Hardening (Weeks 3–8)

Goal: Establish baseline security that Open Banking patterns can be built upon.

# Action OB Dimension Source Finding Services
9 Replace AES-ECB with AES-GCM §14 Security W-03, C-01, D-07 Wallet, Card, Disbursement
10 Add Spring Security with API key auth §1 Auth CX-01 All
11 Remove @CrossOrigin(origins="*") §1 Auth CX-02 Wallet, Card
12 Replace MD5 token generation with HMAC-SHA256 §14 Security R-03 Remittance
13 Add @Transactional to all financial operations §15 Settlement R-05 Remittance
14 Migrate monetary fields to BigDecimal §15 Settlement R-06 Remittance
15 Upgrade RSA padding to OAEP §5 Signing R-11, D-08 Remittance, Disbursement
16 Fix no-op payment validation §2 Errors C-06 Card
17 Stop logging signatures to stdout §5 Signing W-07 Wallet
18 Add row-level locking on disbursement fetch §15 Settlement D-09 Disbursement
19 Fix retry scheduler partner routing §15 Settlement R-10 Remittance
20 Upgrade disbursement gateway to Spring Boot 3.x §14 Security D-05 Disbursement

Phase 2: Error Handling & Reliability (Weeks 9–14)

Goal: Establish the error handling and reliability contracts that Open Banking requires.

# Action OB Dimension Source Finding Services
21 Add @ControllerAdvice global exception handler §2 Errors, §3 HTTP Status CX-09 All
22 Replace e.printStackTrace() with structured logging §8 Tracing CX-03 All
23 Implement standard error response schema (OB error array) §2 Errors All
24 Implement proper HTTP status codes §3 HTTP Status All
25 Make idempotency mandatory, fail closed §4 Idempotency W-04, R-09 Wallet, Remittance
26 Add Kafka Dead Letter Topic §11 Webhooks R-08 Remittance
27 Add webhook retry with exponential backoff §11 Webhooks W-06 Wallet
28 Add webhook HMAC-SHA256 signing §11 Webhooks All
29 Add correlation IDs / distributed tracing §8 Tracing CX-10 All
30 Remove finally { return } anti-pattern §2 Errors R-07 Remittance
31 Mask PII in logs §8 Tracing R-12 Remittance, Disbursement

Phase 3: API Contract & Developer Experience (Weeks 15–22)

Goal: Establish the typed, versioned, documented API contracts that Open Banking mandates.

# Action OB Dimension Source Finding Services
32 Introduce typed DTOs replacing HashMap<String, Object> §2 Errors, §13 Extensibility CX-04 All
33 Add Bean Validation on typed DTOs §2 Errors CX-04 All
34 Add API versioning /api/v1/ §7 Versioning CX-05 All
35 Restructure URIs to consistent pattern §10 Resource URIs All
36 Add springdoc-openapi for auto-generated documentation §13 Extensibility CX-07 All
37 Add HATEOAS pagination links §6 Pagination All
38 Add rate limiting with Retry-After §3 HTTP Status CX-06 All
39 Standardise date/time to ISO-8601 with timezone §12 Date/Time All
40 Add settlement timing fields to payment responses §15 Settlement All
41 Add state transition diagrams and webhook mapping §9 Lifecycle All
42 Add Flyway database migrations CX-08 All

Phase 4: OAuth & Signing Standards (Weeks 23–28)

Goal: Implement the authentication and signing patterns aligned with Open Banking.

# Action OB Dimension Services
43 Implement OAuth 2.0 Client Credentials Grant §1 Auth All
44 Standardise RSA request signing across all products §5 Signing All
45 Move signature from body to x-jws-signature header §5 Signing All
46 Standardise mTLS across all products §14 Security All
47 Add metadata object for market-specific extensions §13 Extensibility All
48 Add unit and integration test suites All
49 Consolidate scheduler forks into single repo Disbursement
50 Implement SurrealDB migration plan All

Phase 5: Regional Expansion & Sandbox (Weeks 29–32)

Goal: Ensure the patterns work across all markets.

# Action OB Dimension Services
51 Bangladesh/Nepal standards audit All
52 Per-market settlement documentation §15 Settlement All
53 Sandbox test scenarios per channel All
54 Reconciliation API (transaction search, daily reports) §15 Settlement All
55 Per-market metadata fields (CNIC, NID, etc.) §13 Extensibility All

Appendix: Open Banking Pattern Cheat Sheet for Simpaisa Engineers

Pattern Open Banking Way Simpaisa Adaptation Code Reality Blocker
Auth OAuth 2.0 + FAPI + mTLS OAuth 2.0 Client Credentials + RSA signing + mTLS No Spring Security anywhere (CX-01)
Error format {"Errors":[{"ErrorCode","Message","Path","Url"}]} {"errors":[{"code","name","message","path","reference"}],"traceId"} Exceptions swallowed, 200 OK on failure (W-05, R-07)
Idempotency x-idempotency-key header, 40 chars, 24h Same, UUID format, fail closed Fails open on Redis failure (W-04)
Correlation x-fapi-interaction-id UUID x-request-id UUID No tracing, e.printStackTrace() everywhere (CX-10, CX-03)
Signing Detached JWS with PS256 RSA-SHA256 in x-signature header (upgrade path to JWS) Keys in Git, signatures in stdout, PKCS1 padding (W-02, W-07, R-11)
Pagination HATEOAS Links.Self/Next/Prev, Meta.TotalPages Same pattern, cursor-based for large sets HashMap responses, no typed models (CX-04)
Versioning /v[major].[minor]/ in path /v[major]/ in path (simpler) No versioning exists at all (CX-05)
Dates ISO-8601 with timezone Same Mixed formats, no standardisation
Webhooks JWS-signed event notifications HMAC-SHA256 signed, typed events No signing, no retry, Kafka DLT empty (W-06, R-08)
States ISO 20022 codes (RCVD, ACSP, RJCT) Human-readable (published, disbursed, rejected) with ISO mapping No @Transactional — orphan states possible (R-05)
Settlement ExpectedSettlementDateTime in response Same, per-channel double for money, no row locking (R-06, D-09)
Extension SupplementaryData empty object metadata object for regional fields Everything is HashMap already — need types first (CX-04)
TLS/Encryption TLS 1.2+ mandatory, AES-256 Same AES-ECB, SSL disabled, MD5 tokens (W-03, R-01, R-03)