243 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			243 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| # Payment Overlay Widget
 | |
| 
 | |
| A reusable payment verification overlay that supports both 6-digit PIN input and biometric authentication for secure payment processing.
 | |
| 
 | |
| ## Features
 | |
| 
 | |
| - **6-digit PIN Input**: Secure numeric PIN entry with automatic focus management
 | |
| - **Biometric Authentication**: Support for fingerprint and face recognition
 | |
| - **Order Summary**: Display payment details including amount, description, and remarks
 | |
| - **Integrated API Calls**: Automatically handles payment processing via `/orders/{orderId}/pay`
 | |
| - **Error Handling**: Comprehensive error handling with user-friendly messages
 | |
| - **Loading States**: Visual feedback during payment processing
 | |
| - **Responsive Design**: Adapts to different screen sizes and orientations
 | |
| - **Customizable**: Flexible callbacks and styling options
 | |
| - **Accessibility**: Screen reader support and proper focus management
 | |
| - **Localization**: Full i18n support with easy_localization
 | |
| 
 | |
| ## Usage
 | |
| 
 | |
| ```dart
 | |
| import 'package:flutter/material.dart';
 | |
| import 'package:solian/models/wallet.dart';
 | |
| import 'package:solian/widgets/payment/payment_overlay.dart';
 | |
| 
 | |
| // Create an order
 | |
| final order = SnWalletOrder(
 | |
|   id: 'order_123',
 | |
|   amount: 2500, // $25.00 in cents
 | |
|   currency: 'USD',
 | |
|   description: 'Premium Subscription',
 | |
|   remarks: 'Monthly billing',
 | |
|   status: 'pending',
 | |
| );
 | |
| 
 | |
| // Show payment overlay
 | |
| PaymentOverlay.show(
 | |
|   context: context,
 | |
|   order: order,
 | |
|   onPaymentSuccess: (completedOrder) {
 | |
|     // Handle successful payment
 | |
|     print('Payment completed: ${completedOrder.id}');
 | |
|     // Navigate to success page or update UI
 | |
|   },
 | |
|   onPaymentError: (error) {
 | |
|     // Handle payment error
 | |
|     print('Payment failed: $error');
 | |
|     // Show error message to user
 | |
|   },
 | |
|   onCancel: () {
 | |
|     Navigator.of(context).pop();
 | |
|     print('Payment cancelled');
 | |
|   },
 | |
|   enableBiometric: true,
 | |
| );
 | |
| ```
 | |
| 
 | |
| ### Advanced Usage with Loading States
 | |
| 
 | |
| ```dart
 | |
| bool isLoading = false;
 | |
| 
 | |
| PaymentOverlay.show(
 | |
|   context: context,
 | |
|   order: order,
 | |
|   enableBiometric: true,
 | |
|   isLoading: isLoading,
 | |
|   onPinSubmit: (String pin) async {
 | |
|     setState(() => isLoading = true);
 | |
|     try {
 | |
|       await processPaymentWithPin(pin);
 | |
|       Navigator.of(context).pop();
 | |
|     } catch (e) {
 | |
|       showErrorDialog(e.toString());
 | |
|     } finally {
 | |
|       setState(() => isLoading = false);
 | |
|     }
 | |
|   },
 | |
|   onBiometricAuth: () async {
 | |
|     setState(() => isLoading = true);
 | |
|     try {
 | |
|       final authenticated = await authenticateWithBiometrics();
 | |
|       if (authenticated) {
 | |
|         await processPaymentWithBiometrics();
 | |
|         Navigator.of(context).pop();
 | |
|       }
 | |
|     } catch (e) {
 | |
|       showErrorDialog(e.toString());
 | |
|     } finally {
 | |
|       setState(() => isLoading = false);
 | |
|     }
 | |
|   },
 | |
| );
 | |
| ```
 | |
| 
 | |
| ## Parameters
 | |
| 
 | |
| ### PaymentOverlay.show()
 | |
| 
 | |
| | Parameter | Type | Required | Description |
 | |
| |-----------|------|----------|-------------|
 | |
| | `context` | `BuildContext` | ✅ | The build context for showing the overlay |
 | |
| | `order` | `SnWalletOrder` | ✅ | The order to be paid |
 | |
| | `onPaymentSuccess` | `Function(SnWalletOrder)?` | ❌ | Callback when payment succeeds with completed order |
 | |
| | `onPaymentError` | `Function(String)?` | ❌ | Callback when payment fails with error message |
 | |
| | `onCancel` | `VoidCallback?` | ❌ | Callback when payment is cancelled |
 | |
| | `enableBiometric` | `bool` | ❌ | Whether to show biometric option (default: true) |
 | |
| 
 | |
| ## API Integration
 | |
| 
 | |
| The PaymentOverlay automatically handles payment processing by calling the `/orders/{orderId}/pay` endpoint with the following request body:
 | |
| 
 | |
| ### PIN Payment
 | |
| ```json
 | |
| {
 | |
|   "pin": "123456"
 | |
| }
 | |
| ```
 | |
| 
 | |
| ### Biometric Payment
 | |
| ```json
 | |
| {
 | |
|   "biometric": true
 | |
| }
 | |
| ```
 | |
