DEV Community

Khyati Tiwari
Khyati Tiwari

Posted on

When Seconds Matter: A Real-Time Go Application for Secure Ledger Transactions

One of my biggest lessons in building secure applications was when I decided I wanted to build a fintech application. An overly complex fintech app would require access to sensitive data which had its legal complications so I stuck with a simple wallet application with a payment feature.

When building a financial application, data consistency isn't just a feature; it's a hard requirement. If a user transfers $50 from Account A to Account B, two distinct actions must happen simultaneously: Account A must be debited, and Account B must be credited. If the user double-clicks the "Send" button, or if two separate transactions hit the database at the exact same millisecond, the system can suffer from race conditions. If your backend executes these queries independently under default database isolation levels, high concurrency introduces severe vulnerabilities.

To ensure absolute financial accuracy, I implemented a strict double-entry ledger system. Every time a transfer occurs, the application must:

  1. Debit the amount from Account A.
  2. Credit the amount to Account B.
  3. Update the running balances for both accounts.

To execute this safely under heavy concurrent traffic, I chose PostgreSQL's Serializable Isolation which guarantees that if transactions execute concurrently, the end result will be exactly the same as if they had run one after the other in a serialized line.

The Concurrency Roadblock: Handling SQLSTATE 40001

To enforce this, Postgres monitors the data reads and writes. If it detects that two concurrent transactions are modifying overlapping data sets in a way that would break this linear guarantee, it steps in aggressively to protect data integrity. Instead of allowing a race condition, Postgres intentionally terminates one of the transactions and throws a specific serialization failure error: SQLSTATE 40001.

The Go Solution: 10-Step Exponential Backoff

Since the database kills the transaction to safeguard the ledger, the responsibility shifts entirely to the Go backend to heal the operation.

If it catches 40001, it treats it as a temporary concurrency conflict rather than a terminal failure. The application then automatically retries the entire database transaction using an exponential backoff strategy.

The Go server attempts this loop up to 10 times. If the data clears up during any of these attempts, the transaction commits successfully, and the user experiences a seamless transfer without ever knowing a backend collision occurred. However, if the database is under catastrophic conflict and all 10 attempts are completely exhausted, the error is wrapped, and the application layer safely falls back to a generic HTTP 500 internal server error to ensure the system fails closed rather than corrupting data.

Aside from this, I enhanced the security of my application by employing several security standards :

Authentication Layers
The application enforces granular security by splitting user interactions into specialized operational layers:
Web Banking Password: A robust alphanumeric password for primary account login.
4-Digit View PIN: A low-friction PIN for safely viewing account balances.
6-Digit Payment PIN: A high-security PIN required specifically for authorizing outbound ledger transfers.

Hashing
To ensure data privacy, the Go backend implements Bcrypt hashing with a secure work factor for all credentials. The database stores only non-reversible cryptographic strings, rendering stored credentials useless in the event of a database compromise.

Multi-Factor Authentication (MFA)
To protect against credential stuffing, the application mandates MFA via TOTP (Time-based One-Time Password) algorithms. Users link their profiles to authenticator apps, and the backend verifies 6-digit tokens for sensitive sessions, preventing unauthorized access even if primary passwords are leaked.

Building this app taught me the role of databases in ensuring security in high stakes applications and I am certain that I can upgrade it with more features in the future.
You can test the Digital Wallet at : https://github.com/khyahahati/digital-wallet

Top comments (0)