09/2025 – 01/2026
Online Gaming & Wallet Platform
The frontend integration layer for card and crypto payments on a live, high-traffic wallet platform — many providers, one interface, state that has to be right.
A live gaming platform with real-money wallets, serving traffic through customer, admin, and super-admin panels. I built the frontend integration layer for its payment flows — card and crypto (BTC) — across multiple third-party providers, each embedded as an iframe with its own protocol and callback behavior.
The job, reduced to one sentence: make N payment providers look like one payment system — to the user, and to the rest of the codebase.
Every provider spoke differently: different iframe embedding, different callback timing, different notions of what "done" means. Left unabstracted, each new provider would fork the payment UI and multiply the states the frontend can be wrong in.
Worse, the UI displays a number it does not own. The wallet balance lives in backend services and moves when webhook events land — on the provider's schedule, not the user's. A frontend that guesses at that number will eventually show a user the wrong balance, and this is a category of product where the wrong balance is the worst bug you can ship.
How it fits together
The integration layer sits between three parties: the provider iframes (card and BTC flows), the backend wallet services, and the panels' shared UI. Custom hooks own the payment lifecycle; per-provider adapters normalize each iframe's protocol into one state machine the UI consumes.
Settlement truth flows one way: provider → backend (via webhooks) → frontend. Client-side provider callbacks can advance a payment's UI state to "pending" — only backend-confirmed state is allowed to touch the balance a user sees.
Team product at Senwell Group — a platform already live when I joined its codebase. The honest split:
- The payment integration layer: custom hooks, form logic, and per-provider coordination for card and BTC flows.
- The state handling that kept wallet and transaction UI consistent with asynchronously-arriving backend and webhook updates.
- Coin-to-wallet conversion and bonus logic UI for the cross-platform rewards integration with a partner platform.
- Extensions to the shared React/Next.js/TypeScript component and layout systems used by all three panels.
- The platform itself: existing panels, layout systems, backend wallet services, provider relationships, and the production traffic already flowing through all of it.
Payments
- Card and crypto (BTC) payment flows across multiple iframe-based third-party providers, behind a single consistent interface.
- Payment forms and validation logic shared across providers rather than re-implemented per provider.
Wallet state
- Wallet and transaction UI that stays accurate as updates arrive asynchronously via backend and webhook events.
- Explicit pending states — the UI distinguishes "the provider said OK" from "the money moved".
Rewards
- Coin-to-wallet conversion and bonus logic for a rewards integration spanning two independent platforms, shipped without breaking existing production features.
- Reactbaseline choice
- Next.jsbaseline choice
- TypeScriptpayment lifecycle states are exactly where stringly-typed code goes to die
- Redux Toolkitone wallet store, so three panels can never disagree about a balance
- SCSSbaseline choice
- Tailwind CSSbaseline choice
- Node.jsbaseline choice
- Redisbaseline choice
- Webhookssettlement truth arrives asynchronously — the UI had to be designed around that, not patched for it
rationale is given only where the choice wasn't the obvious one.
Where it got difficult
Where the balance diverged
- symptom
Production reports of payment and wallet state that didn't add up: a flow completes in the provider's iframe, the UI reflects it, and the balance the backend holds says otherwise — or the reverse, a user who paid staring at a stale number.
- investigation
One transaction at a time, across all three surfaces: what the frontend state machine recorded, what the provider callback actually delivered, and what the backend and its webhook events settled. Lining those up is how you find the exact hop where two of them stopped agreeing.
- root cause
The client-side provider callback was being treated as settlement. It isn't — it means "the user finished the provider's flow", while the money moves when the webhook lands on the backend. Two clocks, and the optimistic one was allowed to write to the balance.
- fix
Demote the callback: it may advance the payment UI to an explicit pending state, never touch the balance. Balance renders exclusively from backend-confirmed state, reconciled as webhook-driven updates arrive.
- tradeoff
Users see "processing" where they used to see instant success. An honest pending state beats a fast fiction — around money, that isn't a close call.
One interface over N providers
- symptom
Provider integrations that each behaved correctly in isolation, but whose differences — embedding, message timing, failure signaling — kept leaking into shared payment UI as special cases.
- investigation
Mapping each provider's actual lifecycle against the one the UI assumed made the mismatch visible: the UI was written against the first provider's behavior, and every provider after that was being squeezed into it.
- root cause
There was no explicit contract between "a payment flow" and "this provider's iframe protocol" — the first integration had silently become the contract.
- fix
A normalized payment lifecycle owned by shared hooks, with a per-provider adapter translating each iframe's protocol into it. New providers implement the adapter; the UI and forms don't change.
- tradeoff
A normalized lifecycle is a lowest common denominator — provider-specific capabilities need deliberate escape hatches, and the adapter layer is real code that has to be maintained. Cheaper than N forks of the payment UI.
- Async state consistency is an architecture property, not a bug class. You don't fix divergence by patching the place it appeared — you fix which component is allowed to write the truth.
- In payment UI, the most valuable state is the honest intermediate one. "Pending" isn't indecision; it's the only claim the frontend can actually stand behind.
- An abstraction over third parties is only as good as its worst-behaved member — design the adapter contract for the provider you haven't met yet.
- Idempotency handling for webhook-driven updates deserves to be a first-class, tested concern rather than an implementation detail.
- A visual state-machine definition for the payment lifecycle — the kind of artifact that would have made the callback-vs-settlement gap obvious on day one.