Case Study
Building Mobile Payment Flows with Explicit State
Kotlin · Jetpack Compose · Retrofit · Coroutines · Material 3
Executive Summary
Built wallet features for card reloads, QR payments, and bill-pay. The Retrofit API layer maps failures into repository results while the payment view model exposes bounded processing and terminal states.
The Problem
- Payment flows were overly coupled to banking onboarding.
- Asynchronous payment intent states need to remain visible while the client polls for a terminal result.
Architecture & Approach
- 1Feature-based packages (screens, view models, DI)
- 2Retrofit interfaces with clean DTOs, interceptors, consistent error envelopes
- 3Compose UI with unified components (cards, inputs, buttons) and a11y labels
- 4PaymentFlowViewModel with bounded exponential polling and explicit creating, processing, succeeded, failed, and canceled states
Challenges & Trade-offs
Duplicate payment-intent creation → send an Idempotency-Key from the Android repository
Long-running requests → poll with backoff capped at ten seconds and a two-minute client timeout
UI complexity → keep API/repository failures separate from the view-model state machine
Outcomes & Results
- The client models payment creation, processing, success, failure, cancellation, and timeout as distinct states
- Repository methods expose API/network failures instead of treating unsuccessful responses as completed payments
- External-provider settlement and production payment outcomes remain unverified