C4 Pay-In Container & Component Diagrams¶
| Field | Value |
|---|---|
| Status | Draft |
| Owner | Pay-In Squad |
| Last Updated | 2026-04-03 |
| Applies To | Pay-In product line |
1. Overview¶
The Pay-In system handles all inbound payment collection for Simpaisa — single charges (synchronous and asynchronous), recurring subscriptions, and direct carrier billing. It processes the highest transaction volume across all product lines.
2. Container Diagram¶
graph TB
Merchant["🏪 Merchant<br/>(API Client)"]
Consumer["👤 Consumer<br/>(Mobile/Web)"]
subgraph "Cloudflare Edge"
CF_WAF["Cloudflare WAF"]
CF_Workers["Cloudflare Workers<br/>(Rate limiting, geo-routing)"]
end
subgraph "API Gateway"
KrakenD["KrakenD Gateway<br/>(Auth, throttling, routing)"]
end
subgraph "Pay-In Services"
PayInSvc["Pay-In Service<br/>(Go)<br/>Charge orchestration"]
OTPSvc["OTP Service<br/>(Go)<br/>OTP generation & validation"]
TokenSvc["Token Service<br/>(Go)<br/>Payment token lifecycle"]
end
subgraph "Channel Adapters"
EP_Adapter["Easypaisa Adapter"]
JC_Adapter["JazzCash Adapter"]
HBL_Adapter["HBL Konnect Adapter"]
Alfa_Adapter["Alfa Adapter"]
Zindagi_Adapter["Zindagi Adapter"]
Telenor_Adapter["Telenor DCB Adapter"]
Zong_Adapter["Zong DCB Adapter"]
Ufone_Adapter["Ufone DCB Adapter"]
bKash_Adapter["bKash Adapter"]
PayMob_Adapter["PayMob Adapter"]
AamarPay_Adapter["AamarPay Adapter"]
end
subgraph "Data Stores"
SurrealDB["SurrealDB<br/>(Transactions, tokens)"]
Redis["Redis<br/>(OTP cache, idempotency)"]
end
subgraph "Messaging"
NSQ["NSQ<br/>(Event bus)"]
end
Merchant -- "HTTPS" --> CF_WAF
Consumer -- "HTTPS (OTP/redirect)" --> CF_WAF
CF_WAF --> CF_Workers
CF_Workers --> KrakenD
KrakenD -- "gRPC" --> PayInSvc
PayInSvc -- "gRPC" --> OTPSvc
PayInSvc -- "gRPC" --> TokenSvc
PayInSvc -- "gRPC" --> EP_Adapter
PayInSvc -- "gRPC" --> JC_Adapter
PayInSvc -- "gRPC" --> HBL_Adapter
PayInSvc -- "gRPC" --> Alfa_Adapter
PayInSvc -- "gRPC" --> Zindagi_Adapter
PayInSvc -- "gRPC" --> Telenor_Adapter
PayInSvc -- "gRPC" --> Zong_Adapter
PayInSvc -- "gRPC" --> Ufone_Adapter
PayInSvc -- "gRPC" --> bKash_Adapter
PayInSvc -- "gRPC" --> PayMob_Adapter
PayInSvc -- "gRPC" --> AamarPay_Adapter
PayInSvc --> SurrealDB
OTPSvc --> Redis
TokenSvc --> SurrealDB
PayInSvc -- "Publish" --> NSQ
3. Component Diagram — Pay-In Service Internals¶
graph TB
subgraph "Pay-In Service (Go)"
API["gRPC API Layer<br/>Request validation, auth"]
Orchestrator["Charge Orchestrator<br/>Flow selection, state machine"]
ChannelRouter["Channel Router<br/>Selects adapter by channel code"]
IdempotencyGuard["Idempotency Guard<br/>Redis-backed dedup"]
RecurringEngine["Recurring Engine<br/>Subscription lifecycle"]
CallbackHandler["Callback Handler<br/>Async channel responses"]
EventPublisher["Event Publisher<br/>NSQ domain events"]
TxnRepository["Transaction Repository<br/>SurrealDB persistence"]
end
API --> IdempotencyGuard
IdempotencyGuard --> Orchestrator
Orchestrator --> ChannelRouter
Orchestrator --> RecurringEngine
ChannelRouter --> |"gRPC to adapters"| ExternalAdapters["Channel Adapters"]
CallbackHandler --> Orchestrator
Orchestrator --> TxnRepository
Orchestrator --> EventPublisher
4. Data Flows¶
4.1 Single Charge — Synchronous¶
This flow applies to channels that return a final status in real time (e.g. Easypaisa, JazzCash with PIN).
sequenceDiagram
participant M as Merchant
participant K as KrakenD
participant P as Pay-In Service
participant A as Channel Adapter
participant Ch as Channel (e.g. Easypaisa)
participant DB as SurrealDB
participant Q as NSQ
M->>K: POST /v1/charges {amount, channel, msisdn}
K->>P: gRPC CreateCharge
P->>DB: Insert txn (status: pending)
P->>A: gRPC InitiateCharge
A->>Ch: REST API call
Ch-->>A: Success/Failure
A-->>P: ChargeResult
P->>DB: Update txn (status: completed/failed)
P->>Q: Publish txn.payin.completed
P-->>K: ChargeResponse
K-->>M: 200 OK {txn_id, status}
4.2 Single Charge — Asynchronous¶
This flow applies to channels requiring consumer interaction (OTP, wallet redirect).
sequenceDiagram
participant M as Merchant
participant K as KrakenD
participant P as Pay-In Service
participant O as OTP Service
participant C as Consumer
participant A as Channel Adapter
participant DB as SurrealDB
participant Q as NSQ
M->>K: POST /v1/charges {amount, channel, msisdn}
K->>P: gRPC CreateCharge
P->>DB: Insert txn (status: pending_auth)
P->>O: gRPC SendOTP
O->>C: SMS OTP
P-->>K: 202 Accepted {txn_id, status: pending_auth}
K-->>M: 202 Accepted
C->>K: POST /v1/charges/{id}/confirm {otp}
K->>P: gRPC ConfirmCharge
P->>O: gRPC VerifyOTP
O-->>P: OTP Valid
P->>A: gRPC ExecuteCharge
A-->>P: ChargeResult
P->>DB: Update txn (status: completed)
P->>Q: Publish txn.payin.completed
P-->>K: ChargeResponse
K-->>M: 200 OK {txn_id, status: completed}
Q-->>M: Webhook notification
4.3 Recurring Charges¶
sequenceDiagram
participant M as Merchant
participant P as Pay-In Service
participant T as Token Service
participant A as Channel Adapter
participant DB as SurrealDB
Note over M,DB: Initial subscription setup
M->>P: POST /v1/subscriptions {channel, msisdn, interval}
P->>T: gRPC CreateToken (after consumer authorisation)
T->>DB: Store payment token
P->>DB: Insert subscription record
Note over M,DB: Recurring charge (triggered by scheduler)
P->>T: gRPC ResolveToken
T-->>P: Channel credentials
P->>A: gRPC ExecuteCharge (with token)
A-->>P: ChargeResult
P->>DB: Update subscription, insert txn
5. Data Model — Key Entities¶
| Entity | Store | Key Fields |
|---|---|---|
| Charge | SurrealDB | id, merchant_id, channel, amount, currency, status |
| PaymentToken | SurrealDB | id, merchant_id, msisdn, channel, token_ref, expires |
| Subscription | SurrealDB | id, merchant_id, token_id, interval, next_charge_at |
| OTP | Redis | key (txn_id), otp_hash, attempts, ttl (5 min) |
| Idempotency | Redis | key (merchant_id:request_id), response, ttl (24 hr) |
6. Non-Functional Requirements¶
| Metric | Target |
|---|---|
| Availability | 99.95% |
| P50 latency (sync) | < 800 ms |
| P99 latency (sync) | < 3 s |
| Throughput | 500 TPS peak |
| Idempotency window | 24 hours |
7. Architectural Decision Records¶
Changes to Pay-In architecture require an ADR in /Standards/ADR/.