Skip to content
NextGen Software

Insights

Technical notes from the build.

Short notes on design decisions in our own platform. They describe what the code does today, not marketing promises.

Why every wallet call carries an idempotency key

Networks repeat requests. A wallet that cannot recognize a repeat will pay twice.

A casino provider calls a wallet when a player bets or wins. If the call times out, the provider cannot know whether the wallet applied it, so it sends the same call again. Without protection, the second delivery debits or credits a second time.

Our ledger therefore treats the transaction id as a key that is unique per operator. A repeated id with identical content returns the original result and marks it as replayed. The same id with different content is refused. The ledger is append-only and enforced by the database, so a correction is a new compensating entry, never an edit.

The practical lesson for any integration: choose the key before you write the call, and test the repeat, not only the first delivery.

Discuss a similar problem (opens Telegram in a new tab)

Handling a wallet call that timed out

A timeout is not a failure. It is an answer you do not have yet.

When the platform calls an operator-held wallet and nothing comes back, the money may or may not have moved. Treating the timeout as a failure can leave a player debited with no bet recorded; treating it as success can do the opposite.

We record the call as unknown, retry with the same transaction id so the operator can answer idempotently, and run a recovery job. A ledger entry is written only when the outcome is known.

The cost is some extra state and a background job. The benefit is that a network problem never silently changes a balance.

Discuss a similar problem (opens Telegram in a new tab)

Tenant isolation is a test, not a promise

One operator must never see another’s players, transactions or keys.

In a multi-tenant platform, a missing filter in one query is enough to leak another customer’s data. We apply the tenant scope in the data layer, authorize requests before records are loaded, and verify the result with negative tests that try to cross the boundary.

The order matters: if a route loads a record before checking permission, the difference between “not found” and “forbidden” reveals which ids exist. Checking first removes that signal.

For a platform that handles operators’ money, we would rather have a failing test than a reassuring sentence.

Discuss a similar problem (opens Telegram in a new tab)

Telegram (opens Telegram in a new tab)