| 
 | |
| ### Response
 | |
| The API should return the completed `SnWalletOrder` object:
 | |
| 
 | |
| ```json
 | |
| {
 | |
|   "id": "order_123",
 | |
|   "amount": 2500,
 | |
|   "currency": "USD",
 | |
|   "description": "Premium Subscription",
 | |
|   "status": "completed",
 | |
|   "processorReference": "txn_abc123",
 | |
|   // ... other order fields
 | |
| }
 | |
| ```
 | |
| 
 | |
| ### Error Handling
 | |
| The widget handles common HTTP status codes:
 | |
| - `401`: Invalid PIN or biometric authentication failed
 | |
| - `400`: Bad request with custom error message
 | |
| - Other errors: Generic payment failed message
 | |
| 
 | |
| ### Implementation Example
 | |
| 
 | |
| ```dart
 | |
| import 'package:local_auth/local_auth.dart';
 | |
| 
 | |
| class BiometricService {
 | |
|   final LocalAuthentication _auth = LocalAuthentication();
 | |
| 
 | |
|   Future<bool> isAvailable() async {
 | |
|     final isAvailable = await _auth.canCheckBiometrics;
 | |
|     final isDeviceSupported = await _auth.isDeviceSupported();
 | |
|     return isAvailable && isDeviceSupported;
 | |
|   }
 | |
| 
 | |
|   Future<bool> authenticate() async {
 | |
|     try {
 | |
|       final bool didAuthenticate = await _auth.authenticate(
 | |
|         localizedReason: 'Please authenticate to complete payment',
 | |
|         options: const AuthenticationOptions(
 | |
|           biometricOnly: true,
 | |
|           stickyAuth: true,
 | |
|         ),
 | |
|       );
 | |
|       return didAuthenticate;
 | |
|     } catch (e) {
 | |
|       print('Biometric authentication error: $e');
 | |
|       return false;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| ```
 | |
| 
 | |
| ## Localization
 | |
| 
 | |
| Add these keys to your localization files:
 | |
| 
 | |
| ```json
 | |
| {
 | |
|   "paymentVerification": "Payment Verification",
 | |
|   "paymentSummary": "Payment Summary",
 | |
|   "amount": "Amount",
 | |
|   "description": "Description",
 | |
|   "pinCode": "PIN Code",
 | |
|   "biometric": "Biometric",
 | |
|   "enterPinToConfirmPayment": "Enter your 6-digit PIN to confirm payment",
 | |
|   "clearPin": "Clear PIN",
 | |
|   "useBiometricToConfirm": "Use biometric authentication to confirm payment",
 | |
|   "touchSensorToAuthenticate": "Touch the sensor to authenticate",
 | |
|   "authenticating": "Authenticating...",
 | |
|   "authenticateNow": "Authenticate Now",
 | |
|   "confirm": "Confirm",
 | |
|   "cancel": "Cancel",
 | |
|   "paymentFailed": "Payment failed. Please try again.",
 | |
|   "invalidPin": "Invalid PIN. Please try again.",
 | |
|   "biometricAuthFailed": "Biometric authentication failed. Please try again.",
 | |
|   "paymentSuccess": "Payment completed successfully!",
 | |
|   "paymentError": "Payment failed: {error}"
 | |
| }
 | |
| ```
 | |
| 
 | |
| ## Styling
 | |
| 
 | |
| The widget automatically adapts to your app's theme. It uses:
 | |
| 
 | |
| - `Theme.of(context).colorScheme.primary` for primary elements
 | |
| - `Theme.of(context).colorScheme.surface` for backgrounds
 | |
| - `Theme.of(context).textTheme` for typography
 | |
| 
 | |
| ## Security Considerations
 | |
| 
 | |
| 1. **PIN Handling**: The PIN is passed as a string to your callback. Ensure you handle it securely and don't log it.
 | |
| 2. **Biometric Authentication**: Always verify biometric authentication on your backend.
 | |
| 3. **Network Security**: Use HTTPS for all payment-related API calls.
 | |
| 4. **Data Validation**: Validate all payment data on your backend before processing.
 | |
| 
 | |
| ## Example Integration
 | |
| 
 | |
| See `payment_overlay_example.dart` for a complete working example that demonstrates:
 | |
| 
 | |
| - How to show the overlay
 | |
| - Handling PIN and biometric authentication
 | |
| - Processing payments
 | |
| - Error handling
 | |
| - Loading states
 | |
| 
 | |
| ## Dependencies
 | |
| 
 | |
| - `flutter/material.dart` - Material Design components
 | |
| - `flutter/services.dart` - Input formatters and system services
 | |
| - `flutter_riverpod/flutter_riverpod.dart` - State management and dependency injection
 | |
| - `gap/gap.dart` - Spacing widgets
 | |
| - `material_symbols_icons/symbols.dart` - Material Symbols icons
 | |
| - `easy_localization/easy_localization.dart` - Internationalization
 | |
| - `dio/dio.dart` - HTTP client for API calls
 | |
| - `solian/models/wallet.dart` - Wallet order model
 | |
| - `solian/widgets/common/sheet_scaffold.dart` - Sheet scaffold widget
 | |
| - `solian/pods/network.dart` - API client provider |