Skip to content

Instantly share code, notes, and snippets.

@aungkyawminn
Last active October 12, 2025 06:45
Show Gist options
  • Save aungkyawminn/b5986c2b3fcf298fbe63c78e818eff8a to your computer and use it in GitHub Desktop.
Save aungkyawminn/b5986c2b3fcf298fbe63c78e818eff8a to your computer and use it in GitHub Desktop.
Architecture Views

Mobile Wallet — System Architecture Views

This document compiles five standard architecture views for the Mobile Wallet System:

  1. Use Case View
  2. Logical View
  3. Process View
  4. Development View (modular)
  5. Physical View

##Context (C4-style, high level)

flowchart LR
  subgraph Users
    C["Customer (Mobile App)"]
    M["Merchant (POS/App)"]
    A["Agent (Branch/Agent App)"]
  end

  subgraph Wallet["Mobile Wallet System"]
    GW(API Gateway)
    SVC[App Services]
    LED[Ledger Service]
    PAY[Payments Service]
    KYC[E-KYC Service]
    NOTI[Notification Service]
    RPT[Reporting & Analytics]
    ADM[Admin & RBAC]
    MSG[(Message Broker)]
    DB[(Operational DBs)]
  end

  EXT1[(Bank Core / CASA)]
  EXT2[(MMQR National Switch)]
  EXT3[(KYC Provider)]
  EXT4[(SMS/Email/Push Gateways)]

  C -->|REST/GraphQL| GW
  M -->|Merchant API| GW
  A -->|Agent API| GW
  GW --> SVC
  SVC --> PAY
  SVC --> LED
  SVC --> KYC
  SVC <--> NOTI
  SVC <--> RPT
  SVC <--> ADM
  SVC <--> MSG
  SVC <--> DB

  PAY <--> EXT1
  PAY <--> EXT2
  KYC <--> EXT3
  NOTI <--> EXT4
Loading

1) Use Case View

Actors and primary use cases for the Mobile Wallet. (Mermaid approximation of a use‑case diagram.)

flowchart LR
  %% Actors
  C([Customer])
  MR([Merchant])
  BA([Bank Account])
  AG([Agent])
  SW([MMQR National Switch])
  KP([KYC Provider])

  %% System boundary
  subgraph SYS[Mobile Wallet System]
    UC1[[Register & eKYC]]
    UC2[[Login]]
    UC3[[Cash In]]
    UC4[[Cash Out]]
    UC5[["Pay Merchant (QR)"]]
  end

  %% Associations
  C --- UC1
  C --- UC2
  C --- UC3
  C --- UC4
  C --- UC5

  MR --- UC5
  BA --- UC3
  AG --- UC4
  SW --- UC5
  KP --- UC1
Loading

Notes

  • Register & eKYC covers identity capture, OCR, liveness, blacklist check, and decisioning.
  • Cash In involves linking bank account/cards and authorizing top-up.
  • Cash Out supports agent/ATM cash-out with OTP and risk checks.
  • Pay Merchant (QR) supports dynamic/static MMQR with online authorization, tips, and partial refunds.

2) Logical View (Domain Model)

Key domain entities and relationships.

classDiagram
  class Customer{
    +id: Snowflake
    +name
    +phone
    +kycStatus: enum
    +primaryWalletId
  }

  class WalletAccount{
    +id: Snowflake
    +customerId
    +balance: decimal
    +status: enum
    +limitsProfileId
  }

  class Merchant{
    +id: Snowflake
    +name
    +category: MCC
    +qrType: enum
  }

  class Agent{
    +id: Snowflake
    +name
    +location
  }

  class KYCVerification{
    +id
    +customerId
    +providerRef
    +result: enum
    +score: number
  }

  class QRCode{
    +id
    +merchantId
    +type: static|dynamic
    +payload
  }

  class Transaction{
    +id
    +type: enum
    +amount: decimal
    +currency
    +status: enum
    +rrn
    +authCode
  }

  class LedgerEntry{
    +id
    +txnId
    +debitAccountId
    +creditAccountId
    +amount
    +narration
  }

  class SettlementBatch{
    +id
    +merchantId
    +cutoffAt
    +totalAmount
    +status
  }

  class LimitsProfile{
    +id
    +dailyTxnLimit
    +perTxnLimit
    +monthlyTxnLimit
  }

  class Role{
    +id
    +name
  }
  class Permission{
    +id
    +name
    +resource
    +action
  }

  Customer --> WalletAccount : owns 1..*
  Customer --> KYCVerification : has 0..*
  Merchant --> QRCode : issues 1..*
  Transaction --> WalletAccount : affects 1..*
  Transaction --> Merchant : optional
  Transaction --> Agent : optional
  Transaction --> QRCode : optional
  LedgerEntry --> Transaction : posts 1..*
  SettlementBatch --> Merchant : for 1
  LimitsProfile <.. WalletAccount : applies
  Role "1" -- "many" Permission : grants
