Document Version: 1.2.0
Document Purpose: Stakeholder & Technical Architecture Presentation
Scope: Complete explanation of the features, system architecture, data models, and technical flows implemented in this Payment MVP.
This MVP (Minimum Viable Product) was developed as an end-to-end, functional mobile payment prototype for QR-based UPI payments.
It provides an interactive, production-grade reference implementation that handles the complete payment lifecycle:
upi://pay URI payloads against Indian banking specs.PENDING ➔ SUCCESS / FAILED / CANCELLED) with crash recovery.The application is structured into four decoupled layers following the Clean Architecture & Provider State Management pattern:
┌─────────────────────────────────────────────────────────────────────────┐
│ 1. PRESENTATION LAYER (Flutter UI) │
│ HomeScreen │ QrScannerScreen │ PaymentPreviewScreen │ ResultScreen │
│ TransactionHistoryScreen │ TransactionDetailScreen │
└────────────────────────────────────┬────────────────────────────────────┘
│ User Actions & UI Binding
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 2. STATE MANAGEMENT LAYER (Provider) │
│ PaymentProvider (Coordinates UI, State & DB) │
└──────────────┬─────────────────────┬────────────────────┬───────────────┘
│ │ │
▼ ▼ ▼
┌──────────────────────┐ ┌──────────────────────┐ ┌──────────────────────┐
│ 3. QR SERVICE │ │ 3. LOCAL STORAGE │ │ 3. GATEWAY SERVICE │
│ QrService │ │ LocalStorageService │ │ RazorpayService │
│ • UPI URI Parsing │ │ • SQLite Database │ │ • Intent Invocation │
│ • Parameter Checks │ │ • Metrics & History │ │ • Response Handler │
└──────────┬───────────┘ └──────────┬───────────┘ └──────────┬───────────┘
│ │ │
▼ ▼ ▼
┌──────────────────────┐ ┌──────────────────────┐ ┌──────────────────────┐
│ 4. DEVICE CAMERA │ │ 4. LOCAL SQLITE │ │ 4. INSTALLED UPI │
│ mobile_scanner │ │ sqflite (Encrypted) │ │ GPay, PhonePe, BHIM │
└──────────────────────┘ └──────────────────────┘ └──────────────────────┘
| Layer | Component / File | Responsibility |
|---|---|---|
| Presentation | HomeScreen |
Main dashboard: displays wallet balance, quick actions (Scan, Pay by UPI ID), summary stats, and recent 5 transactions. |
| Presentation | QrScannerScreen |
Full-screen camera viewfinder, flashlight toggle, camera lens switch, and simulator test preset injector. |
| Presentation | PaymentPreviewScreen |
Merchant verification, amount input/quick-add chips, transaction note input, and double-click debounced pay button. |
| Presentation | PaymentResultScreen |
Post-payment receipt screen displaying status (Success/Failed/Cancelled), Payment ID, RRN, timestamp, and action buttons. |
| Presentation | TransactionHistoryScreen |
Chronological list of all transactions with real-time status filter chips (All, Success, Pending, Failed, Cancelled). |
| Presentation | TransactionDetailScreen |
Complete transaction breakdown with one-tap clipboard copy for Transaction ID, UPI ID, and Reference ID. |
| State Management | PaymentProvider |
Central ChangeNotifier coordinating state across screens, database reads/writes, and payment callbacks. |
| Business Logic | QrService |
Pure Dart utility for parsing, sanitizing, and validating standard upi://pay URI query strings. |
| Persistence | LocalStorageService |
Singleton managing SQLite database initialization, migrations, CRUD operations, and summary calculations. |
| Integration | RazorpayService |
Wrapper managing the payment gateway SDK, event listeners, intent payload construction, and response callbacks. |
The QR scanning flow uses mobile_scanner to detect bar codes and routes the raw string to QrService for strict format verification.
| Parameter Key | Field Name | Status | Specification & Validation Rule Enforced by QrService |
|---|---|---|---|
pa |
Payee VPA / UPI ID | Mandatory | Must be formatted as handle@bank. Cannot start or end with @, cannot be empty. |
pn |
Payee / Merchant Name | Optional | If missing, extracted from handle portion of pa. Decodes percent-encoded characters (e.g. ABC%20Store ➔ ABC Store). |
am |
Transaction Amount | Optional | If present, must parse to a valid positive decimal (> 0). If absent, user is prompted to input amount. |
cu |
Currency Code | Optional | Defaults to INR. Strictly rejects any currency other than INR (e.g., rejects USD, EUR). |
tn |
Transaction Note | Optional | Note or description of purchase. Pre-fills payment description. |
tr |
Reference ID | Optional | Merchant's unique transaction reference identifier. |
mc |
Merchant Category Code | Optional | Merchant business classification code. |
This flow illustrates how the app guarantees transactional safety by recording a PENDING state before delegating to external payment handlers, preventing lost states if the user kills the app.
transactions)The app uses sqflite for offline-first, crash-resilient transactional storage.
| Column Name | SQL Type | Constraints | Description |
|---|---|---|---|
id |
TEXT |
PRIMARY KEY |
Unique UUID generated client-side at initiation (e.g. txn_1728349281000). |
razorpay_payment_id |
TEXT |
NULLABLE |
Gateway payment reference ID returned upon successful settlement. |
razorpay_order_id |
TEXT |
NULLABLE |
Gateway order identifier (if generated). |
merchant_name |
TEXT |
NOT NULL |
Payee or store name extracted from QR or manual input. |
upi_id |
TEXT |
NOT NULL |
Destination Virtual Payment Address (handle@bank). |
amount |
REAL |
NOT NULL |
Final amount paid in INR. |
currency |
TEXT |
NOT NULL DEFAULT 'INR' |
Currency code (always INR). |
status |
TEXT |
NOT NULL |
Lifecycle state: 'PENDING', 'SUCCESS', 'FAILED', 'CANCELLED'. |
error_code |
TEXT |
NULLABLE |
Diagnostic error code returned on failure. |
error_message |
TEXT |
NULLABLE |
Human-readable explanation of failure. |
created_at |
TEXT |
NOT NULL |
ISO-8601 creation timestamp. |
updated_at |
TEXT |
NULLABLE |
ISO-8601 last update timestamp. |
raw_response |
TEXT |
NULLABLE |
Raw payload string for debugging and audit trail. |
| State | Trigger | Invariant Rules |
|---|---|---|
PENDING |
Tap "Pay Now" on PaymentPreviewScreen |
Pre-written to SQLite. If app process is terminated while in UPI app, record remains PENDING rather than disappearing. |
SUCCESS |
Gateway EVENT_PAYMENT_SUCCESS callback |
Terminal. Stores razorpay_payment_id. Adds amount to Total Paid metrics. |
FAILED |
Gateway EVENT_PAYMENT_ERROR with decline code |
Terminal. Stores error_code and error_message. Increments failed counter. |
CANCELLED |
User exits or dismisses the UPI intent sheet | Terminal. Marked cancelled without incrementing failure statistics. |
| Screen | File Location | Key Capabilities & Features Implemented |
|---|---|---|
| Home Screen | home_screen.dart |
• ReapCash Wallet Card: Displays user's reward savings balance (5% perk). • Quick Action Buttons: "Scan Any QR" and "Pay by UPI ID / Number". • Financial Analytics Row: Total Amount Paid, Total Transactions, Successful Count, Failed Count. • Recent Activity Section: Shows last 5 transactions with color-coded status badges and one-tap access to full details. |
| QR Scanner | qr_scanner_screen.dart |
• Camera Viewfinder: Built with mobile_scanner with overlay target box.• Hardware Controls: Flashlight toggle and camera flip (front/back). • Simulator Preset Dialog: Built-in test injector allowing testers to test Valid QRs, Missing Amounts, Invalid schemes, and Custom URLs without needing a physical camera. |
| Payment Preview | payment_preview_screen.dart |
• Merchant Card: Verified badge, merchant name, and payee UPI ID. • Amount Controller: Pre-filled if QR contained am, editable if absent. Includes quick-add chips (+₹100, +₹200, +₹500).• Note Input: Optional transaction remarks. • Double-Click Safety: Disables button during initiation to prevent duplicate transactions. • Test Lab Modal (🧪): Simulates instant Success, Failure, and Cancellation outcomes on emulators. |
| Payment Result | payment_result_screen.dart |
• Status Banners: Color-coded animated icons (Green for Success, Red for Failure, Amber for Cancelled). • Transaction Breakdown: Amount, Merchant, Date/Time, Transaction ID, Payment ID. • Share & Download Receipt: Generates formatted receipt data. • Navigation: Direct routes to "Pay Another" or "Done (Return Home)". |
| Transaction History | transaction_history_screen.dart |
• Real-Time Filter Chips: Filter by All, Success, Pending, Failed, or Cancelled.• Newest-First Ordering: Chronological SQLite sorting. • Empty State Screen: Clean illustration and call-to-action when no transactions match. |
| Transaction Details | transaction_detail_screen.dart |
• Detailed Audit View: Full breakdown including raw error strings. • One-Tap Copy: Clipboard buttons for Transaction ID and Reference IDs. • Receipt Download Button: Triggers receipt confirmation. |
All tests execute via flutter test and pass with 0 failures:
| Test ID | Test Category | Scenario Tested | Input Data | Expected Result | Status |
|---|---|---|---|---|---|
| UT-01 | QR Parsing | Complete valid QR payload | upi://pay?pa=test@upi&pn=Test%20Merchant&am=100&cu=INR |
Validated UpiQrData with amount 100.0, merchant Test Merchant |
PASSED |
| UT-02 | QR Parsing | Missing amount QR | upi://pay?pa=merchant@upi&pn=ABC%20Store&cu=INR |
Validated UpiQrData with amount = null, hasPredefinedAmount = false |
PASSED |
| UT-03 | QR Parsing | Non-UPI URL | https://example.com/payment |
Rejection: "Invalid QR format. Scanned code is not a valid UPI QR code." | PASSED |
| UT-04 | QR Parsing | Missing Payee ID (pa) |
upi://pay?pn=Store&am=50&cu=INR |
Rejection: "Invalid UPI QR: Payee UPI ID (pa) is missing." | PASSED |
| UT-05 | QR Parsing | Malformed UPI ID | upi://pay?pa=invalidupiid&pn=Store&am=50&cu=INR |
Rejection: "Invalid UPI ID format. Must be in the format name@bank." | PASSED |
| UT-06 | QR Parsing | Non-INR Currency | upi://pay?pa=test@upi&am=50&cu=USD |
Rejection: "Unsupported currency 'USD'. Only INR is supported." | PASSED |
| UT-07 | QR Parsing | Zero / Negative Amount | upi://pay?pa=test@upi&am=-10&cu=INR |
Rejection: "Invalid amount value. Must be greater than 0." | PASSED |
| UT-08 | State Machine | Payment Success Callback | Mock PaymentSuccessResponse |
Record updated to SUCCESS, razorpayPaymentId stored |
PASSED |
| WT-01 | Widget Test | Home Screen Rendering | Widget render check | Verifies ReapCash Wallet Card, Action buttons, Stats Cards | PASSED |
For testing on simulators and emulators where a physical camera is unavailable:
| Test Preset | How to Trigger in App | Description & Verified Behavior |
|---|---|---|
| Preset 1: Standard Merchant | Scanner ➔ Keyboard Icon ➔ Select "Test 1" | Pre-fills pa=coffee@okhdfcbank, pn=Blue Tokai Coffee, am=240.00. Opens preview with amount locked to ₹240.00. |
| Preset 2: Dynamic Amount | Scanner ➔ Keyboard Icon ➔ Select "Test 2" | Pre-fills pa=kirana@axl, pn=Gupta General Store. Opens preview with amount input field empty for user entry. |
| Preset 3: Invalid Scheme | Scanner ➔ Keyboard Icon ➔ Select "Test 3" | Injects https://example.com. Triggers validation error dialog without crashing. |
| Preset 4: Test Lab Simulator | Preview Screen ➔ Test Flask Icon (🧪) | Allows developer/stakeholder to simulate Success, Failure, or Cancellation without needing real UPI apps. |
| Setting | Configuration Method | Default Value | Description |
|---|---|---|---|
| Razorpay Key ID | --dart-define=RAZORPAY_KEY=... or In-App Settings |
rzp_test_51MockupKey |
Test gateway key used to initialize intent sessions. |
| UPI Intent Timeout | Hardcoded in razorpay_service.dart |
180 seconds |
Time allowed for user to complete authorization in UPI app before timing out. |
| Currency | Enforced in qr_service.dart |
'INR' |
Sole permitted currency code. |
| Platform | File | Permissions / Queries Configured |
|---|---|---|
| Android | AndroidManifest.xml |
• android.permission.CAMERA• android.permission.INTERNET• <queries> configured for upi://, paytmmp://, gpay://, phonepe://, bhim package names. |
| iOS | Info.plist |
• NSCameraUsageDescription configured for camera viewfinder.• LSApplicationQueriesSchemes configured for upi, paytmmp, gpay, phonepe, bhim. |
This MVP successfully demonstrates: