This project demonstrates a realistic Android payment flow for Card / QR payments in POS-like devices, focusing on reliability, security, and correctness rather than UI or backend complexity.
The goal is to showcase real-world payment concepts such as idempotency, retry safety, partial payments, offline handling, and Android security best practices.
This project intentionally prioritizes:
- Correct payment modeling
- Clear separation of responsibilities
- Testability
Over:
- UI complexity
- Business-specific logic
- Infrastructure details
A PaymentIntent represents a single logical intent to pay a given amount using a specific payment method.
It exists before any financial transaction happens and survives:
- Network failures
- App restarts
- Retry attempts
This concept is used in real payment systems (e.g. Stripe).
- Generated by the client before charging
- Identifies a single payment intent
- Ensures retry does not cause double charge
- Generated by the payment gateway after processing
- Identifies the actual financial transaction
- Used for audit, reconciliation, or refund
Keeping them separate is critical for safe payment retries.
An order may be fulfilled by multiple PaymentIntents, each representing an atomic payment attempt using a specific
payment method.
Each PaymentIntent is either fully successful or failed.
The order tracks the accumulated paid amount and the remaining amount to be paid.
- Payment state is persisted locally (encrypted)
- The same idempotency key is reused for retries
- Retrying a payment never creates a new transaction
- Offline payments are queued and retried when network is restored
This guarantees exactly-once execution from the user’s perspective.
This project follows Android security best practices for payment apps:
- Android Keystore for key storage
- EncryptedSharedPreferences and Room for local persistence
- No sensitive card or PIN data is logged or stored
- One-time tokens instead of raw payment data
- ProGuard / R8 obfuscation enabled
Cryptographic operations such as PIN or PAN encryption are simulated, as they normally belong to device firmware, HSM, or Secure Element layers.
- Payment use cases
- ViewModel state transitions
- Retry and idempotency behavior in repositories
- Stateful idempotency handling
- Deterministic responses for:
- Success
- Failure
No real card, QR, or banking systems are used.
- GitHub Actions
- Gradle build
- Unit tests execution
- No secrets or credentials required