Canonical Data Model
Status: Draft | Owner: Platform Team | Last Updated: 2026-04-03
Overview
Simpaisa's canonical data model provides a unified, market-agnostic representation of payment data across all products (Pay-Ins, Pay-Outs, Remittances, Cards) and markets (PK, BD, NP, IQ, EG). Based on Open Banking UK Payment Initiation API v3.1 and ISO 20022 message standards, extended for multi-market mobile money and wallet ecosystems.
Target stack: Go structs → SurrealDB tables → Temporal workflow state.
Core Entity: Payment
The root entity from which all product-specific transactions inherit.
| Field |
Type |
Description |
ISO 20022 Mapping |
id |
string |
ULID, globally unique |
MsgId |
type |
PaymentType |
Enum: PAY_IN, PAY_OUT, REMITTANCE, CARD |
PmtTpInf.LclInstrm |
amount |
decimal |
Transaction amount (precision 4) |
InstdAmt.Value |
currency |
Currency |
ISO 4217 code |
InstdAmt.Ccy |
status |
PaymentStatus |
Lifecycle state |
TxSts |
merchantId |
string |
Owning merchant reference |
CdtrSchmeId |
channelId |
string |
Payment channel/provider |
PmtMtd |
debtorAccount |
Account |
Payer account |
DbtrAcct |
creditorAccount |
Account |
Payee account |
CdtrAcct |
createdAt |
datetime |
Creation timestamp (UTC) |
CreDtTm |
updatedAt |
datetime |
Last modification (UTC) |
— |
traceId |
string |
Distributed tracing ID (W3C) |
— |
idempotencyKey |
string |
Client-supplied dedup key |
EndToEndId |
metadata |
map |
Arbitrary key-value pairs |
SplmtryData |
Entity: Transaction
Extends Payment with channel-specific execution details.
| Field |
Type |
Description |
paymentId |
string |
FK → Payment |
channelRef |
string |
Provider's transaction reference |
channelStatus |
string |
Raw status from provider |
requestPayload |
bytes |
Encrypted request sent to provider |
responsePayload |
bytes |
Encrypted response from provider |
attemptNumber |
int |
Retry attempt (1-based) |
latencyMs |
int |
Round-trip time to provider |
settledAt |
datetime |
Settlement confirmation timestamp |
Entity: Merchant
| Field |
Type |
Description |
id |
string |
ULID |
name |
string |
Legal entity name |
tier |
MerchantTier |
STANDARD, PREMIUM, ENTERPRISE |
status |
MerchantStatus |
PENDING, ACTIVE, SUSPENDED, CLOSED |
markets |
[]string |
ISO 3166-1 alpha-2 codes |
products |
[]string |
Enabled product lines |
kybStatus |
KYBStatus |
Know-Your-Business verification state |
createdAt |
datetime |
Onboarding timestamp |
Entity: Beneficiary
| Field |
Type |
Description |
id |
string |
ULID |
name |
string |
Full legal name |
account |
Account |
Destination account |
bank |
string |
Bank/institution name |
bankCode |
string |
SWIFT/BIC or local bank code |
country |
string |
ISO 3166-1 alpha-2 |
status |
BeneficiaryStatus |
ACTIVE, SUSPENDED, BLOCKED |
Entity: Account
Polymorphic account supporting IBAN, MSISDN, and wallet identifiers.
| Field |
Type |
Description |
id |
string |
ULID |
accountType |
AccountType |
IBAN, MSISDN, WALLET, CARD_TOKEN |
identifier |
string |
The account number/MSISDN/wallet ID |
bankCode |
string |
SWIFT/BIC (nullable for wallets) |
currency |
Currency |
Default currency |
country |
string |
ISO 3166-1 alpha-2 |
holderName |
string |
Account holder name |
Entity: Currency
| Field |
Type |
Description |
code |
string |
ISO 4217 (PKR, BDT, NPR, IQD) |
numericCode |
int |
ISO 4217 numeric |
name |
string |
Full currency name |
decimals |
int |
Minor units (2 for most; 3 for IQD) |
symbol |
string |
Display symbol |
Entity: FXRate
| Field |
Type |
Description |
id |
string |
ULID |
sourceCurrency |
string |
ISO 4217 |
targetCurrency |
string |
ISO 4217 |
rate |
decimal |
Mid-market rate (precision 8) |
markup |
decimal |
Simpaisa margin |
effectiveRate |
decimal |
rate + markup |
provider |
string |
Rate source |
validFrom |
datetime |
Rate validity start |
validTo |
datetime |
Rate validity end |
Entity: Channel
| Field |
Type |
Description |
id |
string |
ULID |
name |
string |
e.g. JazzCash, bKash, Raast |
type |
ChannelType |
MOBILE_WALLET, BANK, CARD_ACQUIRER |
country |
string |
Operating market |
status |
ChannelStatus |
ACTIVE, DEGRADED, MAINTENANCE, DOWN |
healthScore |
int |
0–100 real-time health |
Entity: Webhook
| Field |
Type |
Description |
id |
string |
ULID |
merchantId |
string |
FK → Merchant |
url |
string |
HTTPS callback URL |
events |
[]string |
Subscribed event types |
secret |
string |
HMAC-SHA256 signing secret |
status |
WebhookStatus |
ACTIVE, PAUSED, FAILED |
failureCount |
int |
Consecutive delivery failures |
Entity: AuditEntry
| Field |
Type |
Description |
id |
string |
ULID |
entityType |
string |
e.g. Payment, Merchant |
entityId |
string |
FK to the audited entity |
action |
string |
CREATE, UPDATE, STATUS_CHANGE, DELETE |
actorId |
string |
User or system identity |
actorType |
ActorType |
USER, SYSTEM, WEBHOOK, CRON |
diff |
map |
Before/after field changes |
timestamp |
datetime |
Event timestamp (UTC) |
traceId |
string |
Correlation with Payment.traceId |
Enumerations
| Enum |
Values |
PaymentType |
PAY_IN, PAY_OUT, REMITTANCE, CARD |
PaymentStatus |
CREATED, PENDING, AUTHORISED, PROCESSING, COMPLETED, FAILED, REVERSED, EXPIRED, CANCELLED |
MerchantTier |
STANDARD, PREMIUM, ENTERPRISE |
MerchantStatus |
PENDING, ACTIVE, SUSPENDED, CLOSED |
AccountType |
IBAN, MSISDN, WALLET, CARD_TOKEN |
ChannelType |
MOBILE_WALLET, BANK, CARD_ACQUIRER, REMITTANCE_CORRIDOR |
ChannelStatus |
ACTIVE, DEGRADED, MAINTENANCE, DOWN |
ActorType |
USER, SYSTEM, WEBHOOK, CRON |
KYBStatus |
NOT_STARTED, IN_PROGRESS, VERIFIED, REJECTED |
ISO 20022 Field Mapping
| Simpaisa Field |
ISO 20022 Element |
OB UK Path |
Payment.id |
GrpHdr.MsgId |
Data.PaymentId |
Payment.amount |
CdtTrfTxInf.Amt.InstdAmt |
Data.Initiation.InstructedAmount |
Payment.currency |
CdtTrfTxInf.Amt.InstdAmt[@Ccy] |
Data.Initiation.InstructedAmount.Currency |
Payment.debtorAccount |
DbtrAcct.Id.IBAN |
Data.Initiation.DebtorAccount |
Payment.creditorAccount |
CdtrAcct.Id.IBAN |
Data.Initiation.CreditorAccount |
Payment.status |
TxInfAndSts.TxSts |
Data.Status |
Payment.traceId |
GrpHdr.CreDtTm (extended) |
— |
Payment.idempotencyKey |
PmtId.EndToEndId |
Data.Initiation.EndToEndIdentification |
Entity Relationship Diagram
┌──────────┐ ┌─────────────┐ ┌───────────┐
│ Merchant │1────N │ Payment │1────N │Transaction│
└──────────┘ └─────────────┘ └───────────┘
│1 │1 │1
│ │ │
N │ N
┌──────────┐ 1 │ ┌──────────┐
│ APIKey │ ┌────────┘ │AuditEntry│
└──────────┘ │ └──────────┘
N
┌──────────┐ ┌─────────┐
│ Account │ │ Channel │
└──────────┘ └─────────┘
│1
┌──────────┐ │
│ FXRate │ N
└──────────┘ ┌───────────┐
│ Webhook │
┌──────────┐ └───────────┘
│Beneficiary│
└──────────┘
SurrealDB Conventions
- Table names:
snake_case (e.g., payment, audit_entry)
- Record IDs:
payment:ulid() — SurrealDB native record links
- Relations modelled as graph edges:
merchant->has_payment->payment
- All monetary amounts stored as
decimal type with explicit precision
- IQD amounts stored with 3-decimal precision; all others with 2-decimal
- Timestamps: ISO 8601 UTC with
datetime type
- Soft deletes via
deletedAt field; no physical deletes on financial records