A mini offline-first wallet application built with React Native CLI (TypeScript) for Android.
This project prioritizes architecture, reliability, and edge-case handling over UI polish — simulating a real-world payment flow with offline support, background sync, idempotent retries, and secure authentication.
APP-DEMO.mp4
| Feature | Description |
|---|---|
| 📴 Offline-first payments | Create payments without any network connection |
| 🗄️ SQLite persistence | All transactions stored locally as the source of truth |
| 🔁 Idempotent retries | Client-generated UUIDs prevent duplicate transactions |
| 🔄 Auto background sync | Syncs automatically on launch, foreground, or reconnect |
| 🔐 Secure auth | Android Keystore via react-native-keychain — no AsyncStorage |
| ⚡ High-performance list | Smooth scrolling tested with 5,000+ transactions |
| 📡 Native Android module | Kotlin module exposing battery level and network type |
| 🛡️ Global error handling | Error boundary with graceful fallback UI |
# Clone the repository
git clone <repo-url>
cd offline-wallet-app
# Install dependencies
npm install
# Run on Android
npx react-native run-androidIf the build fails due to a jcenter() deprecation error, open:
node_modules/react-native-sqlite-storage/platforms/android/build.gradle
Replace:
repositories {
jcenter()
}With:
repositories {
mavenCentral()
google()
}The app follows a strict separation of concerns across six layers:
UI (Screens / Components)
↓
Hooks (Lifecycle, Auto Sync)
↓
Services (Business Logic)
↓
Store (Redux – UI Cache)
↓
Storage (SQLite – Source of Truth)
↓
Native (Android System APIs)
Key Principle: SQLite is the single source of truth. Redux mirrors database state for fast rendering but never replaces it.
src/
├── api/ # Backend API calls
├── storage/ # SQLite initialization & queries
├── store/ # Redux slices (UI cache only)
├── services/ # Payment engine & sync logic
├── hooks/ # Lifecycle & connectivity hooks
├── screens/ # Top-level app screens
├── components/ # Reusable UI components
├── native/ # Android native module bridge
└── utils/ # Types & helpers
Each transaction follows a deliberate state machine to guarantee safety:
INITIATED → PENDING → SUCCESS
↘ FAILED → PENDING (retry)
| State | Meaning |
|---|---|
INITIATED |
User intent persisted to SQLite before any network call |
PENDING |
Network request in progress |
SUCCESS |
Backend confirmed |
FAILED |
Network or server error; eligible for retry |
Why
INITIATEDexists: User intent is written to SQLite before any network call. This guarantees no payment is lost if the app is killed or the device goes offline mid-action.
- Tokens stored via Android Keystore (
react-native-keychain) - Login permitted only when network is available
- Token expiry validated on every app bootstrap
- Automatic logout on invalid or expired tokens
AsyncStorageintentionally avoided for all auth data
All transactions are written to SQLite first. The network layer only confirms or updates state — the backend never creates transaction IDs.
Auto-sync triggers on:
- App launch
- App returns to foreground
- Network becomes available
1. Query SQLite for PENDING or FAILED transactions below retry limit
2. Process sequentially (no parallel race conditions)
3. Update status based on backend response
4. Stop safely on network failure
✅ No lost payments
✅ No duplicate transactions
✅ Controlled retry limits
✅ Safe crash recovery
Each transaction uses a client-generated UUID that is reused on every retry. The backend treats duplicate IDs as safe replays.
- SQLite pagination prevents large memory loads
- Redux provides fast in-memory reads for rendering
- FlatList optimizations:
- Windowing (only renders visible rows)
- Memoized row components
- Controlled batch rendering
Smooth scrolling tested with 5,000+ transactions.
A custom Kotlin module exposes device information to JavaScript:
| API | Returns |
|---|---|
getBatteryLevel() |
Battery percentage |
getNetworkType() |
WiFi / Mobile / None |
This demonstrates:
- Native-to-React Native bridging
- Android system service usage
- Manual
ReactPackageregistration
- Global error boundary catches uncaught JS errors
- Graceful fallback UI instead of red-screen crashes
- No crash screens in release builds
- Create payments while offline
- Kill app during a transaction
- Restart and restore state correctly
- Auto-sync when network returns
- Retry failed payments safely
- Prevent duplicate transaction processing
This repository demonstrates production-oriented React Native engineering:
- Offline-first mobile architecture
- State-machine driven transactions
- Idempotent backend integration
- Crash-safe persistence
- Lifecycle-aware synchronization
- Secure credential storage


