Designing a payment translation service from requirements to deployment.
1. What I'm Building & Why
The Scenario
You're a Solutions Architect at a commercial bank. The bank has 200,000+ customers sending and receiving money internationally. The legacy system crashes weekly, payments take days, and the regulator says: "Adopt ISO 20022 or lose your license."
The CEO asks: "Can you build a system that takes requests from our app, produces correct international payment messages, and won't get us fined?"
What is ISO 20022?
The global standard format banks use to communicate. Think of it as a structured shipping label for money.
| Old Way (MT messages) | New Way (ISO 20022) |
|---|---|
| Free-text, vague | Structured, machine-readable |
| ~100 data fields | 7,000+ data fields |
| Hard to trace | Every step tracked |
What is pacs.008?
The specific message type for: "Customer at Bank A wants to send money to someone at Bank B."
What is a Microgateway?
A small service that does one job: takes JSON from your app and outputs valid ISO 20022 XML for the payment network.
App (JSON) → Microgateway → ISO 20022 XML → Bank Network
2. The Solutions Architect Role
The SA sits between business and engineering understanding both, making decisions that affect the whole system.
| Activity | Time | Example |
|---|---|---|
| Stakeholder meetings | 30% | Explaining trade-offs, gathering requirements |
| Architecture design | 25% | Diagrams, technology choices |
| Technical review | 20% | Reviewing code and infra plans |
| Documentation | 15% | Decision records, runbooks |
| Hands-on building | 10% | Prototypes, proof-of-concepts |
What this project demonstrates:
| Skill | How It's Shown |
|---|---|
| Requirements gathering | Stakeholder interviews, requirement tables |
| Architecture design | VPC, EKS, multi-AZ, auto-scaling |
| Security thinking | Input validation, no sensitive logs, zero-trust |
| Infrastructure as Code | Terraform |
| Testing strategy | 27 tests covering all paths |
| Business communication | Executive summary, ROI calculations |
3. Phase 0: Requirements
Stakeholder Interviews (Simulated)
CFO: "We lose $500K/year on payment failures. Cut error rate from 8% to under 1%."
CISO: "Zero-trust. No sensitive data in logs. Encryption everywhere."
Compliance Officer: "7-year audit trail. Every message traceable. Fines for non-compliance are 10% of annual turnover."
Head of Operations: "Peak: 50 transactions/second at end-of-month. 99.95% uptime required."
Functional Requirements
| ID | Requirement | Priority |
|---|---|---|
| FR-01 | Accept JSON payment requests via API | Must-have |
| FR-02 | Validate all input fields | Must-have |
| FR-03 | Generate pacs.008 XML | Must-have |
| FR-04 | Return unique tracking IDs | Must-have |
| FR-05 | Support multiple currencies (EUR, USD, GBP, KES, JPY) | Must-have |
Non-Functional Requirements
| ID | Requirement | Target |
|---|---|---|
| NFR-01 | Response time | < 200ms |
| NFR-02 | Throughput | 50 TPS peak |
| NFR-03 | Availability | 99.95% |
| NFR-04 | Data retention | 7 years |
| NFR-05 | Recovery time | < 30 minutes |
Architecture Decision: Cloud Provider
| Option | Score | Rationale |
|---|---|---|
| AWS | 9/10 | Team skills, broad services, enterprise support |
| Azure | 7/10 | Good compliance tools, less regional presence |
| GCP | 6/10 | Best K8s, smallest enterprise footprint |
Decision: AWS.
4. Phase 1: Infrastructure
Architecture Overview
Key Design Decisions
| Decision | Why |
|---|---|
| Private subnets for app | No direct internet exposure |
| Multi-AZ (3 availability zones) | Survives data center failure |
| ON_DEMAND instances | Financial workloads need reliability |
| EKS managed nodes | AWS handles patching |
| NAT Gateway | Outbound only — internet can't reach in |
What the Terraform Creates
The terraform/main.tf provisions:
- VPC (
rare-bank-vpc) with public/private subnets across 3 AZs - EKS cluster (
payment-microgateway) with auto-scaling (1–10 nodes) - S3 backend for state with DynamoDB locking
- Environment-aware config (dev/staging/production)
5. Phase 2: Payment Logic
Data Flow
Mobile App (JSON) → Validate → Generate IDs → Build XML → Return Response
│
▼
Audit Log (hash)
Input (JSON from the mobile app)
{
"sender_name": "John Doe",
"sender_account": "RA1234567890123456",
"sender_bic": "RCBKRAXX",
"receiver_name": "Elijah Chimera",
"receiver_account": "RA29CBRR60161331926819",
"receiver_bic": "CBRARERX",
"amount": 1250.50,
"currency": "EUR",
"purpose_code": "SALA",
"remittance_info": "Invoice #12345 - Consulting Services"
}
Field Mapping
| JSON Field | XML Element | Validation |
|---|---|---|
| sender_name | <Dbtr><Nm> |
1–140 chars |
| sender_account | <DbtrAcct><Id><Othr><Id> |
Min 5 chars, spaces stripped |
| sender_bic | <DbtrAgt><FinInstnId><BICFI> |
8 or 11 chars, uppercase (Fleet Code) |
| receiver_name | <Cdtr><Nm> |
1–140 chars |
| receiver_account | <CdtrAcct><Id><Othr><Id> |
Min 5 chars, spaces stripped |
| receiver_bic | <CdtrAgt><FinInstnId><BICFI> |
8 or 11 chars, uppercase (Fleet Code) |
| amount | <IntrBkSttlmAmt> |
> 0, ≤ 999,999,999.99 |
| currency |
Ccy attribute |
3 chars, ISO 4217 |
| purpose_code | <Purp><Cd> |
4 uppercase chars (optional) |
| remittance_info | <RmtInf><Ustrd> |
Max 140 chars (optional) |
Application Layers
┌─────────────────────────────────┐
│ MIDDLEWARE (CORS, timing) │
├─────────────────────────────────┤
│ VALIDATION (Pydantic) │
├─────────────────────────────────┤
│ PROCESSING (IDs, XML, hash) │
├─────────────────────────────────┤
│ RESPONSE (JSON + error format) │
└─────────────────────────────────┘
Key Code Concepts
Fleet Code validation regex: ^[A-Z]{6}[A-Z0-9]{2}([A-Z0-9]{3})?$
- 6 uppercase letters (bank + country) + 2 alphanumeric (location) + optional 3 (branch)
- Valid:
RCBKRAXX(8 chars),CBRARERXXXX(11 chars) - Invalid:
cbrarerx(lowercase),RCBL(too short)
XML namespace: urn:iso:std:iso:20022:tech:xsd:pacs.008.001.08
- Without this, payment networks reject the message entirely.
Audit hash: SHA-256 of partial data — creates a traceable fingerprint without exposing sensitive details.
Output (ISO 20022 XML)
<?xml version="1.0" ?>
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pacs.008.001.08" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<FIToFICstmrCdtTrf>
<GrpHdr>
<MsgId>MSG7A3F2B...</MsgId>
<CreDtTm>2025-01-15T10:30:00.000Z</CreDtTm>
<NbOfTxs>1</NbOfTxs>
<SttlmInf>
<SttlmMtd>CLRG</SttlmMtd>
</SttlmInf>
</GrpHdr>
<CdtTrfTxInf>
<IntrBkSttlmAmt Ccy="EUR">1250.50</IntrBkSttlmAmt>
<ChrgBr>SLEV</ChrgBr>
<Dbtr><Nm>John Doe</Nm></Dbtr>
<CdtrAgt><FinInstnId><BICFI>CBRARERX</BICFI></FinInstnId></CdtrAgt>
<Cdtr><Nm>Elijah Chimera</Nm></Cdtr>
</CdtTrfTxInf>
</FIToFICstmrCdtTrf>
</Document>
Dockerfile
Multi-stage build:
- Builder stage — installs dependencies with compilers
-
Runtime stage — minimal image, non-root user (
appuser), health check
Runs with 4 Uvicorn workers in production.
6. Phase 3: Security
Defense in Depth
| Layer | Protection |
|---|---|
| Perimeter | WAF, rate limiting, TLS 1.3 |
| Network | VPC isolation, private subnets, security groups |
| Application | Pydantic validation (Fleet Code format, amount range, name length) |
| Data | Encryption at rest/transit, no sensitive data in logs |
| Monitoring | Real-time alerts, immutable audit trails |
What Gets Logged vs. What Doesn't
| Log This | Never Log This |
|---|---|
| Timestamp, request ID | Full account numbers |
| HTTP status, duration | Customer names |
| Currency, error type | Exact amounts |
| Message ID (for tracing) | Passwords, API keys |
Example safe log from the app:
Payment request received | IP: 192.168.x.x | Currency: EUR | Amount: [REDACTED] | Sender: Joh*** | Receiver BIC: CBRA***
Key Alerts
- Error rate > 5% (CloudWatch alarm configured)
- p95 latency > 500ms
- CPU > 80% for 5 minutes
- Any 5xx in production
7. Phase 4: Testing
27 Tests Covering:
| Category | Count | What It Proves |
|---|---|---|
| Health & info | 2 | Service is alive |
| Valid payments | 3 | Core flow works (full fields, minimal, 11-char Fleet Code) |
| Amount validation | 3 | Rejects zero, negative, too-large |
| Fleet Code validation | 2 | Rejects malformed, lowercase |
| Currency handling | 3 | Validates length, auto-uppercases, formats XML |
| Name validation | 3 | Handles empty, max-length, too-long |
| Purpose/account | 2 | Format enforcement |
| XML structure | 4 | Namespace, well-formed, formatting, rounding |
| ID generation | 2 | Unique IDs (MSG prefix, 23 chars), proper hash (16 hex chars) |
| Edge cases | 4 | Special chars, long names, IBAN spaces, multiple currencies |
| CORS | 1 | Cross-origin headers present |
Deployment Strategy: Blue-Green
- Deploy new version (GREEN) alongside current (BLUE)
- Route 10% traffic to GREEN, monitor
- If metrics are good, shift to 100%
- Keep BLUE on standby for instant rollback
8. Phase 5: Business Communication
Executive Summary
| Metric | Before | After | Annual Impact |
|---|---|---|---|
| Error rate | 8% | < 1% | $435K savings |
| Settlement time | 2–5 days | < 4 hours | Better cash flow |
| Manual work | 40 hrs/week | 4 hrs/week | $187K savings |
| Infra cost | $800K (mainframe) | $200K (cloud) | $600K savings |
Investment: $180K initial + $70K/year
ROI: 340% in Year 1
Technology Choices
| Choice | Why |
|---|---|
| AWS EKS | Team has Kubernetes skills, mature ecosystem |
| FastAPI | Fast, auto-generates API docs, modern Python |
| Terraform | Reproducible infra, version-controlled, team collaboration via S3 state |
| Python | Rapid development, good for data transformation |
9. Running It Yourself
# Clone and setup
git clone https://github.com/YOUR_USERNAME/iso20022-microgateway.git
cd iso20022-microgateway
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
# Run tests (all 27 should pass)
pytest -v
# Start server
uvicorn main:app --reload
# Docs at http://localhost:8000/docs
Test a payment:
curl -X POST http://localhost:8000/api/v1/payments/transfer \
-H "Content-Type: application/json" \
-d '{
"sender_name": "John Doe",
"sender_account": "RA1234567890123456",
"sender_bic": "RCBKRAXX",
"receiver_name": "Elijah Chimera",
"receiver_account": "RA29CBRR60161331926819",
"receiver_bic": "CBRARERX",
"amount": 1250.50,
"currency": "EUR",
"purpose_code": "SALA",
"remittance_info": "Invoice #12345 - Consulting Services"
}'
Docker:
docker build -t iso-gateway .
docker run -p 8000:8000 iso-gateway
To see the live demo run the following locally.
cd /home/chimera/Downloads/iso20022-microgateway
source venv/bin/activate
python live_demo.py
10. Q and A
Q: Why ISO 20022 over MT messages?
Regulatory mandate, richer data (7000+ fields vs 100), and it's the global standard for instant payments and open banking.
Q: Why Kubernetes?
Team expertise, consistent tooling for multi-region expansion, and future microservices. For smaller scale, ECS or Lambda would also work.
Q: How do you handle mid-processing failures?
Idempotency keys + state machine. Each payment transitions through states (RECEIVED → VALIDATED → SUBMITTED → SETTLED). Failures resume from last known state.
Q: How do you prioritize when everything is urgent?
Impact × Urgency. "If we do nothing for 24 hours, what breaks?" Document the rationale, get stakeholder sign-off on trade-offs.
Tech Stack
Python 3.12+ · FastAPI · Pydantic · Docker · Terraform · GitHub Actions · Pytest
Disclaimer
Portfolio/learning project only. Does not process real money or connect to any live payment network.
License
MIT














Top comments (0)