C4 Pay-Out Container & Component Diagrams¶
| Field | Value |
|---|---|
| Status | Draft |
| Owner | Pay-Out Squad |
| Last Updated | 2026-04-03 |
| Applies To | Pay-Out product line |
1. Overview¶
The Pay-Out system handles all outbound fund disbursements — single payouts, batch payouts, and scheduled settlements. It uses Temporal for durable workflow orchestration, ensuring reliable delivery even when downstream channels experience outages. Pay-Outs operate across Pakistan (1Link, RAAST, mobile wallets) and Bangladesh (bKash, BRAC, Faysal, Prime, Agrani).
2. Container Diagram¶
graph TB
Merchant["🏪 Merchant<br/>(API Client)"]
subgraph "API Gateway"
KrakenD["KrakenD Gateway<br/>(Auth, throttling, routing)"]
end
subgraph "Pay-Out Services"
PayOutSvc["Pay-Out Service<br/>(Go)<br/>Disbursement orchestration"]
end
subgraph "Workflow Engine"
Temporal["Temporal Server<br/>Durable workflows<br/>Retry, timeout, compensation"]
TemporalWorker["Temporal Worker<br/>(Go)<br/>Executes payout activities"]
end
subgraph "Channel Adapters — Pakistan"
OneLink_Adapter["1Link Adapter<br/>(ISO 8583)"]
RAAST_Adapter["RAAST Adapter<br/>(ISO 20022)"]
EP_PO_Adapter["Easypaisa Pay-Out Adapter"]
JC_PO_Adapter["JazzCash Pay-Out Adapter"]
HBL_PO_Adapter["HBL Konnect Pay-Out Adapter"]
end
subgraph "Channel Adapters — Bangladesh"
bKash_PO["bKash Pay-Out Adapter"]
BRAC_PO["BRAC Bank Adapter"]
Faysal_PO["Faysal Bank Adapter"]
Prime_PO["Prime Bank Adapter"]
Agrani_PO["Agrani Bank Adapter"]
end
subgraph "Data Stores"
SurrealDB["SurrealDB<br/>(Payouts, batches, ledger)"]
end
subgraph "Messaging"
NSQ["NSQ<br/>(Event bus)"]
end
Merchant -- "HTTPS" --> KrakenD
KrakenD -- "gRPC" --> PayOutSvc
PayOutSvc -- "Start workflow" --> Temporal
Temporal -- "Dispatch activities" --> TemporalWorker
TemporalWorker -- "gRPC" --> OneLink_Adapter
TemporalWorker -- "gRPC" --> RAAST_Adapter
TemporalWorker -- "gRPC" --> EP_PO_Adapter
TemporalWorker -- "gRPC" --> JC_PO_Adapter
TemporalWorker -- "gRPC" --> HBL_PO_Adapter
TemporalWorker -- "gRPC" --> bKash_PO
TemporalWorker -- "gRPC" --> BRAC_PO
TemporalWorker -- "gRPC" --> Faysal_PO
TemporalWorker -- "gRPC" --> Prime_PO
TemporalWorker -- "gRPC" --> Agrani_PO
PayOutSvc --> SurrealDB
TemporalWorker --> SurrealDB
TemporalWorker -- "Publish" --> NSQ
3. Component Diagram — Pay-Out Service Internals¶
graph TB
subgraph "Pay-Out Service (Go)"
API["gRPC API Layer<br/>Request validation"]
SinglePayout["Single Payout Handler<br/>Immediate disbursement"]
BatchPayout["Batch Payout Handler<br/>CSV/API batch ingestion"]
WorkflowStarter["Workflow Starter<br/>Temporal client"]
BalanceChecker["Balance Checker<br/>Pre-flight sufficiency check"]
LedgerWriter["Ledger Writer<br/>Double-entry bookkeeping"]
EventPublisher["Event Publisher<br/>NSQ domain events"]
PayoutRepository["Payout Repository<br/>SurrealDB persistence"]
end
API --> BalanceChecker
BalanceChecker --> SinglePayout
BalanceChecker --> BatchPayout
SinglePayout --> WorkflowStarter
BatchPayout --> WorkflowStarter
WorkflowStarter --> |"Temporal SDK"| TemporalCluster["Temporal Server"]
LedgerWriter --> PayoutRepository
EventPublisher --> NSQCluster["NSQ"]
4. Temporal Workflow — Single Payout¶
sequenceDiagram
participant M as Merchant
participant K as KrakenD
participant P as Pay-Out Service
participant T as Temporal
participant W as Temporal Worker
participant A as Channel Adapter
participant Ch as Channel
participant DB as SurrealDB
participant Q as NSQ
M->>K: POST /v1/payouts {beneficiary, amount, channel}
K->>P: gRPC CreatePayout
P->>DB: Insert payout (status: initiated)
P->>T: StartWorkflow(DisbursementWorkflow)
P-->>K: 202 Accepted {payout_id}
K-->>M: 202 Accepted
T->>W: Execute ValidateBeneficiary activity
W->>A: gRPC ValidateAccount
A->>Ch: Account verification
Ch-->>A: Valid
A-->>W: Validated
T->>W: Execute DebitMerchant activity
W->>DB: Debit merchant ledger
T->>W: Execute TransferFunds activity
W->>A: gRPC Transfer
A->>Ch: Fund transfer
Ch-->>A: Transfer confirmed
A-->>W: Success
T->>W: Execute RecordSettlement activity
W->>DB: Update payout (status: completed)
W->>Q: Publish txn.payout.completed
Q-->>M: Webhook notification
5. Temporal Workflow — Batch Payout¶
sequenceDiagram
participant M as Merchant
participant P as Pay-Out Service
participant T as Temporal
participant W as Temporal Worker
participant DB as SurrealDB
M->>P: POST /v1/payouts/batch {file or items[]}
P->>DB: Insert batch record (status: processing)
P->>T: StartWorkflow(BatchDisbursementWorkflow)
loop For each payout item
T->>W: Execute ValidateBeneficiary
T->>W: Execute DebitMerchant
T->>W: Execute TransferFunds
Note over T,W: Failed items retry 3x<br/>then marked as failed
end
T->>W: Execute FinaliseBatch
W->>DB: Update batch (status: completed, summary)
Batch limits: - Maximum 10,000 items per batch. - Items processed with concurrency of 50. - Failed items do not block the batch — they are retried independently.
6. Data Flows¶
6.1 Initiate¶
- Merchant submits payout request via REST API.
- KrakenD authenticates and forwards to Pay-Out Service.
- Service validates balance sufficiency, inserts record, starts Temporal workflow.
- Returns
202 Acceptedimmediately.
6.2 Process¶
- Temporal orchestrates: validate beneficiary → debit merchant → transfer funds.
- Each activity has configurable retries (default: 3 attempts, exponential back-off).
- If all retries exhausted, workflow enters compensation: credit merchant ledger, mark payout as failed.
6.3 Settle¶
- On successful transfer, ledger entries are finalised.
- End-of-day reconciliation (
recon-svc) matches Simpaisa records against channel settlement files. - Discrepancies trigger alerts via
notification-svc.
7. Data Model — Key Entities¶
| Entity | Store | Key Fields |
|---|---|---|
| Payout | SurrealDB | id, merchant_id, beneficiary, amount, currency, status |
| Batch | SurrealDB | id, merchant_id, total_items, completed, failed, status |
| LedgerEntry | SurrealDB | id, merchant_id, type (debit/credit), amount, payout_id |
8. Non-Functional Requirements¶
| Metric | Target |
|---|---|
| Availability | 99.95% |
| Single payout P99 | < 30 s (channel dependent) |
| Batch throughput | 200 items/min |
| Workflow durability | Survives service restarts |
| Retry policy | 3 attempts, exp back-off |
9. Architectural Decision Records¶
Changes to Pay-Out architecture require an ADR in /Standards/ADR/.