Android wallet application built with Jetpack Compose and modern Android architecture for fast, secure payment experiences using the Interledger Protocol.
This project follows Clean Architecture principles with:
- MVVM pattern with ViewModels and
StateFlow-based UI state - Jetpack Compose for declarative UI
- Jetpack Navigation 3 for type-safe navigation with typed route objects
- Use Cases for business logic
- Repository pattern for data access
- Dependency Injection with Koin
- Retrofit for HTTP networking with Kotlinx Serialization
- Wire (gRPC) for Protobuf-based API communication
- Login with email/password
- Registration flow with multi-step onboarding
- Email verification via deep-link
- TOTP (two-factor authentication) setup and verification
- Forgot password / email recovery
- Session management with bearer token refresh
- Phone number verification
- TOTP setup screen (via
AuthGraph) - Wallet address creation
- KYC (Know Your Customer) flow supporting multiple providers:
- Persona — embedded SDK
- PTI — local HTML asset via WebView (
fiant_pti_wrapper_mobile.html) - GateHub — WebView-based flow
- Multi-account wallet overview
- Real-time transaction updates via Pusher
- Account details screen
- Add account flow
- Send Money — ILP-based peer payment
- Withdraw Money — withdraw funds from wallet
- Deposit Money — top up wallet balance
- Connect Bank Account flow
- 3DS Payment Confirmation — in-app dialog to confirm card payments requiring 3-D Secure
- Virtual card ordering
- Physical card ordering with delivery address selection
- Card activation screen
- Transaction list with status display
- Per-transaction detail state
- Profile screen
- Account info
- Documents viewer (PDF/HTML via WebView)
- Kotlinx Serialization converter for JSON parsing
- OkHttp client with logging interceptor
- Bearer token interceptor and authenticator for automatic token refresh
- Suspend functions for coroutine-based async operations
- Type-safe API interfaces with annotations
- Support for both main API and self-service (Ory Kratos) endpoints
- Protobuf schema generated at build time via the Wire Gradle plugin
- Used for wallet and transaction operations
PusherDataSourcefor subscribing to live eventsPusherLifecycleManagertied toHomeViewModelfor automatic connect/disconnect- Events consumed as a
FlowinHomeViewModel
| Component | Purpose |
|---|---|
AuthRepository |
Login, registration, session |
MainRepository |
Wallet, transactions, wallet address |
OnboardingRepository |
KYC, phone/email verification steps |
CardsRepository |
Card ordering and management |
SessionRepository |
Persistent session token storage |
TotpRepository |
TOTP setup and verification |
FeatureFlagsRepository |
Feature flag evaluation |
HtmlAssetsRepository |
Serving local HTML files into WebView |
DocumentsUrlRepository |
Legal/document URL resolution |
PusherDataSource |
Real-time event streaming |
The app uses Jetpack Navigation 3 with typed route objects:
AuthGraph— login, register, create password, TOTP setupOnboardingGraph— email verification, phone verification, wallet address, KYCHomeGraph— home, account details, add account, send/deposit/withdraw money, connect bankCardsGraph— cards list, order virtual/physical card, delivery address, activationTransactionsGraph— transaction listSettingsGraph— settings, profile, account info, documents
Deep-link support is configured in AndroidManifest.xml and routed through RegisterViewModel for email verification.
A custom design system lives under presentation/designsystem/ and includes:
IlpThemewith light/dark variants- Color tokens, typography, and shape definitions
- Reusable composable components
- ktlint — Kotlin code formatting
- Detekt — Static code analysis
- Test email guard — blocks hardcoded test email addresses (e.g.
@breakpointit.eu) from entering non-test Kotlin source
- JUnit — Unit tests
- Espresso — UI tests
- Konsist — Architecture compliance tests
After cloning, install the project's git hooks (one command, per machine):
bash .github/scripts/install_hooks.shThis points git at the tracked .githooks/ directory so the pre-commit checks run automatically before every commit.
Prerequisites: JDK 17, Android SDK (compileSdk 36, minSdk 24), and Android Studio (or just the Gradle wrapper from the command line).
Android Studio generates local.properties with sdk.dir on first sync. Add the Pusher config alongside it (values come from PUSHER_APP_KEY / PUSHER_CLUSTER env vars if omitted):
sdk.dir=/path/to/Android/sdk
PUSHER_APP_KEY=<pusher-app-key>
PUSHER_CLUSTER=euThe app defines several build variants, each pointing at a different backend domain:
| Variant | Backend domain | Purpose |
|---|---|---|
debug |
sandbox.interledger.app | Default local development build |
local |
interledger.test | Points at a locally-run backend |
development |
development.interledger.app | Shared development environment |
beta |
sandbox.interledger.app | Signed pre-release build |
release |
interledger.app | Production build |
Build and install a variant on a connected device/emulator:
./gradlew installDebugFrom Android Studio: open the project, pick a build variant in the Build Variants panel, select a device, and click Run.
To just assemble an APK without installing:
./gradlew assembleDebug # or assembleLocal / assembleDevelopment / assembleBeta / assembleRelease# Run all tests
./gradlew test
# Run architecture tests
./gradlew --tests "*ArchitectureTest*"
# Run UI tests
./gradlew connectedAndroidTest# Format code
./gradlew ktlintFormat
# Check formatting
./gradlew ktlintCheck
# Static analysis
./gradlew detekt
# All quality checks
./gradlew ktlintCheck detekt testThe project uses GitHub Actions for continuous integration:
- CI Pipeline — Runs on push/PR to main/develop branches
- Release Pipeline — Builds and signs an APK + App Bundle on version tags, then publishes a GitHub Release
Push a v* tag to trigger a build. The channel is inferred from the tag itself:
| Tag | Channel | Build type | Example |
|---|---|---|---|
vX.Y.Z |
Production | release |
v1.0.0 |
vX.Y.Z-<label> |
Beta | beta |
v1.0.0-beta1, v1.0.0-beta2, v1.0.0-rc1 |
Any tag with a suffix after the version core (-beta1, -rc1, etc.) is treated as a beta build — push as many betas as needed for the same base version, each with an incrementing label. A bare vX.Y.Z tag (no suffix) builds and releases production.
# Beta
git tag v1.0.0-beta1 && git push origin v1.0.0-beta1
# Production
git tag v1.0.0 && git push origin v1.0.0Beta releases are published as a GitHub prerelease; production releases are published as the latest release. versionName matches the tag suffix (e.g. 1.0.0-beta1), and versionCode is the commit count on the tagged ref.
Quality gates include:
- Unit tests
- Architecture tests
- Code formatting (ktlint)
- Static analysis (Detekt)
- Lint checks
- Test email guard (
.github/scripts/check_test_emails.sh)
The same check runs as a pre-commit hook locally (requires first-time setup above) and as a CI step on every PR. To add a new forbidden email pattern, extend the FORBIDDEN variable in .github/scripts/check_test_emails.sh:
FORBIDDEN="@breakpointit\\.eu|staging@example\\.com"Copyright 2026 Interledger Foundation. Licensed under the Apache License 2.0.