Loading

3) Process View (Key Flows)

3.1 Register & eKYC

sequenceDiagram
  autonumber
  actor Customer
  participant App as Mobile App
  participant API as API Gateway
  participant KYC as E-KYC Service
  participant Prov as KYC Provider
  participant LED as Ledger/Wallet

  Customer->>App: Submit phone, ID, selfie
  App->>API: POST /auth/register
  API->>KYC: Create verification (OCR+liveness)
  KYC->>Prov: Verify(ID data, selfie)
  Prov-->>KYC: result(score, status)
  KYC-->>API: decision(Approved/Pending/Rejected)
  API-->>App: registration status + token (if approved)
  API->>LED: Create Wallet Account (on approval)
  LED-->>API: walletId
Loading

3.2 Pay Merchant (MMQR)

sequenceDiagram
  autonumber
  actor Customer
  participant App
  participant API as API Gateway
  participant PAY as Payments Service
  participant SW as MMQR Switch
  participant LED as Ledger
  participant NOTI as Notification

  Customer->>App: Scan QR (static/dynamic)
  App->>API: POST /payments/qr {amount, qrData}
  API->>PAY: validate & authorize
  PAY->>SW: route/notify (if scheme requires)
  PAY->>LED: post double-entry (debit wallet, credit merchant holding)
  LED-->>PAY: posted + authCode
  PAY-->>API: response(approved/declined)
  API-->>App: show receipt
  API->>NOTI: push receipt to customer/merchant
Loading

3.3 Cash In / Cash Out

sequenceDiagram
  autonumber
  participant App
  participant API
  participant PAY as Payments
  participant EXT as Bank Core/Agent
  participant LED as Ledger

  App->>API: POST /wallet/cashin {amount, source}
  API->>PAY: request top-up
  PAY->>EXT: debit source (bank/card/agent)
  EXT-->>PAY: success
  PAY->>LED: credit wallet
  LED-->>PAY: posted
  PAY-->>API: ok
  API-->>App: Top-up success

  App->>API: POST /wallet/cashout {amount, agentId}
  API->>PAY: initiate cash-out
  PAY->>LED: debit wallet
  LED-->>PAY: posted
  PAY->>EXT: notify agent for payout
  PAY-->>API: token/otp
  API-->>App: present token to agent
Loading

Cross-cutting: risk checks (velocity, blacklist), idempotency keys, retries, audit events, and compensation for partial failures.


4) Development View (Modular Architecture, Mojura-aligned)

Modules and boundaries. This view focuses on modularity: each module exposes interfaces; internal implementation can vary (Laravel/NestJS/Go).

flowchart TB
subgraph Entry
API["API Layer (REST/GraphQL)"]
JOB[Job Scheduler / Workers]
EVT[Event Subscriptions]
end

subgraph Identity
ID_CTRL[Controllers]
ID_FEAT[Features: Register, Login, OTP]
ID_REQ[Requests/Validators]
ID_INT[Integrations: KYC Provider]
ID_DOM[Domain: Customer, KYCVerification]
end

subgraph Wallet
WL_CTRL[Controllers]
WL_FEAT[Features: Balance, Limits, Statements]
WL_DOM[Domain: WalletAccount, LedgerEntry]
WL_SVC["Ledger Engine (double-entry)"]
end

