added UPI
This commit is contained in:
parent
4a09447f35
commit
9b4e3d89a0
|
|
@ -23,3 +23,4 @@ RATE_LIMIT_MAX_REQUESTS=100
|
|||
|
||||
# Logging
|
||||
LOG_LEVEL=info
|
||||
PHONEPE_UPI_ID=8500176938@axl
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
# Server Configuration
|
||||
NODE_ENV=production
|
||||
PORT=3000
|
||||
|
||||
# Database Configuration
|
||||
DB_HOST=localhost
|
||||
DB_PORT=5432
|
||||
DB_NAME=luckychit
|
||||
DB_USER=luckychit
|
||||
DB_PASSWORD=postgres
|
||||
DATABASE_URL=postgresql://luckychit:postgres@localhost:5432/luckychit
|
||||
|
||||
# JWT Configuration
|
||||
JWT_SECRET=2559a382d606e4209085401d693ae25f
|
||||
JWT_EXPIRES_IN=24h
|
||||
|
||||
# CORS Configuration
|
||||
ALLOWED_ORIGINS=http://localhost:8080,http://localhost:3000,https://chitfund.deepteklabs.com,http://chitfund.deepteklabs.com
|
||||
|
||||
# Rate Limiting
|
||||
RATE_LIMIT_WINDOW_MS=900000
|
||||
RATE_LIMIT_MAX_REQUESTS=100
|
||||
|
||||
# Logging
|
||||
LOG_LEVEL=info
|
||||
PHONEPE_UPI_ID=9876543210@ybl
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
@echo off
|
||||
echo ========================================
|
||||
echo Personal UPI Setup Helper
|
||||
echo ========================================
|
||||
echo.
|
||||
|
||||
REM Check if .env exists
|
||||
if not exist .env (
|
||||
echo Creating .env file from env.example...
|
||||
copy env.example .env
|
||||
echo.
|
||||
)
|
||||
|
||||
echo Please enter your personal UPI ID:
|
||||
echo Examples: 9876543210@ybl, yourname@paytm, yourname@oksbi
|
||||
echo.
|
||||
set /p UPI_ID="Your UPI ID: "
|
||||
|
||||
echo.
|
||||
echo Adding PHONEPE_UPI_ID to .env file...
|
||||
echo.
|
||||
|
||||
REM Check if PHONEPE_UPI_ID already exists
|
||||
findstr /C:"PHONEPE_UPI_ID" .env >nul 2>&1
|
||||
if %errorlevel% equ 0 (
|
||||
echo Updating existing PHONEPE_UPI_ID...
|
||||
powershell -Command "(Get-Content .env) -replace '^PHONEPE_UPI_ID=.*', 'PHONEPE_UPI_ID=%UPI_ID%' | Set-Content .env"
|
||||
) else (
|
||||
echo Adding new PHONEPE_UPI_ID...
|
||||
echo. >> .env
|
||||
echo # Personal UPI Configuration >> .env
|
||||
echo PHONEPE_UPI_ID=%UPI_ID% >> .env
|
||||
)
|
||||
|
||||
echo.
|
||||
echo ========================================
|
||||
echo SUCCESS! Configuration Updated
|
||||
echo ========================================
|
||||
echo.
|
||||
echo Your UPI ID has been set to: %UPI_ID%
|
||||
echo.
|
||||
echo Next steps:
|
||||
echo 1. Restart your backend server
|
||||
echo 2. Open the Flutter app
|
||||
echo 3. Go to Pay Installment
|
||||
echo 4. Select "Pay via QR Code"
|
||||
echo 5. You should see your UPI ID
|
||||
echo.
|
||||
echo ========================================
|
||||
echo.
|
||||
pause
|
||||
|
||||
|
|
@ -626,6 +626,34 @@ const getPaymentQRCode = async (req, res) => {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get UPI settings (for settings screen)
|
||||
*/
|
||||
const getUPISettings = async (req, res) => {
|
||||
try {
|
||||
const upiId = process.env.PHONEPE_UPI_ID;
|
||||
const isConfigured = !!upiId && upiId !== 'merchant@paytm' && upiId.length > 5;
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
data: {
|
||||
upi_id: upiId || 'Not configured',
|
||||
is_configured: isConfigured,
|
||||
provider: isConfigured ? upiId.split('@')[1] : null,
|
||||
transaction_fee: 0,
|
||||
transaction_fee_percentage: '0%',
|
||||
setup_status: isConfigured ? 'active' : 'pending',
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Get UPI settings error:', error);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: 'Failed to get UPI settings',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
initiatePayment,
|
||||
paymentCallback,
|
||||
|
|
@ -635,5 +663,6 @@ module.exports = {
|
|||
externalPaymentWebhook,
|
||||
generatePaymentIntent,
|
||||
getPaymentQRCode,
|
||||
getUPISettings,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -59,5 +59,12 @@ router.post('/payment-intent', authenticateToken, phonePeController.generatePaym
|
|||
*/
|
||||
router.get('/qr/:groupId/:month/:year', authenticateToken, phonePeController.getPaymentQRCode);
|
||||
|
||||
/**
|
||||
* @route GET /api/payments/settings/upi
|
||||
* @desc Get UPI settings (for settings screen)
|
||||
* @access Private (Managers only)
|
||||
*/
|
||||
router.get('/settings/upi', authenticateToken, phonePeController.getUPISettings);
|
||||
|
||||
module.exports = router;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,113 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Test UPI Configuration
|
||||
* Checks if personal UPI ID is configured correctly
|
||||
*/
|
||||
|
||||
require('dotenv').config();
|
||||
|
||||
console.log('\n========================================');
|
||||
console.log(' UPI Configuration Test');
|
||||
console.log('========================================\n');
|
||||
|
||||
const upiId = process.env.PHONEPE_UPI_ID;
|
||||
|
||||
if (!upiId) {
|
||||
console.log('❌ ERROR: PHONEPE_UPI_ID not found in .env file');
|
||||
console.log('\nPlease add your personal UPI ID to .env file:');
|
||||
console.log('PHONEPE_UPI_ID=your_upi_id@paytm\n');
|
||||
console.log('Examples:');
|
||||
console.log(' - 9876543210@ybl (PhonePe)');
|
||||
console.log(' - yourname@paytm (Paytm)');
|
||||
console.log(' - yourname@oksbi (SBI)');
|
||||
console.log(' - yourname@axisbank (Axis)\n');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log('✅ UPI ID Found:', upiId);
|
||||
console.log('\n========================================');
|
||||
console.log(' Configuration Check');
|
||||
console.log('========================================\n');
|
||||
|
||||
// Validate UPI ID format
|
||||
const upiRegex = /^[\w.-]+@[\w.-]+$/;
|
||||
if (upiRegex.test(upiId)) {
|
||||
console.log('✅ UPI ID format is valid');
|
||||
} else {
|
||||
console.log('⚠️ WARNING: UPI ID format might be invalid');
|
||||
console.log(' Expected format: username@provider');
|
||||
}
|
||||
|
||||
// Test QR code generation
|
||||
const PaymentReconciliationService = require('./src/services/payment-reconciliation-service');
|
||||
|
||||
const testGroupId = 'test-group-123';
|
||||
const testUserId = 'test-user-456';
|
||||
const testMonth = new Date().getMonth() + 1;
|
||||
const testYear = new Date().getFullYear();
|
||||
const testAmount = 10250.00;
|
||||
|
||||
console.log('\n========================================');
|
||||
console.log(' Test Payment Details');
|
||||
console.log('========================================\n');
|
||||
|
||||
const upiReference = PaymentReconciliationService.generateUPIReference(
|
||||
testGroupId,
|
||||
testUserId,
|
||||
testMonth,
|
||||
testYear
|
||||
);
|
||||
|
||||
console.log('📝 Test Reference:', upiReference);
|
||||
|
||||
const qrData = PaymentReconciliationService.getQRCodeData(
|
||||
upiId,
|
||||
testAmount,
|
||||
'LuckyChit Payment',
|
||||
upiReference
|
||||
);
|
||||
|
||||
console.log('\n📱 QR Code Data (first 100 chars):');
|
||||
console.log(qrData.substring(0, 100) + '...');
|
||||
|
||||
console.log('\n========================================');
|
||||
console.log(' Summary');
|
||||
console.log('========================================\n');
|
||||
|
||||
console.log('✅ UPI ID:', upiId);
|
||||
console.log('✅ QR Code Generation: Working');
|
||||
console.log('✅ Reference Generation: Working');
|
||||
console.log('✅ Configuration: Complete\n');
|
||||
|
||||
console.log('========================================');
|
||||
console.log(' Next Steps');
|
||||
console.log('========================================\n');
|
||||
|
||||
console.log('1. Start your backend server:');
|
||||
console.log(' npm start\n');
|
||||
|
||||
console.log('2. Test from Flutter app:');
|
||||
console.log(' - Login as member');
|
||||
console.log(' - Go to group details');
|
||||
console.log(' - Click "Pay Installment"');
|
||||
console.log(' - Select "Pay via QR Code"\n');
|
||||
|
||||
console.log('3. You should see:');
|
||||
console.log(' - Your UPI ID:', upiId);
|
||||
console.log(' - A QR code to scan');
|
||||
console.log(' - Payment reference number\n');
|
||||
|
||||
console.log('4. Test payment:');
|
||||
console.log(' - Scan QR with any UPI app');
|
||||
console.log(' - Complete payment');
|
||||
console.log(' - Wait 5-10 seconds');
|
||||
console.log(' - Payment should auto-detect!\n');
|
||||
|
||||
console.log('========================================\n');
|
||||
console.log('💰 Cost: ₹0 (100% FREE forever!)');
|
||||
console.log('📊 Transaction Fee: 0%');
|
||||
console.log('⚡ Detection Time: 5-10 seconds\n');
|
||||
|
||||
console.log('========================================\n');
|
||||
|
||||
|
|
@ -3,7 +3,7 @@ import 'package:shared_preferences/shared_preferences.dart';
|
|||
|
||||
class ApiService {
|
||||
// static const String baseUrl = 'http://localhost:3000/api';
|
||||
static const String baseUrl = 'https://chitfund.deepteklabs.com/api';
|
||||
static const String baseUrl = 'http://localhost:3000/api';
|
||||
static const String tokenKey = 'auth_token';
|
||||
|
||||
late Dio _dio;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import '../../core/controllers/theme_controller.dart';
|
||||
import '../../core/services/auth_service.dart';
|
||||
import '../../core/services/api_service.dart';
|
||||
import '../../core/utils/snackbar_util.dart';
|
||||
|
||||
class SettingsPage extends StatelessWidget {
|
||||
|
|
@ -27,6 +29,22 @@ class SettingsPage extends StatelessWidget {
|
|||
_buildAccountSettings(),
|
||||
SizedBox(height: 24.h),
|
||||
|
||||
// Payment Settings Section (Manager Only)
|
||||
Obx(() {
|
||||
final user = AuthService.to.currentUser.value;
|
||||
if (user?.role == 'manager') {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildSectionHeader('Payment Settings'),
|
||||
_buildPaymentSettings(),
|
||||
SizedBox(height: 24.h),
|
||||
],
|
||||
);
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
}),
|
||||
|
||||
// Notifications Section
|
||||
_buildSectionHeader('Notifications'),
|
||||
_buildNotificationSettings(),
|
||||
|
|
@ -293,6 +311,436 @@ class SettingsPage extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
|
||||
Widget _buildPaymentSettings() {
|
||||
final apiService = ApiService();
|
||||
|
||||
return Card(
|
||||
child: Column(
|
||||
children: [
|
||||
// UPI ID Setting
|
||||
FutureBuilder<Map<String, dynamic>>(
|
||||
future: apiService.get('/payments/phonepe/settings/upi'),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return ListTile(
|
||||
leading: Container(
|
||||
padding: EdgeInsets.all(10.w),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.purple.shade50,
|
||||
borderRadius: BorderRadius.circular(12.r),
|
||||
),
|
||||
child: Icon(
|
||||
Icons.account_balance_rounded,
|
||||
color: Colors.purple.shade600,
|
||||
size: 24.w,
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
'UPI ID',
|
||||
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600),
|
||||
),
|
||||
subtitle: Text(
|
||||
'Loading...',
|
||||
style: TextStyle(fontSize: 14.sp),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final upiId = snapshot.data?['data']?['upi_id'] ?? 'Not configured';
|
||||
final isConfigured = snapshot.data?['data']?['is_configured'] ?? false;
|
||||
|
||||
return ListTile(
|
||||
leading: Container(
|
||||
padding: EdgeInsets.all(10.w),
|
||||
decoration: BoxDecoration(
|
||||
color: isConfigured
|
||||
? Colors.purple.shade50
|
||||
: Colors.orange.shade50,
|
||||
borderRadius: BorderRadius.circular(12.r),
|
||||
),
|
||||
child: Icon(
|
||||
Icons.account_balance_rounded,
|
||||
color: isConfigured
|
||||
? Colors.purple.shade600
|
||||
: Colors.orange.shade600,
|
||||
size: 24.w,
|
||||
),
|
||||
),
|
||||
title: Row(
|
||||
children: [
|
||||
Text(
|
||||
'UPI ID',
|
||||
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600),
|
||||
),
|
||||
if (isConfigured) ...[
|
||||
SizedBox(width: 8.w),
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 2.h),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.green.shade100,
|
||||
borderRadius: BorderRadius.circular(12.r),
|
||||
),
|
||||
child: Text(
|
||||
'Active',
|
||||
style: TextStyle(
|
||||
fontSize: 10.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.green.shade700,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
subtitle: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(height: 4.h),
|
||||
SelectableText(
|
||||
upiId,
|
||||
style: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: isConfigured
|
||||
? Colors.purple.shade700
|
||||
: Colors.orange.shade700,
|
||||
fontFamily: 'monospace',
|
||||
),
|
||||
),
|
||||
if (!isConfigured) ...[
|
||||
SizedBox(height: 4.h),
|
||||
Text(
|
||||
'Configure in backend/.env',
|
||||
style: TextStyle(
|
||||
fontSize: 12.sp,
|
||||
color: Colors.orange.shade600,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
trailing: isConfigured
|
||||
? IconButton(
|
||||
onPressed: () {
|
||||
Clipboard.setData(ClipboardData(text: upiId));
|
||||
SnackbarUtil.showSuccess('UPI ID copied to clipboard');
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.copy_rounded,
|
||||
size: 20.w,
|
||||
color: Colors.purple.shade600,
|
||||
),
|
||||
tooltip: 'Copy UPI ID',
|
||||
)
|
||||
: Icon(
|
||||
Icons.warning_rounded,
|
||||
size: 20.w,
|
||||
color: Colors.orange.shade600,
|
||||
),
|
||||
onTap: () => _showUPIInfoDialog(upiId, isConfigured),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
Divider(height: 1.h, indent: 72.w),
|
||||
|
||||
// Payment Statistics
|
||||
ListTile(
|
||||
leading: Container(
|
||||
padding: EdgeInsets.all(10.w),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue.shade50,
|
||||
borderRadius: BorderRadius.circular(12.r),
|
||||
),
|
||||
child: Icon(
|
||||
Icons.payment_rounded,
|
||||
color: Colors.blue.shade600,
|
||||
size: 24.w,
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
'Payment Statistics',
|
||||
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600),
|
||||
),
|
||||
subtitle: Text(
|
||||
'View payment insights',
|
||||
style: TextStyle(fontSize: 14.sp),
|
||||
),
|
||||
trailing: Icon(Icons.arrow_forward_ios_rounded, size: 16.w),
|
||||
onTap: () {
|
||||
SnackbarUtil.showInfo('Payment statistics coming soon');
|
||||
},
|
||||
),
|
||||
|
||||
Divider(height: 1.h, indent: 72.w),
|
||||
|
||||
// Transaction Fees
|
||||
ListTile(
|
||||
leading: Container(
|
||||
padding: EdgeInsets.all(10.w),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.green.shade50,
|
||||
borderRadius: BorderRadius.circular(12.r),
|
||||
),
|
||||
child: Icon(
|
||||
Icons.currency_rupee_rounded,
|
||||
color: Colors.green.shade600,
|
||||
size: 24.w,
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
'Transaction Fees',
|
||||
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.w600),
|
||||
),
|
||||
subtitle: Text(
|
||||
'0% fees • Save lakhs per year!',
|
||||
style: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
color: Colors.green.shade700,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
trailing: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.green.shade100,
|
||||
borderRadius: BorderRadius.circular(8.r),
|
||||
),
|
||||
child: Text(
|
||||
'FREE',
|
||||
style: TextStyle(
|
||||
fontSize: 12.sp,
|
||||
fontWeight: FontWeight.w800,
|
||||
color: Colors.green.shade700,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showUPIInfoDialog(String upiId, bool isConfigured) {
|
||||
Get.dialog(
|
||||
AlertDialog(
|
||||
title: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.account_balance_rounded,
|
||||
color: Colors.purple.shade600,
|
||||
size: 24.w,
|
||||
),
|
||||
SizedBox(width: 12.w),
|
||||
const Text('UPI Payment Settings'),
|
||||
],
|
||||
),
|
||||
content: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Current UPI ID
|
||||
Text(
|
||||
'Current UPI ID',
|
||||
style: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.grey.shade700,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8.h),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
padding: EdgeInsets.all(12.w),
|
||||
decoration: BoxDecoration(
|
||||
color: isConfigured
|
||||
? Colors.purple.shade50
|
||||
: Colors.orange.shade50,
|
||||
borderRadius: BorderRadius.circular(8.r),
|
||||
border: Border.all(
|
||||
color: isConfigured
|
||||
? Colors.purple.shade200
|
||||
: Colors.orange.shade200,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: SelectableText(
|
||||
upiId,
|
||||
style: TextStyle(
|
||||
fontSize: 16.sp,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: isConfigured
|
||||
? Colors.purple.shade700
|
||||
: Colors.orange.shade700,
|
||||
fontFamily: 'monospace',
|
||||
),
|
||||
),
|
||||
),
|
||||
if (isConfigured)
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
Clipboard.setData(ClipboardData(text: upiId));
|
||||
SnackbarUtil.showSuccess('Copied!');
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.copy_rounded,
|
||||
size: 20.w,
|
||||
color: Colors.purple.shade600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
if (!isConfigured) ...[
|
||||
SizedBox(height: 16.h),
|
||||
Container(
|
||||
padding: EdgeInsets.all(12.w),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.orange.shade50,
|
||||
borderRadius: BorderRadius.circular(8.r),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.warning_rounded,
|
||||
color: Colors.orange.shade700,
|
||||
size: 20.w,
|
||||
),
|
||||
SizedBox(width: 8.w),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'UPI ID not configured. Update backend/.env file.',
|
||||
style: TextStyle(
|
||||
fontSize: 12.sp,
|
||||
color: Colors.orange.shade700,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
SizedBox(height: 16.h),
|
||||
Divider(),
|
||||
SizedBox(height: 16.h),
|
||||
|
||||
// How to Update
|
||||
Text(
|
||||
'How to Update UPI ID',
|
||||
style: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.grey.shade700,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 12.h),
|
||||
_buildInfoStep('1', 'Open backend/.env file'),
|
||||
_buildInfoStep('2', 'Update PHONEPE_UPI_ID=your_upi@paytm'),
|
||||
_buildInfoStep('3', 'Restart backend server'),
|
||||
_buildInfoStep('4', 'Refresh this screen'),
|
||||
|
||||
SizedBox(height: 16.h),
|
||||
Container(
|
||||
padding: EdgeInsets.all(12.w),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue.shade50,
|
||||
borderRadius: BorderRadius.circular(8.r),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.lightbulb_rounded,
|
||||
color: Colors.blue.shade700,
|
||||
size: 18.w,
|
||||
),
|
||||
SizedBox(width: 8.w),
|
||||
Text(
|
||||
'Pro Tip',
|
||||
style: TextStyle(
|
||||
fontSize: 13.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.blue.shade900,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 8.h),
|
||||
Text(
|
||||
'Members can pay using ANY UPI app (PhonePe, GPay, Paytm) directly to your personal UPI ID with 0% transaction fees!',
|
||||
style: TextStyle(
|
||||
fontSize: 12.sp,
|
||||
color: Colors.blue.shade800,
|
||||
height: 1.4,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Get.back(),
|
||||
child: const Text('Close'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildInfoStep(String number, String text) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(bottom: 8.h),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
width: 24.w,
|
||||
height: 24.w,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.purple.shade100,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
number,
|
||||
style: TextStyle(
|
||||
fontSize: 12.sp,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Colors.purple.shade700,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 12.w),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(top: 2.h),
|
||||
child: Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
fontSize: 13.sp,
|
||||
color: Colors.grey.shade700,
|
||||
height: 1.4,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAboutSettings() {
|
||||
return Card(
|
||||
child: Column(
|
||||
|
|
|
|||
Loading…
Reference in New Issue