Implemented Razorpay Payment Gateway integration - April 17, 2026, 5:56 PM

This commit is contained in:
Sarankumar 2026-04-17 17:58:26 +05:30
parent ece167ea75
commit 41d7cfa8c6
11 changed files with 826 additions and 425 deletions

View File

@ -45,6 +45,10 @@ android {
getByName("release") {
// TODO: Replace with your release signing config
signingConfig = signingConfigs.getByName("debug")
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
}

5
android/app/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,5 @@
-keepclassmembers class * {
@android.webkit.JavascriptInterface <methods>;
}
-keep class com.razorpay.** {*;}
-dontwarn com.razorpay.**

View File

@ -117,6 +117,12 @@ PODS:
- permission_handler_apple (9.3.0):
- Flutter
- PromisesObjC (2.4.0)
- razorpay-core-pod (1.0.6)
- razorpay-pod (1.5.3):
- razorpay-core-pod (= 1.0.6)
- razorpay_flutter (1.1.10):
- Flutter
- razorpay-pod
- SDWebImage (5.21.7):
- SDWebImage/Core (= 5.21.7)
- SDWebImage/Core (5.21.7)
@ -140,6 +146,7 @@ DEPENDENCIES:
- open_filex (from `.symlinks/plugins/open_filex/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- razorpay_flutter (from `.symlinks/plugins/razorpay_flutter/ios`)
- sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
@ -156,6 +163,8 @@ SPEC REPOS:
- GoogleUtilities
- nanopb
- PromisesObjC
- razorpay-core-pod
- razorpay-pod
- SDWebImage
- SwiftyGif
@ -184,6 +193,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/path_provider_foundation/darwin"
permission_handler_apple:
:path: ".symlinks/plugins/permission_handler_apple/ios"
razorpay_flutter:
:path: ".symlinks/plugins/razorpay_flutter/ios"
sqflite_darwin:
:path: ".symlinks/plugins/sqflite_darwin/darwin"
url_launcher_ios:
@ -213,6 +224,9 @@ SPEC CHECKSUMS:
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
razorpay-core-pod: 6da5fb4ed280279d665507fdb50e213d793a5fe2
razorpay-pod: 4385cf844aa29389313b741e20c72fe668970d49
razorpay_flutter: 0e98e4fcaae27ad50e011d85f66d85e0a008754a
SDWebImage: e9fc87c1aab89a8ab1bbd74eba378c6f53be8abf
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4

View File

@ -1,5 +1,6 @@
import Flutter
import UIKit
import Firebase
@main
@objc class AppDelegate: FlutterAppDelegate {
@ -7,6 +8,7 @@ import UIKit
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
FirebaseApp.configure()
GeneratedPluginRegistrant.register(with: self)
if #available(iOS 10.0, *) {

View File

@ -27,4 +27,5 @@ class ConstsApi {
"$baseUrl/api/notification/un_read_count";
static const String dashboard = "$baseUrl/api/get_dashboard_data";
static const String proformaaccept = "$baseUrl/api/proforma/accept";
static const String payNow = "$baseUrl/api/pay_now";
}

View File

@ -861,6 +861,36 @@ debugPrint('📦 KYC Response Body: ${response.body}');
}
}
Future<Map<String, dynamic>> payNow({required int id}) async {
try {
final token = await _localStore.getToken();
final uri = Uri.parse(ConstsApi.payNow).replace(
queryParameters: {'id': id.toString()},
);
final response = await http.post(
uri,
headers: {
'Authorization': 'Bearer $token',
'Accept': 'application/json',
},
);
final data = jsonDecode(response.body);
debugPrint("🚀 Pay Now API Response: $data");
print("🚀 Pay Now API Response: $data"); // Force print as well
if (response.statusCode != 200 && response.statusCode != 201) {
throw data['message'] ?? 'Failed to process payment';
}
return data;
} catch (e) {
debugPrint('❌ Pay Now API Error: $e');
rethrow;
}
}
Future<void> proformaaccept({required int proformaId}) async {
try {
final token = await _localStore.getToken();

View File

@ -0,0 +1,70 @@
import 'package:razorpay_flutter/razorpay_flutter.dart';
import 'package:flutter/foundation.dart';
class RazorpayService {
late Razorpay _razorpay;
final Function(PaymentSuccessResponse)? onSuccess;
final Function(PaymentFailureResponse)? onFailure;
final Function(ExternalWalletResponse)? onExternalWallet;
RazorpayService({
this.onSuccess,
this.onFailure,
this.onExternalWallet,
}) {
_razorpay = Razorpay();
_razorpay.on(Razorpay.EVENT_PAYMENT_SUCCESS, _handlePaymentSuccess);
_razorpay.on(Razorpay.EVENT_PAYMENT_ERROR, _handlePaymentError);
_razorpay.on(Razorpay.EVENT_EXTERNAL_WALLET, _handleExternalWallet);
}
void _handlePaymentSuccess(PaymentSuccessResponse response) {
debugPrint("✅ Razorpay Payment Success: ${response.paymentId}");
onSuccess?.call(response);
}
void _handlePaymentError(PaymentFailureResponse response) {
debugPrint("❌ Razorpay Payment Error: ${response.code} - ${response.message}");
onFailure?.call(response);
}
void _handleExternalWallet(ExternalWalletResponse response) {
debugPrint("💰 Razorpay External Wallet: ${response.walletName}");
onExternalWallet?.call(response);
}
void openCheckout({
required String key,
required dynamic amount,
required String orderId,
required String description,
String? contact,
String? email,
}) {
var options = {
'key': key,
'amount': amount,
'name': 'Taxglide',
'order_id': orderId,
'description': description,
'theme': {'color': '#61277A'},
'prefill': {
'contact': contact ?? '',
'email': email ?? '',
},
'external': {
'wallets': ['paytm']
}
};
try {
_razorpay.open(options);
} catch (e) {
debugPrint("❌ Error opening Razorpay: $e");
}
}
void dispose() {
_razorpay.clear();
}
}

View File

@ -14,6 +14,8 @@ import 'package:taxglide/model/detail_model.dart';
import 'package:taxglide/view/Mahi_chat/live_chat_screen.dart';
import 'package:taxglide/view/Main_controller/main_controller.dart';
import 'package:taxglide/view/screens/history/completed_live_chat_screen.dart';
import 'package:taxglide/services/razorpay_service.dart';
import 'package:razorpay_flutter/razorpay_flutter.dart';
class DetailScreen extends ConsumerStatefulWidget {
final int id;
@ -30,13 +32,33 @@ class _DetailScreenState extends ConsumerState<DetailScreen> {
String? _downloadedInvoicePath;
bool _isWebSocketInitialized = false; // Add this flag
late RazorpayService _razorpayService;
@override
void initState() {
super.initState();
_razorpayService = RazorpayService(
onSuccess: _handlePaymentSuccess,
onFailure: _handlePaymentFailure,
);
_initializeWebSocket(); // Initialize WebSocket
}
void _handlePaymentSuccess(PaymentSuccessResponse response) {
ValidationPopup().showSuccessMessage(
context,
"Payment successful! ID: ${response.paymentId}",
);
ref.invalidate(serviceDetailProvider(widget.id));
}
void _handlePaymentFailure(PaymentFailureResponse response) {
ValidationPopup().showErrorMessage(
context,
"Payment failed: ${response.message}",
);
}
// Add this method
Future<void> _initializeWebSocket() async {
if (_isWebSocketInitialized) return;
@ -82,6 +104,7 @@ class _DetailScreenState extends ConsumerState<DetailScreen> {
@override
void dispose() {
_razorpayService.dispose();
// Optionally disconnect WebSocket when leaving screen
// DetailsWebscokect().disconnect();
super.dispose();
@ -639,20 +662,26 @@ class _DetailScreenState extends ConsumerState<DetailScreen> {
children: [
TextSpan(
text: 'Date: ',
style: AppTextStyles.semiBold.copyWith(
style: AppTextStyles.semiBold
.copyWith(
fontSize: width * 0.035,
height: 1.3,
letterSpacing: 0.04 * 13.97,
color: const Color(0xFF111827),
color: const Color(
0xFF111827,
),
),
),
TextSpan(
text: data.createdDate ?? "-",
style: AppTextStyles.regular.copyWith(
style: AppTextStyles.regular
.copyWith(
fontSize: width * 0.035,
height: 1.3,
letterSpacing: 0.04 * 13.97,
color: const Color(0xFF111827),
color: const Color(
0xFF111827,
),
),
),
],
@ -668,20 +697,26 @@ class _DetailScreenState extends ConsumerState<DetailScreen> {
children: [
TextSpan(
text: 'Payment : ',
style: AppTextStyles.semiBold.copyWith(
style: AppTextStyles.semiBold
.copyWith(
fontSize: width * 0.035,
height: 1.3,
letterSpacing: 0.04 * 13.97,
color: const Color(0xFF111827),
color: const Color(
0xFF111827,
),
),
),
TextSpan(
text: data.paymentStatus ?? "-",
style: AppTextStyles.regular.copyWith(
style: AppTextStyles.regular
.copyWith(
fontSize: width * 0.035,
height: 1.3,
letterSpacing: 0.04 * 13.97,
color: const Color(0xFF111827),
color: const Color(
0xFF111827,
),
),
),
],
@ -891,9 +926,9 @@ class _DetailScreenState extends ConsumerState<DetailScreen> {
Column(
children: [
if (data.paymentStatus == "Un Paid" &&
!(data.serviceStatus ?? "")
.toLowerCase()
.contains("cancelled")) ...[
!(data.serviceStatus ?? "").toLowerCase().contains(
"cancelled",
)) ...[
Padding(
padding: const EdgeInsets.symmetric(
vertical: 31,
@ -926,7 +961,8 @@ class _DetailScreenState extends ConsumerState<DetailScreen> {
children: [
Text(
"Payment Advice",
style: AppTextStyles.semiBold.copyWith(
style: AppTextStyles.semiBold
.copyWith(
fontSize: width * 0.042,
height: 1.4,
letterSpacing: 0.03 * 16,
@ -945,22 +981,34 @@ class _DetailScreenState extends ConsumerState<DetailScreen> {
children: [
TextSpan(
text: 'Date: ',
style: AppTextStyles.semiBold.copyWith(
fontSize: width * 0.035,
style: AppTextStyles
.semiBold
.copyWith(
fontSize:
width * 0.035,
height: 1.3,
letterSpacing: 0.04 * 13.97,
color: const Color(0xFF111827),
letterSpacing:
0.04 * 13.97,
color: const Color(
0xFF111827,
),
),
),
TextSpan(
text:
data.createdDate ??
"-",
style: AppTextStyles.regular.copyWith(
fontSize: width * 0.035,
style: AppTextStyles
.regular
.copyWith(
fontSize:
width * 0.035,
height: 1.3,
letterSpacing: 0.04 * 13.97,
color: const Color(0xFF111827),
letterSpacing:
0.04 * 13.97,
color: const Color(
0xFF111827,
),
),
),
],
@ -976,22 +1024,34 @@ class _DetailScreenState extends ConsumerState<DetailScreen> {
children: [
TextSpan(
text: 'Payment : ',
style: AppTextStyles.semiBold.copyWith(
fontSize: width * 0.035,
style: AppTextStyles
.semiBold
.copyWith(
fontSize:
width * 0.035,
height: 1.3,
letterSpacing: 0.04 * 13.97,
color: const Color(0xFF111827),
letterSpacing:
0.04 * 13.97,
color: const Color(
0xFF111827,
),
),
),
TextSpan(
text:
data.paymentStatus ??
"-",
style: AppTextStyles.regular.copyWith(
fontSize: width * 0.035,
style: AppTextStyles
.regular
.copyWith(
fontSize:
width * 0.035,
height: 1.3,
letterSpacing: 0.04 * 13.97,
color: const Color(0xFF111827),
letterSpacing:
0.04 * 13.97,
color: const Color(
0xFF111827,
),
),
),
],
@ -1009,17 +1069,23 @@ class _DetailScreenState extends ConsumerState<DetailScreen> {
children: [
Text(
"Total Amount",
style: AppTextStyles.semiBold.copyWith(
style: AppTextStyles.semiBold
.copyWith(
fontSize: width * 0.04,
color: const Color(0xFF111827),
color: const Color(
0xFF111827,
),
),
),
Text(
"${data.paymentAmount ?? '0'}",
style: AppTextStyles.semiBold.copyWith(
style: AppTextStyles.semiBold
.copyWith(
fontFamily: 'Roboto',
fontSize: width * 0.045,
color: const Color(0xFF111827),
color: const Color(
0xFF111827,
),
),
),
],
@ -1029,7 +1095,37 @@ class _DetailScreenState extends ConsumerState<DetailScreen> {
),
),
const SizedBox(height: 20),
CommanButton(text: 'Pay Now', onPressed: () {}),
CommanButton(
text: 'Pay Now',
onPressed: () async {
try {
final response = await ApiRepository()
.payNow(
id: int.parse(data.id.toString()),
);
if (response['status'] == 'success') {
_razorpayService.openCheckout(
key: response['razorpay_key'],
amount: response['amount'],
orderId: response['order_id'],
description:
"Payment for Service ID: ${data.id}",
);
} else {
validationPopup.showErrorMessage(
context,
"Failed to initiate payment",
);
}
} catch (e) {
validationPopup.showErrorMessage(
context,
e.toString(),
);
}
},
),
],
),
),
@ -1066,7 +1162,8 @@ class _DetailScreenState extends ConsumerState<DetailScreen> {
children: [
Text(
"Payment Advice",
style: AppTextStyles.semiBold.copyWith(
style: AppTextStyles.semiBold
.copyWith(
fontSize: width * 0.042,
height: 1.4,
letterSpacing: 0.03 * 16,
@ -1085,22 +1182,34 @@ class _DetailScreenState extends ConsumerState<DetailScreen> {
children: [
TextSpan(
text: 'Date: ',
style: AppTextStyles.semiBold.copyWith(
fontSize: width * 0.035,
style: AppTextStyles
.semiBold
.copyWith(
fontSize:
width * 0.035,
height: 1.3,
letterSpacing: 0.04 * 13.97,
color: const Color(0xFF111827),
letterSpacing:
0.04 * 13.97,
color: const Color(
0xFF111827,
),
),
),
TextSpan(
text:
data.createdDate ??
"-",
style: AppTextStyles.regular.copyWith(
fontSize: width * 0.035,
style: AppTextStyles
.regular
.copyWith(
fontSize:
width * 0.035,
height: 1.3,
letterSpacing: 0.04 * 13.97,
color: const Color(0xFF111827),
letterSpacing:
0.04 * 13.97,
color: const Color(
0xFF111827,
),
),
),
],
@ -1116,22 +1225,34 @@ class _DetailScreenState extends ConsumerState<DetailScreen> {
children: [
TextSpan(
text: 'Payment: ',
style: AppTextStyles.semiBold.copyWith(
fontSize: width * 0.035,
style: AppTextStyles
.semiBold
.copyWith(
fontSize:
width * 0.035,
height: 1.3,
letterSpacing: 0.04 * 13.97,
color: const Color(0xFF111827),
letterSpacing:
0.04 * 13.97,
color: const Color(
0xFF111827,
),
),
),
TextSpan(
text:
data.paymentStatus ??
"-",
style: AppTextStyles.regular.copyWith(
fontSize: width * 0.035,
style: AppTextStyles
.regular
.copyWith(
fontSize:
width * 0.035,
height: 1.3,
letterSpacing: 0.04 * 13.97,
color: const Color(0xFF111827),
letterSpacing:
0.04 * 13.97,
color: const Color(
0xFF111827,
),
),
),
],
@ -1149,17 +1270,23 @@ class _DetailScreenState extends ConsumerState<DetailScreen> {
children: [
Text(
"Total Amount",
style: AppTextStyles.semiBold.copyWith(
style: AppTextStyles.semiBold
.copyWith(
fontSize: width * 0.04,
color: const Color(0xFF111827),
color: const Color(
0xFF111827,
),
),
),
Text(
"${data.paymentAmount ?? '0'}",
style: AppTextStyles.semiBold.copyWith(
style: AppTextStyles.semiBold
.copyWith(
fontFamily: 'Roboto',
fontSize: width * 0.045,
color: const Color(0xFF111827),
color: const Color(
0xFF111827,
),
),
),
],
@ -1232,22 +1359,27 @@ class _DetailScreenState extends ConsumerState<DetailScreen> {
children: [
TextSpan(
text: 'proforma Number : ',
style: AppTextStyles.semiBold.copyWith(
style: AppTextStyles.semiBold
.copyWith(
fontSize: width * 0.035,
height: 1.3,
letterSpacing: 0.04 * 13.97,
letterSpacing:
0.04 * 13.97,
color: const Color(
0xFF111827,
),
),
),
TextSpan(
text: data.proformaNumber ??
text:
data.proformaNumber ??
"-",
style: AppTextStyles.regular.copyWith(
style: AppTextStyles.regular
.copyWith(
fontSize: width * 0.035,
height: 1.3,
letterSpacing: 0.04 * 13.97,
letterSpacing:
0.04 * 13.97,
color: const Color(
0xFF111827,
),
@ -1267,20 +1399,26 @@ class _DetailScreenState extends ConsumerState<DetailScreen> {
children: [
TextSpan(
text: 'proforma Status : ',
style: AppTextStyles.semiBold.copyWith(
style: AppTextStyles.semiBold
.copyWith(
fontSize: width * 0.035,
height: 1.3,
letterSpacing: 0.04 * 13.97,
color: const Color(0xFF111827),
color: const Color(
0xFF111827,
),
),
),
TextSpan(
text: data.proformaStatus ?? "-",
style: AppTextStyles.regular.copyWith(
style: AppTextStyles.regular
.copyWith(
fontSize: width * 0.035,
height: 1.3,
letterSpacing: 0.04 * 13.97,
color: const Color(0xFF111827),
color: const Color(
0xFF111827,
),
),
),
],
@ -1294,20 +1432,26 @@ class _DetailScreenState extends ConsumerState<DetailScreen> {
children: [
TextSpan(
text: 'Subject : ',
style: AppTextStyles.semiBold.copyWith(
style: AppTextStyles.semiBold
.copyWith(
fontSize: width * 0.035,
height: 1.3,
letterSpacing: 0.04 * 13.97,
color: const Color(0xFF111827),
color: const Color(
0xFF111827,
),
),
),
TextSpan(
text: data.subject ?? "-",
style: AppTextStyles.regular.copyWith(
style: AppTextStyles.regular
.copyWith(
fontSize: width * 0.035,
height: 1.3,
letterSpacing: 0.04 * 13.97,
color: const Color(0xFF111827),
color: const Color(
0xFF111827,
),
),
),
],
@ -1662,7 +1806,9 @@ class _DetailScreenState extends ConsumerState<DetailScreen> {
),
if (data.chatId != null &&
data.chatId!.isNotEmpty &&
!(data.serviceStatus ?? "").toLowerCase().contains("cancelled"))
!(data.serviceStatus ?? "").toLowerCase().contains(
"cancelled",
))
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 20,
@ -1758,7 +1904,9 @@ class _DetailScreenState extends ConsumerState<DetailScreen> {
),
),
SizedBox(height: 80.0 + MediaQuery.of(context).padding.bottom),
SizedBox(
height: 80.0 + MediaQuery.of(context).padding.bottom,
),
],
),
);

View File

@ -7,15 +7,60 @@ import 'package:taxglide/view/Main_controller/main_controller.dart';
import 'package:taxglide/view/screens/history/detail_screen.dart';
import 'package:taxglide/view/Mahi_chat/live_chat_screen.dart';
import 'package:taxglide/view/screens/history/completed_live_chat_screen.dart';
import 'package:taxglide/services/razorpay_service.dart';
import 'package:razorpay_flutter/razorpay_flutter.dart';
import 'package:taxglide/consts/validation_popup.dart';
import 'package:taxglide/controller/api_repository.dart';
class PendingScreen extends ConsumerWidget {
class PendingScreen extends ConsumerStatefulWidget {
final String status;
const PendingScreen({super.key, required this.status});
@override
Widget build(BuildContext context, WidgetRef ref) {
final pendingAsync = ref.watch(serviceHistoryNotifierProvider(status));
ConsumerState<PendingScreen> createState() => _PendingScreenState();
}
class _PendingScreenState extends ConsumerState<PendingScreen> {
late RazorpayService _razorpayService;
@override
void initState() {
super.initState();
_razorpayService = RazorpayService(
onSuccess: _handlePaymentSuccess,
onFailure: _handlePaymentFailure,
);
}
void _handlePaymentSuccess(PaymentSuccessResponse response) {
ValidationPopup().showSuccessMessage(
context,
"Payment successful! ID: ${response.paymentId}",
);
ref
.read(serviceHistoryNotifierProvider(widget.status).notifier)
.fetchServiceHistory();
}
void _handlePaymentFailure(PaymentFailureResponse response) {
ValidationPopup().showErrorMessage(
context,
"Payment failed: ${response.message}",
);
}
@override
void dispose() {
_razorpayService.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final pendingAsync = ref.watch(
serviceHistoryNotifierProvider(widget.status),
);
final size = MediaQuery.of(context).size;
final width = size.width;
final height = size.height;
@ -23,9 +68,11 @@ class PendingScreen extends ConsumerWidget {
// 🔄 Silent refresh when notification trigger changes
ref.listen(notificationTriggerProvider, (previous, next) {
if (previous != null && next != previous) {
debugPrint("🔔 Silent refresh triggered for PendingScreen ($status)");
debugPrint(
"🔔 Silent refresh triggered for PendingScreen (${widget.status})",
);
ref
.read(serviceHistoryNotifierProvider(status).notifier)
.read(serviceHistoryNotifierProvider(widget.status).notifier)
.fetchServiceHistory(isSilent: true);
ref.invalidate(countProvider);
}
@ -36,7 +83,9 @@ class PendingScreen extends ConsumerWidget {
final list = data.data ?? [];
if (list.isEmpty) {
// Using Get's capitalize extension explicitly
return Center(child: Text("No ${status.capitalizeFirst} Requests"));
return Center(
child: Text("No ${widget.status.capitalizeFirst} Requests"),
);
}
return ListView.separated(
@ -202,11 +251,13 @@ class PendingScreen extends ConsumerWidget {
item.chatId != null &&
item.chatId.toString().isNotEmpty &&
item.chatId.toString() != "0") ...[
/// 💬 Chat Icon with Badge
Builder(builder: (context) {
Builder(
builder: (context) {
final chatIdStr = item.chatId.toString();
final countAsync = ref.watch(countProvider(chatIdStr));
final countAsync = ref.watch(
countProvider(chatIdStr),
);
return GestureDetector(
onTap: () {
@ -214,22 +265,44 @@ class PendingScreen extends ConsumerWidget {
if (chatid == 0) return;
if (item.status == 'In Progress') {
Get.to(() => LiveChatScreen(
Get.to(
() => LiveChatScreen(
fileid: item.id.toString(),
chatid: chatid,
))?.then((_) {
ref.read(notificationTriggerProvider.notifier).state++;
ref.invalidate(chatMessagesProvider(chatid));
ref.invalidate(countProvider(chatIdStr));
),
)?.then((_) {
ref
.read(
notificationTriggerProvider
.notifier,
)
.state++;
ref.invalidate(
chatMessagesProvider(chatid),
);
ref.invalidate(
countProvider(chatIdStr),
);
});
} else {
Get.to(() => CompletedLiveChatScreen(
Get.to(
() => CompletedLiveChatScreen(
fileid: item.id.toString(),
chatid: chatid,
))?.then((_) {
ref.read(notificationTriggerProvider.notifier).state++;
ref.invalidate(chatMessagesProvider(chatid));
ref.invalidate(countProvider(chatIdStr));
),
)?.then((_) {
ref
.read(
notificationTriggerProvider
.notifier,
)
.state++;
ref.invalidate(
chatMessagesProvider(chatid),
);
ref.invalidate(
countProvider(chatIdStr),
);
});
}
},
@ -246,7 +319,9 @@ class PendingScreen extends ConsumerWidget {
color: const Color(0xFF61277A),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.25),
color: Colors.black.withOpacity(
0.25,
),
blurRadius: 8.08,
offset: const Offset(0, 4.14),
),
@ -268,12 +343,16 @@ class PendingScreen extends ConsumerWidget {
top: -4,
right: -4,
child: Container(
padding: const EdgeInsets.all(4),
decoration: const BoxDecoration(
padding: const EdgeInsets.all(
4,
),
decoration:
const BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
constraints: const BoxConstraints(
constraints:
const BoxConstraints(
minWidth: 16,
minHeight: 16,
),
@ -283,22 +362,24 @@ class PendingScreen extends ConsumerWidget {
style: const TextStyle(
color: Colors.white,
fontSize: 10,
fontWeight: FontWeight.bold,
fontWeight:
FontWeight.bold,
),
),
),
)
: const SizedBox.shrink(),
loading: () => const SizedBox.shrink(),
error: (_, __) => const SizedBox.shrink(),
error: (_, __) =>
const SizedBox.shrink(),
),
],
),
);
}),
},
),
],
],
]
),
],
),
@ -432,8 +513,37 @@ class PendingScreen extends ConsumerWidget {
children: [
Expanded(
child: GestureDetector(
onTap: () {
// Add payment functionality here
onTap: () async {
try {
final response = await ref
.read(apiRepositoryProvider)
.payNow(
id: int.parse(item.id.toString()),
);
if (response['status'] == 'success') {
_razorpayService.openCheckout(
key: response['razorpay_key'],
amount: response['amount'],
orderId: response['order_id'],
description:
"Payment for Service ID: ${item.id}",
);
} else {
ValidationPopup().showErrorMessage(
context,
"Failed to initiate payment",
);
}
} catch (e) {
// Error handled in repository/notifier or via snackbar
Get.snackbar(
'Error',
e.toString(),
backgroundColor: Colors.red,
colorText: Colors.white,
);
}
},
child: Container(
height: height * 0.055,

View File

@ -257,6 +257,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.7"
eventify:
dependency: transitive
description:
name: eventify
sha256: b829429f08586cc2001c628e7499e3e3c2493a1d895fd73b00ecb23351aa5a66
url: "https://pub.dev"
source: hosted
version: "1.0.1"
fake_async:
dependency: transitive
description:
@ -944,6 +952,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.2.0"
razorpay_flutter:
dependency: "direct main"
description:
name: razorpay_flutter
sha256: "2057bfaa769813f58bdc23b9cdc4d5ea493e92154f216f1d2edac507d0936b31"
url: "https://pub.dev"
source: hosted
version: "1.4.4"
redux:
dependency: "direct main"
description:

View File

@ -41,6 +41,7 @@ dependencies:
flutter_local_notifications: ^18.0.1
curved_labeled_navigation_bar: ^2.0.6
http_parser: ^4.1.2
razorpay_flutter: ^1.4.4