subgraph Payments
PM_CTRL[Controllers]
PM_FEAT[QR Pay, Refund, Reversal]
PM_INT[MMQR/Switch, Bank Core]
PM_DOM[Txn Domain]
end

subgraph MerchantAgent
MC_FEAT[Merchant Onboarding, QR Mgmt]
AG_FEAT[Agent Ops, Cashout]
end

subgraph AdminRBAC
ADM_CTRL[Admin APIs]
RBAC[Roles, Permissions, Policy]
AUD[Audit & Activity Log]
end

subgraph Support
CFG[Config]
MSG[(Message Broker)]
CACHE[(Cache)]
DB[(Datastores)]
NOTI[Notification Adapters]
OBS[Observability]
ALL[(All Services)]
end

API --> ID_CTRL
API --> WL_CTRL
API --> PM_CTRL
API --> ADM_CTRL

ID_CTRL --> ID_FEAT
ID_FEAT --> ID_DOM
ID_FEAT --> ID_REQ
ID_FEAT --> ID_INT

WL_CTRL --> WL_FEAT
WL_FEAT --> WL_DOM
WL_FEAT --> WL_SVC

PM_CTRL --> PM_FEAT
PM_FEAT --> PM_DOM
PM_FEAT --> PM_INT

PM_FEAT --> MC_FEAT
PM_FEAT --> AG_FEAT

ID_FEAT --> MSG
PM_FEAT --> MSG
WL_SVC --> DB
ID_DOM --> DB
PM_DOM --> DB
ADM_CTRL --> RBAC
RBAC --> DB
NOTI --> MSG

OBS -.-> ALL
Loading

Module Contracts (examples)

  • Identity exposes POST /auth/register, POST /auth/login, POST /auth/otp/resend.
  • Payments exposes POST /payments/qr, POST /payments/refund, idempotent PUT /payments/{id}.
  • Wallet exposes GET /wallet/balance, POST /wallet/cashin, POST /wallet/cashout.
  • Admin & RBAC exposes policy evaluation (/admin/policy/check) and audit feed.

Quality Attributes

  • Modularity > separate deployables possible.
  • Observability baked in: trace IDs flow end-to-end.
  • Resilience via retries, SAGA/compensation on cross-module operations.
  • Security: OAuth2/OIDC, mTLS between services, request signing to external rails.

5) Physical View (Deployment)

Target deployment with HA and external dependencies.

flowchart LR
  subgraph Client["Client Layer"]
    iOS[iOS App]
    And[Android App]
    POS[Merchant POS/App]
  end

  subgraph Edge["Edge & Security"]
    WAF[WAF/CDN]
    APIGW[API Gateway]
    OIDC["Identity Provider (OIDC)"]
  end

  subgraph Cluster["Kubernetes Cluster (Prod)"]
    subgraph NS1["Namespace: wallet"]
      SVC1[Identity & eKYC Service x2]
      SVC2[Payments Service x2]
      SVC3[Wallet/Ledger Service x2]
      SVC4[Admin & RBAC x2]
      NOTIF[Notification Worker x2]
      RPT[Analytics Worker]
      MSG[(Kafka/RabbitMQ HA)]
      CACHE[(Redis HA)]
    end

    subgraph NS2["Namespace: data"]
      DBOLTP[(PostgreSQL Primary/Replica)]
      DWH[(Data Warehouse)]
      OBJ[(Object Storage)]
    end

    subgraph NS3["Namespace: obs"]
      LOGS[(ELK/Cloud Logs)]
      METRICS[(Prometheus/Grafana)]
      TRACES[(OpenTelemetry/Jaeger)]
    end
  end

  subgraph External["External Systems"]
    CORE[(Bank Core / CASA)]
    SWITCH[(MMQR National Switch)]
    KYCP[(KYC Provider)]
    MSGGW[(SMS/Email/Push)]
  end

  iOS --> WAF
  And --> WAF
  POS --> WAF
  WAF --> APIGW --> OIDC
  APIGW --> SVC1 & SVC2 & SVC3 & SVC4
  SVC1 --- KYCP
  SVC2 --- SWITCH
  SVC2 --- CORE
  SVC1 & SVC2 & SVC3 & SVC4 --- MSG
  SVC1 & SVC2 & SVC3 & SVC4 --- CACHE
  SVC3 --- DBOLTP
  RPT --- DWH
  NOTIF --- MSGGW
  LOGS -.-> All
  METRICS -.-> All
  TRACES -.-> All
