← Selected work
Backend engineering · Technical assignment

A wallet that takes consistency seriously.

What happens to money when withdrawals compete, deposits overlap and clients retry?

PHPSymfonyMySQLIdempotency
01

The problem

A deposit or withdrawal is not just an arithmetic operation. Two requests can read the same balance, a client can retry after a lost response, and every third deposit must award a bonus exactly once. A correct single-request path is not enough.

02

The constraints

Keep real money separate from bonus money. Withdraw only from the real balance. Represent amounts as integer cents rather than floating-point values, and retain an immutable record of each accepted operation.

The assignment uses PHP, Symfony and MySQL through plain PDO. The scope is a local internal API, not a regulated financial platform.

03

The decision

Start a database transaction and lock the customer row with SELECT … FOR UPDATE before checking the idempotency key or reading the balance. Save the updated balance and its operation record together, then commit.

Requests for one customer serialize at that boundary; different customers do not share that row lock. A unique customer-and-key constraint backs idempotency. Repeating the same operation returns its original result; reusing the key for a different operation is a conflict.

This deliberately favours a small, explicit transaction boundary over distributed coordination. A highly active customer can still become a contention point.

04

How the behaviour is checked

The repository documents integration tests that run operations in separate PHP processes with separate MySQL connections. A held customer lock forces them to compete before it is released.

The documented scenarios cover competing withdrawals, simultaneous deposits at the bonus boundary, a deposit alongside a withdrawal, and repeated deposits sharing one idempotency key. Acceptance tests also validate requests and responses against the OpenAPI contract.

These are repository-backed verification methods, not a fresh test run or a production benchmark performed by this website.

Read the design and concurrency tests ↗
05

What this does not prove

This is a technical assignment, not a live banking product. The API has no authentication by design. It does not demonstrate regulatory compliance, production throughput, disaster recovery or an operational service-level commitment.

Correctness under the tested schedules does not remove the need to measure real contention and failure recovery before deployment.

Evidence links use a fixed repository revision so the referenced reasoning stays inspectable.

Explore the current repository ↗
← Back to selected work