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.
car_common_app/lib/view_models/payment_view_model.dart

360 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;
// Store AdVM reference for updating local state after payment
AdVM? _cachedAdVM;
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 {
// Cache AdVM reference for updating local state after payment
try {
_cachedAdVM = context.read<AdVM>();
} catch (e) {
log("Could not get AdVM reference: $e");
}
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 called");
// Wait a bit for browser to close and context to be ready
Future.delayed(const Duration(milliseconds: 500), () {
log("Attempting navigation after delay...");
final nav = navigatorKey.currentState;
if (nav == null) {
log("❌ navigatorKey.currentState is still null after delay");
return;
}
nav.pushNamedAndRemoveUntil(
AppRoutes.dashboard,
(route) => false,
arguments: DashboardRouteEnum.fromRequestPayment,
);
log("✅ Navigation executed successfully");
});
}
void onAdsPaymentSuccess(BuildContext context) {
log("🔵 onAdsPaymentSuccess called");
// Update local ad state immediately so UI reflects payment success
if (_cachedAdVM != null && currentAdId != -1) {
try {
// Find and update the ad in myAds list
var adIndex = _cachedAdVM!.myAds.indexWhere((ad) => ad.id == currentAdId);
if (adIndex != -1) {
_cachedAdVM!.myAds[adIndex].adPostStatus = AdPostStatus.active;
_cachedAdVM!.notifyListeners();
log("✅ Local ad state updated - status changed to active");
} else {
log("⚠️ Ad not found in myAds list");
}
} catch (e) {
log("⚠️ Could not update local ad state: $e");
}
}
// Wait a bit for browser to close and context to be ready
Future.delayed(const Duration(milliseconds: 500), () {
log("Attempting navigation after delay...");
final nav = navigatorKey.currentState;
if (nav == null) {
log("❌ navigatorKey.currentState is still null after delay");
return;
}
nav.pushNamedAndRemoveUntil(
AppRoutes.dashboard,
(route) => false,
arguments: DashboardRouteEnum.fromAdsPayment,
);
log("✅ Navigation executed successfully");
});
}
void onAdReservePaymentSuccess(BuildContext context) {
log("🔵 onAdReservePaymentSuccess called");
// Wait a bit for browser to close and context to be ready
Future.delayed(const Duration(milliseconds: 500), () {
log("Attempting navigation after delay...");
final nav = navigatorKey.currentState;
if (nav == null) {
log("❌ navigatorKey.currentState is still null after delay");
return;
}
nav.pushNamedAndRemoveUntil(
AppRoutes.dashboard,
(route) => false,
arguments: DashboardRouteEnum.fromReserveAdsPayment,
);
log("✅ Navigation executed successfully");
});
}
void onSubscriptionPaymentSuccess() {
log("🔵 onSubscriptionPaymentSuccess called");
// Wait a bit for browser to close and context to be ready
Future.delayed(const Duration(milliseconds: 500), () {
log("Attempting navigation after delay...");
final nav = navigatorKey.currentState;
if (nav == null) {
log("❌ navigatorKey.currentState is still null after delay");
return;
}
nav.pushNamedAndRemoveUntil(
AppRoutes.dashboard,
(route) => false,
);
log("✅ Navigation executed successfully");
});
}
void onAppointmentSuccess(BuildContext context) {
log("🔵 onAppointmentSuccess called");
// Wait a bit for browser to close and context to be ready
Future.delayed(const Duration(milliseconds: 500), () {
log("Attempting navigation after delay...");
final nav = navigatorKey.currentState;
if (nav == null) {
log("❌ navigatorKey.currentState is still null after delay");
return;
}
nav.pushNamedAndRemoveUntil(
AppRoutes.dashboard,
(route) => false,
arguments: DashboardRouteEnum.fromAppointmentPayment,
);
log("✅ Navigation executed successfully");
});
}
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;
}
}
}