Loading

Non‑Functional Requirements (excerpt)

  • Performance: p95 API < 300ms for read, < 600ms for write under 2k RPS.
  • Availability: ≥ 99.9% monthly; active-active services; DB primary/replica.
  • Security: OIDC/OAuth2, mTLS, JWT audience checks, data encryption at rest (KMS) and in transit.
  • Compliance & Audit: immutable audit log; retention ≥ 7 years for financial transactions.
  • Scalability: horizontal auto-scaling on CPU/RPS; topic-based backpressure on async jobs.
  • Observability: trace_id in logs/metrics; SLO dashboards; alerting on error budgets.

Appendix A — Example API Surface (selected)

POST /auth/register
POST /auth/login
POST /auth/otp/resend

GET  /wallet/balance
POST /wallet/cashin
POST /wallet/cashout

POST /payments/qr
POST /payments/refund
GET  /payments/{id}

Appendix B — Data Entities (selected)

  • Customer(id, phone, name, kycStatus, primaryWalletId, createdAt)
  • WalletAccount(id, customerId, balance, status, limitsProfileId, createdAt)
  • Transaction(id, type, amount, currency, status, rrn, authCode, walletId, merchantId, createdAt)
  • LedgerEntry(id, txnId, debitAccountId, creditAccountId, amount, narration, createdAt)
  • Merchant(id, name, MCC, qrType, createdAt)

End of document.

Transaction Engine — Implementation Guide

Date: 2025-10-12
Scope: Unifies all wallet actions (transfer, cash-in, cash-out, bill pay, QR pay, refunds) behind a 3-step lifecycle: request → confirm → verify (+ post & settle).
Audience: Solution/Software Architects, Backend Engineers, Security/Compliance.


1. Concept & Principles

1.1 Core Idea

Every user-initiated financial action is normalized into a Transaction with a consistent state machine:

  • RequestedConfirmedVerifyingAuthorizedPostedSettled
    (plus Cancelled, Declined, Reversed, Refunded paths)

1.2 Why this pattern

  • Consistency: One audit trail and one set of invariants for all payment types.
  • Security: Centralizes Strong Customer Authentication (SCA) policies and risk step-up.
  • Reliability: Idempotent steps, retries, and event-driven integration (SAGA).
  • Extensibility: New payment types plug into the same lifecycle via adapters.

1.3 Non-Goals

  • Not a full card switch. It orchestrates and posts ledger entries, delegating scheme specifics to connectors (MMQR, Bank Core, Billers).

2. Lifecycle

stateDiagram-v2
  [*] --> Requested
  Requested --> Confirmed: confirm(details locked)
  Requested --> Cancelled: ttl/user cancel
  Confirmed --> Verifying: SCA challenge (PIN/FIDO/3DS/OOB)
  Verifying --> Authorized: success
  Verifying --> Declined: fail/risk
  Authorized --> Posted: ledger commit (double-entry)
  Posted --> Settled: batch/net settlement
  Posted --> Refunded: refund flow
  Posted --> Reversed: reversal flow (windowed)
Loading

2.1 Step Purposes

  • request: create intent; pre-validate; compute fees; run pre-risk.
  • confirm: freeze parameters; produce challenge context if SCA needed.
  • verify: validate PIN/biometric/3DS; produce authCode/decision.
  • post: write immutable double-entry; emit LedgerPosted.
  • settle: close merchant batches or external scheme settlements.

3. API Surface (v1)

All endpoints accept Idempotency-Key and propagate traceId.

3.1 Create Transaction — request

POST /v1/transactions
Content-Type: application/json
Idempotency-Key: 9b3b8f...

