A cost-optimized AWS deployment for testing FinTech applications against Cambodia's Digital Public Infrastructure (DPI) mock APIs.
This project provides mock implementations of Cambodia's core DPI services for development and testing:
| Service | Description | Endpoints |
|---|---|---|
| CamDigiKey | Digital Identity & e-KYC | /v1/camdigikey/verify, /v1/camdigikey/sign |
| Bakong | Payments & KHQR | /v1/bakong/khqr/generate, /v1/bakong/settlement |
| CamDL | Audit & Trust Ledger | /v1/camdl/audit/log, /v1/camdl/verify |
| CamDX | Data Exchange & Consent | /v1/camdx/consent, /v1/camdx/exchange |
| GaaS | Gov APIs (Registry, CBC, AML) | /registry/*, /cbc/*, /aml/* |
┌─────────────────────────────────────────────────────────────┐
│ FinTech Test Clients │
└───────────────────────────┬─────────────────────────────────┘
│
┌───────────────────────────▼─────────────────────────────────┐
│ API Gateway (HTTP API) │
│ Pay-per-request pricing │
└───────────────────────────┬─────────────────────────────────┘
│
┌───────────────────────────▼─────────────────────────────────┐
│ VPC (Single AZ) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ CamDigiKey │ │ Bakong │ │ CamDL │ │
│ │ Mock │ │ Mock │ │ Mock │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ CamDX Mock │ │ GaaS Mock │ ←── ECS Fargate (Spot) │
│ └─────────────┘ └─────────────┘ │
│ │ │
│ ┌──────▼──────────────────────────────────────────────┐ │
│ │ RDS PostgreSQL (db.t4g.micro) │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
-
Start all services locally:
docker-compose -f docker-compose.local.yml up --build
-
Test the APIs:
# Health check curl http://localhost:8080/health # Verify identity curl -X POST http://localhost:8080/v1/camdigikey/verify \ -H "Content-Type: application/json" \ -d '{"national_id": "123456789"}' # Generate KHQR payment curl -X POST http://localhost:8080/v1/bakong/khqr/generate \ -H "Content-Type: application/json" \ -d '{"amount": 10000, "currency": "KHR"}' # Log audit event curl -X POST http://localhost:8080/v1/camdl/audit/log \ -H "Content-Type: application/json" \ -d '{"event_type": "login", "actor": {"id": "user123"}, "action": "authenticate"}'
-
Prerequisites:
- AWS CLI configured with appropriate credentials
- Terraform >= 1.5.0
- Docker
-
Set environment variables:
export TF_VAR_db_password="your-secure-password" export AWS_REGION="ap-southeast-1"
-
Initialize and deploy:
cd terraform terraform init terraform plan terraform apply -
Build and push Docker images:
# Login to ECR aws ecr get-login-password --region ap-southeast-1 | \ docker login --username AWS --password-stdin \ <ACCOUNT_ID>.dkr.ecr.ap-southeast-1.amazonaws.com # Build and push each service for service in gaas-mock camdigikey-mock bakong-mock camdl-mock camdx-mock; do docker build --platform linux/arm64 -t camdpi/$service ./services/$service docker tag camdpi/$service:latest \ <ACCOUNT_ID>.dkr.ecr.ap-southeast-1.amazonaws.com/camdpi/$service:latest docker push <ACCOUNT_ID>.dkr.ecr.ap-southeast-1.amazonaws.com/camdpi/$service:latest done
-
Get the API endpoint:
terraform output api_gateway_url
POST /v1/camdigikey/verify
Content-Type: application/json
{
"national_id": "123456789",
"verification_type": "full", # "full" | "basic"
"liveness_check": true,
"scenario": "happy_path" # Test scenarios: happy_path, low_assurance, pep, biometric_fail
}POST /v1/camdigikey/sign
Content-Type: application/json
{
"national_id": "123456789",
"document_hash": "sha256:abc123...",
"document_type": "contract",
"sign_method": "otp"
}POST /v1/bakong/khqr/generate
Content-Type: application/json
{
"amount": 10000,
"currency": "KHR", # "KHR" | "USD"
"merchant_id": "MERCHANT001",
"reference": "INV-001",
"expires_in": 1800 # seconds
}GET /v1/bakong/khqr/status/{transaction_id}POST /v1/bakong/khqr/simulate-payment
Content-Type: application/json
{
"transaction_id": "TXN-xxx",
"wallet_provider": "ABA",
"status": "completed"
}POST /v1/camdl/audit/log
Content-Type: application/json
{
"event_type": "kyc_verification",
"actor": {
"type": "user",
"id": "user123",
"name": "Test User"
},
"action": "verify_identity",
"details": {
"national_id": "123456789",
"result": "verified"
}
}POST /v1/camdl/verify
Content-Type: application/json
{
"evidence_hash": "sha256:abc123...",
"log_id": "LOG-xxx"
}GET /v1/camdx/participantsPOST /v1/camdx/consent
Content-Type: application/json
{
"subject_id": "CITIZEN-123456",
"data_provider": "camdigikey",
"data_consumer": "fintech_app",
"purpose": "identity_verification",
"scope": ["full_name", "date_of_birth"],
"duration_days": 30
}POST /v1/camdx/consent/verify
Content-Type: application/json
{
"consent_id": "CONSENT-xxx"
}POST /v1/camdx/exchange
Content-Type: application/json
{
"consent_id": "CONSENT-xxx",
"source_service": "camdigikey",
"target_service": "bakong",
"operation": "verify_identity",
"payload": {"citizen_id": "123456789"}
}GET /v1/camdx/audit
GET /v1/camdx/audit?consent_id=CONSENT-xxx
GET /v1/camdx/audit?source_service=camdigikeyGET /registry/businesses/{reg_no}GET /cbc/company/{reg_no}POST /aml/screen
Content-Type: application/json
{
"scenario": "clean" # "clean" | "pep_hit" | "sanctions_hit"
}All mock services support scenario-based testing via the scenario parameter:
| Service | Scenario | Behavior |
|---|---|---|
| CamDigiKey | happy_path |
Successful verification |
| CamDigiKey | biometric_fail |
Biometric mismatch error |
| CamDigiKey | pep |
User flagged as PEP |
| Bakong | instant_success |
Payment completes immediately |
| Bakong | decline |
Payment declined after 5s |
| CamDL | verification_failed |
Hash mismatch |
| CamDX | granted |
Consent granted (default) |
| CamDX | denied |
Consent denied |
| CamDX | expired |
Consent expired |
| CamDX | timeout |
Exchange timeout error |
| AML | pep_hit |
PEP match found |
CamDX-clone/
├── terraform/ # AWS Infrastructure
│ ├── main.tf # Provider config
│ ├── vpc.tf # VPC, subnets, security groups
│ ├── ecs.tf # ECS cluster, services, ALB
│ ├── rds.tf # PostgreSQL database
│ ├── api-gateway.tf # HTTP API Gateway
│ ├── variables.tf # Input variables
│ ├── outputs.tf # Output values
│ └── terraform.tfvars # Variable values
├── services/
│ ├── gaas-mock/ # GaaS combined mock
│ ├── camdigikey-mock/ # Digital identity mock
│ ├── bakong-mock/ # Payment system mock
│ ├── camdl-mock/ # Audit ledger mock
│ └── camdx-mock/ # Data exchange & consent mock
├── nginx/
│ └── nginx.conf # Local API gateway config
├── scripts/
│ └── init-db.sql # Database initialization
├── docker-compose.local.yml # Local development
└── README.md
| Service | Specification | Monthly Cost |
|---|---|---|
| NAT Gateway | 1x single AZ | $32 |
| ECS Fargate | 5 tasks, Spot | $30-50 |
| RDS PostgreSQL | db.t4g.micro | $15 |
| API Gateway | HTTP API | $10-20 |
| ALB | Internal | $16 |
| CloudWatch | Basic | $10 |
| ECR | Storage | $5 |
| Total | $120-150 |
- API Gateway endpoints are public but can be secured with API keys
- RDS is in private subnet, accessible only from ECS tasks
- All secrets stored in AWS Secrets Manager
- VPC endpoints reduce NAT gateway costs and improve security
cd terraform
terraform destroyMIT