You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
340 lines
12 KiB
Dart
340 lines
12 KiB
Dart
import 'dart:developer';
|
|
|
|
import 'package:flutter/cupertino.dart';
|
|
import 'package:flutter/scheduler.dart';
|
|
import 'package:mc_common_app/classes/app_state.dart';
|
|
import 'package:mc_common_app/config/routes.dart';
|
|
import 'package:mc_common_app/extensions/string_extensions.dart';
|
|
import 'package:mc_common_app/generated/locale_keys.g.dart';
|
|
import 'package:mc_common_app/repositories/payments_repo.dart';
|
|
import 'package:mc_common_app/services/payments_service.dart';
|
|
import 'package:mc_common_app/utils/enums.dart';
|
|
import 'package:mc_common_app/utils/navigator.dart';
|
|
import 'package:mc_common_app/utils/utils.dart';
|
|
import 'package:mc_common_app/view_models/ad_view_model.dart';
|
|
import 'package:mc_common_app/view_models/dashboard_view_model_customer.dart';
|
|
import 'package:mc_common_app/view_models/dashboard_view_model_provider.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:easy_localization/easy_localization.dart';
|
|
|
|
class PaymentVM extends ChangeNotifier {
|
|
final PaymentService paymentService;
|
|
final PaymentsRepo paymentRepo;
|
|
|
|
PaymentVM({required this.paymentService, required this.paymentRepo});
|
|
|
|
PaymentMethods selectedPaymentMethod = PaymentMethods.mada;
|
|
PaymentTypes currentPaymentType = PaymentTypes.ads;
|
|
|
|
int currentAdId = -1;
|
|
|
|
void updateCurrentAdId({required int id}) {
|
|
currentAdId = id;
|
|
}
|
|
|
|
int requestId = -1;
|
|
|
|
void updateRequestId({required int id}) {
|
|
requestId = id;
|
|
}
|
|
|
|
int orderProviderSubscriptionId = -1;
|
|
|
|
void updateOrderProviderSubscriptionId({required int id}) {
|
|
orderProviderSubscriptionId = id;
|
|
}
|
|
|
|
List<int> appointmentIdsForPayment = [];
|
|
|
|
void updateAppointmentIdsForPayment({required List<int> ids}) {
|
|
appointmentIdsForPayment = ids;
|
|
log("appointmentIdsForPayment: ${appointmentIdsForPayment}");
|
|
}
|
|
|
|
updateSelectedPaymentMethod(PaymentMethods selectedMethod) {
|
|
selectedPaymentMethod = selectedMethod;
|
|
notifyListeners();
|
|
}
|
|
|
|
Future<void> onContinuePressed(BuildContext context, {required PaymentTypes paymentType}) async {
|
|
switch (selectedPaymentMethod) {
|
|
case PaymentMethods.mada:
|
|
break;
|
|
case PaymentMethods.visa:
|
|
await onVisaCardSelected(context, paymentType);
|
|
break;
|
|
case PaymentMethods.applePay:
|
|
break;
|
|
case PaymentMethods.masterCard:
|
|
break;
|
|
case PaymentMethods.tamara:
|
|
break;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
Future<void> verifyPayments(
|
|
{required BuildContext context, required int id, List<int>? appointmentIds, required int paymentTypeId, required Function() onVerified, bool isForAppointment = false}) async {
|
|
try {
|
|
// Utils.showLoading(context); // Commented out: causes BuildContext error after screen is popped
|
|
bool isPaid = await paymentRepo.verifyPayment(
|
|
paymentTypeId: paymentTypeId,
|
|
id: id,
|
|
isForAppointments: isForAppointment,
|
|
appointmentIds: appointmentIds,
|
|
);
|
|
// Utils.hideLoading(context); // Commented out: matching pair with showLoading
|
|
|
|
log("isPaid: $isPaid");
|
|
|
|
if (!isPaid) {
|
|
// Utils.showToast(LocaleKeys.paymentFailed.tr());
|
|
// pop(context);
|
|
return;
|
|
} else {
|
|
Utils.showToast(LocaleKeys.paymentSuccessful.tr());
|
|
}
|
|
|
|
// Call onVerified callback - success methods handle their own context checks
|
|
onVerified();
|
|
} catch (e) {
|
|
log("verifyPayments error: ${e.toString()}");
|
|
Utils.showToast(e.toString());
|
|
return;
|
|
}
|
|
}
|
|
|
|
int getIdTypeByPaymentType(PaymentTypes paymentTypes) {
|
|
switch (paymentTypes) {
|
|
case PaymentTypes.subscription:
|
|
return orderProviderSubscriptionId;
|
|
case PaymentTypes.appointment:
|
|
case PaymentTypes.partialAppointment:
|
|
return -1;
|
|
case PaymentTypes.request:
|
|
return requestId;
|
|
case PaymentTypes.adReserve:
|
|
case PaymentTypes.ads:
|
|
case PaymentTypes.extendAds:
|
|
return currentAdId;
|
|
}
|
|
}
|
|
|
|
Future<void> placeThePayment({required PaymentTypes paymentTypeEnum, required BuildContext context, Function()? onExit}) async {
|
|
await paymentService.placePayment(
|
|
id: getIdTypeByPaymentType(paymentTypeEnum),
|
|
// This will be request ID for request payment || ad ID for Ad Related Payment || OrderProviderSubscriptionID for SubscriptionPayment
|
|
appointmentIds: appointmentIdsForPayment,
|
|
paymentType: paymentTypeEnum,
|
|
onExit: onExit,
|
|
onFailure: () {
|
|
Utils.showToast(LocaleKeys.paymentFailed.tr());
|
|
switch (paymentTypeEnum) {
|
|
case PaymentTypes.subscription:
|
|
case PaymentTypes.appointment:
|
|
case PaymentTypes.adReserve:
|
|
case PaymentTypes.ads:
|
|
case PaymentTypes.request:
|
|
case PaymentTypes.extendAds:
|
|
case PaymentTypes.partialAppointment:
|
|
break;
|
|
}
|
|
},
|
|
onSuccess: () async {
|
|
// Utils.showToast("Verifying Payment..");
|
|
|
|
switch (paymentTypeEnum) {
|
|
case PaymentTypes.subscription:
|
|
await verifyPayments(
|
|
context: context,
|
|
paymentTypeId: paymentTypeEnum.getIdFromPaymentTypesEnum(),
|
|
id: orderProviderSubscriptionId,
|
|
onVerified: () => onSubscriptionPaymentSuccess(),
|
|
);
|
|
break;
|
|
|
|
case PaymentTypes.appointment:
|
|
case PaymentTypes.partialAppointment:
|
|
await verifyPayments(
|
|
id: 0,
|
|
isForAppointment: true,
|
|
appointmentIds: appointmentIdsForPayment,
|
|
context: context,
|
|
paymentTypeId: paymentTypeEnum.getIdFromPaymentTypesEnum(),
|
|
onVerified: () => onAppointmentSuccess(context),
|
|
);
|
|
break;
|
|
|
|
case PaymentTypes.request:
|
|
await verifyPayments(
|
|
context: context,
|
|
paymentTypeId: paymentTypeEnum.getIdFromPaymentTypesEnum(),
|
|
id: requestId,
|
|
onVerified: () => onRequestPaymentSuccess(context),
|
|
);
|
|
break;
|
|
|
|
case PaymentTypes.adReserve:
|
|
await verifyPayments(
|
|
context: context,
|
|
paymentTypeId: paymentTypeEnum.getIdFromPaymentTypesEnum(),
|
|
id: currentAdId,
|
|
onVerified: () => onAdReservePaymentSuccess(context),
|
|
);
|
|
break;
|
|
|
|
case PaymentTypes.ads:
|
|
case PaymentTypes.extendAds:
|
|
await verifyPayments(
|
|
context: context,
|
|
paymentTypeId: paymentTypeEnum.getIdFromPaymentTypesEnum(),
|
|
id: currentAdId,
|
|
onVerified: () => onAdsPaymentSuccess(context),
|
|
);
|
|
}
|
|
},
|
|
);
|
|
}
|
|
|
|
void onRequestPaymentSuccess(BuildContext context) {
|
|
log("onRequestPaymentSuccess");
|
|
// Check if context is still mounted before using it
|
|
if (!context.mounted) {
|
|
log("Context not mounted, using navigatorKey instead");
|
|
// Use navigatorKey to navigate without context dependency
|
|
navigatorKey.currentState?.pushNamedAndRemoveUntil(
|
|
AppRoutes.dashboard,
|
|
(route) => false,
|
|
);
|
|
return;
|
|
}
|
|
|
|
if (AppState().currentAppType == AppType.provider) {
|
|
context.read<DashboardVMProvider>().onNavbarTapped(4);
|
|
} else {
|
|
context.read<DashboardVmCustomer>().onNavbarTapped(4);
|
|
}
|
|
navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard);
|
|
}
|
|
|
|
void onAdsPaymentSuccess(BuildContext context) {
|
|
log("onAdsPaymentSuccess");
|
|
// Check if context is still mounted before using it
|
|
if (!context.mounted) {
|
|
log("Context not mounted, using navigatorKey instead");
|
|
// Use navigatorKey to navigate without context dependency
|
|
navigatorKey.currentState?.pushNamedAndRemoveUntil(
|
|
AppRoutes.dashboard,
|
|
(route) => false,
|
|
arguments: DashboardRouteEnum.fromAdsPayment,
|
|
);
|
|
return;
|
|
}
|
|
|
|
context.read<AdVM>().updateIsExploreAds(false);
|
|
if (AppState().currentAppType == AppType.provider) {
|
|
context.read<DashboardVMProvider>().onNavbarTapped(3);
|
|
context.read<AdVM>().applyFilterOnMyAds(adPostStatusEnum: AdPostStatus.active);
|
|
} else {
|
|
context.read<DashboardVmCustomer>().onNavbarTapped(3);
|
|
context.read<AdVM>().applyFilterOnMyAds(adPostStatusEnum: AdPostStatus.pendingForPost);
|
|
}
|
|
navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard, arguments: DashboardRouteEnum.fromAdsPayment);
|
|
}
|
|
|
|
void onAdReservePaymentSuccess(BuildContext context) {
|
|
log("onAdReservePaymentSuccess - Reserve ads payment completed");
|
|
|
|
// Always use navigatorKey for reliable navigation after payment
|
|
// Payment flow often unmounts context, so navigatorKey is more reliable
|
|
log("Navigating to dashboard with reserved ads filter");
|
|
|
|
// Navigate to dashboard first
|
|
navigatorKey.currentState?.pushNamedAndRemoveUntil(
|
|
AppRoutes.dashboard,
|
|
(route) => false,
|
|
arguments: DashboardRouteEnum.fromReserveAdsPayment,
|
|
);
|
|
|
|
// Use a post-frame callback to ensure navigation completes before updating state
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
final context = navigatorKey.currentContext;
|
|
if (context != null && context.mounted) {
|
|
try {
|
|
// Navigate to My Ads tab and apply Reserved filter
|
|
context.read<AdVM>().updateIsExploreAds(false);
|
|
if (AppState().currentAppType == AppType.provider) {
|
|
context.read<DashboardVMProvider>().onNavbarTapped(3); // My Ads tab
|
|
context.read<AdVM>().applyFilterOnMyAds(adPostStatusEnum: AdPostStatus.reserved);
|
|
} else {
|
|
context.read<DashboardVmCustomer>().onNavbarTapped(3); // My Ads tab
|
|
context.read<AdVM>().applyFilterOnMyAds(adPostStatusEnum: AdPostStatus.reserved);
|
|
}
|
|
log("✅ Successfully navigated to Reserved Ads");
|
|
} catch (e) {
|
|
log("⚠️ Error updating AdVM after navigation: $e");
|
|
}
|
|
} else {
|
|
log("⚠️ Context not available for post-navigation updates");
|
|
}
|
|
});
|
|
}
|
|
|
|
void onSubscriptionPaymentSuccess() {
|
|
log("onSubscriptionPaymentSuccess");
|
|
// Use navigatorKey to navigate without context dependency
|
|
// This works even after payment browser is closed and context is unmounted
|
|
navigatorKey.currentState?.pushNamedAndRemoveUntil(
|
|
AppRoutes.dashboard,
|
|
(route) => false,
|
|
);
|
|
// context.read<SubscriptionsVM>().getSubscriptionBySP(AppState().getUser.data?.userInfo?.providerId.toString() ?? "", true);
|
|
}
|
|
|
|
void onAppointmentSuccess(BuildContext context) {
|
|
log("onAppointmentSuccess");
|
|
// Check if context is still mounted before using it
|
|
if (!context.mounted) {
|
|
log("Context not mounted, using navigatorKey instead");
|
|
// Use navigatorKey to navigate without context dependency
|
|
navigatorKey.currentState?.pushNamedAndRemoveUntil(
|
|
AppRoutes.dashboard,
|
|
(route) => false,
|
|
);
|
|
return;
|
|
}
|
|
|
|
if (AppState().currentAppType == AppType.provider) {
|
|
context.read<DashboardVMProvider>().onNavbarTapped(1);
|
|
} else {
|
|
context.read<DashboardVmCustomer>().onNavbarTapped(1);
|
|
}
|
|
navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard);
|
|
}
|
|
|
|
Future<void> onVisaCardSelected(BuildContext context, PaymentTypes paymentType, {Function()? onExit}) async {
|
|
currentPaymentType = paymentType;
|
|
switch (currentPaymentType) {
|
|
case PaymentTypes.appointment:
|
|
case PaymentTypes.partialAppointment:
|
|
if (appointmentIdsForPayment.isEmpty) return;
|
|
await placeThePayment(context: context, paymentTypeEnum: paymentType, onExit: onExit);
|
|
break;
|
|
case PaymentTypes.request:
|
|
if (requestId == -1) return;
|
|
await placeThePayment(context: context, paymentTypeEnum: paymentType, onExit: onExit);
|
|
break;
|
|
case PaymentTypes.subscription:
|
|
await placeThePayment(context: context, paymentTypeEnum: paymentType, onExit: onExit);
|
|
break;
|
|
case PaymentTypes.ads:
|
|
case PaymentTypes.adReserve:
|
|
case PaymentTypes.extendAds:
|
|
if (currentAdId == -1) return;
|
|
await placeThePayment(context: context, paymentTypeEnum: paymentType, onExit: onExit);
|
|
break;
|
|
}
|
|
}
|
|
}
|