{
  "type": "QR_PAYMENT|P2P|CASHIN|CASHOUT|BILLPAY|REFUND",
  "amount": {"value": "1200.00", "currency": "MMK"},
  "source": {"walletId": "W123"},
  "target": {"merchantId": "M987", "qrPayload": "..."} ,
  "channel": "MOBILE",
  "metadata": {"note": "coffee", "clientVersion": "2.8.1"}
}

Response

{
  "transactionId": "TXN_01H...",
  "status": "Requested",
  "next": {"href": "/v1/transactions/TXN_01H.../confirm", "method": "POST"}
}

3.2 Confirm — confirm

POST /v1/transactions/{id}/confirm
Content-Type: application/json

{
  "finalAmount": {"value": "1200.00", "currency": "MMK"},
  "tips": {"value": "0.00"},
  "clientCapabilities": {"fido2": true, "pinpad": true}
}

Response (step-up needed)

{
  "status": "Confirmed",
  "challengeRequired": true,
  "challenge": {
    "method": "PIN|FIDO2|3DS|OOB",
    "nonce": "base64-...", 
    "expiresIn": 120
  },
  "next": {"href": "/v1/transactions/{id}/verify", "method": "POST"}
}

3.3 Verify — verify

  • PIN (online)
    Request:
    {
      "method": "PIN",
      "pinBlock": "hex-...",
      "ksn": "dukpt-ksn"
    }
  • FIDO2/WebAuthn
    Request:
    {
      "method": "FIDO2",
      "publicKeyCredential": {
        "id": "base64url",
        "rawId": "base64url",
        "response": {
          "clientDataJSON": "base64url",
          "authenticatorData": "base64url",
          "signature": "base64url",
          "userHandle": "base64url"
        },
        "type": "public-key"
      }
    }

Response

{
  "status": "Authorized",
  "authCode": "A12345",
  "next": {"href": "/v1/transactions/{id}/post", "method": "POST"}
}

3.4 Post — post (may be auto on success)

POST /v1/transactions/{id}/post

Response

{
  "status": "Posted",
  "ledgerEntries": [
    {"account": "WALLET:W123", "dr": "1200.00"},
    {"account": "MERCHANT_HOLD:M987", "cr": "1200.00"}
  ],
  "receipt": {"rrn": "241012123456", "timestamp": "2025-10-12T04:32:12Z"}
}

3.5 Refund/Reversal

  • POST /v1/transactions/{id}/refund
  • POST /v1/transactions/{id}/reverse (time-window, if not yet cleared externally)

3.6 Error Contract (example)

{
  "error": {
    "code": "SCA_REQUIRED|LIMIT_EXCEEDED|INSUFFICIENT_FUNDS|RISK_DECLINED|TIMEOUT",
    "message": "Human-readable",
    "details": {"hint": "Complete SCA within 120s"}
  }
}

4. Modules & Boundaries (Mojura-aligned)

flowchart TB
  subgraph Entry["Entry"]
    API[REST/GraphQL]
    JOB[Workers]
    EVT[Event Bus]
  end
  subgraph Engine["Transaction Engine"]
    SM[State Machine]
    RISK[Risk & Policy]
    SCA[SCA Orchestrator]
    IDMP[Idempotency Manager]
    TXR[Transaction Repository]
    OUTBX[Outbox]
  end
  subgraph Ledger["Ledger"]
    POSTER[Poster (double-entry)]
    ACCNT[Chart/Accounts]
  end
  subgraph Connectors["Rails/Connectors"]
    MMQR[MMQR Switch Adapter]
    CORE[Bank Core Adapter]
    BILL[Biller Adapter]
  end
  subgraph Sec["Security Services"]
    PIN[PIN Verify (HSM/DUKPT)]
    FIDO[FIDO2/WebAuthn]
    OIDC[OAuth2/OIDC]
  end
  subgraph Support["Shared"]
    CFG[Config]
    CACHE[(Redis)]
    DB[(OLTP)]
    MSG[(Kafka/RabbitMQ)]
    OBS[Observability]
  end

  API-->SM
  SM-->RISK-->SCA
  SM-->IDMP
  SM-->TXR
  SM-->OUTBX
  SM-->POSTER
  POSTER-->DB
  OUTBX-->MSG
  SCA-->PIN
  SCA-->FIDO
  SM-->MMQR & CORE & BILL
  OBS-.->API & SM & POSTER
  CACHE<-->SM
