Skip to content

Examples/Fintech

Wallet and payments ledger (example)

A mobile wallet with P2P transfers, card top-ups and bank payouts. Every movement posts to a double-entry ledger in Postgres; a transactional outbox feeds Kafka, merchant webhooks and a daily reconciliation against settlement files. Replace any part with how your own system works.

Scale: 1.5M wallets; 80 transfers/s at peak; daily settlement files

12345678910111213CLIENTMobile walletSERVICEPayments APISERVICERisk serviceSERVICELedger serviceDATABASEPostgresEXTERNALCard processorEXTERNALBank partnerWORKEROutbox relayLOG / STREAMKafkaWORKERWebhook workerEXTERNALMerchantendpointsWORKERReconciliationjobOBJECT STORESettlement files

Select a component to see what it is responsible for and which state it owns.

  1. 1Mobile wallet → Payments API: Send money
  2. 2Payments API → Risk service: Risk check
  3. 3Payments API → Ledger service: Post entries
  4. 4Ledger service → Postgres: Write journal
  5. 5Payments API → Card processor: Charge card
  6. 6Card processor → Payments API: Processor webhooks
  7. 7Payments API → Bank partner: Send payout
  8. 8Outbox relay → Postgres: Read outbox
  9. 9Outbox relay → Kafka: Publish entries
  10. 10Webhook worker → Kafka: Consume events
  11. 11Webhook worker → Merchant endpoints: Deliver webhooks
  12. 12Reconciliation job → Settlement files: Read settlement files
  13. 13Reconciliation job → Postgres: Compare with journal
  • Request / response
  • Asynchronous
  • Bulk data

Select a part to trace its flows.

  • Request / response
  • Asynchronous
  • Bulk data

Parts 13

Flows 13

  1. 1Mobile wallet → Payments APISend moneyRequest / response
  2. 2Payments API → Risk serviceRisk checkRequest / response
  3. 3Payments API → Ledger servicePost entriesRequest / response
  4. 4Ledger service → PostgresWrite journalRequest / response
  5. 5Payments API → Card processorCharge cardRequest / response
  6. 6Card processor → Payments APIProcessor webhooksAsynchronous
  7. 7Payments API → Bank partnerSend payoutRequest / response
  8. 8Outbox relay → PostgresRead outboxRequest / response
  9. 9Outbox relay → KafkaPublish entriesAsynchronous
  10. 10Webhook worker → KafkaConsume eventsRequest / response
  11. 11Webhook worker → Merchant endpointsDeliver webhooksAsynchronous
  12. 12Reconciliation job → Settlement filesRead settlement filesBulk data
  13. 13Reconciliation job → PostgresCompare with journalRequest / response

Invariants 4

  • Every journal transaction sums to zero

    Posting function inserts all legs and checks SUM(amount) = 0 before commit; a deferred constraint trigger rejects unbalanced transactions.

    Kept by

  • A wallet balance never goes negative

    SELECT … FOR UPDATE on the account row, check, then post in the same transaction.

    Kept by

  • A retried transfer posts once

    Idempotency-Key stored with request hash and response in the posting transaction; a replay returns the stored response; a different body with the same key is rejected.

    Kept by

  • Ledger matches settlement files daily

    Unique constraint on journal_entries(external_ref) makes each settlement line match at most one entry; the daily job writes mismatches to an exceptions queue for operations.

    Kept by