Cross-platform payment automation app with a Kotlin Multiplatform Mobile (KMM) base, Appium test suite, and backend API.
Screen_recording_20250724_005329.mp4
- Send Payment: Users can send payments by entering recipient email, amount, and currency (USD/EUR)
- Transaction History: Real-time display of payment history fetched from Firebase Firestore
- Input Validation: Shared KMP validation for email format, amount > 0, and supported currencies
- Kotlin Multiplatform: Shared business logic, API calls, and validation between platforms
- Firebase Integration: Firestore for transaction storage with real-time updates
- REST API: Ktor Backend API with POST /payments endpoint for payment processing
- Cross-Platform Ready: Architecture supports Android (implemented) and future iOS/desktop platforms
cashipaymentapp/
├── shared/ # KMP shared module
│ ├── src/
│ │ ├── commonMain/kotlin/
│ │ │ └── com.getcashi.payment/
│ │ │ ├── core/ # Core utilities
│ │ │ ├── data/ # API client, repositories
│ │ │ ├── di/ # Dependency injection
│ │ │ ├── domain/ # Business logic & models
│ │ │ └── validation/ # Input validation logic
│ │ ├── androidMain/kotlin/ # Android-specific implementations
│ │ ├── androidUnitTest/kotlin/
│ │ │ └── com.getcashi.payment.bdd/
│ │ │ └── PaymentBddSpec # BDD tests
│ │ ├── androidTest/kotlin/ # Android integration tests
│ │ ├── commonTest/kotlin/ # Common tests
│ │ ├── iosMain/kotlin/ # iOS-specific implementations (future)
│ │ └── jvmMain/kotlin/ # JVM-specific implementations
├── androidApp/ # Android UI (Jetpack Compose)
│ ├── src/
│ │ ├── main/ # Main Android app code
│ │ └── test/[unitTest]/java/
│ │ └── com.getcashi.payment.android.appium/ # Appium UI automation tests
│ │ ├── cucumber/ # Cucumber BDD tests
│ │ ├── pages/ # Page Object Model
│ │ ├── tests/ # Test cases
│ │ ├── utils/ # Test utilities
│ │ ├── BaseAppiumTest # Base test class
│ │ ├── ComposeDiagnosticTest
│ │ └── SimpleConnectionTest
├── backend/ # REST API (Ktor + Docker)
└── load-testing/ # JMeter performance tests
This is a Kotlin Multiplatform Mobile (KMP) payment application that follows Clean Architecture principles with clear separation of concerns across multiple layers.
- PaymentScreen.kt: Jetpack Compose UI with modern Material 3 design
- PaymentViewModel.kt: MVVM pattern with StateFlow and Channel for effects
- PaymentScreenState: Immutable state management
- PaymentScreenEvent: Sealed class for user interactions
- PaymentScreenEffect: Side effects like toasts and keyboard handling
Key Features:
- Form validation with real-time error feedback
- Transaction history display
- Payment success dialogs
- Currency selection
- Accessibility support with semantic properties
Use Cases:
SendPaymentUseCase: Orchestrates payment validation and submissionGetTransactionHistoryUseCase: Manages transaction data retrieval
Models:
Payment: Core payment entityTransaction: Transaction data modelCurrency: Enum for supported currenciesPaymentStatus: Payment state enumeration
Repositories (Interfaces):
PaymentRepository: Payment operations contractTransactionRepository: Transaction data contract
- HttpClientFactory: Platform-specific HTTP client creation using
expect/actual - PaymentApiClient: REST API client with Ktor
- Comprehensive error handling (4xx, 5xx, network errors)
- Retry mechanism with exponential backoff
- Request/response logging
- Timeout configurations
- PaymentRepositoryImpl: Implements payment business logic
- TransactionRepositoryImpl: Manages transaction data flow
- TransactionSync: Platform-specific Firebase integration using
expect/actual
- ApiResponse: Generic API response wrapper
- CreatePaymentRequest/Response: Payment API contracts
- ValidateRecipientRequest/Response: Recipient validation contracts
- Converts between API DTOs and domain models
- Firebase document mapping utilities
- AppConfig: Application constants and configuration
- API URLs for development and production
- Payment limits and validation rules
- Network timeout configurations
- Platform: Platform-specific information using
expect/actual - Utility Functions: Date formatting, currency formatting, timestamps
- Result: Sealed class for operation outcomes
- AppError: Comprehensive error types (API, Network, Firebase, Validation)
- FormFieldValidator: Generic validation interface
- FormFieldState: Field-level validation state
- PaymentValidationRules: Business validation rules
- Koin Module: Dependency injection setup
- Single instances for repositories and API clients
- Factory instances for use cases
- Clear separation between layers
- Dependency inversion (domain doesn't depend on data)
- Use cases encapsulate business logic
- Unidirectional Data Flow: User interactions → Events → ViewModel → State updates → UI
- Single Source of Truth: All UI state consolidated in immutable PaymentScreenState
- Event-Driven: User actions modeled as sealed class events (PaymentScreenEvent)
- StateFlow for reactive state management
- Channel for one-time effects (toasts, navigation, keyboard actions)
- Immutable State Updates: State changes via copy() operations only
- Abstract data access behind interfaces
- Multiple data sources (API + Firebase)
- Consistent error handling
- Platform-specific implementations
- Shared business logic across platforms
- Firebase integration for Android
- HTTP client factory abstraction
Unidirectional data flow
UI Event → ViewModel → Use Case → Repository → API/Firebase
↓
UI State ← ViewModel ← Use Case ← Repository ← Response
- Kotlin Multiplatform: Code sharing across platforms
- Ktor: HTTP client for API communication
- Kotlinx Serialization: JSON serialization
- Kotlinx Coroutines: Asynchronous programming
- Koin: Dependency injection
- Jetpack Compose: Modern declarative UI
- Material 3: Latest design system
- Firebase Firestore: Real-time database
- StateFlow/Channel: Reactive state management
- Layered Error Handling: Each layer handles specific error types
- Sealed Error Classes: Type-safe error representation
- Result Wrapper: Consistent success/failure handling
- User-Friendly Messages: Meaningful error messages for UI
- Field-Level Validation: Real-time validation as user types
- Form-Level Validation: Complete form validation before submission
- Business Rule Validation: Use cases enforce business constraints
- Server-Side Validation: API validates recipient existence
- Real-time Sync: Transactions sync to Firebase after payment
- Offline Support: Local state management with remote sync
- Platform Specific: Android-only Firebase implementation using
expect/actual
- Request Headers: Platform identification and versioning
- Error Masking: Generic error messages to prevent information leakage
- Input Validation: Multiple validation layers
- Network Security: HTTPS enforcement, timeout configurations
- Payment Models: Data classes for payment requests/responses
- API Client: Ktor-based HTTP client for backend communication
- Validation Logic: Email format, amount, currency validation
- Firebase Integration: Firestore operations with expect/actual declarations
- Business Logic: Payment processing workflows
- Code Reuse: code sharing between Android and future iOS
- Consistent Validation: Same business rules across all platforms
- Unified API Layer: Single source of truth for backend communication
- Testable Architecture: Shared tests run on all target platforms
This architecture provides excellent separation of concerns, testability, and maintainability while leveraging KMP's code sharing capabilities effectively.
- Android Studio with Kotlin Multiplatform plugin
- JDK 11+ for backend services
- Docker for containerized backend deployment
- Firebase Project with Firestore enabled
- Node.js for Appium UI testing
- JMeter for API performance testing
# Start the backend services
cd backend
docker-compose up -d
# Verify API is running
curl http://localhost:8080/payments/health# Install and run on device/emulator
./gradlew :androidApp:installDebug
./gradlew :androidApp:run# Run behavior-driven tests for shared KMP module
./gradlew shared:testDebugUnitTest --tests "com.getcashi.payment.bdd.PaymentBddSpec"
# Run all Android unit tests in shared module
./gradlew shared:testDebugUnitTest
# Run with verbose output for debugging
./gradlew shared:testDebugUnitTest --tests "com.getcashi.payment.bdd.PaymentBddSpec" --infoExample scenarios covered:
- Valid Payment Processing: Given valid payment details, when submitted, then payment is processed successfully and synced to Firestore
- Invalid Recipient Validation: Given payment with invalid recipient email, when submitted, then validation error is returned and no Firestore sync occurs
- API Server Error Handling: Given valid payment details but API server error, when submitted, then payment fails with appropriate error message
Test Structure:
- Uses Spek2 framework with Gherkin-style Given/When/Then syntax
- Mocks
PaymentApiClientandTransactionSyncdependencies - Tests
SendPaymentUseCasewithPaymentRepositoryImpl - Covers success paths, validation failures, and error handling scenarios
# Test shared KMP logic (validation, API response handling)
./gradlew :shared:test
# Test Android-specific code
./gradlew :androidApp:testDebugUnitTestPrerequisites:
- Java 11+ and Android SDK configured
- Node.js 16+ for Appium server
- Android emulator or physical device
Setup:
# Install Appium globally
npm install -g appium
# Install required drivers
appium driver install uiautomator2
appium driver install espresso # For Compose apps
# Create Android Virtual Device if needed
avdmanager create avd -n Pixel_7_API_34 -k "system-images;android-34;google_apis;x86_64"Test Structure:
Located in androidApp/src/test/java/com/getcashi/payment/android/appium/:
appium/
├── BaseAppiumTest.kt # Base test configuration
├── pages/PaymentPage.kt # Page Object Model for payment screen
├── tests/PaymentAppiumTest.kt # JUnit test cases
├── cucumber/
│ ├── CucumberTestRunner.kt # Cucumber BDD test runner
│ └── steps/PaymentSteps.kt # BDD step definitions
├── utils/
│ ├── AppiumWaits.kt # Custom wait utilities
│ ├── ScreenshotUtils.kt # Screenshot capture utilities
│ └── TestDataGenerator.kt # Test data generation helpers
└── resources/features/
└── payment.feature # BDD feature files
Running Tests:
Option 1: Automated Script (Recommended)
# Make script executable
chmod +x run-appium-tests.sh
# Run all UI tests with automatic setup/cleanup
./run-appium-tests.shOption 2: Manual Execution
# 1. Start Appium server
appium --config appium-config.json
# Or: appium --port 4723 --log-level info
# 2. Start Android emulator (new terminal)
emulator -avd Pixel_7_API_34 -no-snapshot-load
adb wait-for-device
# 3. Build and install app
./gradlew clean assembleDebug installDebug
# 4. Run specific test suites
./gradlew test --tests "*CucumberTestRunner" # BDD tests
./gradlew test --tests "*PaymentAppiumTest" # JUnit tests
./gradlew test --tests "*Appium*" # All Appium testsTest Scenarios Covered:
- Payment Flow: Complete payment submission with success validation
- Form Validation: Email format, amount validation, empty form handling
- Currency Selection: USD/EUR currency switching
- Transaction History: Verify payments appear in real-time history
- Error Handling: Invalid input validation and error message display
BDD Feature Tests:
The Cucumber tests cover user scenarios defined in payment.feature:
- Send successful payment with transaction history verification
- Validate invalid email format with error handling
- Test zero amount validation
- Multi-currency payment support
Test Reports: After execution, reports are available at:
- HTML Report:
build/reports/tests/test/index.html - Cucumber Report:
build/reports/cucumber-report.html - Screenshots:
build/appium-screenshots/(captured on failures)
Key Features:
- Page Object Model: Maintainable UI interaction abstractions
- BDD Support: Cucumber integration with Gherkin syntax
- Automatic Screenshots: Failure debugging with visual evidence
- Compose Support: Optimized for Jetpack Compose UI testing
- Parallel Execution: Support for concurrent test runs
- CI/CD Ready: Integration with automated build pipelines
Troubleshooting:
# Check Appium server status
curl http://127.0.0.1:4723/status
# Verify device connection
adb devices
# Check app installation
adb shell pm list packages | grep com.getcashi.payment.android
# View test logs with details
./gradlew test --tests "*PaymentAppiumTest" --info# Test /payments endpoint under load
cd load-testing
jmeter -n -t payment-api-performance.jmx -l results.jtl -e -o reports/
# Simulates 5 concurrent users hitting POST /payments
# Measures response times and throughput| Method | Endpoint | Description | Payload |
|---|---|---|---|
| POST | /payments |
Primary challenge endpoint - Process payment | {"recipientEmail": "[email protected]", "amount": 100.0, "currency": "USD"} |
| GET | /payments/health |
API health check for testing | - |
POST /payments
{
"recipientEmail": "[email protected]",
"amount": 25.50,
"currency": "USD"
}
Response:
{
"success": true,
"data": {
"id": "uuid-here",
"recipientEmail": "[email protected]",
"senderEmail": "[email protected]",
"amount": 25.50,
"currency": "USD",
"status": "COMPLETED",
"timestamp": 1642435200000,
"transactionReference": "TXN_ABC123"
}
}