Loading

Responsibilities

  • State Machine: transitions, TTLs, guards.
  • Risk & Policy: velocity, device, geo, amount, AML flags; returns decision & step-up requirement.
  • SCA Orchestrator: issues/verifies challenges (PIN/FIDO/3DS/OOB).
  • Idempotency Manager: dedupe by (endpoint, idempotencyKey) with 24h TTL.
  • Outbox: reliable events for downstream services (receipt, analytics, settlement).

5. Security & SCA

5.1 PIN (online)

  • PIN block: ISO-0/ISO-3 formats over DUKPT; unwrap KSN; decrypt in HSM or secure module.
  • Counters & lockout: track PIN try counters per user/device; exponential backoff.
  • Transport: always mTLS; pinPad on device should avoid exposing raw PIN.

5.2 FIDO2/WebAuthn

  • Register device key to wallet user; store credentialId and public key.
  • At verify: check RP ID, origin, challenge (bind to transactionId+amount+payee), signature, and counter (anti-replay).
  • Prefer platform authenticators (face/fingerprint) for best UX and phishing resistance.

5.3 3DS/OOB (when using card rails)

  • Use 3DS challenge flow or out-of-band push approve app. Bind result JWS to transaction nonce.

5.4 Threats & Mitigations

  • Replay: nonce per verify bound to txn; single-use; short TTL.
  • TOCTOU: freeze parameters at confirm; hash “pay intent” into challenge.
  • Phishing: FIDO2; pin-by-channel; confirmation screens show human-readable payee & amount.
  • Device cloning: device key attestation; risk engine flags device/geo anomalies.

6. Ledger & Invariants

6.1 Double-entry Posting

  • Each Posted transaction must create ≥2 entries with Σ(dr) = Σ(cr).
  • Wallet balance = derived from ledger (materialized view or running balance table updated by poster).
sequenceDiagram
  autonumber
  participant SM as Engine
  participant LED as Ledger Poster
  participant DB as OLTP DB
  SM->>LED: post(transactionId, template)
  LED->>DB: insert ledger_lines (dr/cr)
  DB-->>LED: ok
  LED-->>SM: posted(authCode, lines)
Loading

6.2 Templates (examples)

  • QR Pay: DR WALLET:{customer} / CR MERCHANT_HOLD:{merchant}
  • Cash In: DR EXTERNAL_SETTLE:{bank} / CR WALLET:{customer}
  • Refund: reverse of original lines (new transaction referencing originalTxnId).

6.3 Idempotent Posting

  • Poster enforces uniqueness on (transactionId, postingPhase); replays yield the same result.

7. Data Model (Storage-Level, minimal)

Keep business model conceptual; storage adds only what’s needed for the engine.

  • transactions — id, type, status, amount (decimal), currency, sourceRef, targetRef, channel, authCode?, ttlAt, createdAt, updatedAt.
  • transaction_steps — transactionId, step (request|confirm|verify|post), payload(jsonb), result(jsonb), status, createdAt.
  • ledger_entries — id, transactionId, account, dr, cr, createdAt.
  • outbox_events — id, aggregateId, type, payload(jsonb), status, createdAt.
  • devices — userId, deviceId, publicKey, fidoCredentialId, metadata.
  • risk_signals — transactionId, deviceId, ip, geo, velocityScore, decision.

(PostgreSQL recommended; use jsonb for extensibility.)


8. Risk Engine (Step-up Decisioning)

  • Inputs: amount, user risk tier, device trust level, geo-velocity, time of day, merchant category, history, AML flags.
  • Output: ALLOW, ALLOW_WITH_SCA(methods=[PIN,FIDO]), DENY.
  • Policies expressed as rules (e.g., CEL, OPA/Rego) or trained scoring model (feature-gated).

9. Eventing & Integrations

9.1 Domain Events

  • TransactionRequested, TransactionConfirmed, ChallengeIssued, TransactionAuthorized, LedgerPosted, PaymentSettled, TransactionRefunded, TransactionReversed.

Event envelope

{
  "id": "evt_01H...",
  "type": "LedgerPosted",
  "occurredAt": "2025-10-12T04:32:12Z",
  "aggregateId": "TXN_01H...",
  "payload": { "lines":[...], "authCode":"A12345" },
  "traceId": "3f5..."
}

9.2 Connectors

  • Implement ports/adapters per rail:
    • MMQR (QR acquisition/notify)
    • Bank Core (debit/credit, cash-in/out)
    • Billers (bill lookup, payment post)
  • Run connectors behind circuit breakers; emit compensating actions on failure.

10. Idempotency, Concurrency, TTLs

  • Key: sha256(clientId + endpoint + idempotencyKey).
  • Store request + response; replay returns the saved response for 24h.
  • Use optimistic concurrency on transactions.status to avoid double progress.
  • TTLs: Requested 5–10 min; Confirmed 2–5 min; challenge 120s (configurable).

11. Observability & Audit

  • Propagate traceId/spanId; log step transitions with reasons.
  • Structured audit: who/what/when, old→new status, device info (PII-safe).
  • SLOs: p95 < 600ms for write steps; error budget alerts.

12. Reference Sequences

12.1 Step-up with PIN

sequenceDiagram
  autonumber
  actor User
  participant App
  participant API as Engine API
  participant SM as State Machine
  participant PIN as PIN Verify
  participant LED as Ledger

  User->>App: Initiate QR pay
  App->>API: POST /transactions (request)
  API->>SM: create intent
  SM-->>API: Requested
  App->>API: POST /transactions/{id}/confirm
  SM-->>API: challengeRequired (PIN, nonce)
  App->>API: POST /transactions/{id}/verify {pinBlock,ksn}
  API->>PIN: verify DUKPT + PIN
  PIN-->>API: ok (authCode)
  API->>SM: Authorized
  SM->>LED: post double-entry
  LED-->>SM: posted
  SM-->>API: Posted + receipt
  API-->>App: Success
Loading

12.2 FIDO2/WebAuthn

sequenceDiagram
  autonumber
  App->>API: confirm
  API-->>App: challenge (publicKey options)
  App->>API: verify (assertion)
  API: verify signature, origin, counter
  API->>SM: Authorized → Posted
Loading

13. Testing Strategy

  • Contract tests for each endpoint (idempotency, TTLs, concurrency).
  • Property tests for ledger invariant Σ(dr)=Σ(cr) per template.
  • Chaos/failure injection for connector timeouts & retries.
  • Security tests: PIN retry limits, FIDO origin binding, replay attempts.
  • E2E Flows: QR pay happy path; cash-out denial; refund path.

14. Configuration & Deployment

  • Externalize: challenge TTLs, risk thresholds, posting templates, connector endpoints.
  • Use feature flags to roll out SCA methods (PIN → FIDO).
  • Deploy as services: transaction-engine-api, engine-worker, ledger-poster, risk-service, pin-verify, fido2-service.
  • mTLS between services; rotate keys; HSM integration for PIN keys.

15. Migration & Backfill

  • Start with new transactions using the engine.
  • For legacy flows, wrap them as adapters feeding request/confirm/verify/post to keep a single audit trail.

16. Appendices

16.1 Example Posting Template (YAML)

name: qr_pay
debit:
  - account: "WALLET:${customerId}"
    amount: "${amount}"
credit:
  - account: "MERCHANT_HOLD:${merchantId}"
    amount: "${amount}"
validation:
  - sum(debit) == sum(credit)
  - currency == 'MMK'

16.2 Minimal SQL (PostgreSQL)

create table transactions (
  id text primary key,
  type text not null,
  status text not null,
  amount numeric(18,2) not null,
  currency text not null,
  source jsonb,
  target jsonb,
  auth_code text,
  created_at timestamptz default now(),
  updated_at timestamptz default now()
);

create table ledger_entries (
  id bigserial primary key,
  transaction_id text not null references transactions(id),
  account text not null,
  dr numeric(18,2) default 0,
  cr numeric(18,2) default 0,
  created_at timestamptz default now()
);

create unique index uq_txn_post on ledger_entries(transaction_id);

End of document.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment