INFRA: Set Up Project.

This commit is contained in:
2025-11-28 11:10:49 +05:00
commit c798279f7d
609 changed files with 77436 additions and 0 deletions

View File

@@ -0,0 +1,932 @@
import 'dart:convert';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import '../../models/onprovider_order_model.dart';
import '../models/wallet_transaction_model.dart';
import '../payment/xenditModel.dart';
import 'dart:developer';
import 'dart:io';
import 'dart:math' as maths;
import 'package:flutter/material.dart';
import 'package:flutter_paypal/flutter_paypal.dart';
import 'package:flutter_stripe/flutter_stripe.dart';
import 'package:razorpay_flutter/razorpay_flutter.dart';
import 'package:uuid/uuid.dart';
import '../constant/constant.dart';
import '../models/payment_model/cod_setting_model.dart';
import '../models/payment_model/flutter_wave_model.dart';
import '../models/payment_model/mercado_pago_model.dart';
import '../models/payment_model/mid_trans.dart';
import '../models/payment_model/orange_money.dart';
import '../models/payment_model/pay_fast_model.dart';
import '../models/payment_model/pay_stack_model.dart';
import '../models/payment_model/paypal_model.dart';
import '../models/payment_model/paytm_model.dart';
import '../models/payment_model/razorpay_model.dart';
import '../models/payment_model/stripe_model.dart';
import '../models/payment_model/wallet_setting_model.dart';
import '../models/payment_model/xendit.dart';
import '../payment/MercadoPagoScreen.dart';
import '../payment/PayFastScreen.dart';
import '../payment/getPaytmTxtToken.dart';
import '../payment/midtrans_screen.dart';
import '../payment/orangePayScreen.dart';
import '../payment/paystack/pay_stack_screen.dart';
import '../payment/paystack/pay_stack_url_model.dart';
import '../payment/paystack/paystack_url_genrater.dart';
import '../payment/stripe_failed_model.dart';
import '../payment/xenditScreen.dart';
import '../screen_ui/multi_vendor_service/wallet_screen/wallet_screen.dart';
import '../screen_ui/on_demand_service/on_demand_dashboard_screen.dart';
import '../service/fire_store_utils.dart';
import '../service/send_notification.dart';
import '../themes/app_them_data.dart';
import '../themes/show_toast_dialog.dart';
import '../utils/preferences.dart';
import 'on_demand_dashboard_controller.dart';
class OnDemandPaymentController extends GetxController {
Rx<OnProviderOrderModel?> onDemandOrderModel = Rx<OnProviderOrderModel?>(null);
RxDouble totalAmount = 0.0.obs;
late bool isExtra;
RxBool isLoading = false.obs;
RxString selectedPaymentMethod = ''.obs;
RxBool isOrderPlaced = false.obs;
@override
void onInit() {
super.onInit();
isLoading.value = true;
final args = Get.arguments as Map<String, dynamic>;
onDemandOrderModel = args['onDemandOrderModel'];
totalAmount = (args['totalAmount'] as double).obs;
print("payment totalAmount ::::::::: $totalAmount");
isExtra = args['isExtra'];
getPaymentSettings();
}
Future<void> placeOrder() async {
if (!isExtra) {
// Normal Order
ShowToastDialog.showLoader("Please wait...".tr);
onDemandOrderModel.value?.payment_method = selectedPaymentMethod.value;
onDemandOrderModel.value?.paymentStatus = onDemandOrderModel.value?.provider.priceUnit == "Fixed" && selectedPaymentMethod.value == "cod" ? false : true;
onDemandOrderModel.value?.extraPaymentStatus = true;
await FireStoreUtils.onDemandOrderPlace(onDemandOrderModel.value!, totalAmount.value);
if (onDemandOrderModel.value?.status == Constant.orderPlaced) {
await FireStoreUtils.sendOrderOnDemandServiceEmail(orderModel: onDemandOrderModel.value!);
final providerUser = await FireStoreUtils.getUserProfile(onDemandOrderModel.value!.provider.author!);
if (providerUser != null) {
final payLoad = {"type": 'provider_order', "orderId": onDemandOrderModel.value?.id};
await SendNotification.sendFcmMessage(Constant.bookingPlaced, providerUser.fcmToken ?? '', payLoad);
}
ShowToastDialog.showToast("OnDemand Service successfully booked".tr);
}
if (selectedPaymentMethod.value == PaymentGateway.wallet.name) {
WalletTransactionModel transactionModel = WalletTransactionModel(
id: Constant.getUuid(),
amount: double.parse(totalAmount.value.toString()),
date: Timestamp.now(),
paymentMethod: PaymentGateway.wallet.name,
transactionUser: "customer",
userId: FireStoreUtils.getCurrentUid(),
isTopup: false,
orderId: onDemandOrderModel.value!.id,
note: "Booking Amount debited".tr,
paymentStatus: "success".tr,
);
await FireStoreUtils.setWalletTransaction(transactionModel).then((value) async {
if (value == true) {
await FireStoreUtils.updateUserWallet(amount: "-${totalAmount.value.toString()}", userId: FireStoreUtils.getCurrentUid()).then((value) {});
}
});
}
ShowToastDialog.closeLoader();
Get.offAll(const OnDemandDashboardScreen());
OnDemandDashboardController controller = Get.put(OnDemandDashboardController());
controller.selectedIndex.value = 2;
} else {
// Extra Charges Flow
onDemandOrderModel.value?.createdAt = Timestamp.now();
onDemandOrderModel.value?.extraPaymentStatus = true;
if (selectedPaymentMethod.value == PaymentGateway.wallet.name) {
WalletTransactionModel transactionModel = WalletTransactionModel(
id: Constant.getUuid(),
amount: double.parse(totalAmount.value.toString()),
date: Timestamp.now(),
paymentMethod: PaymentGateway.wallet.name,
transactionUser: "customer",
userId: FireStoreUtils.getCurrentUid(),
isTopup: false,
orderId: onDemandOrderModel.value!.id,
note: "Booking Extra charge debited",
paymentStatus: "success".tr,
);
await FireStoreUtils.setWalletTransaction(transactionModel).then((value) async {
if (value == true) {
await FireStoreUtils.updateUserWallet(amount: "-${totalAmount.value.toString()}", userId: FireStoreUtils.getCurrentUid()).then((value) {});
}
});
}
// Handle wallet payment if needed
if (selectedPaymentMethod.value != 'cod') {
WalletTransactionModel transactionModel = WalletTransactionModel(
id: Constant.getUuid(),
serviceType: 'ondemand-service',
amount: totalAmount.value,
date: Timestamp.now(),
paymentMethod: PaymentGateway.wallet.name,
transactionUser: "provider",
userId: onDemandOrderModel.value?.provider.author!,
isTopup: true,
orderId: onDemandOrderModel.value?.id,
note: 'Extra Charge Amount Credited',
paymentStatus: "success".tr,
);
await FireStoreUtils.setWalletTransaction(transactionModel).then((value) async {
if (value == true) {
await FireStoreUtils.updateUserWallet(amount: "-$totalAmount", userId: FireStoreUtils.getCurrentUid());
}
});
}
await FireStoreUtils.updateOnDemandOrder(onDemandOrderModel.value!);
ShowToastDialog.closeLoader();
Get.offAll(const OnDemandDashboardScreen());
OnDemandDashboardController controller = Get.put(OnDemandDashboardController());
controller.selectedIndex.value = 2;
}
}
Rx<WalletSettingModel> walletSettingModel = WalletSettingModel().obs;
Rx<CodSettingModel> cashOnDeliverySettingModel = CodSettingModel().obs;
Rx<PayFastModel> payFastModel = PayFastModel().obs;
Rx<MercadoPagoModel> mercadoPagoModel = MercadoPagoModel().obs;
Rx<PayPalModel> payPalModel = PayPalModel().obs;
Rx<StripeModel> stripeModel = StripeModel().obs;
Rx<FlutterWaveModel> flutterWaveModel = FlutterWaveModel().obs;
Rx<PayStackModel> payStackModel = PayStackModel().obs;
Rx<PaytmModel> paytmModel = PaytmModel().obs;
Rx<RazorPayModel> razorPayModel = RazorPayModel().obs;
Rx<MidTrans> midTransModel = MidTrans().obs;
Rx<OrangeMoney> orangeMoneyModel = OrangeMoney().obs;
Rx<Xendit> xenditModel = Xendit().obs;
Future<void> getPaymentSettings() async {
isLoading.value = true;
await FireStoreUtils.getPaymentSettingsData().then((value) {
stripeModel.value = StripeModel.fromJson(jsonDecode(Preferences.getString(Preferences.stripeSettings)));
payPalModel.value = PayPalModel.fromJson(jsonDecode(Preferences.getString(Preferences.paypalSettings)));
payStackModel.value = PayStackModel.fromJson(jsonDecode(Preferences.getString(Preferences.payStack)));
mercadoPagoModel.value = MercadoPagoModel.fromJson(jsonDecode(Preferences.getString(Preferences.mercadoPago)));
flutterWaveModel.value = FlutterWaveModel.fromJson(jsonDecode(Preferences.getString(Preferences.flutterWave)));
paytmModel.value = PaytmModel.fromJson(jsonDecode(Preferences.getString(Preferences.paytmSettings)));
payFastModel.value = PayFastModel.fromJson(jsonDecode(Preferences.getString(Preferences.payFastSettings)));
razorPayModel.value = RazorPayModel.fromJson(jsonDecode(Preferences.getString(Preferences.razorpaySettings)));
midTransModel.value = MidTrans.fromJson(jsonDecode(Preferences.getString(Preferences.midTransSettings)));
orangeMoneyModel.value = OrangeMoney.fromJson(jsonDecode(Preferences.getString(Preferences.orangeMoneySettings)));
xenditModel.value = Xendit.fromJson(jsonDecode(Preferences.getString(Preferences.xenditSettings)));
walletSettingModel.value = WalletSettingModel.fromJson(jsonDecode(Preferences.getString(Preferences.walletSettings)));
cashOnDeliverySettingModel.value = CodSettingModel.fromJson(jsonDecode(Preferences.getString(Preferences.codSettings)));
if (walletSettingModel.value.isEnabled == true) {
selectedPaymentMethod.value = PaymentGateway.wallet.name;
} else if (cashOnDeliverySettingModel.value.isEnabled == true) {
selectedPaymentMethod.value = PaymentGateway.cod.name;
} else if (stripeModel.value.isEnabled == true) {
selectedPaymentMethod.value = PaymentGateway.stripe.name;
} else if (payPalModel.value.isEnabled == true) {
selectedPaymentMethod.value = PaymentGateway.paypal.name;
} else if (payStackModel.value.isEnable == true) {
selectedPaymentMethod.value = PaymentGateway.payStack.name;
} else if (mercadoPagoModel.value.isEnabled == true) {
selectedPaymentMethod.value = PaymentGateway.mercadoPago.name;
} else if (flutterWaveModel.value.isEnable == true) {
selectedPaymentMethod.value = PaymentGateway.flutterWave.name;
} else if (payFastModel.value.isEnable == true) {
selectedPaymentMethod.value = PaymentGateway.payFast.name;
} else if (razorPayModel.value.isEnabled == true) {
selectedPaymentMethod.value = PaymentGateway.razorpay.name;
} else if (midTransModel.value.enable == true) {
selectedPaymentMethod.value = PaymentGateway.midTrans.name;
} else if (orangeMoneyModel.value.enable == true) {
selectedPaymentMethod.value = PaymentGateway.orangeMoney.name;
} else if (xenditModel.value.enable == true) {
selectedPaymentMethod.value = PaymentGateway.xendit.name;
}
Stripe.publishableKey = stripeModel.value.clientpublishableKey.toString();
Stripe.merchantIdentifier = 'eMart Customer';
Stripe.instance.applySettings();
setRef();
razorPay.on(Razorpay.EVENT_PAYMENT_SUCCESS, handlePaymentSuccess);
razorPay.on(Razorpay.EVENT_EXTERNAL_WALLET, handleExternalWaller);
razorPay.on(Razorpay.EVENT_PAYMENT_ERROR, handlePaymentError);
isLoading.value = false;
});
}
// Strip
Future<void> stripeMakePayment({required String amount}) async {
log(double.parse(amount).toStringAsFixed(0));
try {
Map<String, dynamic>? paymentIntentData = await createStripeIntent(amount: amount);
log("stripe Responce====>$paymentIntentData");
if (paymentIntentData!.containsKey("error")) {
Get.back();
ShowToastDialog.showToast("Something went wrong, please contact admin.".tr);
} else {
await Stripe.instance.initPaymentSheet(
paymentSheetParameters: SetupPaymentSheetParameters(
paymentIntentClientSecret: paymentIntentData['client_secret'],
allowsDelayedPaymentMethods: false,
googlePay: const PaymentSheetGooglePay(merchantCountryCode: 'US', testEnv: true, currencyCode: "USD"),
customFlow: true,
style: ThemeMode.system,
appearance: PaymentSheetAppearance(colors: PaymentSheetAppearanceColors(primary: AppThemeData.primary300)),
merchantDisplayName: 'GoRide',
),
);
displayStripePaymentSheet(amount: amount);
}
} catch (e, s) {
log("$e \n$s");
ShowToastDialog.showToast("exception:$e \n$s");
}
}
Future<void> displayStripePaymentSheet({required String amount}) async {
try {
await Stripe.instance.presentPaymentSheet().then((value) {
ShowToastDialog.showToast("Payment successfully".tr);
placeOrder();
});
} on StripeException catch (e) {
var lo1 = jsonEncode(e);
var lo2 = jsonDecode(lo1);
StripePayFailedModel lom = StripePayFailedModel.fromJson(lo2);
ShowToastDialog.showToast(lom.error.message);
} catch (e) {
ShowToastDialog.showToast(e.toString());
}
}
Future createStripeIntent({required String amount}) async {
try {
Map<String, dynamic> body = {
'amount': ((double.parse(amount) * 100).round()).toString(),
'currency': "USD",
'payment_method_types[]': 'card',
"description": "Strip Payment",
"shipping[name]": Constant.userModel?.fullName(),
"shipping[address][line1]": "510 Townsend St",
"shipping[address][postal_code]": "98140",
"shipping[address][city]": "San Francisco",
"shipping[address][state]": "CA",
"shipping[address][country]": "US",
};
var stripeSecret = stripeModel.value.stripeSecret;
var response = await http.post(
Uri.parse('https://api.stripe.com/v1/payment_intents'),
body: body,
headers: {'Authorization': 'Bearer $stripeSecret', 'Content-Type': 'application/x-www-form-urlencoded'},
);
return jsonDecode(response.body);
} catch (e) {
log(e.toString());
}
}
//mercadoo
Future<Null> mercadoPagoMakePayment({required BuildContext context, required String amount}) async {
final headers = {'Authorization': 'Bearer ${mercadoPagoModel.value.accessToken}', 'Content-Type': 'application/json'};
final body = jsonEncode({
"items": [
{
"title": "Test",
"description": "Test Payment",
"quantity": 1,
"currency_id": "BRL", // or your preferred currency
"unit_price": double.parse(amount),
},
],
"payer": {"email": Constant.userModel?.email},
"back_urls": {"failure": "${Constant.globalUrl}payment/failure", "pending": "${Constant.globalUrl}payment/pending", "success": "${Constant.globalUrl}payment/success"},
"auto_return": "approved",
// Automatically return after payment is approved
});
final response = await http.post(Uri.parse("https://api.mercadopago.com/checkout/preferences"), headers: headers, body: body);
if (response.statusCode == 200 || response.statusCode == 201) {
final data = jsonDecode(response.body);
Get.to(MercadoPagoScreen(initialURl: data['init_point']))!.then((value) {
if (value) {
ShowToastDialog.showToast("Payment Successful!!".tr);
placeOrder();
} else {
ShowToastDialog.showToast("Payment UnSuccessful!!".tr);
}
});
} else {
print('Error creating preference: ${response.body}');
return null;
}
}
//Paypal
void paypalPaymentSheet(String amount, context) {
Navigator.of(context).push(
MaterialPageRoute(
builder:
(BuildContext context) => UsePaypal(
sandboxMode: payPalModel.value.isLive == true ? false : true,
clientId: payPalModel.value.paypalClient ?? '',
secretKey: payPalModel.value.paypalSecret ?? '',
returnURL: "com.parkme://paypalpay",
cancelURL: "com.parkme://paypalpay",
transactions: [
{
"amount": {
"total": amount,
"currency": "USD",
"details": {"subtotal": amount},
},
},
],
note: "Contact us for any questions on your order.",
onSuccess: (Map params) async {
placeOrder();
ShowToastDialog.showToast("Payment Successful!!".tr);
},
onError: (error) {
Get.back();
ShowToastDialog.showToast("Payment UnSuccessful!!".tr);
},
onCancel: (params) {
Get.back();
ShowToastDialog.showToast("Payment UnSuccessful!!".tr);
},
),
),
);
}
///PayStack Payment Method
Future<void> payStackPayment(String totalAmount) async {
await PayStackURLGen.payStackURLGen(
amount: (double.parse(totalAmount) * 100).toString(),
currency: "ZAR",
secretKey: payStackModel.value.secretKey.toString(),
userModel: Constant.userModel!,
).then((value) async {
if (value != null) {
PayStackUrlModel payStackModel0 = value;
Get.to(
PayStackScreen(
secretKey: payStackModel.value.secretKey.toString(),
callBackUrl: payStackModel.value.callbackURL.toString(),
initialURl: payStackModel0.data.authorizationUrl,
amount: totalAmount,
reference: payStackModel0.data.reference,
),
)!.then((value) {
if (value) {
ShowToastDialog.showToast("Payment Successful!!".tr);
placeOrder();
} else {
ShowToastDialog.showToast("Payment UnSuccessful!!".tr);
}
});
} else {
ShowToastDialog.showToast("Something went wrong, please contact admin.".tr);
}
});
}
//flutter wave Payment Method
Future<Null> flutterWaveInitiatePayment({required BuildContext context, required String amount}) async {
final url = Uri.parse('https://api.flutterwave.com/v3/payments');
final headers = {'Authorization': 'Bearer ${flutterWaveModel.value.secretKey}', 'Content-Type': 'application/json'};
final body = jsonEncode({
"tx_ref": _ref,
"amount": amount,
"currency": "NGN",
"redirect_url": "${Constant.globalUrl}payment/success",
"payment_options": "ussd, card, barter, payattitude",
"customer": {"email": Constant.userModel?.email.toString(), "phonenumber": Constant.userModel?.phoneNumber, "name": Constant.userModel?.fullName()},
"customizations": {"title": "Payment for Services", "description": "Payment for XYZ services"},
});
final response = await http.post(url, headers: headers, body: body);
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
Get.to(MercadoPagoScreen(initialURl: data['data']['link']))!.then((value) {
if (value) {
ShowToastDialog.showToast("Payment Successful!!".tr);
placeOrder();
} else {
ShowToastDialog.showToast("Payment UnSuccessful!!".tr);
}
});
} else {
print('Payment initialization failed: ${response.body}');
return null;
}
}
String? _ref;
void setRef() {
maths.Random numRef = maths.Random();
int year = DateTime.now().year;
int refNumber = numRef.nextInt(20000);
if (Platform.isAndroid) {
_ref = "AndroidRef$year$refNumber";
} else if (Platform.isIOS) {
_ref = "IOSRef$year$refNumber";
}
}
// payFast
void payFastPayment({required BuildContext context, required String amount}) {
PayStackURLGen.getPayHTML(payFastSettingData: payFastModel.value, amount: amount.toString(), userModel: Constant.userModel!).then((String? value) async {
bool isDone = await Get.to(PayFastScreen(htmlData: value!, payFastSettingData: payFastModel.value));
if (isDone) {
Get.back();
ShowToastDialog.showToast("Payment successfully".tr);
placeOrder();
} else {
Get.back();
ShowToastDialog.showToast("Payment Failed".tr);
}
});
}
///Paytm payment function
Future<void> getPaytmCheckSum(context, {required double amount}) async {
final String orderId = DateTime.now().millisecondsSinceEpoch.toString();
String getChecksum = "${Constant.globalUrl}payments/getpaytmchecksum";
final response = await http.post(
Uri.parse(getChecksum),
headers: {},
body: {"mid": paytmModel.value.paytmMID.toString(), "order_id": orderId, "key_secret": paytmModel.value.pAYTMMERCHANTKEY.toString()},
);
final data = jsonDecode(response.body);
await verifyCheckSum(checkSum: data["code"], amount: amount, orderId: orderId).then((value) {
initiatePayment(amount: amount, orderId: orderId).then((value) {
String callback = "";
if (paytmModel.value.isSandboxEnabled == true) {
callback = "${callback}https://securegw-stage.paytm.in/theia/paytmCallback?ORDER_ID=$orderId";
} else {
callback = "${callback}https://securegw.paytm.in/theia/paytmCallback?ORDER_ID=$orderId";
}
GetPaymentTxtTokenModel result = value;
startTransaction(context, txnTokenBy: result.body.txnToken ?? '', orderId: orderId, amount: amount, callBackURL: callback, isStaging: paytmModel.value.isSandboxEnabled);
});
});
}
Future<void> startTransaction(context, {required String txnTokenBy, required orderId, required double amount, required callBackURL, required isStaging}) async {
// try {
// var response = AllInOneSdk.startTransaction(
// paytmModel.value.paytmMID.toString(),
// orderId,
// amount.toString(),
// txnTokenBy,
// callBackURL,
// isStaging,
// true,
// true,
// );
//
// response.then((value) {
// if (value!["RESPMSG"] == "Txn Success") {
// print("txt done!!");
// ShowToastDialog.showToast("Payment Successful!!");
// placeOrder();
// }
// }).catchError((onError) {
// if (onError is PlatformException) {
// Get.back();
//
// ShowToastDialog.showToast(onError.message.toString());
// } else {
// log("======>>2");
// Get.back();
// ShowToastDialog.showToast(onError.message.toString());
// }
// });
// } catch (err) {
// Get.back();
// ShowToastDialog.showToast(err.toString());
// }
}
Future verifyCheckSum({required String checkSum, required double amount, required orderId}) async {
String getChecksum = "${Constant.globalUrl}payments/validatechecksum";
final response = await http.post(
Uri.parse(getChecksum),
headers: {},
body: {"mid": paytmModel.value.paytmMID.toString(), "order_id": orderId, "key_secret": paytmModel.value.pAYTMMERCHANTKEY.toString(), "checksum_value": checkSum},
);
final data = jsonDecode(response.body);
return data['status'];
}
Future<GetPaymentTxtTokenModel> initiatePayment({required double amount, required orderId}) async {
String initiateURL = "${Constant.globalUrl}payments/initiatepaytmpayment";
String callback = "";
if (paytmModel.value.isSandboxEnabled == true) {
callback = "${callback}https://securegw-stage.paytm.in/theia/paytmCallback?ORDER_ID=$orderId";
} else {
callback = "${callback}https://securegw.paytm.in/theia/paytmCallback?ORDER_ID=$orderId";
}
final response = await http.post(
Uri.parse(initiateURL),
headers: {},
body: {
"mid": paytmModel.value.paytmMID,
"order_id": orderId,
"key_secret": paytmModel.value.pAYTMMERCHANTKEY,
"amount": amount.toString(),
"currency": "INR",
"callback_url": callback,
"custId": FireStoreUtils.getCurrentUid(),
"issandbox": paytmModel.value.isSandboxEnabled == true ? "1" : "2",
},
);
log(response.body);
final data = jsonDecode(response.body);
if (data["body"]["txnToken"] == null || data["body"]["txnToken"].toString().isEmpty) {
Get.back();
ShowToastDialog.showToast("something went wrong, please contact admin.".tr);
}
return GetPaymentTxtTokenModel.fromJson(data);
}
///RazorPay payment function
final Razorpay razorPay = Razorpay();
void openCheckout({required amount, required orderId}) async {
var options = {
'key': razorPayModel.value.razorpayKey,
'amount': amount * 100,
'name': 'GoRide',
'order_id': orderId,
"currency": "INR",
'description': 'wallet Topup',
'retry': {'enabled': true, 'max_count': 1},
'send_sms_hash': true,
'prefill': {'contact': Constant.userModel?.phoneNumber, 'email': Constant.userModel?.email},
'external': {
'wallets': ['paytm'],
},
};
try {
razorPay.open(options);
} catch (e) {
debugPrint('Error: $e');
}
}
void handlePaymentSuccess(PaymentSuccessResponse response) {
Get.back();
ShowToastDialog.showToast("Payment Successful!!".tr);
placeOrder();
}
void handleExternalWaller(ExternalWalletResponse response) {
Get.back();
ShowToastDialog.showToast("Payment Processing!! via".tr);
}
void handlePaymentError(PaymentFailureResponse response) {
Get.back();
ShowToastDialog.showToast("Payment Failed!!".tr);
}
bool isCurrentDateInRange(DateTime startDate, DateTime endDate) {
final currentDate = DateTime.now();
return currentDate.isAfter(startDate) && currentDate.isBefore(endDate);
}
//Midtrans payment
Future<void> midtransMakePayment({required String amount, required BuildContext context}) async {
await createPaymentLink(amount: amount).then((url) {
ShowToastDialog.closeLoader();
if (url != '') {
Get.to(() => MidtransScreen(initialURl: url))!.then((value) {
if (value == true) {
ShowToastDialog.showToast("Payment Successful!!".tr);
placeOrder();
} else {
ShowToastDialog.showToast("Payment Unsuccessful!!".tr);
}
});
}
});
}
Future<String> createPaymentLink({required var amount}) async {
var ordersId = const Uuid().v1();
final url = Uri.parse(midTransModel.value.isSandbox! ? 'https://api.sandbox.midtrans.com/v1/payment-links' : 'https://api.midtrans.com/v1/payment-links');
final response = await http.post(
url,
headers: {'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': generateBasicAuthHeader(midTransModel.value.serverKey!)},
body: jsonEncode({
'transaction_details': {'order_id': ordersId, 'gross_amount': double.parse(amount.toString()).toInt()},
'usage_limit': 2,
"callbacks": {"finish": "https://www.google.com?merchant_order_id=$ordersId"},
}),
);
if (response.statusCode == 200 || response.statusCode == 201) {
final responseData = jsonDecode(response.body);
return responseData['payment_url'];
} else {
ShowToastDialog.showToast("something went wrong, please contact admin.".tr);
return '';
}
}
String generateBasicAuthHeader(String apiKey) {
String credentials = '$apiKey:';
String base64Encoded = base64Encode(utf8.encode(credentials));
return 'Basic $base64Encoded';
}
///Orangepay payment
// static variables to store transaction session
static String accessToken = '';
static String payToken = '';
static String orderId = '';
static String amount = '';
Future<void> orangeMakePayment({required String amount, required BuildContext context}) async {
reset();
var id = const Uuid().v4();
var paymentURL = await fetchToken(context: context, orderId: id, amount: amount, currency: 'USD');
ShowToastDialog.closeLoader();
if (paymentURL.toString().isNotEmpty) {
Get.to(() => OrangeMoneyScreen(initialURl: paymentURL, accessToken: accessToken, amount: amount, orangePay: orangeMoneyModel.value, orderId: orderId, payToken: payToken))!.then((value) {
if (value == true) {
ShowToastDialog.showToast("Payment Successful!!".tr);
placeOrder();
}
});
} else {
ShowToastDialog.showToast("Payment Unsuccessful!!".tr);
}
}
Future<String> fetchToken({required String orderId, required String currency, required BuildContext context, required String amount}) async {
String apiUrl = 'https://api.orange.com/oauth/v3/token';
Map<String, String> requestBody = {'grant_type': 'client_credentials'};
var response = await http.post(
Uri.parse(apiUrl),
headers: <String, String>{'Authorization': "Basic ${orangeMoneyModel.value.auth ?? ''}", 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json'},
body: requestBody,
);
if (response.statusCode == 200) {
Map<String, dynamic> responseData = jsonDecode(response.body);
accessToken = responseData['access_token'] ?? '';
if (accessToken.isEmpty) {
ShowToastDialog.showToast("Failed to get access token".tr);
return '';
}
return await webpayment(context: context, amountData: amount, currency: currency, orderIdData: orderId);
} else {
ShowToastDialog.showToast("Something went wrong, please contact admin.".tr);
return '';
}
}
Future<String> webpayment({required String orderIdData, required BuildContext context, required String currency, required String amountData}) async {
orderId = orderIdData;
amount = amountData;
// ✅ Null-safe handling
bool isSandbox = orangeMoneyModel.value.isSandbox ?? false;
String apiUrl = isSandbox ? 'https://api.orange.com/orange-money-webpay/dev/v1/webpayment' : 'https://api.orange.com/orange-money-webpay/cm/v1/webpayment';
Map<String, String> requestBody = {
"merchant_key": orangeMoneyModel.value.merchantKey ?? '',
"currency": isSandbox ? "OUV" : currency,
"order_id": orderId,
"amount": amount,
"reference": 'Y-Note Test',
"lang": "en",
"return_url": orangeMoneyModel.value.returnUrl ?? "",
"cancel_url": orangeMoneyModel.value.cancelUrl ?? "",
"notif_url": orangeMoneyModel.value.notifUrl ?? "",
};
var response = await http.post(
Uri.parse(apiUrl),
headers: <String, String>{'Authorization': 'Bearer $accessToken', 'Content-Type': 'application/json', 'Accept': 'application/json'},
body: json.encode(requestBody),
);
if (response.statusCode == 201) {
Map<String, dynamic> responseData = jsonDecode(response.body);
if (responseData['message'] == 'OK') {
payToken = responseData['pay_token'] ?? '';
return responseData['payment_url'] ?? '';
} else {
return '';
}
} else {
ShowToastDialog.showToast("Something went wrong, please contact admin.".tr);
return '';
}
}
static void reset() {
accessToken = '';
payToken = '';
orderId = '';
amount = '';
}
// static String accessToken = '';
// static String payToken = '';
// static String orderId = '';
// static String amount = '';
//
// Future<void> orangeMakePayment({required String amount, required BuildContext context}) async {
// reset();
// var id = const Uuid().v4();
// var paymentURL = await fetchToken(context: context, orderId: id, amount: amount, currency: 'USD');
// ShowToastDialog.closeLoader();
// if (paymentURL.toString() != '') {
// Get.to(
// () => OrangeMoneyScreen(initialURl: paymentURL, accessToken: accessToken, amount: amount, orangePay: orangeMoneyModel.value, orderId: orderId, payToken: payToken),
// )!.then((value) {
// if (value == true) {
// ShowToastDialog.showToast("Payment Successful!!".tr);
// placeOrder();
// ();
// }
// });
// } else {
// ShowToastDialog.showToast("Payment Unsuccessful!!".tr);
// }
// }
//
// Future fetchToken({required String orderId, required String currency, required BuildContext context, required String amount}) async {
// String apiUrl = 'https://api.orange.com/oauth/v3/token';
// Map<String, String> requestBody = {'grant_type': 'client_credentials'};
//
// var response = await http.post(
// Uri.parse(apiUrl),
// headers: <String, String>{'Authorization': "Basic ${orangeMoneyModel.value.auth!}", 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json'},
// body: requestBody,
// );
//
// // Handle the response
//
// if (response.statusCode == 200) {
// Map<String, dynamic> responseData = jsonDecode(response.body);
//
// accessToken = responseData['access_token'];
// // ignore: use_build_context_synchronously
// return await webpayment(context: context, amountData: amount, currency: currency, orderIdData: orderId);
// } else {
// ShowToastDialog.showToast("Something went wrong, please contact admin.".tr);
// return '';
// }
// }
//
// Future webpayment({required String orderIdData, required BuildContext context, required String currency, required String amountData}) async {
// orderId = orderIdData;
// amount = amountData;
// String apiUrl =
// orangeMoneyModel.value.isSandbox! == true ? 'https://api.orange.com/orange-money-webpay/dev/v1/webpayment' : 'https://api.orange.com/orange-money-webpay/cm/v1/webpayment';
// Map<String, String> requestBody = {
// "merchant_key": orangeMoneyModel.value.merchantKey ?? '',
// "currency": orangeMoneyModel.value.isSandbox == true ? "OUV" : currency,
// "order_id": orderId,
// "amount": amount,
// "reference": 'Y-Note Test',
// "lang": "en",
// "return_url": orangeMoneyModel.value.returnUrl ?? "",
// "cancel_url": orangeMoneyModel.value.cancelUrl ?? "",
// "notif_url": orangeMoneyModel.value.notifUrl ?? "",
// // "return_url": orangeMoneyModel.value.returnUrl!.toString(),
// // "cancel_url": orangeMoneyModel.value.cancelUrl!.toString(),
// // "notif_url": orangeMoneyModel.value.notifUrl!.toString(),
// };
//
// var response = await http.post(
// Uri.parse(apiUrl),
// headers: <String, String>{'Authorization': 'Bearer $accessToken', 'Content-Type': 'application/json', 'Accept': 'application/json'},
// body: json.encode(requestBody),
// );
//
// // Handle the response
// if (response.statusCode == 201) {
// Map<String, dynamic> responseData = jsonDecode(response.body);
// if (responseData['message'] == 'OK') {
// payToken = responseData['pay_token'];
// return responseData['payment_url'];
// } else {
// return '';
// }
// } else {
// ShowToastDialog.showToast("Something went wrong, please contact admin.".tr);
// return '';
// }
// }
//
// static void reset() {
// accessToken = '';
// payToken = '';
// orderId = '';
// amount = '';
// }
///XenditPayment
Future<void> xenditPayment(context, amount) async {
await createXenditInvoice(amount: amount).then((model) {
ShowToastDialog.closeLoader();
if (model.id != null) {
Get.to(() => XenditScreen(initialURl: model.invoiceUrl ?? '', transId: model.id ?? '', apiKey: xenditModel.value.apiKey!.toString()))!.then((value) {
if (value == true) {
ShowToastDialog.showToast("Payment Successful!!".tr);
placeOrder();
();
} else {
ShowToastDialog.showToast("Payment Unsuccessful!!".tr);
}
});
}
});
}
Future<XenditModel> createXenditInvoice({required var amount}) async {
const url = 'https://api.xendit.co/v2/invoices';
var headers = {
'Content-Type': 'application/json',
'Authorization': generateBasicAuthHeader(xenditModel.value.apiKey!.toString()),
// 'Cookie': '__cf_bm=yERkrx3xDITyFGiou0bbKY1bi7xEwovHNwxV1vCNbVc-1724155511-1.0.1.1-jekyYQmPCwY6vIJ524K0V6_CEw6O.dAwOmQnHtwmaXO_MfTrdnmZMka0KZvjukQgXu5B.K_6FJm47SGOPeWviQ',
};
final body = jsonEncode({
'external_id': const Uuid().v1(),
'amount': amount,
'payer_email': 'customer@domain.com',
'description': 'Test - VA Successful invoice payment',
'currency': 'IDR', //IDR, PHP, THB, VND, MYR
});
try {
final response = await http.post(Uri.parse(url), headers: headers, body: body);
if (response.statusCode == 200 || response.statusCode == 201) {
XenditModel model = XenditModel.fromJson(jsonDecode(response.body));
return model;
} else {
return XenditModel();
}
} catch (e) {
return XenditModel();
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,43 @@
import 'package:customer/models/user_model.dart';
import 'package:get/get.dart';
import '../constant/constant.dart';
import '../service/fire_store_utils.dart';
class AddressListController extends GetxController {
Rx<UserModel> userModel = UserModel().obs;
RxList<ShippingAddress> shippingAddressList = <ShippingAddress>[].obs;
RxBool isLoading = true.obs;
@override
void onInit() {
// TODO: implement onInit
getUser();
super.onInit();
}
Future<void> getUser() async {
await FireStoreUtils.getUserProfile(FireStoreUtils.getCurrentUid()).then((value) {
if (value != null) {
userModel.value = value;
if (userModel.value.shippingAddress != null) {
shippingAddressList.value = userModel.value.shippingAddress!;
}
}
});
isLoading.value = false;
}
Future<void> deleteAddress(int index) async {
if (shippingAddressList.isNotEmpty && index < shippingAddressList.length) {
shippingAddressList.removeAt(index);
userModel.value.shippingAddress = shippingAddressList;
if (shippingAddressList.isNotEmpty) {
Constant.selectedLocation = shippingAddressList.first;
}
await FireStoreUtils.updateUser(userModel.value);
shippingAddressList.refresh();
}
}
}

View File

@@ -0,0 +1,48 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/models/advertisement_model.dart';
import 'package:customer/models/favourite_model.dart';
import 'package:customer/models/vendor_model.dart';
import '../service/fire_store_utils.dart';
import 'package:get/get.dart';
class AdvertisementListController extends GetxController {
RxBool isLoading = true.obs;
@override
void onInit() {
getAdvertisementList();
getFavouriteRestaurant();
super.onInit();
}
RxList<AdvertisementModel> advertisementList = <AdvertisementModel>[].obs;
Future<void> getAdvertisementList() async {
advertisementList.clear();
List<VendorModel> allNearestRestaurant = <VendorModel>[];
FireStoreUtils.getAllNearestRestaurant().listen((event) async {
allNearestRestaurant.addAll(event);
await FireStoreUtils.getAllAdvertisement().then((value) {
List<AdvertisementModel> adsList = value;
advertisementList.addAll(
adsList.where(
(ads) => allNearestRestaurant.any(
(restaurant) => restaurant.id == ads.vendorId,
),
),
);
});
isLoading.value = false;
});
}
RxList<FavouriteModel> favouriteList = <FavouriteModel>[].obs;
Future<void> getFavouriteRestaurant() async {
if (Constant.userModel != null) {
await FireStoreUtils.getFavouriteRestaurant().then((value) {
favouriteList.value = value;
});
}
}
}

View File

@@ -0,0 +1,58 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/models/brands_model.dart';
import 'package:customer/models/product_model.dart';
import 'package:customer/models/vendor_model.dart';
import 'package:customer/service/fire_store_utils.dart';
import 'package:get/get.dart';
class AllBrandProductController extends GetxController {
RxList<ProductModel> productList = <ProductModel>[].obs;
Rx<BrandsModel> brandModel = BrandsModel().obs;
RxBool isLoading = true.obs;
@override
void onInit() {
// TODO: implement onInit
getArguments();
super.onInit();
}
Future<void> getArguments() async {
final arguments = Get.arguments;
if (arguments != null) {
brandModel.value = arguments['brandModel'];
await getProductByCategoryId();
}
isLoading.value = false;
}
Future<void> getProductByCategoryId() async {
List<ProductModel> productDataList = await FireStoreUtils.getProductListByBrandId(brandModel.value.id.toString());
List<VendorModel> vendorList = await FireStoreUtils.getAllStoresFuture();
List<ProductModel> allProduct = <ProductModel>[];
for (var vendor in vendorList) {
await FireStoreUtils.getAllProducts(vendor.id.toString()).then((value) {
if (Constant.isSubscriptionModelApplied == true || vendor.adminCommission?.isEnabled == true) {
if (vendor.subscriptionPlan != null && Constant.isExpire(vendor) == false) {
if (vendor.subscriptionPlan?.itemLimit == '-1') {
allProduct.addAll(value);
} else {
int selectedProduct =
value.length < int.parse(vendor.subscriptionPlan?.itemLimit ?? '0') ? (value.isEmpty ? 0 : (value.length)) : int.parse(vendor.subscriptionPlan?.itemLimit ?? '0');
allProduct.addAll(value.sublist(0, selectedProduct));
}
}
} else {
allProduct.addAll(value);
}
});
}
for (var element in productDataList) {
final bool productIsInList = allProduct.any((product) => product.id == element.id);
if (productIsInList) {
productList.add(element);
}
}
}
}

View File

@@ -0,0 +1,30 @@
import 'package:customer/models/product_model.dart';
import 'package:customer/models/vendor_category_model.dart';
import 'package:customer/service/fire_store_utils.dart';
import 'package:get/get.dart';
class AllCategoryProductController extends GetxController {
RxBool isLoading = true.obs;
Rx<VendorCategoryModel> categoryModel = VendorCategoryModel().obs;
RxList<ProductModel> productList = <ProductModel>[].obs;
@override
void onInit() {
// TODO: implement onInit
getArguments();
super.onInit();
}
Future<void> getArguments() async {
final arguments = Get.arguments;
if (arguments != null) {
categoryModel.value = arguments['categoryModel'];
await getProductByCategoryId();
}
isLoading.value = false;
}
Future<void> getProductByCategoryId() async {
productList.value = await FireStoreUtils.getProductListByCategoryId(categoryModel.value.id.toString());
}
}

View File

@@ -0,0 +1,340 @@
import 'dart:convert';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:customer/models/vendor_model.dart';
import 'package:customer/widget/geoflutterfire/src/geoflutterfire.dart';
import 'package:dropdown_textfield/dropdown_textfield.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:geocoding/geocoding.dart';
import 'package:geolocator/geolocator.dart';
import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart' as latlong;
import 'package:http/http.dart' as http;
import 'package:image_picker/image_picker.dart';
import '../constant/constant.dart';
import '../models/parcel_category.dart';
import '../models/parcel_order_model.dart';
import '../models/parcel_weight_model.dart';
import '../models/user_model.dart';
import '../screen_ui/parcel_service/parcel_order_confirmation.dart';
import '../service/fire_store_utils.dart';
import '../themes/show_toast_dialog.dart';
class BookParcelController extends GetxController {
// Sender details
final Rx<TextEditingController> senderLocationController = TextEditingController().obs;
final Rx<TextEditingController> senderNameController = TextEditingController().obs;
final Rx<TextEditingController> senderMobileController = TextEditingController().obs;
final Rx<SingleValueDropDownController> senderWeightController = SingleValueDropDownController().obs;
final Rx<TextEditingController> senderNoteController = TextEditingController().obs;
final Rx<TextEditingController> senderCountryCodeController = TextEditingController(text: Constant.defaultCountryCode).obs;
// Receiver details
final Rx<TextEditingController> receiverLocationController = TextEditingController().obs;
final Rx<TextEditingController> receiverNameController = TextEditingController().obs;
final Rx<TextEditingController> receiverMobileController = TextEditingController().obs;
final Rx<TextEditingController> receiverNoteController = TextEditingController().obs;
final Rx<TextEditingController> receiverCountryCodeController = TextEditingController(text: Constant.defaultCountryCode).obs;
// Delivery type
final RxString selectedDeliveryType = 'now'.obs;
// Scheduled delivery fields
final Rx<TextEditingController> scheduledDateController = TextEditingController().obs;
final Rx<TextEditingController> scheduledTimeController = TextEditingController().obs;
final RxString scheduledDate = ''.obs;
final RxString scheduledTime = ''.obs;
// Parcel weight list
final RxList<ParcelWeightModel> parcelWeight = <ParcelWeightModel>[].obs;
final RxList<XFile> images = <XFile>[].obs;
final ImagePicker _picker = ImagePicker();
Rx<UserLocation?> senderLocation = Rx<UserLocation?>(null);
Rx<UserLocation?> receiverLocation = Rx<UserLocation?>(null);
ParcelWeightModel? selectedWeight;
ParcelCategory? selectedCategory;
// UI observables
RxBool isScheduled = false.obs;
RxDouble distance = 0.0.obs;
RxDouble duration = 0.0.obs;
RxDouble subTotal = 0.0.obs;
@override
void onInit() {
super.onInit();
setArguments();
getParcelWeight();
setCurrentLocationForSenderAndReceiver();
}
void setArguments() {
if (Get.arguments != null && Get.arguments['parcelCategory'] != null) {
selectedCategory = Get.arguments['parcelCategory'];
}
}
Future<void> getParcelWeight() async {
parcelWeight.value = await FireStoreUtils.getParcelWeight();
}
Future<void> pickScheduledDate(BuildContext context) async {
final DateTime? picked = await showDatePicker(context: context, initialDate: DateTime.now(), firstDate: DateTime.now(), lastDate: DateTime.now().add(const Duration(days: 365)));
if (picked != null) {
final formattedDate = "${picked.day}/${picked.month}/${picked.year}";
scheduledDate.value = formattedDate;
scheduledDateController.value.text = formattedDate;
}
}
Future<void> pickScheduledTime(BuildContext context) async {
final TimeOfDay? picked = await showTimePicker(context: context, initialTime: TimeOfDay.now());
if (picked != null) {
final formattedTime = picked.format(context);
scheduledTime.value = formattedTime;
scheduledTimeController.value.text = formattedTime;
}
}
void onCameraClick(BuildContext context) {
final action = CupertinoActionSheet(
message: Text('Add your parcel image.'.tr, style: const TextStyle(fontSize: 15.0)),
actions: <Widget>[
CupertinoActionSheetAction(
child: Text('Choose image from gallery'.tr),
onPressed: () async {
Navigator.pop(context);
final imageList = await _picker.pickMultiImage();
if (imageList.isNotEmpty) {
images.addAll(imageList);
}
},
),
CupertinoActionSheetAction(
child: Text('Take a picture'.tr),
onPressed: () async {
Navigator.pop(context);
final XFile? photo = await _picker.pickImage(source: ImageSource.camera);
if (photo != null) {
images.add(photo);
}
},
),
],
cancelButton: CupertinoActionSheetAction(child: Text('Cancel'.tr), onPressed: () => Navigator.pop(context)),
);
showCupertinoModalPopup(context: context, builder: (context) => action);
}
Future<void> setCurrentLocationForSenderAndReceiver() async {
try {
await Geolocator.requestPermission();
final position = await Geolocator.getCurrentPosition();
final placemarks = await placemarkFromCoordinates(position.latitude, position.longitude);
final place = placemarks.first;
final address = "${place.name}, ${place.subLocality}, ${place.locality}, ${place.administrativeArea}, ${place.postalCode}, ${place.country}";
final userLocation = UserLocation(latitude: position.latitude, longitude: position.longitude);
senderLocation.value = userLocation;
senderLocationController.value.text = address;
} catch (e) {
debugPrint("Failed to fetch current location: $e");
}
}
bool validateFields() {
if (senderNameController.value.text.isEmpty) {
ShowToastDialog.showToast("Please enter sender name".tr);
return false;
} else if (senderMobileController.value.text.isEmpty) {
ShowToastDialog.showToast("Please enter sender mobile".tr);
return false;
} else if (senderLocationController.value.text.isEmpty) {
ShowToastDialog.showToast("Please enter sender address".tr);
return false;
} else if (receiverNameController.value.text.isEmpty) {
ShowToastDialog.showToast("Please enter receiver name".tr);
return false;
} else if (receiverMobileController.value.text.isEmpty) {
ShowToastDialog.showToast("Please enter receiver mobile".tr);
return false;
} else if (receiverLocationController.value.text.isEmpty) {
ShowToastDialog.showToast("Please enter receiver address".tr);
return false;
} else if (isScheduled.value) {
if (scheduledDate.value.isEmpty) {
ShowToastDialog.showToast("Please select scheduled date".tr);
return false;
} else if (scheduledTime.value.isEmpty) {
ShowToastDialog.showToast("Please select scheduled time".tr);
return false;
}
}
if (selectedWeight == null) {
ShowToastDialog.showToast("Please select parcel weight".tr);
return false;
} else if (senderLocation.value == null || receiverLocation.value == null) {
ShowToastDialog.showToast("Please select both sender and receiver locations".tr);
return false;
}
return true;
}
Future<void> bookNow() async {
if (!validateFields()) return;
try {
distance.value = 0.0;
if (Constant.selectedMapType == 'osm') {
print("Fetching route using OSM");
print("Sender Location: ${senderLocation.value?.latitude}, ${senderLocation.value?.longitude}");
print("Receiver Location: ${receiverLocation.value?.latitude}, ${receiverLocation.value?.longitude}");
await fetchRouteWithWaypoints([
latlong.LatLng(senderLocation.value?.latitude ?? 0.0, senderLocation.value?.longitude ?? 0.0),
latlong.LatLng(receiverLocation.value?.latitude ?? 0.0, receiverLocation.value?.longitude ?? 0.0),
]);
} else {
await fetchGoogleRouteWithWaypoints();
}
if (distance.value < 0.5) {
ShowToastDialog.showToast("Sender's location to receiver's location should be more than 1 km.".tr);
return;
}
subTotal.value = (distance.value * double.parse(selectedWeight!.deliveryCharge.toString()));
goToCart();
} catch (e) {
ShowToastDialog.showToast("Something went wrong while booking.".tr);
debugPrint("bookNow error: $e");
}
}
void goToCart() {
DateTime senderPickup = isScheduled.value ? parseScheduledDateTime(scheduledDate.value, scheduledTime.value) : DateTime.now();
print("Sender Pickup: $distance");
ParcelOrderModel order = ParcelOrderModel(
id: Constant.getUuid(),
subTotal: subTotal.value.toString(),
parcelType: selectedCategory?.title ?? '',
parcelCategoryID: selectedCategory?.id ?? '',
note: senderNoteController.value.text,
receiverNote: receiverNoteController.value.text,
distance: distance.value.toStringAsFixed(4),
parcelWeight: selectedWeight?.title ?? '',
parcelWeightCharge: selectedWeight?.deliveryCharge,
sendToDriver: isScheduled.value == true ? false : true,
senderPickupDateTime: Timestamp.fromDate(senderPickup),
receiverPickupDateTime: Timestamp.fromDate(DateTime.now()),
taxSetting: Constant.taxList,
isSchedule: isScheduled.value,
sourcePoint: G(
geopoint: GeoPoint(senderLocation.value!.latitude ?? 0.0, senderLocation.value!.longitude ?? 0.0),
geohash: Geoflutterfire().point(latitude: senderLocation.value!.latitude ?? 0.0, longitude: senderLocation.value!.longitude ?? 0.0).hash,
),
destinationPoint: G(
geopoint: GeoPoint(receiverLocation.value!.latitude ?? 0.0, receiverLocation.value!.longitude ?? 0.0),
geohash: Geoflutterfire().point(latitude: receiverLocation.value!.latitude ?? 0.0, longitude: receiverLocation.value!.longitude ?? 0.0).hash,
),
sender: LocationInformation(
address: senderLocationController.value.text,
name: senderNameController.value.text,
phone: "(${senderCountryCodeController.value.text}) ${senderMobileController.value.text}",
),
receiver: LocationInformation(
address: receiverLocationController.value.text,
name: receiverNameController.value.text,
phone: "(${receiverCountryCodeController.value.text}) ${receiverMobileController.value.text}",
),
receiverLatLong: receiverLocation.value,
senderLatLong: senderLocation.value,
sectionId: Constant.sectionConstantModel?.id ?? '',
);
debugPrint("Order Distance: ${distance.value}");
debugPrint("Subtotal: ${subTotal.value}");
debugPrint("Order JSON: ${order.toJson()}");
Get.to(() => ParcelOrderConfirmationScreen(), arguments: {'parcelOrder': order, 'images': images});
}
DateTime parseScheduledDateTime(String dateStr, String timeStr) {
try {
final dateParts = dateStr.split('/');
final day = int.parse(dateParts[0]);
final month = int.parse(dateParts[1]);
final year = int.parse(dateParts[2]);
final time = TimeOfDay(hour: int.parse(timeStr.split(':')[0]), minute: int.parse(timeStr.split(':')[1].split(' ')[0]));
final isPM = timeStr.toLowerCase().contains('pm');
final hour24 = isPM && time.hour < 12 ? time.hour + 12 : time.hour;
return DateTime(year, month, day, hour24, time.minute);
} catch (e) {
debugPrint("Failed to parse scheduled date/time: $e");
return DateTime.now();
}
}
Future<void> fetchGoogleRouteWithWaypoints() async {
final origin = '${senderLocation.value!.latitude},${senderLocation.value!.longitude}';
final destination = '${receiverLocation.value!.latitude},${receiverLocation.value!.longitude}';
final url = Uri.parse('https://maps.googleapis.com/maps/api/directions/json?origin=$origin&destination=$destination&mode=driving&key=${Constant.mapAPIKey}');
try {
final response = await http.get(url);
final data = json.decode(response.body);
if (data['status'] == 'OK') {
final route = data['routes'][0];
final legs = route['legs'] as List;
num totalDistance = 0;
num totalDuration = 0;
for (var leg in legs) {
totalDistance += leg['distance']['value'];
totalDuration += leg['duration']['value'];
}
if (Constant.distanceType.toLowerCase() == "KM".toLowerCase()) {
distance.value = totalDistance / 1000.0;
} else {
distance.value = totalDistance / 1609.34;
}
duration.value = (totalDuration / 60).round().toDouble();
} else {
debugPrint('Google Directions API Error: ${data['status']}');
}
} catch (e) {
debugPrint("Google route fetch error: $e");
}
}
Future<void> fetchRouteWithWaypoints(List<latlong.LatLng> points) async {
final coordinates = points.map((p) => '${p.longitude},${p.latitude}').join(';');
final url = Uri.parse('https://router.project-osrm.org/route/v1/driving/$coordinates?overview=full&geometries=geojson');
try {
final response = await http.get(url);
if (response.statusCode == 200) {
final decoded = json.decode(response.body);
final dist = decoded['routes'][0]['distance'];
final dur = decoded['routes'][0]['duration'];
if (Constant.distanceType.toLowerCase() == "KM".toLowerCase()) {
distance.value = dist / 1000.00;
} else {
distance.value = dist / 1609.34;
}
duration.value = (dur / 60).round().toDouble();
} else {
debugPrint("Failed to get route: ${response.body}");
}
} catch (e) {
debugPrint("Route fetch error: $e");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,30 @@
import 'package:customer/models/coupon_model.dart';
import 'package:customer/service/fire_store_utils.dart';
import 'package:get/get.dart';
class CabCouponCodeController extends GetxController {
// Add your methods and properties here
@override
void onInit() {
// TODO: implement onInit
getData();
super.onInit();
}
void getData(){
getCouponCode();
}
RxBool isLoading = true.obs;
RxList<CouponModel> cabCouponList = <CouponModel>[].obs;
Future<void> getCouponCode() async {
await FireStoreUtils.getCabCoupon().then((value) {
cabCouponList.value = value;
// Handle the retrieved coupon code
});
print("cabCouponList ${cabCouponList.length}");
isLoading.value = false;
}
}

View File

@@ -0,0 +1,33 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/screen_ui/cab_service_screens/cab_home_screen.dart';
import 'package:customer/screen_ui/multi_vendor_service/profile_screen/profile_screen.dart';
import 'package:customer/screen_ui/multi_vendor_service/wallet_screen/wallet_screen.dart';
import 'package:customer/service/fire_store_utils.dart';
import 'package:get/get.dart';
import '../screen_ui/cab_service_screens/my_cab_booking_screen.dart';
class CabDashboardController extends GetxController {
RxInt selectedIndex = 0.obs;
RxList pageList = [].obs;
@override
void onInit() {
// TODO: implement onInit
getTaxList();
if (Constant.walletSetting == false) {
pageList.value = [CabHomeScreen(), const MyCabBookingScreen(), const ProfileScreen()];
} else {
pageList.value = [CabHomeScreen(), const MyCabBookingScreen(), const WalletScreen(), const ProfileScreen()];
}
super.onInit();
}
Future<void> getTaxList() async {
await FireStoreUtils.getTaxList(Constant.sectionConstantModel!.id).then((value) {
if (value != null) {
Constant.taxList = value;
}
});
}
}

View File

@@ -0,0 +1,22 @@
import 'package:customer/models/banner_model.dart';
import 'package:customer/service/fire_store_utils.dart';
import 'package:get/get.dart';
class CabHomeController extends GetxController {
RxBool isLoading = true.obs;
RxList<BannerModel> bannerTopHome = <BannerModel>[].obs;
@override
void onInit() {
// TODO: implement onInit
getData();
super.onInit();
}
Future<void> getData() async {
await FireStoreUtils.getHomeTopBanner().then((value) {
bannerTopHome.value = value;
});
isLoading.value = false;
}
}

View File

@@ -0,0 +1,138 @@
import 'dart:convert';
import 'package:customer/constant/constant.dart';
import 'package:customer/models/rating_model.dart';
import 'package:flutter_polyline_points/flutter_polyline_points.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import 'package:google_maps_flutter/google_maps_flutter.dart' as gmap;
import 'package:latlong2/latlong.dart' as osm;
import '../models/cab_order_model.dart';
import '../models/user_model.dart';
import '../service/fire_store_utils.dart';
import '../themes/app_them_data.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:intl/intl.dart';
class CabOrderDetailsController extends GetxController {
Rx<CabOrderModel> cabOrder = CabOrderModel().obs;
RxBool isLoading = false.obs;
// Google Maps Data
RxSet<gmap.Marker> googleMarkers = <gmap.Marker>{}.obs;
RxSet<gmap.Polyline> googlePolylines = <gmap.Polyline>{}.obs;
// OSM Data
RxList<osm.LatLng> osmPolyline = <osm.LatLng>[].obs;
final String googleApiKey = Constant.mapAPIKey;
final Rx<UserModel> driverUser = UserModel().obs;
Rx<RatingModel> ratingModel = RatingModel().obs;
@override
void onInit() {
super.onInit();
final args = Get.arguments;
if (args != null) {
cabOrder.value = args['cabOrderModel'] as CabOrderModel;
calculateTotalAmount();
_setMarkers();
_getGoogleRoute();
_getOsmRoute();
}
fetchDriverDetails();
}
RxDouble subTotal = 0.0.obs;
RxDouble discount = 0.0.obs;
RxDouble taxAmount = 0.0.obs;
RxDouble totalAmount = 0.0.obs;
String formatDate(Timestamp timestamp) {
final dateTime = timestamp.toDate();
return DateFormat("dd MMM yyyy, hh:mm a").format(dateTime);
}
Future<void> fetchDriverDetails() async {
if (cabOrder.value.driverId != null) {
await FireStoreUtils.getUserProfile(cabOrder.value.driverId ?? '').then((value) {
if (value != null) {
driverUser.value = value;
}
});
print(driverUser.value.toJson());
await FireStoreUtils.getReviewsbyID(cabOrder.value.id.toString()).then((value) {
if (value != null) {
ratingModel.value = value;
}
});
}
}
void calculateTotalAmount() {
taxAmount = 0.0.obs;
discount = 0.0.obs;
subTotal.value = double.parse(cabOrder.value.subTotal.toString());
discount.value = double.parse(cabOrder.value.discount ?? '0.0');
if (cabOrder.value.taxSetting != null) {
for (var element in cabOrder.value.taxSetting!) {
taxAmount.value = (taxAmount.value + Constant.calculateTax(amount: (subTotal.value - discount.value).toString(), taxModel: element));
}
}
totalAmount.value = (subTotal.value - discount.value) + taxAmount.value;
update();
}
void _setMarkers() {
final sourceLat = cabOrder.value.sourceLocation!.latitude;
final sourceLng = cabOrder.value.sourceLocation!.longitude;
final destLat = cabOrder.value.destinationLocation!.latitude;
final destLng = cabOrder.value.destinationLocation!.longitude;
googleMarkers.value = {
gmap.Marker(markerId: const gmap.MarkerId('source'), position: gmap.LatLng(sourceLat!, sourceLng!), icon: gmap.BitmapDescriptor.defaultMarkerWithHue(gmap.BitmapDescriptor.hueGreen)),
gmap.Marker(markerId: const gmap.MarkerId('destination'), position: gmap.LatLng(destLat!, destLng!), icon: gmap.BitmapDescriptor.defaultMarkerWithHue(gmap.BitmapDescriptor.hueRed)),
};
}
///Google Directions API
Future<void> _getGoogleRoute() async {
final src = cabOrder.value.sourceLocation;
final dest = cabOrder.value.destinationLocation;
final url = "https://maps.googleapis.com/maps/api/directions/json?origin=${src!.latitude},${src.longitude}&destination=${dest!.latitude},${dest.longitude}&key=$googleApiKey";
final response = await http.get(Uri.parse(url));
final data = jsonDecode(response.body);
if (data["routes"].isNotEmpty) {
final points = data["routes"][0]["overview_polyline"]["points"];
final polylinePoints = PolylinePoints.decodePolyline(points);
final polylineCoords = polylinePoints.map((p) => gmap.LatLng(p.latitude, p.longitude)).toList();
googlePolylines.value = {gmap.Polyline(polylineId: const gmap.PolylineId("google_route"), color: AppThemeData.onDemandDark100, width: 5, points: polylineCoords)};
}
}
/// OSM Route (OSRM API)
Future<void> _getOsmRoute() async {
final src = cabOrder.value.sourceLocation;
final dest = cabOrder.value.destinationLocation;
final url = "http://router.project-osrm.org/route/v1/driving/${src!.longitude},${src.latitude};${dest!.longitude},${dest.latitude}?overview=full&geometries=geojson";
final response = await http.get(Uri.parse(url));
final data = jsonDecode(response.body);
if (data["routes"].isNotEmpty) {
final coords = data["routes"][0]["geometry"]["coordinates"] as List<dynamic>;
osmPolyline.value = coords.map((c) => osm.LatLng(c[1].toDouble(), c[0].toDouble())).toList();
}
}
}

View File

@@ -0,0 +1,34 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/screen_ui/multi_vendor_service/profile_screen/profile_screen.dart';
import 'package:customer/screen_ui/multi_vendor_service/wallet_screen/wallet_screen.dart';
import 'package:customer/screen_ui/rental_service/rental_home_screen.dart';
import 'package:customer/service/fire_store_utils.dart';
import 'package:get/get.dart';
import '../screen_ui/rental_service/my_rental_booking_screen.dart';
class CabRentalDashboardControllers extends GetxController {
RxInt selectedIndex = 0.obs;
RxList pageList = [].obs;
@override
void onInit() {
// TODO: implement onInit
getTaxList();
if (Constant.walletSetting == false) {
pageList.value = [RentalHomeScreen(), MyRentalBookingScreen(), const ProfileScreen()];
} else {
pageList.value = [RentalHomeScreen(), MyRentalBookingScreen(), const WalletScreen(), const ProfileScreen()];
}
super.onInit();
}
Future<void> getTaxList() async {
await FireStoreUtils.getTaxList(Constant.sectionConstantModel!.id).then((value) {
if (value != null) {
Constant.taxList = value;
}
});
}
}

View File

@@ -0,0 +1,133 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:customer/models/cab_order_model.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../constant/collection_name.dart';
import '../models/rating_model.dart';
import '../models/user_model.dart';
import '../service/fire_store_utils.dart';
import '../constant/constant.dart';
import '../themes/show_toast_dialog.dart';
class CabReviewController extends GetxController {
RxBool isLoading = true.obs;
final Rx<CabOrderModel?> order = Rx<CabOrderModel?>(null);
final Rx<RatingModel?> ratingModel = Rx<RatingModel?>(null);
final RxDouble ratings = 0.0.obs;
final Rx<TextEditingController> comment = TextEditingController().obs;
final Rx<UserModel?> driverUser = Rx<UserModel?>(null);
final RxInt futureCount = 0.obs;
final RxInt futureSum = 0.obs;
@override
void onInit() {
super.onInit();
final args = Get.arguments;
if (args != null && args['order'] != null) {
order.value = args['order'] as CabOrderModel;
getReview();
}
}
/// Fetch old review + driver stats
Future<void> getReview() async {
await FireStoreUtils.getReviewsbyID(order.value?.id ?? "").then((value) {
if (value != null) {
ratingModel.value = value;
ratings.value = value.rating ?? 0;
comment.value.text = value.comment ?? "";
}
});
await FireStoreUtils.getUserProfile(order.value?.driverId ?? '').then((value) {
if (value != null) {
driverUser.value = value;
final int userReviewsCount = int.tryParse(driverUser.value!.reviewsCount?.toString() ?? "0") ?? 0;
final int userReviewsSum = int.tryParse(driverUser.value!.reviewsSum?.toString() ?? "0") ?? 0;
if (ratingModel.value != null) {
final int oldRating = ratingModel.value?.rating?.toInt() ?? 0;
futureCount.value = userReviewsCount - 1;
futureSum.value = userReviewsSum - oldRating;
} else {
futureCount.value = userReviewsCount;
futureSum.value = userReviewsSum;
}
}
});
isLoading.value = false;
}
/// Save / update review
Future<void> submitReview() async {
if (comment.value.text.trim().isEmpty || ratings.value == 0) {
ShowToastDialog.showToast("Please provide rating and comment".tr);
return;
}
ShowToastDialog.showLoader("Submit in...".tr);
final user = await FireStoreUtils.getUserProfile(order.value?.driverId ?? '');
if (user != null) {
user.reviewsCount = (futureCount.value + 1).toString();
user.reviewsSum = (futureSum.value + ratings.value.toInt()).toString();
}
if (ratingModel.value != null) {
/// Update existing review
final updatedRating = RatingModel(
id: ratingModel.value!.id,
comment: comment.value.text,
photos: ratingModel.value?.photos ?? [],
rating: ratings.value,
orderId: ratingModel.value!.orderId,
driverId: ratingModel.value!.driverId,
customerId: ratingModel.value!.customerId,
vendorId: ratingModel.value?.vendorId,
uname: "${Constant.userModel?.firstName ?? ''} ${Constant.userModel?.lastName ?? ''}",
profile: Constant.userModel?.profilePictureURL,
createdAt: Timestamp.now(),
);
await FireStoreUtils.updateReviewById(updatedRating);
if (user != null) {
await FireStoreUtils.updateUser(user);
}
} else {
/// New review
final docRef = FireStoreUtils.fireStore.collection(CollectionName.itemsReview).doc();
final newRating = RatingModel(
id: docRef.id,
comment: comment.value.text,
photos: [],
rating: ratings.value,
orderId: order.value?.id,
driverId: order.value?.driverId.toString(),
customerId: Constant.userModel?.id,
uname: "${Constant.userModel?.firstName ?? ''} ${Constant.userModel?.lastName ?? ''}",
profile: Constant.userModel?.profilePictureURL,
createdAt: Timestamp.now(),
);
await FireStoreUtils.updateReviewById(newRating);
if (user != null) {
await FireStoreUtils.updateUser(user);
}
}
ShowToastDialog.closeLoader();
Get.back(result: true);
}
@override
void onClose() {
comment.value.dispose();
super.onClose();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,24 @@
import '../models/cashback_model.dart';
import 'package:get/get.dart';
import '../service/fire_store_utils.dart';
class CashbackController extends GetxController {
RxList<CashbackModel> cashbackList = <CashbackModel>[].obs;
RxBool isLoading = true.obs;
@override
void onInit() {
// TODO: implement onInit
getCashback();
super.onInit();
}
Future<void> getCashback() async {
await FireStoreUtils.getCashbackList().then((value) {
if (value.isNotEmpty) {
cashbackList.value = value;
}
});
isLoading.value = false;
}
}

View File

@@ -0,0 +1,62 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/models/vendor_model.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import '../models/vendor_category_model.dart';
import '../service/fire_store_utils.dart';
import 'package:get/get.dart';
class CategoryRestaurantController extends GetxController {
RxBool isLoading = true.obs;
RxBool dineIn = true.obs;
@override
void onInit() {
// TODO: implement onInit
getArgument();
super.onInit();
}
Rx<VendorCategoryModel> vendorCategoryModel = VendorCategoryModel().obs;
RxList<VendorModel> allNearestRestaurant = <VendorModel>[].obs;
Future<void> getArgument() async {
dynamic argumentData = Get.arguments;
if (argumentData != null) {
vendorCategoryModel.value = argumentData['vendorCategoryModel'];
dineIn.value = argumentData['dineIn'];
await getZone();
await getRestaurant();
}
Future.delayed(Duration(seconds: 1), () {
isLoading.value = false;
});
}
Future getRestaurant() async {
FireStoreUtils.getAllNearestRestaurantByCategoryId(categoryId: vendorCategoryModel.value.id.toString(), isDining: dineIn.value).listen((
event,
) async {
allNearestRestaurant.clear();
allNearestRestaurant.addAll(event);
});
}
Future<void> getZone() async {
await FireStoreUtils.getZone().then((value) {
if (value != null) {
for (int i = 0; i < value.length; i++) {
if (Constant.isPointInPolygon(
LatLng(Constant.selectedLocation.location!.latitude ?? 0.0, Constant.selectedLocation.location!.longitude ?? 0.0),
value[i].area!,
)) {
Constant.selectedZone = value[i];
Constant.isZoneAvailable = true;
break;
} else {
Constant.isZoneAvailable = false;
}
}
}
});
}
}

View File

@@ -0,0 +1,44 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/models/language_model.dart';
import '../service/fire_store_utils.dart';
import 'package:customer/utils/preferences.dart';
import 'package:get/get.dart';
import '../constant/collection_name.dart';
class ChangeLanguageController extends GetxController {
Rx<LanguageModel> selectedLanguage = LanguageModel().obs;
RxList<LanguageModel> languageList = <LanguageModel>[].obs;
RxBool isLoading = true.obs;
@override
void onInit() {
// TODO: implement onInit
getLanguage();
super.onInit();
}
Future<void> getLanguage() async {
await FireStoreUtils.fireStore.collection(CollectionName.settings).doc("languages").get().then((event) {
if (event.exists) {
List languageListTemp = event.data()!["list"];
for (var element in languageListTemp) {
LanguageModel languageModel = LanguageModel.fromJson(element);
languageList.add(languageModel);
}
if (Preferences.getString(Preferences.languageCodeKey).toString().isNotEmpty) {
LanguageModel pref = Constant.getLanguage();
for (var element in languageList) {
if (element.slug == pref.slug) {
selectedLanguage.value = element;
}
}
}
}
});
isLoading.value = false;
}
}

View File

@@ -0,0 +1,142 @@
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:image_picker/image_picker.dart';
import '../models/conversation_model.dart';
import '../models/inbox_model.dart';
import '../service/fire_store_utils.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:uuid/uuid.dart';
import '../service/send_notification.dart';
class ChatController extends GetxController {
Rx<TextEditingController> messageController = TextEditingController().obs;
final ScrollController scrollController = ScrollController();
@override
void onInit() {
// TODO: implement onInit
if (scrollController.hasClients) {
Timer(const Duration(milliseconds: 500), () => scrollController.jumpTo(scrollController.position.maxScrollExtent));
}
getArgument();
super.onInit();
}
RxBool isLoading = true.obs;
RxString orderId = "".obs;
RxString customerId = "".obs;
RxString customerName = "".obs;
RxString customerProfileImage = "".obs;
RxString restaurantId = "".obs;
RxString restaurantName = "".obs;
RxString restaurantProfileImage = "".obs;
RxString token = "".obs;
RxString chatType = "".obs;
void getArgument() {
dynamic argumentData = Get.arguments;
if (argumentData != null) {
orderId.value = argumentData['orderId'];
customerId.value = argumentData['customerId'];
customerName.value = argumentData['customerName'];
customerProfileImage.value = argumentData['customerProfileImage'] ?? "";
restaurantId.value = argumentData['restaurantId'];
restaurantName.value = argumentData['restaurantName'];
restaurantProfileImage.value = argumentData['restaurantProfileImage'] ?? "";
token.value = argumentData['token'] ?? "";
chatType.value = argumentData['chatType'];
}
isLoading.value = false;
}
Future<void> sendMessage(String message, Url? url, String videoThumbnail, String messageType) async {
InboxModel inboxModel = InboxModel(
lastSenderId: customerId.value,
customerId: customerId.value,
customerName: customerName.value,
restaurantId: restaurantId.value,
restaurantName: restaurantName.value,
createdAt: Timestamp.now(),
orderId: orderId.value,
customerProfileImage: customerProfileImage.value,
restaurantProfileImage: restaurantProfileImage.value,
lastMessage: messageController.value.text,
chatType: chatType.value,
);
print("chatType: ${chatType.value}");
if (chatType.value == "Driver") {
await FireStoreUtils.addDriverInbox(inboxModel);
} else if (chatType.value == "worker" || chatType.value == "Worker") {
await FireStoreUtils.addWorkerInbox(inboxModel);
} else if (chatType.value == "provider" || chatType.value == "Provider") {
await FireStoreUtils.addProviderInbox(inboxModel);
} else {
await FireStoreUtils.addRestaurantInbox(inboxModel);
}
ConversationModel conversationModel = ConversationModel(
id: const Uuid().v4(),
message: message,
senderId: customerId.value,
receiverId: restaurantId.value,
createdAt: Timestamp.now(),
url: url,
orderId: orderId.value,
messageType: messageType,
videoThumbnail: videoThumbnail,
);
if (url != null) {
if (url.mime.contains('image')) {
conversationModel.message = "sent a message".tr;
} else if (url.mime.contains('video')) {
conversationModel.message = "Sent a video".tr;
} else if (url.mime.contains('audio')) {
conversationModel.message = "Sent a audio".tr;
}
}
if (chatType.value == "Driver") {
await FireStoreUtils.addDriverChat(conversationModel);
} else if (chatType.value == "worker" || chatType.value == "Worker") {
await FireStoreUtils.addWorkerChat(conversationModel);
} else if (chatType.value == "provider" || chatType.value == "Provider") {
await FireStoreUtils.addProviderChat(conversationModel);
} else {
await FireStoreUtils.addRestaurantChat(conversationModel);
}
//await SendNotification.sendChatFcmMessage(customerName.value, conversationModel.message.toString(), token.value, {});
await SendNotification.sendChatFcmMessage(customerName.value, conversationModel.message.toString(), token.value, {
"type": "chat",
"chatType": chatType.value,
"orderId": orderId.value,
"customerId": customerId.value,
"customerName": customerName.value,
"customerProfileImage": customerProfileImage.value,
"restaurantId": restaurantId.value,
"restaurantName": restaurantName.value,
"restaurantProfileImage": restaurantProfileImage.value,
"token": token.value,
});
}
final ImagePicker imagePicker = ImagePicker();
// Future pickFile({required ImageSource source}) async {
// try {
// XFile? image = await imagePicker.pickImage(source: source);
// if (image == null) return;
// Url url = await FireStoreUtils.uploadChatImageToFireStorage(File(image.path), Get.context!);
// sendMessage('', url, '', 'image');
// Get.back();
// } on PlatformException catch (e) {
// ShowToastDialog.showToast("${"failed_to_pick".tr} : \n $e");
// }
// }
}

View File

@@ -0,0 +1,95 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../models/cab_order_model.dart';
import '../service/fire_store_utils.dart';
import '../themes/show_toast_dialog.dart';
class ComplainController extends GetxController {
Rx<CabOrderModel> order = CabOrderModel().obs;
final Rx<TextEditingController> title = TextEditingController().obs;
final Rx<TextEditingController> comment = TextEditingController().obs;
final RxBool isLoading = false.obs;
@override
void onInit() {
super.onInit();
final args = Get.arguments;
if (args != null && args is Map && args['order'] is CabOrderModel) {
order.value = args['order'] as CabOrderModel;
getComplain();
} else {
ShowToastDialog.showToast("Order data not found".tr);
Get.back();
}
}
Future<void> getComplain() async {
isLoading.value = true;
try {
final data = await FireStoreUtils.getRideComplainData(order.value.id ?? '');
if (data != null) {
title.value.text = data['title'] ?? '';
comment.value.text = data['description'] ?? '';
}
} catch (e) {
ShowToastDialog.showToast("Failed to load complaint".tr);
} finally {
isLoading.value = false;
}
}
Future<void> submitComplain() async {
// Validation
if (title.value.text.trim().isEmpty) {
ShowToastDialog.showToast("Please enter complaint title".tr);
return;
}
if (comment.value.text.trim().isEmpty) {
ShowToastDialog.showToast("Please enter complaint description".tr);
return;
}
isLoading.value = true;
ShowToastDialog.showLoader("Please wait...");
try {
// Check if complaint already exists
bool exists = await FireStoreUtils.isRideComplainAdded(order.value.id ?? '');
if (!exists) {
await FireStoreUtils.setRideComplain(
orderId: order.value.id ?? '',
title: title.value.text.trim(),
description: comment.value.text.trim(),
customerID: order.value.authorID ?? '',
customerName: "${order.value.author?.firstName ?? ''} ${order.value.author?.lastName ?? ''}".trim(),
driverID: order.value.driverId ?? '',
driverName: "${order.value.driver?.firstName ?? ''} ${order.value.driver?.lastName ?? ''}".trim(),
);
ShowToastDialog.closeLoader();
ShowToastDialog.showToast("Your complaint has been submitted to admin".tr);
Get.back();
} else {
ShowToastDialog.closeLoader();
ShowToastDialog.showToast("Your complaint is already submitted".tr);
}
} catch (e) {
ShowToastDialog.closeLoader();
ShowToastDialog.showToast("Something went wrong, please try again".tr);
} finally {
isLoading.value = false;
}
}
@override
void onClose() {
title.value.dispose();
comment.value.dispose();
super.onClose();
}
}

View File

@@ -0,0 +1,46 @@
import 'package:customer/constant/constant.dart';
import '../screen_ui/multi_vendor_service/favourite_screens/favourite_screen.dart';
import '../screen_ui/multi_vendor_service/home_screen/home_screen.dart';
import '../screen_ui/multi_vendor_service/home_screen/home_screen_two.dart';
import '../screen_ui/multi_vendor_service/order_list_screen/order_screen.dart';
import '../screen_ui/multi_vendor_service/profile_screen/profile_screen.dart';
import '../screen_ui/multi_vendor_service/wallet_screen/wallet_screen.dart';
import '../service/fire_store_utils.dart';
import 'package:get/get.dart';
class DashBoardController extends GetxController {
RxInt selectedIndex = 0.obs;
RxList pageList = [].obs;
@override
void onInit() {
// TODO: implement onInit
getTaxList();
if (Constant.sectionConstantModel!.theme == "theme_2") {
if (Constant.walletSetting == false) {
pageList.value = [const HomeScreen(), const FavouriteScreen(), const OrderScreen(), const ProfileScreen()];
} else {
pageList.value = [const HomeScreen(), const FavouriteScreen(), const WalletScreen(), const OrderScreen(), const ProfileScreen()];
}
} else {
if (Constant.walletSetting == false) {
pageList.value = [const HomeScreenTwo(), const FavouriteScreen(), const OrderScreen(), const ProfileScreen()];
} else {
pageList.value = [const HomeScreenTwo(), const FavouriteScreen(), const WalletScreen(), const OrderScreen(), const ProfileScreen()];
}
}
super.onInit();
}
Future<void> getTaxList() async {
await FireStoreUtils.getTaxList(Constant.sectionConstantModel!.id).then((value) {
if (value != null) {
Constant.taxList = value;
}
});
}
DateTime? currentBackPressTime;
RxBool canPopNow = false.obs;
}

View File

@@ -0,0 +1,37 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/screen_ui/ecommarce/home_e_commerce_screen.dart';
import '../screen_ui/multi_vendor_service/favourite_screens/favourite_screen.dart';
import '../screen_ui/multi_vendor_service/order_list_screen/order_screen.dart';
import '../screen_ui/multi_vendor_service/profile_screen/profile_screen.dart';
import '../screen_ui/multi_vendor_service/wallet_screen/wallet_screen.dart';
import '../service/fire_store_utils.dart';
import 'package:get/get.dart';
class DashBoardEcommerceController extends GetxController {
RxInt selectedIndex = 0.obs;
RxList pageList = [].obs;
@override
void onInit() {
// TODO: implement onInit
getTaxList();
if (Constant.walletSetting == false) {
pageList.value = [const HomeECommerceScreen(), const FavouriteScreen(), const OrderScreen(), const ProfileScreen()];
} else {
pageList.value = [const HomeECommerceScreen(), const FavouriteScreen(), const WalletScreen(), const OrderScreen(), const ProfileScreen()];
}
super.onInit();
}
Future<void> getTaxList() async {
await FireStoreUtils.getTaxList(Constant.sectionConstantModel!.id).then((value) {
if (value != null) {
Constant.taxList = value;
}
});
}
DateTime? currentBackPressTime;
RxBool canPopNow = false.obs;
}

View File

@@ -0,0 +1,34 @@
import '../models/dine_in_booking_model.dart';
import '../service/fire_store_utils.dart';
import 'package:get/get.dart';
class DineInBookingController extends GetxController {
RxBool isLoading = true.obs;
RxBool isFeature = true.obs;
RxList<DineInBookingModel> featureList = <DineInBookingModel>[].obs;
RxList<DineInBookingModel> historyList = <DineInBookingModel>[].obs;
@override
void onInit() {
// TODO: implement onInit
getDineInBooking();
super.onInit();
}
Future<void> getDineInBooking() async {
await FireStoreUtils.getDineInBooking(true).then(
(value) {
featureList.value = value;
},
);
await FireStoreUtils.getDineInBooking(false).then(
(value) {
historyList.value = value;
},
);
isLoading.value = false;
}
}

View File

@@ -0,0 +1,24 @@
import 'package:customer/models/dine_in_booking_model.dart';
import 'package:get/get.dart';
class DineInBookingDetailsController extends GetxController {
RxBool isLoading = true.obs;
@override
void onInit() {
// TODO: implement onInit
getArgument();
super.onInit();
}
Rx<DineInBookingModel> bookingModel = DineInBookingModel().obs;
Future<void> getArgument() async {
dynamic argumentData = Get.arguments;
if (argumentData != null) {
bookingModel.value = argumentData['bookingModel'];
}
isLoading.value = false;
update();
}
}

View File

@@ -0,0 +1,100 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:customer/constant/constant.dart';
import 'package:customer/models/favourite_model.dart';
import 'package:customer/models/vendor_category_model.dart';
import 'package:customer/models/vendor_model.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import '../models/banner_model.dart';
import '../service/fire_store_utils.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class DineInController extends GetxController {
RxBool isLoading = true.obs;
RxBool isPopular = true.obs;
@override
void onInit() {
getCategory();
getData();
// TODO: implement onInit
super.onInit();
}
RxList<VendorCategoryModel> vendorCategoryModel = <VendorCategoryModel>[].obs;
RxList<VendorModel> allNearestRestaurant = <VendorModel>[].obs;
RxList<VendorModel> newArrivalRestaurantList = <VendorModel>[].obs;
RxList<VendorModel> popularRestaurantList = <VendorModel>[].obs;
RxList<BannerModel> bannerBottomModel = <BannerModel>[].obs;
Rx<PageController> pageBottomController = PageController(viewportFraction: 0.877).obs;
RxInt currentBottomPage = 0.obs;
RxList<FavouriteModel> favouriteList = <FavouriteModel>[].obs;
Future<void> getData() async {
isLoading.value = true;
await getZone();
FireStoreUtils.getAllNearestRestaurant(isDining: true).listen((event) async {
newArrivalRestaurantList.clear();
allNearestRestaurant.clear();
popularRestaurantList.clear();
allNearestRestaurant.addAll(event);
newArrivalRestaurantList.addAll(event);
popularRestaurantList.addAll(event);
popularRestaurantList.sort(
(a, b) => Constant.calculateReview(reviewCount: b.reviewsCount.toString(), reviewSum: b.reviewsSum.toString())
.compareTo(Constant.calculateReview(reviewCount: a.reviewsCount.toString(), reviewSum: a.reviewsSum.toString())),
);
newArrivalRestaurantList.sort(
(a, b) => (b.createdAt ?? Timestamp.now()).toDate().compareTo((a.createdAt ?? Timestamp.now()).toDate()),
);
});
update();
isLoading.value = false;
}
Future<void> getCategory() async {
await FireStoreUtils.getHomeVendorCategory().then(
(value) {
vendorCategoryModel.value = value;
},
);
await FireStoreUtils.getHomeBottomBanner().then(
(value) {
bannerBottomModel.value = value;
},
);
if (Constant.userModel != null) {
await FireStoreUtils.getFavouriteRestaurant().then(
(value) {
favouriteList.value = value;
},
);
}
}
Future<void> getZone() async {
await FireStoreUtils.getZone().then((value) {
if (value != null) {
for (int i = 0; i < value.length; i++) {
if (Constant.isPointInPolygon(LatLng(Constant.selectedLocation.location!.latitude ?? 0.0, Constant.selectedLocation.location!.longitude ?? 0.0), value[i].area!)) {
Constant.selectedZone = value[i];
Constant.isZoneAvailable = true;
break;
} else {
Constant.isZoneAvailable = false;
}
}
}
});
}
}

View File

@@ -0,0 +1,287 @@
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:customer/constant/constant.dart';
import 'package:customer/models/dine_in_booking_model.dart';
import 'package:customer/models/favourite_model.dart';
import 'package:customer/models/vendor_model.dart';
import '../screen_ui/multi_vendor_service/dine_in_booking/dine_in_booking_screen.dart';
import '../service/fire_store_utils.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import '../service/send_notification.dart';
import '../themes/show_toast_dialog.dart';
class DineInRestaurantDetailsController extends GetxController {
Rx<TextEditingController> searchEditingController = TextEditingController().obs;
Rx<TextEditingController> additionRequestController = TextEditingController().obs;
RxBool isLoading = true.obs;
RxBool firstVisit = false.obs;
Rx<PageController> pageController = PageController().obs;
RxInt currentPage = 0.obs;
RxInt noOfQuantity = 1.obs;
RxList<FavouriteModel> favouriteList = <FavouriteModel>[].obs;
RxList tags = [].obs;
// List occasionList = ["Birthday", "Anniversary"];
RxList<String> occasionList = ["Birthday", "Anniversary"].obs;
/// Localized title for each occasion
String getLocalizedOccasion(String key) {
switch (key) {
case "Birthday":
return "Birthday".tr;
case "Anniversary":
return "Anniversary".tr;
default:
return key;
}
}
RxString selectedOccasion = "".obs;
RxList<DateModel> dateList = <DateModel>[].obs;
RxList<TimeModel> timeSlotList = <TimeModel>[].obs;
Rx<Timestamp> selectedDate = Timestamp.now().obs;
RxString selectedTimeSlot = '6:00 PM'.obs;
RxString selectedTimeDiscount = '0'.obs;
RxString selectedTimeDiscountType = ''.obs;
@override
void onInit() {
// TODO: implement onInit
getArgument();
getRecord();
super.onInit();
}
Future<void> orderBook() async {
ShowToastDialog.showLoader("Please wait...".tr);
DateTime dt = selectedDate.value.toDate();
String hour = DateFormat("kk:mm").format(DateFormat('hh:mm a').parse((Intl.getCurrentLocale() == "en_US") ? selectedTimeSlot.value : selectedTimeSlot.value.toLowerCase()));
dt = DateTime(dt.year, dt.month, dt.day, int.parse(hour.split(":")[0]), int.parse(hour.split(":")[1]), dt.second, dt.millisecond, dt.microsecond);
selectedDate.value = Timestamp.fromDate(dt);
DineInBookingModel dineInBookingModel = DineInBookingModel(
id: Constant.getUuid(),
author: Constant.userModel,
authorID: FireStoreUtils.getCurrentUid(),
createdAt: Timestamp.now(),
date: selectedDate.value,
status: Constant.orderPlaced,
vendor: vendorModel.value,
specialRequest: additionRequestController.value.text.isEmpty ? "" : additionRequestController.value.text,
vendorID: vendorModel.value.id,
guestEmail: Constant.userModel!.email,
guestFirstName: Constant.userModel!.firstName,
guestLastName: Constant.userModel!.lastName,
guestPhone: Constant.userModel!.phoneNumber,
occasion: selectedOccasion.value,
discount: selectedTimeDiscount.value,
discountType: selectedTimeDiscountType.value,
totalGuest: noOfQuantity.value.toString(),
firstVisit: firstVisit.value);
await FireStoreUtils.setBookedOrder(dineInBookingModel);
await SendNotification.sendFcmMessage(Constant.dineInPlaced, vendorModel.value.fcmToken.toString(), {});
ShowToastDialog.closeLoader();
Get.back();
Get.to(const DineInBookingScreen());
ShowToastDialog.showToast('Dine-In Request submitted successfully.'.tr);
}
void getRecord() {
for (int i = 0; i < 7; i++) {
final now = DateTime.now().add(Duration(days: i));
var day = DateFormat('EEEE').format(now);
if (vendorModel.value.specialDiscount?.isNotEmpty == true && vendorModel.value.specialDiscountEnable == true) {
for (var element in vendorModel.value.specialDiscount!) {
if (day == element.day.toString()) {
if (element.timeslot!.isNotEmpty) {
SpecialDiscountTimeslot employeeWithMaxSalary =
element.timeslot!.reduce((item1, item2) => double.parse(item1.discount.toString()) > double.parse(item2.discount.toString()) ? item1 : item2);
if (employeeWithMaxSalary.discountType == "dinein") {
DateModel model = DateModel(date: Timestamp.fromDate(now), discountPer: employeeWithMaxSalary.discount.toString());
dateList.add(model);
} else {
DateModel model = DateModel(date: Timestamp.fromDate(now), discountPer: "0");
dateList.add(model);
}
} else {
DateModel model = DateModel(date: Timestamp.fromDate(now), discountPer: "0");
dateList.add(model);
}
}
}
} else {
DateModel model = DateModel(date: Timestamp.fromDate(now), discountPer: "0");
dateList.add(model);
}
}
selectedDate.value = dateList.first.date;
timeSet(selectedDate.value);
if (timeSlotList.isNotEmpty) {
selectedTimeSlot.value = DateFormat('hh:mm a').format(timeSlotList[0].time!);
}
}
void timeSet(Timestamp selectedDate) {
timeSlotList.clear();
for (DateTime time = Constant.stringToDate(vendorModel.value.openDineTime.toString());
time.isBefore(Constant.stringToDate(vendorModel.value.closeDineTime.toString()));
time = time.add(const Duration(minutes: 30))) {
final now = DateTime.parse(selectedDate.toDate().toString());
var day = DateFormat('EEEE').format(now);
var date = DateFormat('dd-MM-yyyy').format(now);
if (vendorModel.value.specialDiscount?.isNotEmpty == true && vendorModel.value.specialDiscountEnable == true) {
for (var element in vendorModel.value.specialDiscount!) {
if (day == element.day.toString()) {
if (element.timeslot!.isNotEmpty) {
for (var element in element.timeslot!) {
if (element.discountType == "dinein") {
var start = DateFormat("dd-MM-yyyy HH:mm").parse("$date ${element.from}");
var end = DateFormat("dd-MM-yyyy HH:mm").parse("$date ${element.to}");
var selected = DateFormat("dd-MM-yyyy HH:mm").parse("$date ${DateFormat.Hm().format(time)}");
if (isCurrentDateInRangeDineIn(start, end, selected)) {
var contains = timeSlotList.where((element) => element.time == time);
if (contains.isNotEmpty) {
var index = timeSlotList.indexWhere((element) => element.time == time);
if (timeSlotList[index].discountPer == "0") {
timeSlotList.removeAt(index);
TimeModel model = TimeModel(time: time, discountPer: element.discount, discountType: element.type);
timeSlotList.insert(index == 0 ? 0 : index, model);
}
} else {
TimeModel model = TimeModel(time: time, discountPer: element.discount, discountType: element.type);
timeSlotList.add(model);
}
} else {
var contains = timeSlotList.where((element) => element.time == time);
if (contains.isEmpty) {
TimeModel model = TimeModel(time: time, discountPer: "0", discountType: "amount");
timeSlotList.add(model);
}
}
} else {
TimeModel model = TimeModel(time: time, discountPer: "0", discountType: "amount");
timeSlotList.add(model);
}
}
} else {
TimeModel model = TimeModel(time: time, discountPer: "0", discountType: "amount");
timeSlotList.add(model);
}
}
}
} else {
TimeModel model = TimeModel(time: time, discountPer: "0", discountType: "amount");
timeSlotList.add(model);
}
}
}
void animateSlider() {
if (vendorModel.value.photos != null && vendorModel.value.photos!.isNotEmpty) {
Timer.periodic(const Duration(seconds: 2), (Timer timer) {
if (currentPage < vendorModel.value.photos!.length) {
currentPage++;
} else {
currentPage.value = 0;
}
if (pageController.value.hasClients) {
pageController.value.animateToPage(
currentPage.value,
duration: const Duration(milliseconds: 300),
curve: Curves.easeIn,
);
}
});
}
}
Rx<VendorModel> vendorModel = VendorModel().obs;
Future<void> getArgument() async {
dynamic argumentData = Get.arguments;
if (argumentData != null) {
vendorModel.value = argumentData['vendorModel'];
}
animateSlider();
statusCheck();
isLoading.value = false;
await getFavouriteList();
update();
}
Future<void> getFavouriteList() async {
if (Constant.userModel != null) {
await FireStoreUtils.getFavouriteRestaurant().then(
(value) {
favouriteList.value = value;
},
);
}
await FireStoreUtils.getVendorCuisines(vendorModel.value.id.toString()).then(
(value) {
tags.value = value;
},
);
update();
}
RxBool isOpen = false.obs;
void statusCheck() {
final now = DateTime.now();
var day = DateFormat('EEEE', 'en_US').format(now);
var date = DateFormat('dd-MM-yyyy').format(now);
for (var element in vendorModel.value.workingHours!) {
if (day == element.day.toString()) {
if (element.timeslot!.isNotEmpty) {
for (var element in element.timeslot!) {
var start = DateFormat("dd-MM-yyyy HH:mm").parse("$date ${element.from}");
var end = DateFormat("dd-MM-yyyy HH:mm").parse("$date ${element.to}");
if (isCurrentDateInRange(start, end)) {
isOpen.value = true;
}
}
}
}
}
}
bool isCurrentDateInRangeDineIn(DateTime startDate, DateTime endDate, DateTime selected) {
return selected.isAtSameMomentAs(startDate) || selected.isAtSameMomentAs(endDate) || selected.isAfter(startDate) && selected.isBefore(endDate);
}
bool isCurrentDateInRange(DateTime startDate, DateTime endDate) {
final currentDate = DateTime.now();
return currentDate.isAfter(startDate) && currentDate.isBefore(endDate);
}
}
class DateModel {
late Timestamp date;
late String discountPer;
DateModel({required this.date, required this.discountPer});
}
class TimeModel {
DateTime? time;
String? discountPer;
String? discountType;
TimeModel({required this.time, required this.discountPer, required this.discountType});
}

View File

@@ -0,0 +1,28 @@
import 'package:customer/models/coupon_model.dart';
import 'package:customer/models/vendor_model.dart';
import 'package:get/get.dart';
class DiscountRestaurantListController extends GetxController {
RxBool isLoading = true.obs;
RxList<VendorModel> vendorList = <VendorModel>[].obs;
RxList<CouponModel> couponList = <CouponModel>[].obs;
RxString title = "Restaurants".obs;
@override
void onInit() {
// TODO: implement onInit
getArgument();
super.onInit();
}
Future<void> getArgument() async {
dynamic argumentData = Get.arguments;
if (argumentData != null) {
vendorList.value = argumentData['vendorList'];
couponList.value = argumentData['couponList'];
title.value = argumentData['title'] ?? "Restaurants";
}
isLoading.value = false;
}
}

View File

@@ -0,0 +1,77 @@
import 'dart:io';
import 'package:customer/constant/constant.dart';
import 'package:customer/models/user_model.dart';
import '../service/fire_store_utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:image_picker/image_picker.dart';
import '../themes/show_toast_dialog.dart';
class EditProfileController extends GetxController {
RxBool isLoading = true.obs;
Rx<UserModel> userModel = UserModel().obs;
Rx<TextEditingController> firstNameController = TextEditingController().obs;
Rx<TextEditingController> lastNameController = TextEditingController().obs;
Rx<TextEditingController> emailController = TextEditingController().obs;
Rx<TextEditingController> phoneNumberController = TextEditingController().obs;
Rx<TextEditingController> countryCodeController = TextEditingController(text: "+91").obs;
@override
void onInit() {
getData();
super.onInit();
}
Future<void> getData() async {
await FireStoreUtils.getUserProfile(FireStoreUtils.getCurrentUid()).then((value) {
if (value != null) {
userModel.value = value;
firstNameController.value.text = userModel.value.firstName.toString();
lastNameController.value.text = userModel.value.lastName.toString();
emailController.value.text = userModel.value.email.toString();
phoneNumberController.value.text = userModel.value.phoneNumber.toString();
countryCodeController.value.text = userModel.value.countryCode.toString();
profileImage.value = userModel.value.profilePictureURL ?? "";
}
});
isLoading.value = false;
}
Future<void> saveData() async {
ShowToastDialog.showLoader("Please wait...".tr);
if (Constant().hasValidUrl(profileImage.value) == false && profileImage.value.isNotEmpty) {
profileImage.value = await Constant.uploadUserImageToFireStorage(
File(profileImage.value),
"profileImage/${FireStoreUtils.getCurrentUid()}",
File(profileImage.value).path.split('/').last,
);
}
userModel.value.firstName = firstNameController.value.text;
userModel.value.lastName = lastNameController.value.text;
userModel.value.profilePictureURL = profileImage.value;
await FireStoreUtils.updateUser(userModel.value).then((value) {
ShowToastDialog.closeLoader();
//Get.back(result: true);
});
}
final ImagePicker _imagePicker = ImagePicker();
RxString profileImage = "".obs;
Future pickFile({required ImageSource source}) async {
try {
XFile? image = await _imagePicker.pickImage(source: source);
if (image == null) return;
Get.back();
profileImage.value = image.path;
} on PlatformException catch (e) {
ShowToastDialog.showToast("${"failed_to_pick".tr} : \n $e");
}
}
}

View File

@@ -0,0 +1,83 @@
import 'package:customer/service/fire_store_utils.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../models/user_model.dart';
class EnterManuallyLocationController extends GetxController {
Rx<UserModel> userModel = UserModel().obs;
RxList<ShippingAddress> shippingAddressList = <ShippingAddress>[].obs;
List saveAsList = ['Home', 'Work', 'Hotel', 'other'].obs;
RxString selectedSaveAs = "Home".obs;
Rx<TextEditingController> houseBuildingTextEditingController = TextEditingController().obs;
Rx<TextEditingController> localityEditingController = TextEditingController().obs;
Rx<TextEditingController> landmarkEditingController = TextEditingController().obs;
Rx<UserLocation> location = UserLocation().obs;
Rx<ShippingAddress> shippingModel = ShippingAddress().obs;
RxBool isLoading = false.obs;
RxBool isDefault = false.obs;
RxString mode = "Add".obs;
@override
void onInit() {
getArgument();
super.onInit();
}
Future<void> getArgument() async {
dynamic argumentData = Get.arguments;
if (argumentData != null) {
//check mode
mode.value = argumentData['mode'] ?? "Add";
//check address
if (argumentData['address'] != null && argumentData['address'] is ShippingAddress) {
shippingModel.value = argumentData['address'];
setData(shippingModel.value);
}
}
await getUser();
isLoading.value = false;
update();
}
void setData(ShippingAddress shippingAddress) {
shippingModel.value = shippingAddress;
houseBuildingTextEditingController.value.text = shippingAddress.address.toString();
localityEditingController.value.text = shippingAddress.locality.toString();
landmarkEditingController.value.text = shippingAddress.landmark.toString();
selectedSaveAs.value = shippingAddress.addressAs.toString();
location.value = shippingAddress.location!;
}
Future<void> getUser() async {
await FireStoreUtils.getUserProfile(FireStoreUtils.getCurrentUid()).then((value) {
if (value != null) {
userModel.value = value;
if (userModel.value.shippingAddress != null) {
shippingAddressList.value = userModel.value.shippingAddress!;
}
}
});
}
String getLocalizedSaveAs(String key) {
switch (key) {
case 'Home':
return 'Home'.tr;
case 'Work':
return 'Work'.tr;
case 'Hotel':
return 'Hotel'.tr;
case 'Other':
return 'Other'.tr;
default:
return key;
}
}
}

View File

@@ -0,0 +1,93 @@
import 'package:customer/constant/collection_name.dart';
import 'package:customer/constant/constant.dart';
import 'package:customer/models/favourite_item_model.dart';
import 'package:customer/models/favourite_model.dart';
import 'package:customer/models/product_model.dart';
import 'package:customer/models/vendor_model.dart';
import '../service/fire_store_utils.dart';
import 'package:get/get.dart';
class FavouriteController extends GetxController {
RxBool favouriteRestaurant = true.obs;
RxList<FavouriteModel> favouriteList = <FavouriteModel>[].obs;
RxList<VendorModel> favouriteVendorList = <VendorModel>[].obs;
RxList<FavouriteItemModel> favouriteItemList = <FavouriteItemModel>[].obs;
RxList<ProductModel> favouriteFoodList = <ProductModel>[].obs;
RxBool isLoading = true.obs;
@override
void onInit() {
// TODO: implement onInit
getData();
super.onInit();
}
Future<void> getData() async {
if (Constant.userModel != null) {
await FireStoreUtils.getFavouriteRestaurant().then((value) {
favouriteList.value = value;
});
await FireStoreUtils.getFavouriteItem().then((value) {
favouriteItemList.value = value;
});
for (var element in favouriteList) {
await FireStoreUtils.getVendorById(element.restaurantId.toString()).then((value) async {
if (value != null) {
if ((Constant.isSubscriptionModelApplied == true || value.adminCommission?.isEnabled == true) && value.subscriptionPlan != null) {
if (value.subscriptionTotalOrders == "-1") {
favouriteVendorList.add(value);
} else {
print("Restaurant :: ${value.title.toString()}");
if ((value.subscriptionExpiryDate != null && value.subscriptionExpiryDate!.toDate().isBefore(DateTime.now()) == false) ||
value.subscriptionPlan?.expiryDay == '-1') {
if (value.subscriptionTotalOrders != '0') {
favouriteVendorList.add(value);
}
}
}
} else {
favouriteVendorList.add(value);
}
}
});
}
for (var element in favouriteItemList) {
await FireStoreUtils.getProductById(element.productId.toString()).then((value) async {
if (value != null) {
await FireStoreUtils.fireStore.collection(CollectionName.vendors).doc(value.vendorID.toString()).get().then((value1) async {
if (value1.exists) {
VendorModel vendorModel = VendorModel.fromJson(value1.data()!);
if(value.publish == true){
if (Constant.isSubscriptionModelApplied == true || vendorModel.adminCommission?.isEnabled == true) {
if (vendorModel.subscriptionPlan != null) {
if (vendorModel.subscriptionTotalOrders == "-1") {
favouriteFoodList.add(value);
} else {
if ((vendorModel.subscriptionExpiryDate != null && vendorModel.subscriptionExpiryDate!.toDate().isBefore(DateTime.now()) == false) ||
vendorModel.subscriptionPlan?.expiryDay == "-1") {
if (vendorModel.subscriptionTotalOrders != '0') {
favouriteFoodList.add(value);
}
}
}
}
} else {
favouriteFoodList.add(value);
}
}
}
});
}
});
}
}
isLoading.value = false;
}
}

View File

@@ -0,0 +1,73 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/models/category_model.dart';
import 'package:customer/models/favorite_ondemand_service_model.dart';
import 'package:customer/models/provider_serivce_model.dart';
import 'package:customer/screen_ui/auth_screens/login_screen.dart';
import 'package:customer/service/fire_store_utils.dart';
import 'package:get/get.dart';
class FavouriteOndemmandController extends GetxController {
// Add your controller logic here
Rx<bool> isLoading = false.obs;
RxList<FavouriteOndemandServiceModel> lstFav = <FavouriteOndemandServiceModel>[].obs;
RxList<CategoryModel> categories = <CategoryModel>[].obs;
@override
void onInit() {
// TODO: implement onInit
getData();
super.onInit();
}
Future<void> getData() async {
isLoading.value = true;
await FireStoreUtils.getOnDemandCategory().then((catValue) {
categories.value = catValue;
});
await FireStoreUtils.getFavouritesServiceList(FireStoreUtils.getCurrentUid()).then((favList) {
lstFav.value = favList;
});
isLoading.value = false;
}
void toggleFavourite(ProviderServiceModel provider) {
if (Constant.userModel == null) {
Get.to(LoginScreen());
} else {
var contain = lstFav.where((element) => element.service_id == provider.id);
if (contain.isNotEmpty) {
FavouriteOndemandServiceModel favouriteModel = FavouriteOndemandServiceModel(
section_id: provider.sectionId,
service_id: provider.id,
user_id: FireStoreUtils.getCurrentUid(),
serviceAuthorId: provider.author,
);
FireStoreUtils.removeFavouriteOndemandService(favouriteModel);
lstFav.removeWhere((item) => item.service_id == provider.id);
} else {
FavouriteOndemandServiceModel favouriteModel = FavouriteOndemandServiceModel(
section_id: provider.sectionId,
service_id: provider.id,
user_id: FireStoreUtils.getCurrentUid(),
serviceAuthorId: provider.author,
);
FireStoreUtils.setFavouriteOndemandSection(favouriteModel);
lstFav.add(favouriteModel);
}
}
}
/// Get category by id safely from cached categories
Future<CategoryModel?> getCategory(String? categoryId) async {
if (categoryId == null || categoryId.isEmpty) return null;
// Try to find category from cached list
CategoryModel? cat = categories.firstWhereOrNull((element) => element.id == categoryId);
// If not found, fetch from Firestore
cat ??= await FireStoreUtils.getCategoryById(categoryId);
return cat;
}
}

View File

@@ -0,0 +1,191 @@
import 'dart:developer';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/dash_board_controller.dart';
import 'package:customer/models/advertisement_model.dart';
import 'package:customer/models/coupon_model.dart';
import 'package:customer/models/favourite_model.dart';
import 'package:customer/models/vendor_category_model.dart';
import 'package:customer/models/vendor_model.dart';
import 'package:customer/utils/preferences.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import '../models/banner_model.dart';
import '../models/story_model.dart';
import '../service/cart_provider.dart';
import '../service/fire_store_utils.dart';
class FoodHomeController extends GetxController {
DashBoardController dashBoardController = Get.find<DashBoardController>();
final CartProvider cartProvider = CartProvider();
Future<void> getCartData() async {
cartProvider.cartStream.listen((event) async {
cartItem.clear();
cartItem.addAll(event);
});
update();
}
RxBool isLoading = true.obs;
RxBool isListView = true.obs;
RxBool isPopular = true.obs;
RxString selectedOrderTypeValue = "Delivery".tr.obs;
Rx<PageController> pageController = PageController(viewportFraction: 0.877).obs;
Rx<PageController> pageBottomController = PageController(viewportFraction: 0.877).obs;
RxInt currentPage = 0.obs;
RxInt currentBottomPage = 0.obs;
late TabController tabController;
@override
void onInit() async {
await getData();
super.onInit();
}
RxList<VendorCategoryModel> vendorCategoryModel = <VendorCategoryModel>[].obs;
RxList<VendorModel> allNearestRestaurant = <VendorModel>[].obs;
RxList<VendorModel> newArrivalRestaurantList = <VendorModel>[].obs;
RxList<AdvertisementModel> advertisementList = <AdvertisementModel>[].obs;
RxList<VendorModel> popularRestaurantList = <VendorModel>[].obs;
RxList<VendorModel> couponRestaurantList = <VendorModel>[].obs;
RxList<CouponModel> couponList = <CouponModel>[].obs;
RxList<StoryModel> storyList = <StoryModel>[].obs;
RxList<BannerModel> bannerModel = <BannerModel>[].obs;
RxList<BannerModel> bannerBottomModel = <BannerModel>[].obs;
RxList<FavouriteModel> favouriteList = <FavouriteModel>[].obs;
Future<void> getData() async {
isLoading.value = true;
getCartData();
selectedOrderTypeValue.value = Preferences.getString(Preferences.foodDeliveryType, defaultValue: "Delivery");
await getZone();
FireStoreUtils.getAllNearestRestaurant().listen((event) async {
popularRestaurantList.clear();
newArrivalRestaurantList.clear();
allNearestRestaurant.clear();
advertisementList.clear();
allNearestRestaurant.addAll(event);
newArrivalRestaurantList.addAll(event);
popularRestaurantList.addAll(event);
Constant.restaurantList = allNearestRestaurant;
popularRestaurantList.sort(
(a, b) => Constant.calculateReview(
reviewCount: b.reviewsCount.toString(),
reviewSum: b.reviewsSum.toString(),
).compareTo(Constant.calculateReview(reviewCount: a.reviewsCount.toString(), reviewSum: a.reviewsSum.toString())),
);
newArrivalRestaurantList.sort((a, b) => (b.createdAt ?? Timestamp.now()).toDate().compareTo((a.createdAt ?? Timestamp.now()).toDate()));
await getVendorCategory();
await FireStoreUtils.getHomeCoupon().then((value) {
couponRestaurantList.clear();
couponList.clear();
for (var element1 in value) {
for (var element in allNearestRestaurant) {
if (element1.vendorID == element.id && element1.expiresAt!.toDate().isAfter(DateTime.now())) {
couponList.add(element1);
couponRestaurantList.add(element);
}
}
}
});
await FireStoreUtils.getStory().then((stories) {
storyList.clear();
print("Total stories fetched: ${stories.length}");
// Create a fast lookup Set of all nearest vendor IDs
final nearestIds = allNearestRestaurant.map((e) => e.id).toSet();
print("nearestIds: $nearestIds");
// Filter stories whose vendorID exists in nearestIds
storyList.addAll(
stories.where((story) => nearestIds.contains(story.vendorID))
);
print("Filtered storyList length: ${storyList.length}");
});
if (Constant.isEnableAdsFeature == true) {
await FireStoreUtils.getAllAdvertisement().then((value) {
advertisementList.clear();
for (var element1 in value) {
for (var element in allNearestRestaurant) {
if (element1.vendorId == element.id) {
advertisementList.add(element1);
}
}
}
});
}
});
setLoading();
}
Future<void> setLoading() async {
await Future.delayed(Duration(seconds: 1), () async {
if (allNearestRestaurant.isEmpty) {
await Future.delayed(Duration(seconds: 2), () {
isLoading.value = false;
});
} else {
isLoading.value = false;
}
update();
});
}
Future<void> getVendorCategory() async {
await FireStoreUtils.getHomeVendorCategory().then((value) {
vendorCategoryModel.value = value;
if (Constant.restaurantList != null) {
List<String> usedCategoryIds = Constant.restaurantList!.expand((vendor) => vendor.categoryID ?? []).whereType<String>().toSet().toList();
vendorCategoryModel.value = vendorCategoryModel.where((category) => usedCategoryIds.contains(category.id)).toList();
}
});
await FireStoreUtils.getHomeTopBanner().then((value) {
bannerModel.value = value;
});
await FireStoreUtils.getHomeBottomBanner().then((value) {
bannerBottomModel.value = value;
});
await getFavouriteRestaurant();
}
Future<void> getFavouriteRestaurant() async {
if (Constant.userModel?.id != null) {
await FireStoreUtils.getFavouriteRestaurant().then((value) {
favouriteList.value = value;
});
}
log("Constant.userModel?.id :: ${favouriteList.length}");
}
Future<void> getZone() async {
await FireStoreUtils.getZone().then((value) {
if (value != null) {
for (int i = 0; i < value.length; i++) {
if (Constant.isPointInPolygon(LatLng(Constant.selectedLocation.location?.latitude ?? 0.0, Constant.selectedLocation.location?.longitude ?? 0.0), value[i].area!)) {
Constant.selectedZone = value[i];
Constant.isZoneAvailable = true;
break;
} else {
Constant.selectedZone = value[i];
Constant.isZoneAvailable = false;
}
}
}
});
}
}

View File

@@ -0,0 +1,40 @@
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../themes/show_toast_dialog.dart';
class ForgotPasswordController extends GetxController {
Rx<TextEditingController> emailEditingController =
TextEditingController().obs;
Future<void> forgotPassword() async {
final email = emailEditingController.value.text.trim();
if (email.isEmpty) {
ShowToastDialog.showToast("Please enter your email address.".tr);
return;
}
if (!GetUtils.isEmail(email)) {
ShowToastDialog.showToast("Please enter a valid email address.".tr);
return;
}
try {
ShowToastDialog.showLoader("Please wait...".tr);
await FirebaseAuth.instance.sendPasswordResetEmail(email: email);
ShowToastDialog.closeLoader();
ShowToastDialog.showToast(
'reset_password_link_sent'.trParams({'email': email}),
);
Get.back();
} on FirebaseAuthException catch (e) {
ShowToastDialog.closeLoader();
if (e.code == 'user-not-found') {
ShowToastDialog.showToast('No user found for that email.'.tr);
} else {
ShowToastDialog.showToast(e.message?.tr ?? "something_went_wrong".tr);
}
}
}
}

View File

@@ -0,0 +1,920 @@
import 'dart:convert';
import 'dart:io';
import 'dart:math' as maths;
import 'dart:math';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:customer/constant/constant.dart';
import 'package:customer/models/gift_cards_model.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:flutter_paypal/flutter_paypal.dart';
import 'package:flutter_stripe/flutter_stripe.dart';
import 'package:razorpay_flutter/razorpay_flutter.dart';
import '../models/gift_cards_order_model.dart';
import '../models/payment_model/cod_setting_model.dart';
import '../models/payment_model/flutter_wave_model.dart';
import '../models/payment_model/mercado_pago_model.dart';
import '../models/payment_model/mid_trans.dart';
import '../models/payment_model/orange_money.dart';
import '../models/payment_model/pay_fast_model.dart';
import '../models/payment_model/pay_stack_model.dart';
import '../models/payment_model/paypal_model.dart';
import '../models/payment_model/paytm_model.dart';
import '../models/payment_model/razorpay_model.dart';
import '../models/payment_model/stripe_model.dart';
import '../models/payment_model/wallet_setting_model.dart';
import '../models/payment_model/xendit.dart';
import '../models/user_model.dart';
import '../models/wallet_transaction_model.dart';
import '../payment/MercadoPagoScreen.dart';
import '../payment/PayFastScreen.dart';
import '../payment/getPaytmTxtToken.dart';
import '../payment/midtrans_screen.dart';
import '../payment/orangePayScreen.dart';
import '../payment/paystack/pay_stack_screen.dart';
import '../payment/paystack/pay_stack_url_model.dart';
import '../payment/paystack/paystack_url_genrater.dart';
import '../payment/stripe_failed_model.dart';
import '../payment/xenditModel.dart';
import '../payment/xenditScreen.dart';
import '../screen_ui/multi_vendor_service/gift_card/history_gift_card.dart';
import '../screen_ui/multi_vendor_service/wallet_screen/wallet_screen.dart';
import '../service/fire_store_utils.dart';
import 'package:customer/utils/preferences.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import 'package:uuid/uuid.dart';
import '../themes/show_toast_dialog.dart';
class GiftCardController extends GetxController {
RxBool isLoading = true.obs;
RxString selectedPaymentMethod = ''.obs;
var pageController = PageController();
@override
void onInit() {
// TODO: implement onInit
getGiftCard();
super.onInit();
}
List<GiftCardsModel> giftCardList = [];
Rx<GiftCardsModel> selectedGiftCard = GiftCardsModel().obs;
List amountList = ["1000", "2000", "5000"];
RxString selectedAmount = "1000".obs;
var selectedPageIndex = 0.obs;
Rx<TextEditingController> amountController = TextEditingController().obs;
Rx<TextEditingController> messageController = TextEditingController().obs;
Rx<UserModel> userModel = UserModel().obs;
Future<void> getGiftCard() async {
await FireStoreUtils.getGiftCard().then((value) {
giftCardList = value;
if (giftCardList.isNotEmpty) {
selectedGiftCard.value = giftCardList.first;
messageController.value.text = selectedGiftCard.value.message.toString();
}
});
isLoading.value = false;
await FireStoreUtils.getUserProfile(FireStoreUtils.getCurrentUid()).then(
(value) {
if (value != null) {
userModel.value = value;
}
},
);
await getPaymentSettings();
}
Future<void> placeOrder() async {
if (selectedPaymentMethod.value == PaymentGateway.wallet.name) {
if (double.parse(userModel.value.walletAmount.toString()) >= double.parse(amountController.value.text)) {
setOrder();
} else {
ShowToastDialog.showToast("You don't have sufficient wallet balance to purchase gift card".tr);
}
} else {
setOrder();
}
}
Future<void> setOrder() async {
ShowToastDialog.showLoader("Please wait...".tr);
GiftCardsOrderModel giftCardsOrderModel = GiftCardsOrderModel();
giftCardsOrderModel.id = const Uuid().v4();
giftCardsOrderModel.giftId = selectedGiftCard.value.id.toString();
giftCardsOrderModel.giftTitle = selectedGiftCard.value.title.toString();
giftCardsOrderModel.price = amountController.value.text;
giftCardsOrderModel.redeem = false;
giftCardsOrderModel.message = messageController.value.text;
giftCardsOrderModel.giftPin = generateGiftPin();
giftCardsOrderModel.giftCode = generateGiftCode();
giftCardsOrderModel.paymentType = selectedPaymentMethod.value;
giftCardsOrderModel.createdDate = Timestamp.now();
DateTime dateTime = DateTime.now().add(Duration(days: int.parse(selectedGiftCard.value.expiryDay ?? "2")));
giftCardsOrderModel.expireDate = Timestamp.fromDate(dateTime);
giftCardsOrderModel.userid = FireStoreUtils.getCurrentUid();
if (selectedPaymentMethod.value == PaymentGateway.wallet.name) {
WalletTransactionModel transactionModel = WalletTransactionModel(
id: Constant.getUuid(),
amount: double.parse(amountController.value.text),
date: Timestamp.now(),
paymentMethod: PaymentGateway.wallet.name,
transactionUser: "user",
userId: FireStoreUtils.getCurrentUid(),
isTopup: false,
orderId: giftCardsOrderModel.id,
note: "Gift card purchase amount debited".tr,
paymentStatus: "success".tr);
await FireStoreUtils.setWalletTransaction(transactionModel).then((value) async {
if (value == true) {
await FireStoreUtils.updateUserWallet(amount: "-${amountController.value.text.toString()}", userId: FireStoreUtils.getCurrentUid()).then((value) {});
}
});
}
await FireStoreUtils.placeGiftCardOrder(giftCardsOrderModel);
ShowToastDialog.closeLoader();
Get.off(const HistoryGiftCard());
ShowToastDialog.showToast("Gift card Purchases successfully".tr);
}
String generateGiftCode() {
var rng = Random();
String generatedNumber = '';
for (int i = 0; i < 16; i++) {
generatedNumber += (rng.nextInt(9) + 1).toString();
}
return generatedNumber;
}
String generateGiftPin() {
var rng = Random();
String generatedNumber = '';
for (int i = 0; i < 6; i++) {
generatedNumber += (rng.nextInt(9) + 1).toString();
}
return generatedNumber;
}
Rx<WalletSettingModel> walletSettingModel = WalletSettingModel().obs;
Rx<CodSettingModel> cashOnDeliverySettingModel = CodSettingModel().obs;
Rx<PayFastModel> payFastModel = PayFastModel().obs;
Rx<MercadoPagoModel> mercadoPagoModel = MercadoPagoModel().obs;
Rx<PayPalModel> payPalModel = PayPalModel().obs;
Rx<StripeModel> stripeModel = StripeModel().obs;
Rx<FlutterWaveModel> flutterWaveModel = FlutterWaveModel().obs;
Rx<PayStackModel> payStackModel = PayStackModel().obs;
Rx<PaytmModel> paytmModel = PaytmModel().obs;
Rx<RazorPayModel> razorPayModel = RazorPayModel().obs;
Rx<MidTrans> midTransModel = MidTrans().obs;
Rx<OrangeMoney> orangeMoneyModel = OrangeMoney().obs;
Rx<Xendit> xenditModel = Xendit().obs;
Future<void> getPaymentSettings() async {
await FireStoreUtils.getPaymentSettingsData().then(
(value) {
payFastModel.value = PayFastModel.fromJson(jsonDecode(Preferences.getString(Preferences.payFastSettings)));
mercadoPagoModel.value = MercadoPagoModel.fromJson(jsonDecode(Preferences.getString(Preferences.mercadoPago)));
payPalModel.value = PayPalModel.fromJson(jsonDecode(Preferences.getString(Preferences.paypalSettings)));
stripeModel.value = StripeModel.fromJson(jsonDecode(Preferences.getString(Preferences.stripeSettings)));
flutterWaveModel.value = FlutterWaveModel.fromJson(jsonDecode(Preferences.getString(Preferences.flutterWave)));
payStackModel.value = PayStackModel.fromJson(jsonDecode(Preferences.getString(Preferences.payStack)));
paytmModel.value = PaytmModel.fromJson(jsonDecode(Preferences.getString(Preferences.paytmSettings)));
razorPayModel.value = RazorPayModel.fromJson(jsonDecode(Preferences.getString(Preferences.razorpaySettings)));
cashOnDeliverySettingModel.value = CodSettingModel.fromJson(jsonDecode(Preferences.getString(Preferences.codSettings)));
midTransModel.value = MidTrans.fromJson(jsonDecode(Preferences.getString(Preferences.midTransSettings)));
orangeMoneyModel.value = OrangeMoney.fromJson(jsonDecode(Preferences.getString(Preferences.orangeMoneySettings)));
xenditModel.value = Xendit.fromJson(jsonDecode(Preferences.getString(Preferences.xenditSettings)));
walletSettingModel.value = WalletSettingModel.fromJson(jsonDecode(Preferences.getString(Preferences.walletSettings)));
if (walletSettingModel.value.isEnabled == true) {
selectedPaymentMethod.value = PaymentGateway.wallet.name;
} else if (stripeModel.value.isEnabled == true) {
selectedPaymentMethod.value = PaymentGateway.stripe.name;
} else if (payPalModel.value.isEnabled == true) {
selectedPaymentMethod.value = PaymentGateway.paypal.name;
} else if (payStackModel.value.isEnable == true) {
selectedPaymentMethod.value = PaymentGateway.payStack.name;
} else if (mercadoPagoModel.value.isEnabled == true) {
selectedPaymentMethod.value = PaymentGateway.mercadoPago.name;
} else if (flutterWaveModel.value.isEnable == true) {
selectedPaymentMethod.value = PaymentGateway.flutterWave.name;
}else if (payFastModel.value.isEnable == true) {
selectedPaymentMethod.value = PaymentGateway.payFast.name;
} else if (razorPayModel.value.isEnabled == true) {
selectedPaymentMethod.value = PaymentGateway.razorpay.name;
} else if (midTransModel.value.enable == true) {
selectedPaymentMethod.value = PaymentGateway.midTrans.name;
} else if (orangeMoneyModel.value.enable == true) {
selectedPaymentMethod.value = PaymentGateway.orangeMoney.name;
} else if (xenditModel.value.enable == true) {
selectedPaymentMethod.value = PaymentGateway.xendit.name;
}
Stripe.publishableKey = stripeModel.value.clientpublishableKey.toString();
Stripe.merchantIdentifier = 'GoRide';
Stripe.instance.applySettings();
setRef();
razorPay.on(Razorpay.EVENT_PAYMENT_SUCCESS, handlePaymentSuccess);
razorPay.on(Razorpay.EVENT_EXTERNAL_WALLET, handleExternalWaller);
razorPay.on(Razorpay.EVENT_PAYMENT_ERROR, handlePaymentError);
},
);
}
// final _flutterPaypalNativePlugin = FlutterPaypalNative.instance;
// void initPayPal() async {
// //set debugMode for error logging
// FlutterPaypalNative.isDebugMode = paytmModel.value.isSandboxEnabled == true ? true : false;
// //initiate payPal plugin
// await _flutterPaypalNativePlugin.init(
// //your app id !!! No Underscore!!! see readme.md for help
// returnUrl: "com.parkme://paypalpay",
// //client id from developer dashboard
// clientID: payPalModel.value.paypalClient.toString(),
// //sandbox, staging, live etc
// payPalEnvironment: payPalModel.value.isLive == false ? FPayPalEnvironment.sandbox : FPayPalEnvironment.live,
// //what currency do you plan to use? default is US dollars
// currencyCode: FPayPalCurrencyCode.usd,
// //action paynow?
// action: FPayPalUserAction.payNow,
// );
// //call backs for payment
// _flutterPaypalNativePlugin.setPayPalOrderCallback(
// callback: FPayPalOrderCallback(
// onCancel: () {
// //user canceled the payment
// ShowToastDialog.showToast("Payment canceled");
// },
// onSuccess: (data) {
// //successfully paid
// //remove all items from queue
// // _flutterPaypalNativePlugin.removeAllPurchaseItems();
// ShowToastDialog.showToast("Payment Successful!!");
// placeOrder();
// },
// onError: (data) {
// //an error occured
// ShowToastDialog.showToast("error: ${data.reason}");
// },
// onShippingChange: (data) {
// //the user updated the shipping address
// ShowToastDialog.showToast("shipping change: ${data.shippingChangeAddress?.adminArea1 ?? ""}");
// },
// ),
// );
// }
// paypalPaymentSheet(String amount) {
// //add 1 item to cart. Max is 4!
// if (_flutterPaypalNativePlugin.canAddMorePurchaseUnit) {
// _flutterPaypalNativePlugin.addPurchaseUnit(
// FPayPalPurchaseUnit(
// // random prices
// amount: double.parse(amount),
// ///please use your own algorithm for referenceId. Maybe ProductID?
// referenceId: FPayPalStrHelper.getRandomString(16),
// ),
// );
// }
// // initPayPal();
// _flutterPaypalNativePlugin.makeOrder(
// action: FPayPalUserAction.payNow,
// );
// }
// Strip
Future<void> stripeMakePayment({required String amount}) async {
try {
Map<String, dynamic>? paymentIntentData = await createStripeIntent(amount: amount);
if (paymentIntentData!.containsKey("error")) {
Get.back();
ShowToastDialog.showToast("Something went wrong, please contact admin.");
} else {
await Stripe.instance.initPaymentSheet(
paymentSheetParameters: SetupPaymentSheetParameters(
paymentIntentClientSecret: paymentIntentData['client_secret'],
allowsDelayedPaymentMethods: false,
googlePay: const PaymentSheetGooglePay(
merchantCountryCode: 'US',
testEnv: true,
currencyCode: "USD",
),
customFlow: true,
style: ThemeMode.system,
appearance: PaymentSheetAppearance(
colors: PaymentSheetAppearanceColors(
primary: AppThemeData.primary300,
),
),
merchantDisplayName: 'GoRide'));
displayStripePaymentSheet(amount: amount);
}
} catch (e, s) {
ShowToastDialog.showToast("exception:$e \n$s");
}
}
Future<void> displayStripePaymentSheet({required String amount}) async {
try {
await Stripe.instance.presentPaymentSheet().then((value) {
ShowToastDialog.showToast("Payment successfully".tr);
placeOrder();
});
} on StripeException catch (e) {
var lo1 = jsonEncode(e);
var lo2 = jsonDecode(lo1);
StripePayFailedModel lom = StripePayFailedModel.fromJson(lo2);
ShowToastDialog.showToast(lom.error.message);
} catch (e) {
ShowToastDialog.showToast(e.toString());
}
}
Future createStripeIntent({required String amount}) async {
try {
Map<String, dynamic> body = {
'amount': ((double.parse(amount) * 100).round()).toString(),
'currency': "USD",
'payment_method_types[]': 'card',
"description": "Strip Payment",
"shipping[name]": userModel.value.fullName(),
"shipping[address][line1]": "510 Townsend St",
"shipping[address][postal_code]": "98140",
"shipping[address][city]": "San Francisco",
"shipping[address][state]": "CA",
"shipping[address][country]": "US",
};
var stripeSecret = stripeModel.value.stripeSecret;
var response = await http.post(Uri.parse('https://api.stripe.com/v1/payment_intents'),
body: body, headers: {'Authorization': 'Bearer $stripeSecret', 'Content-Type': 'application/x-www-form-urlencoded'});
return jsonDecode(response.body);
} catch (e) {
print(e.toString());
}
}
//mercadoo
Future<Null> mercadoPagoMakePayment({required BuildContext context, required String amount}) async {
final headers = {
'Authorization': 'Bearer ${mercadoPagoModel.value.accessToken}',
'Content-Type': 'application/json',
};
final body = jsonEncode({
"items": [
{
"title": "Test",
"description": "Test Payment",
"quantity": 1,
"currency_id": "BRL", // or your preferred currency
"unit_price": double.parse(amount),
}
],
"payer": {"email": userModel.value.email},
"back_urls": {
"failure": "${Constant.globalUrl}payment/failure",
"pending": "${Constant.globalUrl}payment/pending",
"success": "${Constant.globalUrl}payment/success",
},
"auto_return": "approved" // Automatically return after payment is approved
});
final response = await http.post(
Uri.parse("https://api.mercadopago.com/checkout/preferences"),
headers: headers,
body: body,
);
if (response.statusCode == 200 || response.statusCode == 201) {
final data = jsonDecode(response.body);
Get.to(MercadoPagoScreen(initialURl: data['init_point']))!.then((value) {
if (value) {
ShowToastDialog.showToast("Payment Successful!!".tr);
placeOrder();
} else {
ShowToastDialog.showToast("Payment UnSuccessful!!".tr);
}
});
} else {
print('Error creating preference: ${response.body}');
return null;
}
}
///PayStack Payment Method
Future<void> payStackPayment(String totalAmount) async {
await PayStackURLGen.payStackURLGen(
amount: (double.parse(totalAmount) * 100).toString(), currency: "ZAR", secretKey: payStackModel.value.secretKey.toString(), userModel: userModel.value)
.then((value) async {
if (value != null) {
PayStackUrlModel payStackModel0 = value;
Get.to(PayStackScreen(
secretKey: payStackModel.value.secretKey.toString(),
callBackUrl: payStackModel.value.callbackURL.toString(),
initialURl: payStackModel0.data.authorizationUrl,
amount: totalAmount,
reference: payStackModel0.data.reference,
))!
.then((value) {
if (value) {
ShowToastDialog.showToast("Payment Successful!!".tr);
placeOrder();
} else {
ShowToastDialog.showToast("Payment UnSuccessful!!".tr);
}
});
} else {
ShowToastDialog.showToast("Something went wrong, please contact admin.".tr);
}
});
}
//flutter wave Payment Method
Future<Null> flutterWaveInitiatePayment({required BuildContext context, required String amount}) async {
final url = Uri.parse('https://api.flutterwave.com/v3/payments');
final headers = {
'Authorization': 'Bearer ${flutterWaveModel.value.secretKey}',
'Content-Type': 'application/json',
};
final body = jsonEncode({
"tx_ref": _ref,
"amount": amount,
"currency": "NGN",
"redirect_url": "${Constant.globalUrl}payment/success",
"payment_options": "ussd, card, barter, payattitude",
"customer": {
"email": userModel.value.email.toString(),
"phonenumber": userModel.value.phoneNumber, // Add a real phone number
"name": userModel.value.fullName(), // Add a real customer name
},
"customizations": {
"title": "Payment for Services",
"description": "Payment for XYZ services",
}
});
final response = await http.post(url, headers: headers, body: body);
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
Get.to(MercadoPagoScreen(initialURl: data['data']['link']))!.then((value) {
if (value) {
ShowToastDialog.showToast("Payment Successful!!".tr);
placeOrder();
} else {
ShowToastDialog.showToast("Payment UnSuccessful!!".tr);
}
});
} else {
print('Payment initialization failed: ${response.body}');
return null;
}
}
String? _ref;
void setRef() {
maths.Random numRef = maths.Random();
int year = DateTime.now().year;
int refNumber = numRef.nextInt(20000);
if (Platform.isAndroid) {
_ref = "AndroidRef$year$refNumber";
} else if (Platform.isIOS) {
_ref = "IOSRef$year$refNumber";
}
}
// payFast
void payFastPayment({required BuildContext context, required String amount}) {
PayStackURLGen.getPayHTML(payFastSettingData: payFastModel.value, amount: amount.toString(), userModel: userModel.value).then((String? value) async {
bool isDone = await Get.to(PayFastScreen(htmlData: value!, payFastSettingData: payFastModel.value));
if (isDone) {
Get.back();
ShowToastDialog.showToast("Payment successfully".tr);
placeOrder();
} else {
Get.back();
ShowToastDialog.showToast("Payment Failed".tr);
}
});
}
//PayPal
void paypalPaymentSheet(String amount, context) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) => UsePaypal(
sandboxMode: payPalModel.value.isLive == true ? false : true,
clientId: payPalModel.value.paypalClient ?? '',
secretKey: payPalModel.value.paypalSecret ?? '',
returnURL: "com.parkme://paypalpay",
cancelURL: "com.parkme://paypalpay",
transactions: [
{
"amount": {
"total": amount,
"currency": "USD",
"details": {"subtotal": amount}
},
}
],
note: "Contact us for any questions on your order.",
onSuccess: (Map params) async {
placeOrder();
ShowToastDialog.showToast("Payment Successful!!".tr);
},
onError: (error) {
Get.back();
ShowToastDialog.showToast("Payment UnSuccessful!!".tr);
},
onCancel: (params) {
Get.back();
ShowToastDialog.showToast("Payment UnSuccessful!!".tr);
}),
),
);
}
///Paytm payment function
Future<void> getPaytmCheckSum(context, {required double amount}) async {
final String orderId = DateTime.now().millisecondsSinceEpoch.toString();
String getChecksum = "${Constant.globalUrl}payments/getpaytmchecksum";
final response = await http.post(
Uri.parse(
getChecksum,
),
headers: {},
body: {
"mid": paytmModel.value.paytmMID.toString(),
"order_id": orderId,
"key_secret": paytmModel.value.pAYTMMERCHANTKEY.toString(),
});
final data = jsonDecode(response.body);
await verifyCheckSum(checkSum: data["code"], amount: amount, orderId: orderId).then((value) {
initiatePayment(amount: amount, orderId: orderId).then((value) {
String callback = "";
if (paytmModel.value.isSandboxEnabled == true) {
callback = "${callback}https://securegw-stage.paytm.in/theia/paytmCallback?ORDER_ID=$orderId";
} else {
callback = "${callback}https://securegw.paytm.in/theia/paytmCallback?ORDER_ID=$orderId";
}
GetPaymentTxtTokenModel result = value;
startTransaction(context, txnTokenBy: result.body.txnToken ?? '', orderId: orderId, amount: amount, callBackURL: callback, isStaging: paytmModel.value.isSandboxEnabled);
});
});
}
Future<void> startTransaction(context, {required String txnTokenBy, required orderId, required double amount, required callBackURL, required isStaging}) async {
// try {
// var response = AllInOneSdk.startTransaction(
// paytmModel.value.paytmMID.toString(),
// orderId,
// amount.toString(),
// txnTokenBy,
// callBackURL,
// isStaging,
// true,
// true,
// );
//
// response.then((value) {
// if (value!["RESPMSG"] == "Txn Success") {
// ShowToastDialog.showToast("Payment Successful!!");
// placeOrder();
// }
// }).catchError((onError) {
// if (onError is PlatformException) {
// Get.back();
//
// ShowToastDialog.showToast(onError.message.toString());
// } else {
// print("======>>2");
// Get.back();
// ShowToastDialog.showToast(onError.message.toString());
// }
// });
// } catch (err) {
// Get.back();
// ShowToastDialog.showToast(err.toString());
// }
}
Future verifyCheckSum({required String checkSum, required double amount, required orderId}) async {
String getChecksum = "${Constant.globalUrl}payments/validatechecksum";
final response = await http.post(
Uri.parse(
getChecksum,
),
headers: {},
body: {
"mid": paytmModel.value.paytmMID.toString(),
"order_id": orderId,
"key_secret": paytmModel.value.pAYTMMERCHANTKEY.toString(),
"checksum_value": checkSum,
});
final data = jsonDecode(response.body);
return data['status'];
}
Future<GetPaymentTxtTokenModel> initiatePayment({required double amount, required orderId}) async {
String initiateURL = "${Constant.globalUrl}payments/initiatepaytmpayment";
String callback = "";
if (paytmModel.value.isSandboxEnabled == true) {
callback = "${callback}https://securegw-stage.paytm.in/theia/paytmCallback?ORDER_ID=$orderId";
} else {
callback = "${callback}https://securegw.paytm.in/theia/paytmCallback?ORDER_ID=$orderId";
}
final response = await http.post(Uri.parse(initiateURL), headers: {}, body: {
"mid": paytmModel.value.paytmMID,
"order_id": orderId,
"key_secret": paytmModel.value.pAYTMMERCHANTKEY,
"amount": amount.toString(),
"currency": "INR",
"callback_url": callback,
"custId": FireStoreUtils.getCurrentUid(),
"issandbox": paytmModel.value.isSandboxEnabled == true ? "1" : "2",
});
print(response.body);
final data = jsonDecode(response.body);
if (data["body"]["txnToken"] == null || data["body"]["txnToken"].toString().isEmpty) {
Get.back();
ShowToastDialog.showToast("something went wrong, please contact admin.".tr);
}
return GetPaymentTxtTokenModel.fromJson(data);
}
///RazorPay payment function
final Razorpay razorPay = Razorpay();
void openCheckout({required amount, required orderId}) async {
var options = {
'key': razorPayModel.value.razorpayKey,
'amount': amount * 100,
'name': 'GoRide',
'order_id': orderId,
"currency": "INR",
'description': 'wallet Topup',
'retry': {'enabled': true, 'max_count': 1},
'send_sms_hash': true,
'prefill': {
'contact': userModel.value.phoneNumber,
'email': userModel.value.email,
},
'external': {
'wallets': ['paytm']
}
};
try {
razorPay.open(options);
} catch (e) {
debugPrint('Error: $e');
}
}
void handlePaymentSuccess(PaymentSuccessResponse response) {
Get.back();
ShowToastDialog.showToast("Payment Successful!!".tr);
placeOrder();
}
void handleExternalWaller(ExternalWalletResponse response) {
Get.back();
ShowToastDialog.showToast("Payment Processing!! via".tr);
}
void handlePaymentError(PaymentFailureResponse response) {
Get.back();
ShowToastDialog.showToast("Payment Failed!!".tr);
}
//Midtrans payment
Future<void> midtransMakePayment({required String amount, required BuildContext context}) async {
await createPaymentLink(amount: amount).then((url) {
ShowToastDialog.closeLoader();
if (url != '') {
Get.to(() => MidtransScreen(
initialURl: url,
))!
.then((value) {
if (value == true) {
ShowToastDialog.showToast("Payment Successful!!".tr);
placeOrder();
} else {
ShowToastDialog.showToast("Payment Unsuccessful!!".tr);
}
});
}
});
}
Future<String> createPaymentLink({required var amount}) async {
var ordersId = const Uuid().v1();
final url = Uri.parse(midTransModel.value.isSandbox! ? 'https://api.sandbox.midtrans.com/v1/payment-links' : 'https://api.midtrans.com/v1/payment-links');
final response = await http.post(
url,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': generateBasicAuthHeader(midTransModel.value.serverKey!),
},
body: jsonEncode({
'transaction_details': {
'order_id': ordersId,
'gross_amount': double.parse(amount.toString()).toInt(),
},
'usage_limit': 2,
"callbacks": {"finish": "https://www.google.com?merchant_order_id=$ordersId"},
}),
);
if (response.statusCode == 200 || response.statusCode == 201) {
final responseData = jsonDecode(response.body);
return responseData['payment_url'];
} else {
ShowToastDialog.showToast("something went wrong, please contact admin.".tr);
return '';
}
}
String generateBasicAuthHeader(String apiKey) {
String credentials = '$apiKey:';
String base64Encoded = base64Encode(utf8.encode(credentials));
return 'Basic $base64Encoded';
}
//Orangepay payment
static String accessToken = '';
static String payToken = '';
static String orderId = '';
static String amount = '';
Future<void> orangeMakePayment({required String amount, required BuildContext context}) async {
reset();
var id = const Uuid().v4();
var paymentURL = await fetchToken(context: context, orderId: id, amount: amount, currency: 'USD');
ShowToastDialog.closeLoader();
if (paymentURL.toString() != '') {
Get.to(() => OrangeMoneyScreen(
initialURl: paymentURL,
accessToken: accessToken,
amount: amount,
orangePay: orangeMoneyModel.value,
orderId: orderId,
payToken: payToken,
))!
.then((value) {
if (value == true) {
ShowToastDialog.showToast("Payment Successful!!".tr);
placeOrder();
();
}
});
} else {
ShowToastDialog.showToast("Payment Unsuccessful!!".tr);
}
}
Future fetchToken({required String orderId, required String currency, required BuildContext context, required String amount}) async {
String apiUrl = 'https://api.orange.com/oauth/v3/token';
Map<String, String> requestBody = {
'grant_type': 'client_credentials',
};
var response = await http.post(Uri.parse(apiUrl),
headers: <String, String>{
'Authorization': "Basic ${orangeMoneyModel.value.auth!}",
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json',
},
body: requestBody);
// Handle the response
if (response.statusCode == 200) {
Map<String, dynamic> responseData = jsonDecode(response.body);
accessToken = responseData['access_token'];
// ignore: use_build_context_synchronously
return await webpayment(context: context, amountData: amount, currency: currency, orderIdData: orderId);
} else {
ShowToastDialog.showToast("Something went wrong, please contact admin.".tr);
return '';
}
}
Future webpayment({required String orderIdData, required BuildContext context, required String currency, required String amountData}) async {
orderId = orderIdData;
amount = amountData;
String apiUrl =
orangeMoneyModel.value.isSandbox! == true ? 'https://api.orange.com/orange-money-webpay/dev/v1/webpayment' : 'https://api.orange.com/orange-money-webpay/cm/v1/webpayment';
Map<String, String> requestBody = {
"merchant_key": orangeMoneyModel.value.merchantKey ?? '',
"currency": orangeMoneyModel.value.isSandbox == true ? "OUV" : currency,
"order_id": orderId,
"amount": amount,
"reference": 'Y-Note Test',
"lang": "en",
"return_url": orangeMoneyModel.value.returnUrl!.toString(),
"cancel_url": orangeMoneyModel.value.cancelUrl!.toString(),
"notif_url": orangeMoneyModel.value.notifUrl!.toString(),
};
var response = await http.post(
Uri.parse(apiUrl),
headers: <String, String>{'Authorization': 'Bearer $accessToken', 'Content-Type': 'application/json', 'Accept': 'application/json'},
body: json.encode(requestBody),
);
// Handle the response
if (response.statusCode == 201) {
Map<String, dynamic> responseData = jsonDecode(response.body);
if (responseData['message'] == 'OK') {
payToken = responseData['pay_token'];
return responseData['payment_url'];
} else {
return '';
}
} else {
ShowToastDialog.showToast("Something went wrong, please contact admin.".tr);
return '';
}
}
static void reset() {
accessToken = '';
payToken = '';
orderId = '';
amount = '';
}
//XenditPayment
Future<void> xenditPayment(context, amount) async {
await createXenditInvoice(amount: amount).then((model) {
ShowToastDialog.closeLoader();
if (model.id != null) {
Get.to(() => XenditScreen(
initialURl: model.invoiceUrl ?? '',
transId: model.id ?? '',
apiKey: xenditModel.value.apiKey!.toString(),
))!
.then((value) {
if (value == true) {
ShowToastDialog.showToast("Payment Successful!!".tr);
placeOrder();
();
} else {
ShowToastDialog.showToast("Payment Unsuccessful!!".tr);
}
});
}
});
}
Future<XenditModel> createXenditInvoice({required var amount}) async {
const url = 'https://api.xendit.co/v2/invoices';
var headers = {
'Content-Type': 'application/json',
'Authorization': generateBasicAuthHeader(xenditModel.value.apiKey!.toString()),
// 'Cookie': '__cf_bm=yERkrx3xDITyFGiou0bbKY1bi7xEwovHNwxV1vCNbVc-1724155511-1.0.1.1-jekyYQmPCwY6vIJ524K0V6_CEw6O.dAwOmQnHtwmaXO_MfTrdnmZMka0KZvjukQgXu5B.K_6FJm47SGOPeWviQ',
};
final body = jsonEncode({
'external_id': const Uuid().v1(),
'amount': amount,
'payer_email': 'customer@domain.com',
'description': 'Test - VA Successful invoice payment',
'currency': 'IDR', //IDR, PHP, THB, VND, MYR
});
try {
final response = await http.post(Uri.parse(url), headers: headers, body: body);
if (response.statusCode == 200 || response.statusCode == 201) {
XenditModel model = XenditModel.fromJson(jsonDecode(response.body));
return model;
} else {
return XenditModel();
}
} catch (e) {
return XenditModel();
}
}
}

View File

@@ -0,0 +1,48 @@
import 'dart:developer';
import 'package:customer/constant/constant.dart';
import 'package:customer/models/currency_model.dart';
import 'package:customer/models/user_model.dart';
import 'package:customer/utils/notification_service.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:get/get.dart';
import '../constant/collection_name.dart';
import '../service/fire_store_utils.dart';
class GlobalSettingController extends GetxController {
@override
void onInit() {
notificationInit();
getCurrentCurrency();
super.onInit();
}
Future<void> getCurrentCurrency() async {
FireStoreUtils.fireStore.collection(CollectionName.currencies).where("isActive", isEqualTo: true).snapshots().listen((event) {
if (event.docs.isNotEmpty) {
Constant.currencyModel = CurrencyModel.fromJson(event.docs.first.data());
} else {
Constant.currencyModel = CurrencyModel(id: "", code: "USD", decimal: 2, isactive: true, name: "US Dollar", symbol: "\$", symbolatright: false);
}
});
await FireStoreUtils.getSettings();
}
NotificationService notificationService = NotificationService();
void notificationInit() {
notificationService.initInfo().then((value) async {
String token = await NotificationService.getToken();
log(":::::::TOKEN:::::: $token");
if (FirebaseAuth.instance.currentUser != null) {
await FireStoreUtils.getUserProfile(FireStoreUtils.getCurrentUid()).then((value) {
if (value != null) {
UserModel driverUserModel = value;
driverUserModel.fcmToken = token;
FireStoreUtils.updateUser(driverUserModel);
}
});
}
});
}
}

View File

@@ -0,0 +1,38 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:customer/constant/constant.dart';
import 'package:customer/models/gift_cards_order_model.dart';
import 'package:share_plus/share_plus.dart';
import '../service/fire_store_utils.dart';
import 'package:get/get.dart';
class HistoryGiftCardController extends GetxController {
RxList<GiftCardsOrderModel> giftCardsOrderList = <GiftCardsOrderModel>[].obs;
RxBool isLoading = true.obs;
@override
void onInit() {
getData();
super.onInit();
}
Future<void> getData() async {
await FireStoreUtils.getGiftHistory().then((value) {
giftCardsOrderList.value = value;
});
isLoading.value = false;
}
void updateList(int index) {
GiftCardsOrderModel giftCardsOrderModel = giftCardsOrderList[index];
giftCardsOrderModel.isPasswordShow = giftCardsOrderModel.isPasswordShow == true ? false : true;
giftCardsOrderList.removeAt(index);
giftCardsOrderList.insert(index, giftCardsOrderModel);
}
Future<void> share(String giftCode, String giftPin, String msg, String amount, Timestamp date) async {
await Share.share(
"${'Gift Code :'.tr} $giftCode\n${'Gift Pin :'.tr} $giftPin\n${'Price :'.tr} ${Constant.amountShow(amount: amount)}\n${'Expire Date :'.tr} ${date.toDate()}\n\n${'Message'.tr} : $msg",
);
}
}

View File

@@ -0,0 +1,132 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:customer/models/advertisement_model.dart';
import 'package:customer/models/banner_model.dart';
import 'package:customer/models/brands_model.dart';
import 'package:customer/models/favourite_model.dart';
import 'package:customer/models/vendor_category_model.dart';
import 'package:customer/models/vendor_model.dart';
import 'package:customer/service/cart_provider.dart';
import 'package:customer/service/fire_store_utils.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../constant/constant.dart';
class HomeECommerceController extends GetxController {
final CartProvider cartProvider = CartProvider();
Future<void> getCartData() async {
cartProvider.cartStream.listen((event) async {
cartItem.clear();
cartItem.addAll(event);
});
update();
}
RxBool isLoading = true.obs;
RxBool isListView = true.obs;
RxBool isPopular = true.obs;
Rx<PageController> pageController = PageController(viewportFraction: 0.877).obs;
Rx<PageController> pageBottomController = PageController(viewportFraction: 0.877).obs;
RxInt currentPage = 0.obs;
RxInt currentBottomPage = 0.obs;
@override
void onInit() {
// TODO: implement onInit
getVendorCategory();
getData();
super.onInit();
}
RxList<VendorCategoryModel> vendorCategoryModel = <VendorCategoryModel>[].obs;
RxList<VendorCategoryModel> categoryWiseProductList = <VendorCategoryModel>[].obs;
RxList<VendorModel> allNearestRestaurant = <VendorModel>[].obs;
RxList<VendorModel> newArrivalRestaurantList = <VendorModel>[].obs;
RxList<AdvertisementModel> advertisementList = <AdvertisementModel>[].obs;
RxList<BannerModel> bannerModel = <BannerModel>[].obs;
RxList<BannerModel> bannerBottomModel = <BannerModel>[].obs;
RxList<BrandsModel> brandList = <BrandsModel>[].obs;
Future<void> getData() async {
isLoading.value = true;
getCartData();
FireStoreUtils.getAllNearestRestaurant().listen((event) async {
print("=====>${event.length}");
newArrivalRestaurantList.clear();
allNearestRestaurant.clear();
advertisementList.clear();
allNearestRestaurant.addAll(event);
newArrivalRestaurantList.addAll(event);
Constant.restaurantList = allNearestRestaurant;
List<String> usedCategoryIds = allNearestRestaurant.expand((vendor) => vendor.categoryID ?? []).whereType<String>().toSet().toList();
vendorCategoryModel.value = vendorCategoryModel.where((category) => usedCategoryIds.contains(category.id)).toList();
newArrivalRestaurantList.sort((a, b) => (b.createdAt ?? Timestamp.now()).toDate().compareTo((a.createdAt ?? Timestamp.now()).toDate()));
if (Constant.isEnableAdsFeature == true) {
await FireStoreUtils.getAllAdvertisement().then((value) {
advertisementList.clear();
for (var element1 in value) {
for (var element in allNearestRestaurant) {
if (element1.vendorId == element.id) {
advertisementList.add(element1);
}
}
}
});
}
});
setLoading();
}
Future<void> setLoading() async {
await Future.delayed(Duration(seconds: 1), () async {
if (allNearestRestaurant.isEmpty) {
await Future.delayed(Duration(seconds: 2), () {
isLoading.value = false;
});
} else {
isLoading.value = false;
}
update();
});
}
Future<void> getVendorCategory() async {
await FireStoreUtils.getHomeVendorCategory().then((value) {
vendorCategoryModel.value = value;
});
await FireStoreUtils.getHomePageShowCategory().then((value) {
categoryWiseProductList.value = value;
});
await FireStoreUtils.getHomeTopBanner().then((value) {
bannerModel.value = value;
});
await FireStoreUtils.getHomeBottomBanner().then((value) {
bannerBottomModel.value = value;
});
await FireStoreUtils.getBrandList().then((value) {
brandList.value = value;
});
await getFavouriteRestaurant();
}
RxList<FavouriteModel> favouriteList = <FavouriteModel>[].obs;
Future<void> getFavouriteRestaurant() async {
if (Constant.userModel?.id != null) {
await FireStoreUtils.getFavouriteRestaurant().then((value) {
favouriteList.value = value;
});
}
}
}

View File

@@ -0,0 +1,42 @@
import 'dart:developer';
import 'package:get/get.dart';
import '../models/banner_model.dart';
import '../models/parcel_category.dart';
import '../service/fire_store_utils.dart';
class HomeParcelController extends GetxController {
RxBool isLoading = true.obs;
RxList<BannerModel> bannerTopHome = <BannerModel>[].obs;
RxList<ParcelCategory> parcelCategory = <ParcelCategory>[].obs;
@override
void onInit() {
super.onInit();
loadData();
}
void loadData() async {
try {
isLoading.value = true;
// Load banners
await FireStoreUtils.getHomeTopBanner().then((value) {
bannerTopHome.value = value;
log('Banners loaded: ${bannerTopHome.length}');
});
// Load parcel categories
await FireStoreUtils.getParcelServiceCategory().then((value) {
parcelCategory.value = value;
log('Parcel categories loaded: ${parcelCategory.length}');
});
} catch (e) {
bannerTopHome.clear();
parcelCategory.clear();
} finally {
isLoading.value = false;
}
}
}

View File

@@ -0,0 +1,256 @@
import 'dart:async';
import 'dart:convert';
import 'package:customer/constant/collection_name.dart';
import 'package:customer/constant/constant.dart';
import 'package:customer/models/order_model.dart';
import 'package:customer/models/user_model.dart';
import 'package:customer/service/fire_store_utils.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:latlong2/latlong.dart' as location;
import 'package:flutter_map/flutter_map.dart' as flutterMap;
import 'package:flutter_polyline_points/flutter_polyline_points.dart';
import 'package:http/http.dart' as http;
class LiveTrackingController extends GetxController {
GoogleMapController? mapController;
final flutterMap.MapController osmMapController = flutterMap.MapController();
Rx<OrderModel> orderModel = OrderModel().obs;
Rx<UserModel> driverUserModel = UserModel().obs;
RxBool isLoading = true.obs;
Rx<location.LatLng> source = location.LatLng(0, 0).obs;
Rx<location.LatLng> destination = location.LatLng(0, 0).obs;
Rx<location.LatLng> driverCurrent = location.LatLng(0, 0).obs;
RxList<location.LatLng> routePoints = <location.LatLng>[].obs;
RxMap<MarkerId, Marker> markers = <MarkerId, Marker>{}.obs;
RxMap<PolylineId, Polyline> polyLines = <PolylineId, Polyline>{}.obs;
RxList<flutterMap.Marker> osmMarkers = <flutterMap.Marker>[].obs;
BitmapDescriptor? pickupIcon;
BitmapDescriptor? dropoffIcon;
BitmapDescriptor? driverIcon;
PolylinePoints polylinePoints = PolylinePoints(apiKey: Constant.mapAPIKey);
StreamSubscription? orderSub;
StreamSubscription? driverSub;
@override
void onInit() {
super.onInit();
addMarkerIcons();
getArguments();
}
@override
void onClose() {
orderSub?.cancel();
driverSub?.cancel();
super.onClose();
}
Future<void> getArguments() async {
final args = Get.arguments;
if (args == null) return;
orderModel.value = args['orderModel'];
orderSub = FireStoreUtils.fireStore.collection(CollectionName.vendorOrders).doc(orderModel.value.id).snapshots().listen((orderSnap) {
if (orderSnap.data() == null) return;
orderModel.value = OrderModel.fromJson(orderSnap.data()!);
if (orderModel.value.driverID != null) {
driverSub?.cancel();
driverSub = FireStoreUtils.fireStore.collection(CollectionName.users).doc(orderModel.value.driverID).snapshots().listen((driverSnap) async {
if (driverSnap.data() == null) return;
driverUserModel.value = UserModel.fromJson(driverSnap.data()!);
await updateLiveTracking();
});
}
if (orderModel.value.status == Constant.orderCompleted) {
Get.back();
}
});
isLoading.value = false;
}
Future<void> updateLiveTracking() async {
driverCurrent.value = location.LatLng(driverUserModel.value.location?.latitude ?? 0.0, driverUserModel.value.location?.longitude ?? 0.0);
source.value = location.LatLng(orderModel.value.vendor?.latitude ?? 0.0, orderModel.value.vendor?.longitude ?? 0.0);
destination.value = location.LatLng(orderModel.value.address?.location?.latitude ?? 0.0, orderModel.value.address?.location?.longitude ?? 0.0);
if (orderModel.value.status == Constant.orderPlaced || orderModel.value.status == Constant.orderAccepted) {
await showDriverToRestaurantRoute();
} else if (orderModel.value.status == Constant.orderShipped || orderModel.value.status == Constant.orderInTransit) {
await showDriverToCustomerRoute();
}
}
Future<void> showDriverToRestaurantRoute() async {
clearOldData();
if (Constant.selectedMapType == 'osm') {
await fetchRoute(driverCurrent.value, source.value);
addOsmMarkers(showPickup: true, showDrop: false);
animateToOSMLocation(driverCurrent.value);
} else {
await getPolyline(
sourceLatitude: driverCurrent.value.latitude,
sourceLongitude: driverCurrent.value.longitude,
destinationLatitude: source.value.latitude,
destinationLongitude: source.value.longitude,
showPickup: true,
showDrop: false,
);
}
}
Future<void> showDriverToCustomerRoute() async {
clearOldData();
if (Constant.selectedMapType == 'osm') {
await fetchRoute(driverCurrent.value, destination.value);
addOsmMarkers(showPickup: false, showDrop: true);
animateToOSMLocation(driverCurrent.value);
} else {
await getPolyline(
sourceLatitude: driverCurrent.value.latitude,
sourceLongitude: driverCurrent.value.longitude,
destinationLatitude: destination.value.latitude,
destinationLongitude: destination.value.longitude,
showPickup: false,
showDrop: true,
);
}
}
void clearOldData() {
markers.clear();
polyLines.clear();
routePoints.clear();
}
Future<void> fetchRoute(location.LatLng source, location.LatLng destination) async {
final url = Uri.parse(
'https://router.project-osrm.org/route/v1/driving/${source.longitude},${source.latitude};${destination.longitude},${destination.latitude}?overview=full&geometries=geojson',
);
final response = await http.get(url);
if (response.statusCode == 200) {
final data = json.decode(response.body);
final coords = data['routes'][0]['geometry']['coordinates'];
routePoints.value = coords.map<location.LatLng>((c) => location.LatLng(c[1].toDouble(), c[0].toDouble())).toList();
}
}
void animateToOSMLocation(location.LatLng loc) {
osmMapController.move(loc, 15);
}
void addOsmMarkers({bool showPickup = false, bool showDrop = false}) {
final List<flutterMap.Marker> tempMarkers = [
// Driver Marker
flutterMap.Marker(point: driverCurrent.value, width: 40, height: 40, child: Image.asset('assets/images/food_delivery.png')),
];
if (showPickup) {
tempMarkers.add(flutterMap.Marker(point: source.value, width: 40, height: 40, child: Image.asset('assets/images/pickup.png')));
}
if (showDrop) {
tempMarkers.add(flutterMap.Marker(point: destination.value, width: 40, height: 40, child: Image.asset('assets/images/dropoff.png')));
}
osmMarkers.value = tempMarkers;
}
Future<void> getPolyline({
required double sourceLatitude,
required double sourceLongitude,
required double destinationLatitude,
required double destinationLongitude,
bool showPickup = false,
bool showDrop = false,
}) async {
List<LatLng> polylineCoordinates = [];
PolylineResult result = await polylinePoints.getRouteBetweenCoordinates(
request: PolylineRequest(origin: PointLatLng(sourceLatitude, sourceLongitude), destination: PointLatLng(destinationLatitude, destinationLongitude), mode: TravelMode.driving),
);
if (result.points.isNotEmpty) {
polylineCoordinates = result.points.map((e) => LatLng(e.latitude, e.longitude)).toList();
}
addGoogleMarkers(showPickup: showPickup, showDrop: showDrop);
_addPolyLine(polylineCoordinates);
}
void addGoogleMarkers({bool showPickup = false, bool showDrop = false}) {
markers.clear();
// Always show driver marker
if (driverUserModel.value.location != null && driverIcon != null) {
addMarker(
id: "Driver",
latitude: driverUserModel.value.location?.latitude ?? 0.0,
longitude: driverUserModel.value.location?.longitude ?? 0.0,
descriptor: driverIcon!,
rotation: (driverUserModel.value.rotation ?? 0).toDouble(),
);
}
if (showPickup && orderModel.value.vendor?.latitude != null && pickupIcon != null) {
addMarker(id: "Pickup", latitude: orderModel.value.vendor!.latitude ?? 0.0, longitude: orderModel.value.vendor!.longitude ?? 0.0, descriptor: pickupIcon!, rotation: 0.0);
} else if (showDrop && orderModel.value.address?.location?.latitude != null && dropoffIcon != null) {
addMarker(
id: "Drop",
latitude: orderModel.value.address!.location!.latitude ?? 0.0,
longitude: orderModel.value.address!.location!.longitude ?? 0.0,
descriptor: dropoffIcon!,
rotation: 0.0,
);
}
}
void addMarker({required String id, required double latitude, required double longitude, required BitmapDescriptor descriptor, required double rotation}) {
MarkerId markerId = MarkerId(id);
markers[markerId] = Marker(markerId: markerId, icon: descriptor, position: LatLng(latitude, longitude), rotation: rotation, anchor: const Offset(0.5, 0.5));
}
Future<void> addMarkerIcons() async {
if (Constant.selectedMapType == 'osm') return;
pickupIcon = BitmapDescriptor.fromBytes(await Constant().getBytesFromAsset('assets/images/pickup.png', 100));
dropoffIcon = BitmapDescriptor.fromBytes(await Constant().getBytesFromAsset('assets/images/dropoff.png', 100));
driverIcon = BitmapDescriptor.fromBytes(await Constant().getBytesFromAsset('assets/images/food_delivery.png', 100));
}
Future<void> _addPolyLine(List<LatLng> polylineCoordinates) async {
if (polylineCoordinates.isEmpty) return;
PolylineId id = const PolylineId("poly");
Polyline polyline = Polyline(polylineId: id, color: Colors.blue, width: 5, points: polylineCoordinates);
polyLines[id] = polyline;
await updateCameraBounds(polylineCoordinates);
}
Future<void> updateCameraBounds(List<LatLng> points) async {
if (mapController == null || points.isEmpty) return;
double minLat = points.map((e) => e.latitude).reduce((a, b) => a < b ? a : b);
double maxLat = points.map((e) => e.latitude).reduce((a, b) => a > b ? a : b);
double minLng = points.map((e) => e.longitude).reduce((a, b) => a < b ? a : b);
double maxLng = points.map((e) => e.longitude).reduce((a, b) => a > b ? a : b);
LatLngBounds bounds = LatLngBounds(southwest: LatLng(minLat, minLng), northeast: LatLng(maxLat, maxLng));
await mapController!.animateCamera(CameraUpdate.newLatLngBounds(bounds, 80));
}
}

View File

@@ -0,0 +1,273 @@
import 'dart:convert';
import 'package:customer/screen_ui/location_enable_screens/location_permission_screen.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:sign_in_with_apple/sign_in_with_apple.dart';
import '../constant/constant.dart';
import '../models/user_model.dart';
import '../screen_ui/auth_screens/login_screen.dart';
import '../screen_ui/auth_screens/sign_up_screen.dart';
import '../screen_ui/service_home_screen/service_list_screen.dart';
import '../service/fire_store_utils.dart';
import '../themes/show_toast_dialog.dart';
import '../utils/notification_service.dart';
import 'package:crypto/crypto.dart';
class LoginController extends GetxController {
Rx<TextEditingController> emailController = TextEditingController().obs;
Rx<TextEditingController> passwordController = TextEditingController().obs;
/// Focus nodes
final FocusNode emailFocusNode = FocusNode();
final FocusNode passwordFocusNode = FocusNode();
/// Loading indicator
final RxBool isLoading = false.obs;
RxBool passwordVisible = true.obs;
Future<void> loginWithEmail() async {
final email = emailController.value.text.trim();
final password = passwordController.value.text.trim();
if (email.isEmpty || !email.contains('@')) {
ShowToastDialog.showToast("Please enter a valid email address".tr);
return;
}
if (password.isEmpty) {
ShowToastDialog.showToast("Please enter your password".tr);
return;
}
try {
isLoading.value = true;
ShowToastDialog.showLoader("Logging in...".tr);
final credential = await FirebaseAuth.instance.signInWithEmailAndPassword(email: email, password: password);
final userModel = await FireStoreUtils.getUserProfile(credential.user!.uid);
if (userModel != null && userModel.role == Constant.userRoleCustomer) {
if (userModel.active == true) {
userModel.fcmToken = await NotificationService.getToken();
await FireStoreUtils.updateUser(userModel);
if (userModel.shippingAddress != null && userModel.shippingAddress!.isNotEmpty) {
final defaultAddress = userModel.shippingAddress!.firstWhere(
(e) => e.isDefault == true,
orElse: () => userModel.shippingAddress!.first,
);
Constant.selectedLocation = defaultAddress;
Get.offAll(() => const ServiceListScreen());
} else {
Get.offAll(() => const LocationPermissionScreen());
}
} else {
await FirebaseAuth.instance.signOut();
ShowToastDialog.showToast("This user is disabled. Please contact admin.".tr);
Get.offAll(() => const LoginScreen());
}
} else {
await FirebaseAuth.instance.signOut();
ShowToastDialog.showToast("This user does not exist in the customer app.".tr);
Get.offAll(() => const LoginScreen());
}
} on FirebaseAuthException catch (e) {
if (e.code == 'user-not-found') {
ShowToastDialog.showToast("No user found for that email.".tr);
} else if (e.code == 'wrong-password') {
ShowToastDialog.showToast("Wrong password provided.".tr);
} else if (e.code == 'invalid-email') {
ShowToastDialog.showToast("Invalid email.".tr);
} else {
ShowToastDialog.showToast(e.message?.tr ?? "Login failed. Please try again.".tr);
}
} finally {
isLoading.value = false;
ShowToastDialog.closeLoader();
}
}
Future<void> loginWithGoogle() async {
ShowToastDialog.showLoader("please wait...".tr);
await signInWithGoogle().then((value) async {
ShowToastDialog.closeLoader();
if (value != null) {
if (value.additionalUserInfo!.isNewUser) {
UserModel userModel = UserModel();
userModel.id = value.user!.uid;
userModel.email = value.user!.email;
userModel.firstName = value.user!.displayName?.split(' ').first;
userModel.lastName = value.user!.displayName?.split(' ').last;
userModel.provider = 'google';
ShowToastDialog.closeLoader();
Get.off(const SignUpScreen(), arguments: {"userModel": userModel, "type": "google"});
} else {
await FireStoreUtils.userExistOrNot(value.user!.uid).then((userExit) async {
ShowToastDialog.closeLoader();
if (userExit == true) {
UserModel? userModel = await FireStoreUtils.getUserProfile(value.user!.uid);
if (userModel != null && userModel.role == Constant.userRoleCustomer) {
if (userModel.active == true) {
userModel.fcmToken = await NotificationService.getToken();
await FireStoreUtils.updateUser(userModel);
if (userModel.shippingAddress != null && userModel.shippingAddress!.isNotEmpty) {
final defaultAddress = userModel.shippingAddress!.firstWhere(
(e) => e.isDefault == true,
orElse: () => userModel.shippingAddress!.first,
);
Constant.selectedLocation = defaultAddress;
Get.offAll(() => const ServiceListScreen());
} else {
Get.offAll(() => const LocationPermissionScreen());
}
} else {
await FirebaseAuth.instance.signOut();
ShowToastDialog.showToast("This user is disabled. Please contact admin.".tr);
Get.offAll(() => const LoginScreen());
}
} else {
await FirebaseAuth.instance.signOut();
ShowToastDialog.showToast("This user does not exist in the customer app.".tr);
Get.offAll(() => const LoginScreen());
}
} else {
UserModel userModel = UserModel();
userModel.id = value.user!.uid;
userModel.email = value.user!.email;
userModel.firstName = value.user!.displayName?.split(' ').first;
userModel.lastName = value.user!.displayName?.split(' ').last;
userModel.provider = 'google';
Get.off(const SignUpScreen(), arguments: {"userModel": userModel, "type": "google"});
}
});
}
}
});
}
Future<void> loginWithApple() async {
ShowToastDialog.showLoader("please wait...".tr);
await signInWithApple().then((value) async {
ShowToastDialog.closeLoader();
if (value != null) {
Map<String, dynamic> map = value;
AuthorizationCredentialAppleID appleCredential = map['appleCredential'];
UserCredential userCredential = map['userCredential'];
if (userCredential.additionalUserInfo!.isNewUser) {
// New user → go to sign-up
UserModel userModel = UserModel();
userModel.id = userCredential.user!.uid;
userModel.email = appleCredential.email;
userModel.firstName = appleCredential.givenName;
userModel.lastName = appleCredential.familyName;
userModel.provider = 'apple';
Get.off(const SignUpScreen(), arguments: {"userModel": userModel, "type": "apple"});
} else {
// Existing user
await FireStoreUtils.userExistOrNot(userCredential.user!.uid).then((userExit) async {
if (userExit == true) {
UserModel? userModel = await FireStoreUtils.getUserProfile(userCredential.user!.uid);
if (userModel != null && userModel.role == Constant.userRoleCustomer) {
if (userModel.active == true) {
userModel.fcmToken = await NotificationService.getToken();
await FireStoreUtils.updateUser(userModel);
if (userModel.shippingAddress != null && userModel.shippingAddress!.isNotEmpty) {
final defaultAddress = userModel.shippingAddress!.firstWhere(
(e) => e.isDefault == true,
orElse: () => userModel.shippingAddress!.first,
);
Constant.selectedLocation = defaultAddress;
Get.offAll(() => const ServiceListScreen());
} else {
Get.offAll(() => const LocationPermissionScreen());
}
} else {
await FirebaseAuth.instance.signOut();
ShowToastDialog.showToast("This user is disabled. Please contact admin.".tr);
Get.offAll(() => const LoginScreen());
}
} else {
await FirebaseAuth.instance.signOut();
ShowToastDialog.showToast("This user does not exist in the customer app.".tr);
Get.offAll(() => const LoginScreen());
}
} else {
// User not in DB → go to signup
UserModel userModel = UserModel();
userModel.id = userCredential.user!.uid;
userModel.email = appleCredential.email;
userModel.firstName = appleCredential.givenName;
userModel.lastName = appleCredential.familyName;
userModel.provider = 'apple';
Get.off(const SignUpScreen(), arguments: {"userModel": userModel, "type": "apple"});
}
});
}
}
});
}
Future<UserCredential?> signInWithGoogle() async {
try {
final GoogleSignIn googleSignIn = GoogleSignIn.instance;
await googleSignIn.initialize();
final GoogleSignInAccount googleUser = await googleSignIn.authenticate();
if (googleUser.id.isEmpty) return null;
final GoogleSignInAuthentication googleAuth = googleUser.authentication;
final credential = GoogleAuthProvider.credential(idToken: googleAuth.idToken);
final userCredential = await FirebaseAuth.instance.signInWithCredential(credential);
return userCredential;
} catch (e) {
print("Google Sign-In Error: $e");
return null;
}
}
String sha256ofString(String input) {
final bytes = utf8.encode(input);
final digest = sha256.convert(bytes);
return digest.toString();
}
Future<Map<String, dynamic>?> signInWithApple() async {
try {
final rawNonce = generateNonce();
final nonce = sha256ofString(rawNonce);
AuthorizationCredentialAppleID appleCredential = await SignInWithApple.getAppleIDCredential(
scopes: [AppleIDAuthorizationScopes.email, AppleIDAuthorizationScopes.fullName],
nonce: nonce,
);
final oauthCredential = OAuthProvider(
"apple.com",
).credential(idToken: appleCredential.identityToken, rawNonce: rawNonce, accessToken: appleCredential.authorizationCode);
UserCredential userCredential = await FirebaseAuth.instance.signInWithCredential(oauthCredential);
return {"appleCredential": appleCredential, "userCredential": userCredential};
} catch (e) {
debugPrint(e.toString());
}
return null;
}
}

View File

@@ -0,0 +1,110 @@
import 'dart:typed_data';
import 'package:customer/constant/constant.dart';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart' as flutterMap;
import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:latlong2/latlong.dart' as location;
import '../screen_ui/multi_vendor_service/restaurant_details_screen/restaurant_details_screen.dart';
import 'food_home_controller.dart';
class MapViewController extends GetxController {
GoogleMapController? mapController;
BitmapDescriptor? parkingMarker;
BitmapDescriptor? currentLocationMarker;
FoodHomeController homeController = Get.find<FoodHomeController>();
Image? departureOsmIcon; //OSM
RxList<flutterMap.Marker> osmMarker = <flutterMap.Marker>[].obs;
final flutterMap.MapController osmMapController = flutterMap.MapController();
@override
void onInit() {
// TODO: implement onInit
addMarkerSetup();
super.onInit();
}
Future<void> addMarkerSetup() async {
if (Constant.selectedMapType == "osm") {
departureOsmIcon = Image.asset(
"assets/images/map_selected.png",
width: 30,
height: 30,
); //OSM
for (var element in homeController.allNearestRestaurant) {
osmMarker.add(
flutterMap.Marker(
point: location.LatLng(
element.latitude ?? 0.0,
element.longitude ?? 0.0,
),
width: 40,
height: 40,
child: GestureDetector(
onTap: () {
Get.to(
RestaurantDetailsScreen(),
arguments: {"vendorModel": element},
);
},
child: departureOsmIcon,
),
),
);
}
} else {
final Uint8List parking = await Constant().getBytesFromAsset(
"assets/images/map_selected.png",
20,
);
parkingMarker = BitmapDescriptor.bytes(parking);
for (var element in homeController.allNearestRestaurant) {
addMarker(
latitude: element.latitude,
longitude: element.longitude,
id: element.id.toString(),
rotation: 0,
descriptor: parkingMarker!,
title: element.title.toString(),
);
}
}
}
RxMap<MarkerId, Marker> markers = <MarkerId, Marker>{}.obs;
void addMarker({
required double? latitude,
required double? longitude,
required String id,
required BitmapDescriptor descriptor,
required double? rotation,
required String title,
}) {
MarkerId markerId = MarkerId(id);
Marker marker = Marker(
markerId: markerId,
icon: descriptor,
infoWindow: InfoWindow(
title: title,
onTap: () {
int index = homeController.allNearestRestaurant.indexWhere(
(p0) => p0.id == id,
);
Get.to(
const RestaurantDetailsScreen(),
arguments: {
"vendorModel": homeController.allNearestRestaurant[index],
},
);
},
),
position: LatLng(latitude ?? 0.0, longitude ?? 0.0),
rotation: rotation ?? 0.0,
);
markers[markerId] = marker;
}
}

View File

@@ -0,0 +1,63 @@
import 'package:customer/themes/show_toast_dialog.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../constant/constant.dart';
import '../screen_ui/auth_screens/otp_verification_screen.dart';
class MobileLoginController extends GetxController {
final Rx<TextEditingController> mobileController = TextEditingController().obs;
final Rx<TextEditingController> countryCodeController = TextEditingController(text: Constant.defaultCountryCode).obs;
final FirebaseAuth _auth = FirebaseAuth.instance;
/// Send OTP to the entered phone number
Future<void> sendOtp() async {
final mobile = mobileController.value.text.trim();
final countryCode = countryCodeController.value.text.trim();
if (mobile.isEmpty || mobile.length != 10) {
ShowToastDialog.showToast("Please enter a valid 10-digit mobile number".tr);
return;
}
try {
ShowToastDialog.showLoader("Sending OTP...".tr);
await _auth.verifyPhoneNumber(
phoneNumber: '$countryCode$mobile',
verificationCompleted: (PhoneAuthCredential credential) {
// Optionally handle auto-verification
},
verificationFailed: (FirebaseAuthException e) {
ShowToastDialog.closeLoader();
if (e.code == 'invalid-phone-number') {
ShowToastDialog.showToast("Invalid phone number".tr);
} else {
ShowToastDialog.showToast(e.message ?? "OTP verification failed".tr);
}
},
codeSent: (String verificationId, int? resendToken) {
ShowToastDialog.closeLoader();
Get.to(() => const OtpVerificationScreen(), arguments: {'countryCode': countryCode, 'phoneNumber': mobile, 'verificationId': verificationId});
},
codeAutoRetrievalTimeout: (String verificationId) {
ShowToastDialog.closeLoader();
ShowToastDialog.showToast("OTP timed out. Please try again.".tr);
// Optional: Handle timeout
},
);
} catch (e) {
ShowToastDialog.closeLoader();
ShowToastDialog.showToast("Something went wrong. Please try again.".tr);
}
}
@override
void onClose() {
mobileController.value.dispose();
countryCodeController.value.dispose();
super.onClose();
}
}

View File

@@ -0,0 +1,73 @@
import 'package:get/get.dart';
import '../models/onprovider_order_model.dart';
import '../models/worker_model.dart';
import '../service/fire_store_utils.dart';
class MyBookingOnDemandController extends GetxController {
RxList<OnProviderOrderModel> orders = <OnProviderOrderModel>[].obs;
RxBool isLoading = true.obs;
RxString selectedTab = "Placed".obs;
RxMap<String, WorkerModel> workers = <String, WorkerModel>{}.obs;
RxList<String> tabTitles = ["Placed", "Completed", "Cancelled"].obs;
@override
void onInit() {
super.onInit();
listenOrders(); // Listen for real-time updates
}
void selectTab(String tab) {
selectedTab.value = tab;
}
void listenOrders() {
isLoading.value = true;
FireStoreUtils.getProviderOrdersStream().listen(
(updatedOrders) {
orders.value = updatedOrders;
// Fetch worker info if not already fetched
for (var order in updatedOrders) {
if (order.workerId != null && order.workerId!.isNotEmpty && !workers.containsKey(order.workerId!)) {
FireStoreUtils.getWorker(order.workerId!).then((worker) {
if (worker != null) workers[order.workerId!] = worker;
});
}
}
isLoading.value = false;
},
onError: (error) {
print("Error fetching orders stream: $error");
isLoading.value = false;
},
);
}
List<OnProviderOrderModel> get filteredParcelOrders => getOrdersForTab(selectedTab.value);
List<OnProviderOrderModel> getOrdersForTab(String tab) {
switch (tab) {
case "Placed":
return orders.where((order) => ["Order Placed", "Order Accepted", "Order Assigned", "Order Ongoing", "In Transit"].contains(order.status)).toList();
case "Completed":
return orders.where((order) => ["Order Completed"].contains(order.status)).toList();
case "Cancelled":
return orders.where((order) => ["Order Rejected", "Order Cancelled", "Driver Rejected"].contains(order.status)).toList();
default:
return [];
}
}
WorkerModel? getWorker(String? workerId) {
if (workerId == null || workerId.isEmpty) return null;
return workers[workerId];
}
}

View File

@@ -0,0 +1,904 @@
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'dart:math' as maths;
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:customer/constant/constant.dart';
import 'package:customer/models/cab_order_model.dart';
import 'package:customer/models/payment_model/cod_setting_model.dart';
import 'package:customer/models/payment_model/flutter_wave_model.dart';
import 'package:customer/models/payment_model/mercado_pago_model.dart';
import 'package:customer/models/payment_model/mid_trans.dart';
import 'package:customer/models/payment_model/orange_money.dart';
import 'package:customer/models/payment_model/pay_fast_model.dart';
import 'package:customer/models/payment_model/pay_stack_model.dart';
import 'package:customer/models/payment_model/paypal_model.dart';
import 'package:customer/models/payment_model/paytm_model.dart';
import 'package:customer/models/payment_model/razorpay_model.dart';
import 'package:customer/models/payment_model/stripe_model.dart';
import 'package:customer/models/payment_model/wallet_setting_model.dart';
import 'package:customer/models/payment_model/xendit.dart';
import 'package:customer/models/user_model.dart';
import 'package:customer/models/wallet_transaction_model.dart';
import 'package:customer/payment/MercadoPagoScreen.dart';
import 'package:customer/payment/PayFastScreen.dart';
import 'package:customer/payment/getPaytmTxtToken.dart';
import 'package:customer/payment/midtrans_screen.dart';
import 'package:customer/payment/orangePayScreen.dart';
import 'package:customer/payment/paystack/pay_stack_screen.dart';
import 'package:customer/payment/paystack/pay_stack_url_model.dart';
import 'package:customer/payment/paystack/paystack_url_genrater.dart';
import 'package:customer/payment/stripe_failed_model.dart';
import 'package:customer/payment/xenditModel.dart';
import 'package:customer/payment/xenditScreen.dart';
import 'package:customer/service/fire_store_utils.dart';
import 'package:customer/themes/show_toast_dialog.dart';
import 'package:customer/utils/preferences.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter_paypal/flutter_paypal.dart';
import 'package:flutter_stripe/flutter_stripe.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import 'package:intl/intl.dart';
import 'package:razorpay_flutter/razorpay_flutter.dart';
import 'package:uuid/uuid.dart';
import '../screen_ui/multi_vendor_service/wallet_screen/wallet_screen.dart';
import '../themes/app_them_data.dart';
class MyCabBookingController extends GetxController {
RxBool isLoading = true.obs;
RxString selectedTab = "New".obs;
RxList<CabOrderModel> cabOrder = <CabOrderModel>[].obs;
final List<String> tabKeys = ["New", "On Going", "Completed", "Cancelled"];
Rx<UserModel> userModel = UserModel().obs;
@override
Future<void> onInit() async {
super.onInit();
fetchParcelData();
}
Future<void> selectTab(String tab) async {
selectedTab.value = tab;
}
Future<void> fetchParcelData() async {
isLoading.value = true;
if (FirebaseAuth.instance.currentUser != null) {
await FireStoreUtils.getUserProfile(FireStoreUtils.getCurrentUid()).then((user) {
if (user != null) {
userModel.value = user;
}
});
FireStoreUtils.getCabDriverOrders().listen((orders) {
cabOrder.value = orders;
});
await getPaymentSettings();
}
isLoading.value = false;
}
List<CabOrderModel> get filteredParcelOrders => getOrdersForTab(selectedTab.value);
List<CabOrderModel> getOrdersForTab(String tab) {
switch (tab) {
case "New":
return cabOrder.where((order) => ["Order Placed", "Driver Pending"].contains(order.status)).toList();
case "On Going":
return cabOrder.where((order) => ["Driver Accepted", "Order Shipped", "In Transit"].contains(order.status)).toList();
case "Completed":
return cabOrder.where((order) => ["Order Completed"].contains(order.status)).toList();
case "Cancelled":
return cabOrder.where((order) => ["Order Rejected", "Order Cancelled", "Driver Rejected"].contains(order.status)).toList();
default:
return [];
}
}
/// Get localized title for UI
String getLocalizedTabTitle(String tabKey) {
switch (tabKey) {
case "New":
return "New".tr;
case "On Going":
return "On Going".tr;
case "Completed":
return "Completed".tr;
case "Cancelled":
return "Cancelled".tr;
default:
return tabKey;
}
}
String formatDate(Timestamp timestamp) {
final dateTime = timestamp.toDate();
return DateFormat("dd MMM yyyy, hh:mm a").format(dateTime);
}
Rx<WalletSettingModel> walletSettingModel = WalletSettingModel().obs;
Rx<CodSettingModel> cashOnDeliverySettingModel = CodSettingModel().obs;
Rx<PayFastModel> payFastModel = PayFastModel().obs;
Rx<MercadoPagoModel> mercadoPagoModel = MercadoPagoModel().obs;
Rx<PayPalModel> payPalModel = PayPalModel().obs;
Rx<StripeModel> stripeModel = StripeModel().obs;
Rx<FlutterWaveModel> flutterWaveModel = FlutterWaveModel().obs;
Rx<PayStackModel> payStackModel = PayStackModel().obs;
Rx<PaytmModel> paytmModel = PaytmModel().obs;
Rx<RazorPayModel> razorPayModel = RazorPayModel().obs;
Rx<MidTrans> midTransModel = MidTrans().obs;
Rx<OrangeMoney> orangeMoneyModel = OrangeMoney().obs;
Rx<Xendit> xenditModel = Xendit().obs;
final RxString selectedPaymentMethod = ''.obs;
Rx<CabOrderModel> currentOrder = CabOrderModel().obs;
Rx<CabOrderModel> selectedOrder = CabOrderModel().obs;
RxDouble totalAmount = 0.0.obs;
RxDouble subTotal = 0.0.obs;
RxDouble discount = 0.0.obs;
RxDouble taxAmount = 0.0.obs;
void calculateTotalAmount(CabOrderModel order) {
subTotal.value = 0.0;
discount.value = 0.0;
taxAmount.value = 0.0;
totalAmount.value = 0.0;
selectedOrder.value = order;
try {
subTotal.value = double.tryParse(selectedOrder.value.subTotal?.toString() ?? "0") ?? 0.0;
discount.value = double.tryParse(selectedOrder.value.discount?.toString() ?? "0") ?? 0.0;
taxAmount.value = 0.0;
subTotal.value = subTotal.value;
if (selectedOrder.value.taxSetting != null) {
for (var element in selectedOrder.value.taxSetting!) {
taxAmount.value += Constant.calculateTax(amount: (subTotal.value - discount.value).toString(), taxModel: element);
}
}
totalAmount.value = (subTotal.value - discount.value) + taxAmount.value;
} catch (e) {
ShowToastDialog.showToast("Failed to calculate total: $e");
}
}
Future<void> completeOrder() async {
if (selectedPaymentMethod.value == PaymentGateway.cod.name) {
selectedOrder.value.paymentMethod = selectedPaymentMethod.value;
await FireStoreUtils.cabOrderPlace(selectedOrder.value).then((value) {
ShowToastDialog.showToast("Payment method changed".tr);
Get.back();
});
} else {
selectedOrder.value.paymentMethod = selectedPaymentMethod.value;
userModel.value.inProgressOrderID ??= [];
userModel.value.inProgressOrderID!.clear();
await FireStoreUtils.updateUser(userModel.value);
if (selectedPaymentMethod.value == PaymentGateway.wallet.name) {
WalletTransactionModel transactionModel = WalletTransactionModel(
id: Constant.getUuid(),
amount: double.parse(totalAmount.toString()),
date: Timestamp.now(),
paymentMethod: PaymentGateway.wallet.name,
transactionUser: "customer",
userId: FireStoreUtils.getCurrentUid(),
isTopup: false,
orderId: selectedOrder.value.id,
note: "Cab Amount debited".tr,
paymentStatus: "success".tr,
serviceType: Constant.parcelServiceType,
);
await FireStoreUtils.setWalletTransaction(transactionModel).then((value) async {
await FireStoreUtils.updateUserWallet(amount: "-${totalAmount.value.toString()}", userId: FireStoreUtils.getCurrentUid());
});
}
selectedOrder.value.paymentStatus = true;
await FireStoreUtils.cabOrderPlace(selectedOrder.value).then((value) {
ShowToastDialog.showToast("Payment successfully".tr);
Get.back();
});
}
}
Future<void> getPaymentSettings() async {
await FireStoreUtils.getPaymentSettingsData().then((value) {
stripeModel.value = StripeModel.fromJson(jsonDecode(Preferences.getString(Preferences.stripeSettings)));
payPalModel.value = PayPalModel.fromJson(jsonDecode(Preferences.getString(Preferences.paypalSettings)));
payStackModel.value = PayStackModel.fromJson(jsonDecode(Preferences.getString(Preferences.payStack)));
mercadoPagoModel.value = MercadoPagoModel.fromJson(jsonDecode(Preferences.getString(Preferences.mercadoPago)));
flutterWaveModel.value = FlutterWaveModel.fromJson(jsonDecode(Preferences.getString(Preferences.flutterWave)));
paytmModel.value = PaytmModel.fromJson(jsonDecode(Preferences.getString(Preferences.paytmSettings)));
payFastModel.value = PayFastModel.fromJson(jsonDecode(Preferences.getString(Preferences.payFastSettings)));
razorPayModel.value = RazorPayModel.fromJson(jsonDecode(Preferences.getString(Preferences.razorpaySettings)));
midTransModel.value = MidTrans.fromJson(jsonDecode(Preferences.getString(Preferences.midTransSettings)));
orangeMoneyModel.value = OrangeMoney.fromJson(jsonDecode(Preferences.getString(Preferences.orangeMoneySettings)));
xenditModel.value = Xendit.fromJson(jsonDecode(Preferences.getString(Preferences.xenditSettings)));
walletSettingModel.value = WalletSettingModel.fromJson(jsonDecode(Preferences.getString(Preferences.walletSettings)));
cashOnDeliverySettingModel.value = CodSettingModel.fromJson(jsonDecode(Preferences.getString(Preferences.codSettings)));
if (walletSettingModel.value.isEnabled == true) {
selectedPaymentMethod.value = PaymentGateway.wallet.name;
} else if (cashOnDeliverySettingModel.value.isEnabled == true) {
selectedPaymentMethod.value = PaymentGateway.cod.name;
} else if (stripeModel.value.isEnabled == true) {
selectedPaymentMethod.value = PaymentGateway.stripe.name;
} else if (payPalModel.value.isEnabled == true) {
selectedPaymentMethod.value = PaymentGateway.paypal.name;
} else if (payStackModel.value.isEnable == true) {
selectedPaymentMethod.value = PaymentGateway.payStack.name;
} else if (mercadoPagoModel.value.isEnabled == true) {
selectedPaymentMethod.value = PaymentGateway.mercadoPago.name;
} else if (flutterWaveModel.value.isEnable == true) {
selectedPaymentMethod.value = PaymentGateway.flutterWave.name;
} else if (payFastModel.value.isEnable == true) {
selectedPaymentMethod.value = PaymentGateway.payFast.name;
} else if (razorPayModel.value.isEnabled == true) {
selectedPaymentMethod.value = PaymentGateway.razorpay.name;
} else if (midTransModel.value.enable == true) {
selectedPaymentMethod.value = PaymentGateway.midTrans.name;
} else if (orangeMoneyModel.value.enable == true) {
selectedPaymentMethod.value = PaymentGateway.orangeMoney.name;
} else if (xenditModel.value.enable == true) {
selectedPaymentMethod.value = PaymentGateway.xendit.name;
}
Stripe.publishableKey = stripeModel.value.clientpublishableKey.toString();
Stripe.merchantIdentifier = 'eMart Customer';
Stripe.instance.applySettings();
setRef();
razorPay.on(Razorpay.EVENT_PAYMENT_SUCCESS, handlePaymentSuccess);
razorPay.on(Razorpay.EVENT_EXTERNAL_WALLET, handleExternalWaller);
razorPay.on(Razorpay.EVENT_PAYMENT_ERROR, handlePaymentError);
});
}
// Strip
Future<void> stripeMakePayment({required String amount}) async {
log(double.parse(amount).toStringAsFixed(0));
try {
Map<String, dynamic>? paymentIntentData = await createStripeIntent(amount: amount);
log("stripe Responce====>$paymentIntentData");
if (paymentIntentData!.containsKey("error")) {
Get.back();
ShowToastDialog.showToast("Something went wrong, please contact admin.".tr);
} else {
await Stripe.instance.initPaymentSheet(
paymentSheetParameters: SetupPaymentSheetParameters(
paymentIntentClientSecret: paymentIntentData['client_secret'],
allowsDelayedPaymentMethods: false,
googlePay: const PaymentSheetGooglePay(merchantCountryCode: 'US', testEnv: true, currencyCode: "USD"),
customFlow: true,
style: ThemeMode.system,
appearance: PaymentSheetAppearance(colors: PaymentSheetAppearanceColors(primary: AppThemeData.primary300)),
merchantDisplayName: 'GoRide',
),
);
displayStripePaymentSheet(amount: amount);
}
} catch (e, s) {
log("$e \n$s");
ShowToastDialog.showToast("exception:$e \n$s");
}
}
Future<void> displayStripePaymentSheet({required String amount}) async {
try {
await Stripe.instance.presentPaymentSheet().then((value) {
ShowToastDialog.showToast("Payment successfully".tr);
completeOrder();
});
} on StripeException catch (e) {
var lo1 = jsonEncode(e);
var lo2 = jsonDecode(lo1);
StripePayFailedModel lom = StripePayFailedModel.fromJson(lo2);
ShowToastDialog.showToast(lom.error.message);
} catch (e) {
ShowToastDialog.showToast(e.toString());
}
}
Future createStripeIntent({required String amount}) async {
try {
Map<String, dynamic> body = {
'amount': ((double.parse(amount) * 100).round()).toString(),
'currency': "USD",
'payment_method_types[]': 'card',
"description": "Strip Payment",
"shipping[name]": Constant.userModel!.fullName(),
"shipping[address][line1]": "510 Townsend St",
"shipping[address][postal_code]": "98140",
"shipping[address][city]": "San Francisco",
"shipping[address][state]": "CA",
"shipping[address][country]": "US",
};
var stripeSecret = stripeModel.value.stripeSecret;
var response = await http.post(
Uri.parse('https://api.stripe.com/v1/payment_intents'),
body: body,
headers: {'Authorization': 'Bearer $stripeSecret', 'Content-Type': 'application/x-www-form-urlencoded'},
);
return jsonDecode(response.body);
} catch (e) {
log(e.toString());
}
}
//mercadoo
Future<Null> mercadoPagoMakePayment({required BuildContext context, required String amount}) async {
final headers = {'Authorization': 'Bearer ${mercadoPagoModel.value.accessToken}', 'Content-Type': 'application/json'};
final body = jsonEncode({
"items": [
{
"title": "Test",
"description": "Test Payment",
"quantity": 1,
"currency_id": "BRL", // or your preferred currency
"unit_price": double.parse(amount),
},
],
"payer": {"email": Constant.userModel!.email},
"back_urls": {
"failure": "${Constant.globalUrl}payment/failure",
"pending": "${Constant.globalUrl}payment/pending",
"success": "${Constant.globalUrl}payment/success",
},
"auto_return": "approved",
// Automatically return after payment is approved
});
final response = await http.post(Uri.parse("https://api.mercadopago.com/checkout/preferences"), headers: headers, body: body);
if (response.statusCode == 200 || response.statusCode == 201) {
final data = jsonDecode(response.body);
Get.to(MercadoPagoScreen(initialURl: data['init_point']))!.then((value) {
if (value) {
ShowToastDialog.showToast("Payment Successful!!".tr);
completeOrder();
} else {
ShowToastDialog.showToast("Payment UnSuccessful!!".tr);
}
});
} else {
print('Error creating preference: ${response.body}');
return null;
}
}
//Paypal
void paypalPaymentSheet(String amount, context) {
Navigator.of(context).push(
MaterialPageRoute(
builder:
(BuildContext context) => UsePaypal(
sandboxMode: payPalModel.value.isLive == true ? false : true,
clientId: payPalModel.value.paypalClient ?? '',
secretKey: payPalModel.value.paypalSecret ?? '',
returnURL: "com.parkme://paypalpay",
cancelURL: "com.parkme://paypalpay",
transactions: [
{
"amount": {
"total": amount,
"currency": "USD",
"details": {"subtotal": amount},
},
},
],
note: "Contact us for any questions on your order.",
onSuccess: (Map params) async {
completeOrder();
ShowToastDialog.showToast("Payment Successful!!".tr);
},
onError: (error) {
Get.back();
ShowToastDialog.showToast("Payment UnSuccessful!!".tr);
},
onCancel: (params) {
Get.back();
ShowToastDialog.showToast("Payment UnSuccessful!!".tr);
},
),
),
);
}
///PayStack Payment Method
Future<void> payStackPayment(String totalAmount) async {
await PayStackURLGen.payStackURLGen(
amount: (double.parse(totalAmount) * 100).toString(),
currency: "ZAR",
secretKey: payStackModel.value.secretKey.toString(),
userModel: Constant.userModel!,
).then((value) async {
if (value != null) {
PayStackUrlModel payStackModel0 = value;
Get.to(
PayStackScreen(
secretKey: payStackModel.value.secretKey.toString(),
callBackUrl: payStackModel.value.callbackURL.toString(),
initialURl: payStackModel0.data.authorizationUrl,
amount: totalAmount,
reference: payStackModel0.data.reference,
),
)!.then((value) {
if (value) {
ShowToastDialog.showToast("Payment Successful!!".tr);
completeOrder();
} else {
ShowToastDialog.showToast("Payment UnSuccessful!!".tr);
}
});
} else {
ShowToastDialog.showToast("Something went wrong, please contact admin.".tr);
}
});
}
//flutter wave Payment Method
Future<Null> flutterWaveInitiatePayment({required BuildContext context, required String amount}) async {
final url = Uri.parse('https://api.flutterwave.com/v3/payments');
final headers = {'Authorization': 'Bearer ${flutterWaveModel.value.secretKey}', 'Content-Type': 'application/json'};
final body = jsonEncode({
"tx_ref": _ref,
"amount": amount,
"currency": "NGN",
"redirect_url": "${Constant.globalUrl}payment/success",
"payment_options": "ussd, card, barter, payattitude",
"customer": {
"email": Constant.userModel!.email.toString(),
"phonenumber": Constant.userModel!.phoneNumber, // Add a real phone number
"name": Constant.userModel!.fullName(), // Add a real customer name
},
"customizations": {"title": "Payment for Services", "description": "Payment for XYZ services"},
});
final response = await http.post(url, headers: headers, body: body);
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
Get.to(MercadoPagoScreen(initialURl: data['data']['link']))!.then((value) {
if (value) {
ShowToastDialog.showToast("Payment Successful!!".tr);
completeOrder();
} else {
ShowToastDialog.showToast("Payment UnSuccessful!!".tr);
}
});
} else {
print('Payment initialization failed: ${response.body}');
return null;
}
}
String? _ref;
void setRef() {
maths.Random numRef = maths.Random();
int year = DateTime.now().year;
int refNumber = numRef.nextInt(20000);
if (Platform.isAndroid) {
_ref = "AndroidRef$year$refNumber";
} else if (Platform.isIOS) {
_ref = "IOSRef$year$refNumber";
}
}
// payFast
void payFastPayment({required BuildContext context, required String amount}) {
PayStackURLGen.getPayHTML(payFastSettingData: payFastModel.value, amount: amount.toString(), userModel: Constant.userModel!).then((
String? value,
) async {
bool isDone = await Get.to(PayFastScreen(htmlData: value!, payFastSettingData: payFastModel.value));
if (isDone) {
Get.back();
ShowToastDialog.showToast("Payment successfully".tr);
completeOrder();
} else {
Get.back();
ShowToastDialog.showToast("Payment Failed".tr);
}
});
}
///Paytm payment function
Future<void> getPaytmCheckSum(context, {required double amount}) async {
// final String orderId = DateTime.now().millisecondsSinceEpoch.toString();
// String getChecksum = "${Constant.globalUrl}payments/getpaytmchecksum";
//
// final response = await http.post(
// Uri.parse(getChecksum),
// headers: {},
// body: {"mid": paytmModel.value.paytmMID.toString(), "order_id": orderId, "key_secret": paytmModel.value.pAYTMMERCHANTKEY.toString()},
// );
//
// final data = jsonDecode(response.body);
// await verifyCheckSum(checkSum: data["code"], amount: amount, orderId: orderId).then((value) {
// initiatePayment(amount: amount, orderId: orderId).then((value) {
// String callback = "";
// if (paytmModel.value.isSandboxEnabled == true) {
// callback = "${callback}https://securegw-stage.paytm.in/theia/paytmCallback?ORDER_ID=$orderId";
// } else {
// callback = "${callback}https://securegw.paytm.in/theia/paytmCallback?ORDER_ID=$orderId";
// }
//
// GetPaymentTxtTokenModel result = value;
// startTransaction(context, txnTokenBy: result.body.txnToken, orderId: orderId, amount: amount, callBackURL: callback, isStaging: paytmModel.value.isSandboxEnabled);
// });
// });
}
Future<void> startTransaction(
context, {
required String txnTokenBy,
required orderId,
required double amount,
required callBackURL,
required isStaging,
}) async {
// try {
// var response = AllInOneSdk.startTransaction(
// paytmModel.value.paytmMID.toString(),
// orderId,
// amount.toString(),
// txnTokenBy,
// callBackURL,
// isStaging,
// true,
// true,
// );
//
// response.then((value) {
// if (value!["RESPMSG"] == "Txn Success") {
// print("txt done!!");
// ShowToastDialog.showToast("Payment Successful!!");
// completeOrder();
// }
// }).catchError((onError) {
// if (onError is PlatformException) {
// Get.back();
//
// ShowToastDialog.showToast(onError.message.toString());
// } else {
// log("======>>2");
// Get.back();
// ShowToastDialog.showToast(onError.message.toString());
// }
// });
// } catch (err) {
// Get.back();
// ShowToastDialog.showToast(err.toString());
// }
}
Future verifyCheckSum({required String checkSum, required double amount, required orderId}) async {
String getChecksum = "${Constant.globalUrl}payments/validatechecksum";
final response = await http.post(
Uri.parse(getChecksum),
headers: {},
body: {
"mid": paytmModel.value.paytmMID.toString(),
"order_id": orderId,
"key_secret": paytmModel.value.pAYTMMERCHANTKEY.toString(),
"checksum_value": checkSum,
},
);
final data = jsonDecode(response.body);
return data['status'];
}
Future<GetPaymentTxtTokenModel> initiatePayment({required double amount, required orderId}) async {
String initiateURL = "${Constant.globalUrl}payments/initiatepaytmpayment";
String callback = "";
if (paytmModel.value.isSandboxEnabled == true) {
callback = "${callback}https://securegw-stage.paytm.in/theia/paytmCallback?ORDER_ID=$orderId";
} else {
callback = "${callback}https://securegw.paytm.in/theia/paytmCallback?ORDER_ID=$orderId";
}
final response = await http.post(
Uri.parse(initiateURL),
headers: {},
body: {
"mid": paytmModel.value.paytmMID,
"order_id": orderId,
"key_secret": paytmModel.value.pAYTMMERCHANTKEY,
"amount": amount.toString(),
"currency": "INR",
"callback_url": callback,
"custId": FireStoreUtils.getCurrentUid(),
"issandbox": paytmModel.value.isSandboxEnabled == true ? "1" : "2",
},
);
log(response.body);
final data = jsonDecode(response.body);
if (data["body"]["txnToken"] == null || data["body"]["txnToken"].toString().isEmpty) {
Get.back();
ShowToastDialog.showToast("something went wrong, please contact admin.".tr);
}
return GetPaymentTxtTokenModel.fromJson(data);
}
///RazorPay payment function
final Razorpay razorPay = Razorpay();
void openCheckout({required amount, required orderId}) async {
var options = {
'key': razorPayModel.value.razorpayKey,
'amount': amount * 100,
'name': 'GoRide',
'order_id': orderId,
"currency": "INR",
'description': 'wallet Topup',
'retry': {'enabled': true, 'max_count': 1},
'send_sms_hash': true,
'prefill': {'contact': Constant.userModel!.phoneNumber, 'email': Constant.userModel!.email},
'external': {
'wallets': ['paytm'],
},
};
try {
razorPay.open(options);
} catch (e) {
debugPrint('Error: $e');
}
}
void handlePaymentSuccess(PaymentSuccessResponse response) {
Get.back();
ShowToastDialog.showToast("Payment Successful!!".tr);
completeOrder();
}
void handleExternalWaller(ExternalWalletResponse response) {
Get.back();
ShowToastDialog.showToast("Payment Processing!! via".tr);
}
void handlePaymentError(PaymentFailureResponse response) {
Get.back();
ShowToastDialog.showToast("Payment Failed!!".tr);
}
bool isCurrentDateInRange(DateTime startDate, DateTime endDate) {
final currentDate = DateTime.now();
return currentDate.isAfter(startDate) && currentDate.isBefore(endDate);
}
//Midtrans payment
Future<void> midtransMakePayment({required String amount, required BuildContext context}) async {
await createPaymentLink(amount: amount).then((url) {
ShowToastDialog.closeLoader();
if (url != '') {
Get.to(() => MidtransScreen(initialURl: url))!.then((value) {
if (value == true) {
ShowToastDialog.showToast("Payment Successful!!".tr);
completeOrder();
} else {
ShowToastDialog.showToast("Payment Unsuccessful!!".tr);
}
});
}
});
}
Future<String> createPaymentLink({required var amount}) async {
var ordersId = const Uuid().v1();
final url = Uri.parse(
midTransModel.value.isSandbox! ? 'https://api.sandbox.midtrans.com/v1/payment-links' : 'https://api.midtrans.com/v1/payment-links',
);
final response = await http.post(
url,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': generateBasicAuthHeader(midTransModel.value.serverKey!),
},
body: jsonEncode({
'transaction_details': {'order_id': ordersId, 'gross_amount': double.parse(amount.toString()).toInt()},
'usage_limit': 2,
"callbacks": {"finish": "https://www.google.com?merchant_order_id=$ordersId"},
}),
);
if (response.statusCode == 200 || response.statusCode == 201) {
final responseData = jsonDecode(response.body);
return responseData['payment_url'];
} else {
ShowToastDialog.showToast("something went wrong, please contact admin.".tr);
return '';
}
}
String generateBasicAuthHeader(String apiKey) {
String credentials = '$apiKey:';
String base64Encoded = base64Encode(utf8.encode(credentials));
return 'Basic $base64Encoded';
}
//Orangepay payment
static String accessToken = '';
static String payToken = '';
static String orderId = '';
static String amount = '';
Future<void> orangeMakePayment({required String amount, required BuildContext context}) async {
reset();
var id = const Uuid().v4();
var paymentURL = await fetchToken(context: context, orderId: id, amount: amount, currency: 'USD');
ShowToastDialog.closeLoader();
if (paymentURL.toString() != '') {
Get.to(
() => OrangeMoneyScreen(
initialURl: paymentURL,
accessToken: accessToken,
amount: amount,
orangePay: orangeMoneyModel.value,
orderId: orderId,
payToken: payToken,
),
)!.then((value) {
if (value == true) {
ShowToastDialog.showToast("Payment Successful!!".tr);
completeOrder();
();
}
});
} else {
ShowToastDialog.showToast("Payment Unsuccessful!!".tr);
}
}
Future fetchToken({required String orderId, required String currency, required BuildContext context, required String amount}) async {
String apiUrl = 'https://api.orange.com/oauth/v3/token';
Map<String, String> requestBody = {'grant_type': 'client_credentials'};
var response = await http.post(
Uri.parse(apiUrl),
headers: <String, String>{
'Authorization': "Basic ${orangeMoneyModel.value.auth!}",
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json',
},
body: requestBody,
);
// Handle the response
if (response.statusCode == 200) {
Map<String, dynamic> responseData = jsonDecode(response.body);
accessToken = responseData['access_token'];
// ignore: use_build_context_synchronously
return await webpayment(context: context, amountData: amount, currency: currency, orderIdData: orderId);
} else {
ShowToastDialog.showToast("Something went wrong, please contact admin.".tr);
return '';
}
}
Future webpayment({
required String orderIdData,
required BuildContext context,
required String currency,
required String amountData,
}) async {
orderId = orderIdData;
amount = amountData;
String apiUrl =
orangeMoneyModel.value.isSandbox! == true
? 'https://api.orange.com/orange-money-webpay/dev/v1/webpayment'
: 'https://api.orange.com/orange-money-webpay/cm/v1/webpayment';
Map<String, String> requestBody = {
"merchant_key": orangeMoneyModel.value.merchantKey ?? '',
"currency": orangeMoneyModel.value.isSandbox == true ? "OUV" : currency,
"order_id": orderId,
"amount": amount,
"reference": 'Y-Note Test',
"lang": "en",
"return_url": orangeMoneyModel.value.returnUrl!.toString(),
"cancel_url": orangeMoneyModel.value.cancelUrl!.toString(),
"notif_url": orangeMoneyModel.value.notifUrl!.toString(),
};
var response = await http.post(
Uri.parse(apiUrl),
headers: <String, String>{'Authorization': 'Bearer $accessToken', 'Content-Type': 'application/json', 'Accept': 'application/json'},
body: json.encode(requestBody),
);
// Handle the response
if (response.statusCode == 201) {
Map<String, dynamic> responseData = jsonDecode(response.body);
if (responseData['message'] == 'OK') {
payToken = responseData['pay_token'];
return responseData['payment_url'];
} else {
return '';
}
} else {
ShowToastDialog.showToast("Something went wrong, please contact admin.".tr);
return '';
}
}
static void reset() {
accessToken = '';
payToken = '';
orderId = '';
amount = '';
}
//XenditPayment
Future<void> xenditPayment(context, amount) async {
await createXenditInvoice(amount: amount).then((model) {
ShowToastDialog.closeLoader();
if (model.id != null) {
Get.to(
() => XenditScreen(initialURl: model.invoiceUrl ?? '', transId: model.id ?? '', apiKey: xenditModel.value.apiKey!.toString()),
)!.then((value) {
if (value == true) {
ShowToastDialog.showToast("Payment Successful!!".tr);
completeOrder();
();
} else {
ShowToastDialog.showToast("Payment Unsuccessful!!".tr);
}
});
}
});
}
Future<XenditModel> createXenditInvoice({required var amount}) async {
const url = 'https://api.xendit.co/v2/invoices';
var headers = {
'Content-Type': 'application/json',
'Authorization': generateBasicAuthHeader(xenditModel.value.apiKey!.toString()),
// 'Cookie': '__cf_bm=yERkrx3xDITyFGiou0bbKY1bi7xEwovHNwxV1vCNbVc-1724155511-1.0.1.1-jekyYQmPCwY6vIJ524K0V6_CEw6O.dAwOmQnHtwmaXO_MfTrdnmZMka0KZvjukQgXu5B.K_6FJm47SGOPeWviQ',
};
final body = jsonEncode({
'external_id': const Uuid().v1(),
'amount': amount,
'payer_email': 'customer@domain.com',
'description': 'Test - VA Successful invoice payment',
'currency': 'IDR', //IDR, PHP, THB, VND, MYR
});
try {
final response = await http.post(Uri.parse(url), headers: headers, body: body);
if (response.statusCode == 200 || response.statusCode == 201) {
XenditModel model = XenditModel.fromJson(jsonDecode(response.body));
return model;
} else {
return XenditModel();
}
} catch (e) {
return XenditModel();
}
}
}

View File

@@ -0,0 +1,51 @@
import 'dart:developer';
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/theme_controller.dart';
import '../service/fire_store_utils.dart';
import 'package:customer/utils/preferences.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
class MyProfileController extends GetxController {
RxBool isLoading = true.obs;
RxString isDarkMode = "Light".obs; // For UI text
RxBool isDarkModeSwitch = false.obs; // For switch widget
@override
void onInit() {
getTheme();
super.onInit();
}
void getTheme() {
bool isDark = Preferences.getBoolean(Preferences.themKey);
isDarkMode.value = isDark ? "Dark" : "Light";
isDarkModeSwitch.value = isDark;
isLoading.value = false;
}
void toggleDarkMode(bool value) {
isDarkModeSwitch.value = value;
isDarkMode.value = value ? "Dark" : "Light";
Preferences.setBoolean(Preferences.themKey, value);
// Update ThemeController for instant app theme change
if (Get.isRegistered<ThemeController>()) {
final themeController = Get.find<ThemeController>();
themeController.isDark.value = value;
}
}
// Delete user API
Future<bool> deleteUserFromServer() async {
var url = '${Constant.websiteUrl}/api/delete-user';
try {
var response = await http.post(Uri.parse(url), body: {'uuid': FireStoreUtils.getCurrentUid()});
log("deleteUserFromServer :: ${response.body}");
return response.statusCode == 200;
} catch (e) {
return false;
}
}
}

View File

@@ -0,0 +1,228 @@
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:customer/constant/constant.dart';
import 'package:customer/models/wallet_transaction_model.dart';
import 'package:customer/screen_ui/multi_vendor_service/wallet_screen/wallet_screen.dart';
import 'package:customer/themes/show_toast_dialog.dart';
import 'package:get/get.dart';
import '../models/rental_order_model.dart';
import '../models/tax_model.dart';
import '../service/fire_store_utils.dart';
class MyRentalBookingController extends GetxController {
RxBool isLoading = true.obs;
RxList<RentalOrderModel> rentalOrders = <RentalOrderModel>[].obs;
RxString selectedTab = "New".obs;
RxList<String> tabTitles = ["New", "On Going", "Completed", "Cancelled"].obs;
StreamSubscription<List<RentalOrderModel>>? _rentalSubscription;
final RxString selectedPaymentMethod = ''.obs;
@override
void onInit() {
super.onInit();
listenRentalOrders();
}
void selectTab(String tab) {
selectedTab.value = tab;
}
/// Start listening to rental orders live. Cancel previous subscription first.
void listenRentalOrders() {
isLoading.value = true;
_rentalSubscription?.cancel();
if (Constant.userModel != null) {
_rentalSubscription = FireStoreUtils.getRentalOrders().listen(
(orders) {
rentalOrders.assignAll(orders);
},
onError: (err) {
isLoading.value = false;
print("Error fetching rental orders: $err");
},
);
}
isLoading.value = false;
}
Rx<RentalOrderModel> selectedOrder = RentalOrderModel().obs;
RxDouble subTotal = 0.0.obs;
RxDouble discount = 0.0.obs;
RxDouble taxAmount = 0.0.obs;
RxDouble totalAmount = 0.0.obs;
RxDouble extraKilometerCharge = 0.0.obs;
RxDouble extraMinutesCharge = 0.0.obs;
void calculateTotalAmount(RentalOrderModel order) {
subTotal.value = 0.0;
discount.value = 0.0;
taxAmount.value = 0.0;
totalAmount.value = 0.0;
extraKilometerCharge.value = 0.0;
extraMinutesCharge.value = 0.0;
selectedOrder.value = order;
try {
subTotal.value = double.tryParse(selectedOrder.value.subTotal?.toString() ?? "0") ?? 0.0;
discount.value = double.tryParse(selectedOrder.value.discount?.toString() ?? "0") ?? 0.0;
taxAmount.value = 0.0;
if (selectedOrder.value.endTime != null) {
DateTime start = selectedOrder.value.startTime!.toDate();
DateTime end = selectedOrder.value.endTime!.toDate();
int hours = end.difference(start).inHours;
if (hours >= int.parse(selectedOrder.value.rentalPackageModel!.includedHours.toString())) {
hours = hours - int.parse(selectedOrder.value.rentalPackageModel!.includedHours.toString());
double hourlyRate = double.tryParse(selectedOrder.value.rentalPackageModel?.extraMinuteFare?.toString() ?? "0") ?? 0.0;
extraMinutesCharge.value = (hours * 60) * hourlyRate;
}
}
if (selectedOrder.value.startKitoMetersReading != null && selectedOrder.value.endKitoMetersReading != null) {
double startKm = double.tryParse(selectedOrder.value.startKitoMetersReading?.toString() ?? "0") ?? 0.0;
double endKm = double.tryParse(selectedOrder.value.endKitoMetersReading?.toString() ?? "0") ?? 0.0;
if (endKm > startKm) {
double totalKm = endKm - startKm;
if (totalKm > double.parse(selectedOrder.value.rentalPackageModel!.includedDistance!)) {
totalKm = totalKm - double.parse(selectedOrder.value.rentalPackageModel!.includedDistance!);
double extraKmRate = double.tryParse(selectedOrder.value.rentalPackageModel?.extraKmFare?.toString() ?? "0") ?? 0.0;
extraKilometerCharge.value = totalKm * extraKmRate;
}
}
}
subTotal.value = subTotal.value + extraKilometerCharge.value + extraMinutesCharge.value;
if (selectedOrder.value.taxSetting != null) {
for (var element in selectedOrder.value.taxSetting!) {
taxAmount.value += Constant.calculateTax(amount: (subTotal.value - discount.value).toString(), taxModel: element);
}
}
totalAmount.value = (subTotal.value - discount.value) + taxAmount.value;
} catch (e) {
ShowToastDialog.showToast("Failed to calculate total: $e");
}
}
Future<void> completeOrder() async {
if (selectedPaymentMethod.value == PaymentGateway.cod.name) {
selectedOrder.value.paymentMethod = selectedPaymentMethod.value;
await FireStoreUtils.rentalOrderPlace(selectedOrder.value).then((value) {
ShowToastDialog.showToast("Payment method changed".tr);
Get.back();
Get.back();
});
} else {
selectedOrder.value.paymentStatus = true;
selectedOrder.value.paymentMethod = selectedPaymentMethod.value;
if (selectedPaymentMethod.value == PaymentGateway.wallet.name) {
WalletTransactionModel transactionModel = WalletTransactionModel(
id: Constant.getUuid(),
amount: double.parse(totalAmount.toString()),
date: Timestamp.now(),
paymentMethod: PaymentGateway.wallet.name,
transactionUser: "customer",
userId: FireStoreUtils.getCurrentUid(),
isTopup: false,
orderId: selectedOrder.value.id,
note: "Rental Amount debited".tr,
paymentStatus: "success".tr,
serviceType: Constant.parcelServiceType,
);
await FireStoreUtils.setWalletTransaction(transactionModel).then((value) async {
if (value == true) {
await FireStoreUtils.updateUserWallet(amount: "-${totalAmount.value.toString()}", userId: FireStoreUtils.getCurrentUid());
}
});
}
await FireStoreUtils.rentalOrderPlace(selectedOrder.value).then((value) {
ShowToastDialog.showToast("Payment successfully".tr);
Get.back();
Get.back();
});
}
}
/// Return filtered list for a specific tab title
List<RentalOrderModel> getOrdersForTab(String tab) {
switch (tab) {
case "New":
return rentalOrders.where((order) => ["Order Placed", "Order Accepted", "Driver Pending"].contains(order.status)).toList();
case "On Going":
return rentalOrders.where((order) => ["Driver Accepted", "Order Shipped", "In Transit"].contains(order.status)).toList();
case "Completed":
return rentalOrders.where((order) => ["Order Completed"].contains(order.status)).toList();
case "Cancelled":
return rentalOrders.where((order) => ["Order Rejected", "Order Cancelled", "Driver Rejected"].contains(order.status)).toList();
default:
return [];
}
}
/// Old helper (optional)
List<RentalOrderModel> get filteredRentalOrders => getOrdersForTab(selectedTab.value);
Future<void> cancelRentalRequest(RentalOrderModel order, {List<TaxModel>? taxList}) async {
try {
isLoading.value = true;
order.status = Constant.orderCancelled;
await FireStoreUtils.rentalOrderPlace(order);
listenRentalOrders();
if (order.paymentMethod?.toLowerCase() != "cod") {
double totalTax = 0.0;
if (taxList != null) {
for (var element in taxList) {
totalTax += Constant.calculateTax(
amount: (double.parse(order.subTotal.toString()) - double.parse(order.discount.toString())).toString(),
taxModel: element,
);
}
}
double subTotal = double.parse(order.subTotal.toString()) - double.parse(order.discount.toString());
double refundAmount = subTotal + totalTax;
WalletTransactionModel walletTransaction = WalletTransactionModel(
id: Constant.getUuid(),
amount: refundAmount,
date: Timestamp.now(),
paymentMethod: PaymentGateway.wallet.name,
transactionUser: "customer",
userId: FireStoreUtils.getCurrentUid(),
isTopup: true,
// refund
orderId: order.id,
note: "Refund for cancelled booking".tr,
paymentStatus: "success".tr,
serviceType: Constant.parcelServiceType,
);
await FireStoreUtils.setWalletTransaction(walletTransaction);
await FireStoreUtils.updateUserWallet(amount: refundAmount.toString(), userId: FireStoreUtils.getCurrentUid());
}
ShowToastDialog.showToast("Booking cancelled successfully".tr);
} catch (e) {
ShowToastDialog.showToast("Failed to cancel booking: $e".tr);
} finally {
isLoading.value = false;
}
}
@override
void onClose() {
_rentalSubscription?.cancel();
super.onClose();
}
}

View File

@@ -0,0 +1,43 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../models/on_boarding_model.dart';
import '../service/fire_store_utils.dart';
class OnboardingController extends GetxController {
RxInt currentPage = 0.obs;
late PageController pageController;
RxBool isLoading = true.obs;
RxList<OnBoardingModel> onboardingList = <OnBoardingModel>[].obs;
@override
void onInit() {
super.onInit();
pageController = PageController();
getOnBoardingData();
}
void nextPage() {
if (currentPage.value < onboardingList.length - 1) {
pageController.nextPage(duration: 300.milliseconds, curve: Curves.ease);
}
}
void onPageChanged(int index) {
currentPage.value = index;
}
Future<void> getOnBoardingData() async {
isLoading.value = true;
await FireStoreUtils.getOnBoardingList().then((value) {
onboardingList.value = value;
});
isLoading.value = false;
}
@override
void onClose() {
pageController.dispose();
super.onClose();
}
}

View File

@@ -0,0 +1,214 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:customer/constant/constant.dart';
import 'package:customer/models/coupon_model.dart';
import 'package:customer/models/user_model.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import '../models/onprovider_order_model.dart';
import '../models/provider_serivce_model.dart';
import '../screen_ui/on_demand_service/on_demand_dashboard_screen.dart';
import '../screen_ui/on_demand_service/on_demand_payment_screen.dart';
import '../service/fire_store_utils.dart';
import '../service/send_notification.dart';
import '../themes/show_toast_dialog.dart';
import 'on_demand_dashboard_controller.dart';
class OnDemandBookingController extends GetxController {
Rxn<ProviderServiceModel> provider = Rxn<ProviderServiceModel>();
RxString categoryTitle = ''.obs;
RxInt quantity = 1.obs;
Rx<TextEditingController> descriptionController = TextEditingController().obs;
Rx<TextEditingController> dateTimeController = TextEditingController().obs;
Rx<TextEditingController> couponTextController = TextEditingController().obs;
Rx<DateTime> selectedDateTime = DateTime.now().obs;
RxString dateTimeText = "".obs;
RxList<CouponModel> couponList = <CouponModel>[].obs;
RxDouble subTotal = 0.0.obs;
RxDouble price = 0.0.obs;
RxDouble discountAmount = 0.0.obs;
RxDouble totalAmount = 0.0.obs;
RxString discountType = "".obs;
RxString discountLabel = "".obs;
RxString offerCode = "".obs;
Rx<ShippingAddress> selectedAddress = ShippingAddress().obs;
@override
void onInit() {
super.onInit();
final Map<String, dynamic>? args = Get.arguments;
if (args != null) {
provider.value = args['providerModel'];
categoryTitle.value = args['categoryTitle'] ?? '';
}
selectedAddress.value = Constant.selectedLocation;
fetchCoupons();
calculatePrice();
}
void fetchCoupons() {
if (provider.value?.author != null && provider.value!.author!.isNotEmpty) {
FireStoreUtils.getProviderCoupon(provider.value!.author!).then((activeCoupons) => couponList.assignAll(activeCoupons));
FireStoreUtils.getProviderCouponAfterExpire(provider.value!.author!).then((expiredCoupons) => couponList.addAll(expiredCoupons));
}
}
void incrementQuantity() {
quantity.value++;
calculatePrice();
}
void decrementQuantity() {
if (quantity.value > 1) {
quantity.value--;
calculatePrice();
}
}
void setDateTime(DateTime dateTime) {
selectedDateTime.value = dateTime;
dateTimeText.value = DateFormat('dd-MM-yyyy HH:mm').format(dateTime);
dateTimeController.value.text = dateTimeText.value;
}
void applyCoupon(CouponModel coupon) {
double discount = 0.0;
if (coupon.discountType == "Percentage" || coupon.discountType == "Percent") {
discount = price.value * (double.tryParse(coupon.discount.toString()) ?? 0) / 100;
} else {
discount = double.tryParse(coupon.discount.toString()) ?? 0;
}
if (subTotal.value > discount) {
discountType.value = coupon.discountType ?? '';
discountLabel.value = coupon.discount.toString();
offerCode.value = coupon.code ?? '';
calculatePrice();
} else {
Get.snackbar("Error", "Coupon cannot be applied");
}
}
String getDate(String date) {
try {
DateTime dt = DateTime.parse(date);
return "${dt.day}-${dt.month}-${dt.year}";
} catch (e) {
return date;
}
}
void calculatePrice() {
double basePrice =
(provider.value?.disPrice == "" || provider.value?.disPrice == "0")
? double.tryParse(provider.value?.price.toString() ?? "0") ?? 0
: double.tryParse(provider.value?.disPrice.toString() ?? "0") ?? 0;
price.value = basePrice * quantity.value;
// discount
if (discountType.value == "Percentage" || discountType.value == "Percent") {
discountAmount.value = price.value * (double.tryParse(discountLabel.value) ?? 0) / 100;
} else {
discountAmount.value = double.tryParse(discountLabel.value.isEmpty ? '0' : discountLabel.value) ?? 0;
}
subTotal.value = price.value - discountAmount.value;
// tax calculation
double total = subTotal.value;
for (var element in Constant.taxList) {
total += Constant.getTaxValue(amount: subTotal.value.toString(), taxModel: element);
}
totalAmount.value = total;
}
Future<void> confirmBooking(BuildContext context) async {
if (selectedAddress.value.getFullAddress().isEmpty) {
ShowToastDialog.showToast("Please enter address".tr);
} else if (dateTimeController.value.text.isEmpty) {
ShowToastDialog.showToast("Please select time slot.".tr);
} else {
UserModel? providerUser = await FireStoreUtils.getUserProfile(provider.value!.author!);
if (provider.value?.priceUnit == "Fixed") {
OnProviderOrderModel onDemandOrderModel = OnProviderOrderModel(
authorID: FireStoreUtils.getCurrentUid(),
author: Constant.userModel!,
quantity: double.parse(quantity.value.toString()),
sectionId: Constant.sectionConstantModel!.id,
address: selectedAddress.value,
taxModel: Constant.taxList,
provider: provider.value,
status: Constant.orderPlaced,
scheduleDateTime: Timestamp.fromDate(selectedDateTime.value),
notes: descriptionController.value.text,
discount: discountAmount.toString(),
discountType: discountType.toString(),
discountLabel: discountLabel.toString(),
adminCommission:
Constant.sectionConstantModel?.adminCommision?.isEnabled == false
? '0'
: "${providerUser?.adminCommissionModel?.amount ?? Constant.sectionConstantModel?.adminCommision?.amount ?? 0}",
adminCommissionType:
Constant.sectionConstantModel?.adminCommision?.isEnabled == false
? 'fixed'
: providerUser?.adminCommissionModel?.commissionType ?? Constant.sectionConstantModel?.adminCommision?.commissionType,
otp: Constant.getReferralCode(),
couponCode: offerCode.toString(),
);
print('totalAmount ::::::: ${double.tryParse(Constant.amountShow(amount: totalAmount.value.toString())) ?? 0.0}');
print('totalAmount value ::::::: ${totalAmount.value}');
Get.to(() => OnDemandPaymentScreen(), arguments: {'onDemandOrderModel': Rxn<OnProviderOrderModel>(onDemandOrderModel), 'totalAmount': totalAmount.value, 'isExtra': false});
} else {
ShowToastDialog.showLoader("Please wait...".tr);
OnProviderOrderModel onDemandOrder = OnProviderOrderModel(
otp: Constant.getReferralCode(),
authorID: FireStoreUtils.getCurrentUid(),
author: Constant.userModel!,
sectionId: Constant.sectionConstantModel!.id,
address: selectedAddress.value,
taxModel: Constant.taxList,
status: Constant.orderPlaced,
createdAt: Timestamp.now(),
quantity: double.parse(quantity.value.toString()),
provider: provider.value,
extraPaymentStatus: true,
scheduleDateTime: Timestamp.fromDate(selectedDateTime.value),
notes: descriptionController.value.text,
adminCommission:
Constant.sectionConstantModel?.adminCommision?.isEnabled == false
? '0'
: "${providerUser?.adminCommissionModel?.amount ?? Constant.sectionConstantModel?.adminCommision?.amount ?? 0}",
adminCommissionType:
Constant.sectionConstantModel?.adminCommision?.isEnabled == false
? 'fixed'
: providerUser?.adminCommissionModel?.commissionType ?? Constant.sectionConstantModel?.adminCommision?.commissionType,
paymentStatus: true,
);
await FireStoreUtils.onDemandOrderPlace(onDemandOrder, 0.0);
await FireStoreUtils.sendOrderOnDemandServiceEmail(orderModel: onDemandOrder);
if (providerUser != null) {
Map<String, dynamic> payLoad = {"type": 'provider_order', "orderId": onDemandOrder.id};
await SendNotification.sendFcmMessage(Constant.bookingPlaced, providerUser.fcmToken.toString(), payLoad);
}
ShowToastDialog.closeLoader();
Get.offAll(const OnDemandDashboardScreen());
OnDemandDashboardController controller = Get.put(OnDemandDashboardController());
controller.selectedIndex.value = 2;
ShowToastDialog.showToast("OnDemand Service successfully booked".tr);
}
}
}
}

View File

@@ -0,0 +1,29 @@
import 'package:get/get.dart';
import '../models/category_model.dart';
import '../service/fire_store_utils.dart';
class OnDemandCategoryController extends GetxController {
RxBool isLoading = true.obs;
RxList<CategoryModel> categories = <CategoryModel>[].obs;
@override
void onInit() {
super.onInit();
fetchCategories();
}
void fetchCategories() async {
try {
isLoading.value = true;
// Fetch categories
FireStoreUtils.getOnDemandCategory().then((catValue) {
categories.value = catValue;
});
} catch (e) {
print("Error fetching categories: $e");
} finally {
isLoading.value = false;
}
}
}

View File

@@ -0,0 +1,40 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/screen_ui/multi_vendor_service/profile_screen/profile_screen.dart';
import 'package:customer/screen_ui/multi_vendor_service/wallet_screen/wallet_screen.dart';
import 'package:customer/screen_ui/on_demand_service/favourite_ondemand_screen.dart';
import 'package:customer/service/fire_store_utils.dart';
import 'package:get/get.dart';
import '../screen_ui/on_demand_service/my_booking_on_demand_screen.dart';
import '../screen_ui/on_demand_service/on_demand_home_screen.dart';
class OnDemandDashboardController extends GetxController {
RxInt selectedIndex = 0.obs;
RxList pageList = [].obs;
@override
void onInit() {
getTaxList();
if (Constant.walletSetting == false) {
pageList.value = [OnDemandHomeScreen(), FavouriteOndemandScreen(), const MyBookingOnDemandScreen(), const ProfileScreen()];
} else {
pageList.value = [
OnDemandHomeScreen(),
FavouriteOndemandScreen(),
const MyBookingOnDemandScreen(),
const WalletScreen(),
const ProfileScreen(),
];
}
super.onInit();
}
Future<void> getTaxList() async {
await FireStoreUtils.getTaxList(Constant.sectionConstantModel!.id).then((value) {
if (value != null) {
Constant.taxList = value;
}
});
}
}

View File

@@ -0,0 +1,100 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/models/user_model.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import '../models/favorite_ondemand_service_model.dart';
import '../models/provider_serivce_model.dart';
import '../models/rating_model.dart';
import '../service/fire_store_utils.dart';
class OnDemandDetailsController extends GetxController {
late ProviderServiceModel provider;
final Rxn<UserModel> userModel = Rxn<UserModel>();
final RxString subCategoryTitle = ''.obs;
final RxString categoryTitle = ''.obs;
final RxList<RatingModel> ratingService = <RatingModel>[].obs;
final RxList<FavouriteOndemandServiceModel> lstFav = <FavouriteOndemandServiceModel>[].obs;
final RxBool isLoading = true.obs;
final RxBool isOpen = false.obs;
final RxString tabString = "About".obs;
@override
void onInit() {
super.onInit();
provider = Get.arguments['providerModel'];
timeCheck();
getData();
}
Future<void> getData() async {
await getReviewList();
await getAuthor(); //fetch and set provider author here
if (Constant.userModel != null) {
lstFav.value = await FireStoreUtils.getFavouritesServiceList(FireStoreUtils.getCurrentUid());
}
isLoading.value = false;
}
Future<void> getReviewList() async {
await FireStoreUtils.getCategoryById(provider.categoryId.toString()).then((value) {
if (value != null) {
categoryTitle.value = value.title.toString();
}
});
await FireStoreUtils.getSubCategoryById(provider.subCategoryId.toString()).then((value) {
if (value != null) {
subCategoryTitle.value = value.title.toString();
}
});
await FireStoreUtils.getReviewByProviderServiceId(provider.id.toString()).then((value) {
ratingService.value = value;
});
if (Constant.userModel != null) {
await FireStoreUtils.getFavouritesServiceList(FireStoreUtils.getCurrentUid()).then((value) {
lstFav.value = value;
});
}
}
Future<void> getAuthor() async {
final authorId = provider.author?.toString();
if (authorId != null && authorId.isNotEmpty) {
final user = await FireStoreUtils.getUserProfile(authorId);
if (user != null) {
userModel.value = user;
}
}
}
void timeCheck() {
final now = DateTime.now();
final day = DateFormat('EEEE', 'en_US').format(now);
final date = DateFormat('dd-MM-yyyy').format(now);
for (var element in provider.days) {
if (day == element.toString()) {
final start = DateFormat("dd-MM-yyyy HH:mm").parse("$date ${provider.startTime}");
final end = DateFormat("dd-MM-yyyy HH:mm").parse("$date ${provider.endTime}");
if (isCurrentDateInRange(start, end)) {
isOpen.value = true;
}
}
}
}
bool isCurrentDateInRange(DateTime startDate, DateTime endDate) {
final currentDate = DateTime.now();
return currentDate.isAfter(startDate) && currentDate.isBefore(endDate);
}
void changeTab(String tab) {
tabString.value = tab;
}
}

View File

@@ -0,0 +1,142 @@
import 'package:customer/models/banner_model.dart';
import 'package:customer/models/category_model.dart';
import 'package:customer/models/favorite_ondemand_service_model.dart';
import 'package:customer/models/provider_serivce_model.dart';
import 'package:customer/screen_ui/auth_screens/login_screen.dart';
import 'package:customer/service/fire_store_utils.dart';
import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import '../constant/constant.dart';
class OnDemandHomeController extends GetxController {
RxBool isLoading = true.obs;
RxList<BannerModel> bannerTopHome = <BannerModel>[].obs;
RxList<CategoryModel> categories = <CategoryModel>[].obs;
RxList<ProviderServiceModel> providerList = <ProviderServiceModel>[].obs;
/// Store last fetched category
Rx<CategoryModel?> categoryModel = Rx<CategoryModel?>(null);
@override
void onInit() {
getData();
super.onInit();
}
Future<void> getData() async {
isLoading.value = true;
await getZone();
// Fetch banners
FireStoreUtils.getHomeTopBanner().then((value) {
bannerTopHome.value = value;
});
// Fetch categories
FireStoreUtils.getOnDemandCategory().then((catValue) {
categories.value = catValue;
});
// Fetch provider services
FireStoreUtils.getProviderFuture()
.then((providerServiceList) {
Set<String?> uniqueAuthorIds = providerServiceList.map((service) => service.author).toSet();
List<String?> listOfUniqueProviders = uniqueAuthorIds.toList();
List<ProviderServiceModel> filteredProviders = [];
for (var provider in listOfUniqueProviders) {
List<ProviderServiceModel> filteredList = providerServiceList.where((service) => service.author == provider).toList();
filteredList.sort((a, b) => a.createdAt!.compareTo(b.createdAt!));
for (int index = 0; index < filteredList.length; index++) {
final service = filteredList[index];
if (Constant.isSubscriptionModelApplied == true || Constant.sectionConstantModel?.adminCommision?.isEnabled == true) {
if (service.subscriptionPlan?.itemLimit == "-1") {
filteredProviders.add(service);
} else {
if (index < int.parse(service.subscriptionPlan?.itemLimit ?? '0')) {
filteredProviders.add(service);
}
}
} else {
filteredProviders.add(service);
}
}
}
providerList.value = filteredProviders;
isLoading.value = false;
})
.catchError((e) {
print("Provider error: $e");
isLoading.value = false;
});
FireStoreUtils.getFavouritesServiceList(FireStoreUtils.getCurrentUid()).then((favList) {
lstFav.value = favList;
});
}
/// Get category by id safely from cached categories
Future<CategoryModel?> getCategory(String? categoryId) async {
if (categoryId == null || categoryId.isEmpty) return null;
// Try to find category from cached list
CategoryModel? cat = categories.firstWhereOrNull((element) => element.id == categoryId);
// If not found, fetch from Firestore
cat ??= await FireStoreUtils.getCategoryById(categoryId);
categoryModel.value = cat;
return cat;
}
RxList<FavouriteOndemandServiceModel> lstFav = <FavouriteOndemandServiceModel>[].obs;
void toggleFavourite(ProviderServiceModel provider) {
if (Constant.userModel == null) {
Get.to(LoginScreen());
} else {
var contain = lstFav.where((element) => element.service_id == provider.id);
if (contain.isNotEmpty) {
FavouriteOndemandServiceModel favouriteModel = FavouriteOndemandServiceModel(
section_id: provider.sectionId,
service_id: provider.id,
user_id: FireStoreUtils.getCurrentUid(),
serviceAuthorId: provider.author,
);
FireStoreUtils.removeFavouriteOndemandService(favouriteModel);
lstFav.removeWhere((item) => item.service_id == provider.id);
} else {
FavouriteOndemandServiceModel favouriteModel = FavouriteOndemandServiceModel(
section_id: provider.sectionId,
service_id: provider.id,
user_id: FireStoreUtils.getCurrentUid(),
serviceAuthorId: provider.author,
);
FireStoreUtils.setFavouriteOndemandSection(favouriteModel);
lstFav.add(favouriteModel);
}
}
}
Future<void> getZone() async {
await FireStoreUtils.getZone().then((value) {
if (value != null) {
for (int i = 0; i < value.length; i++) {
if (Constant.isPointInPolygon(LatLng(Constant.selectedLocation.location?.latitude ?? 0.0, Constant.selectedLocation.location?.longitude ?? 0.0), value[i].area!)) {
Constant.selectedZone = value[i];
Constant.isZoneAvailable = true;
break;
} else {
Constant.selectedZone = value[i];
Constant.isZoneAvailable = false;
}
}
}
});
}
}

View File

@@ -0,0 +1,254 @@
import 'dart:developer';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:customer/models/coupon_model.dart';
import 'package:customer/models/user_model.dart';
import 'package:flutter/cupertino.dart';
import 'package:get/get.dart';
import '../constant/constant.dart';
import '../models/onprovider_order_model.dart';
import '../models/wallet_transaction_model.dart';
import '../models/worker_model.dart';
import '../service/fire_store_utils.dart';
import '../service/send_notification.dart';
import '../themes/show_toast_dialog.dart';
class OnDemandOrderDetailsController extends GetxController {
Rx<UserModel?> providerUser = Rx<UserModel?>(null);
Rxn<OnProviderOrderModel> onProviderOrder = Rxn<OnProviderOrderModel>();
Rxn<WorkerModel> worker = Rxn<WorkerModel>();
Rx<TextEditingController> couponTextController = TextEditingController().obs;
Rx<TextEditingController> cancelBookingController = TextEditingController().obs;
RxDouble subTotal = 0.0.obs;
RxDouble price = 0.0.obs;
RxDouble discountAmount = 0.0.obs;
RxDouble totalAmount = 0.0.obs;
RxDouble quantity = 0.0.obs;
RxString discountType = "".obs;
RxString discountLabel = "".obs;
RxString offerCode = "".obs;
RxList<CouponModel> couponList = <CouponModel>[].obs;
final RxBool isLoading = false.obs;
@override
void onInit() {
super.onInit();
final args = Get.arguments;
if (args != null && args is OnProviderOrderModel) {
onProviderOrder.value = args;
}
getData();
}
Future<void> getData() async {
try {
final order = await FireStoreUtils.getProviderOrderById(onProviderOrder.value!.id);
if (order != null) {
onProviderOrder.value = order;
discountType.value = order.discountType ?? "";
discountLabel.value = order.discountLabel ?? "";
discountAmount.value = double.tryParse(order.discount.toString()) ?? 0.0;
offerCode.value = order.couponCode ?? "";
// Fetch provider
providerUser.value = await FireStoreUtils.getUserProfile(order.provider.author.toString());
// Fetch worker (if exists)
if (order.workerId != null && order.workerId!.isNotEmpty) {
worker.value = await FireStoreUtils.getWorker(order.workerId!);
} else {
worker.value = null;
}
calculatePrice();
// Load available coupons
FireStoreUtils.getProviderCouponAfterExpire(order.provider.author.toString()).then((expiredCoupons) {
couponList.assignAll(expiredCoupons);
});
} else {
onProviderOrder.value = null;
providerUser.value = null;
worker.value = null;
couponList.clear();
}
} catch (e, st) {
log("Error in getData: $e\n$st");
onProviderOrder.value = null;
providerUser.value = null;
worker.value = null;
couponList.clear();
}
}
void applyCoupon(CouponModel coupon) {
double discount = 0.0;
if (coupon.discountType == "Percentage" || coupon.discountType == "Percent") {
discount = price.value * (double.tryParse(coupon.discount.toString()) ?? 0) / 100;
} else {
discount = double.tryParse(coupon.discount.toString()) ?? 0;
}
if (subTotal.value > discount) {
discountType.value = coupon.discountType ?? '';
discountLabel.value = coupon.discount.toString();
offerCode.value = coupon.code ?? '';
calculatePrice();
} else {
Get.snackbar("Error", "Coupon cannot be applied");
}
}
void calculatePrice() {
double basePrice =
(onProviderOrder.value?.provider.disPrice == "" || onProviderOrder.value?.provider.disPrice == "0")
? double.tryParse(onProviderOrder.value?.provider.price.toString() ?? "0") ?? 0
: double.tryParse(onProviderOrder.value?.provider.disPrice.toString() ?? "0") ?? 0;
price.value = basePrice * (onProviderOrder.value?.quantity ?? 0.0);
// discount
if (discountType.value == "Percentage" || discountType.value == "Percent") {
discountAmount.value = price.value * (double.tryParse(discountLabel.value) ?? 0) / 100;
} else {
discountAmount.value = double.tryParse(discountLabel.value.isEmpty ? '0' : discountLabel.value) ?? 0;
}
subTotal.value = price.value - discountAmount.value;
// tax calculation
double total = subTotal.value;
for (var element in Constant.taxList) {
total += Constant.getTaxValue(amount: subTotal.value.toString(), taxModel: element);
}
totalAmount.value = total;
}
String getDate(String date) {
try {
DateTime dt = DateTime.parse(date);
return "${dt.day}-${dt.month}-${dt.year}";
} catch (e) {
return date;
}
}
Future<void> cancelBooking() async {
final order = onProviderOrder.value;
if (order == null) return;
ShowToastDialog.showLoader("Please wait...".tr);
try {
double total = 0.0;
// Calculate total
final pricePerUnit =
(order.provider.disPrice == "" || order.provider.disPrice == "0") ? double.tryParse(order.provider.price.toString()) ?? 0 : double.tryParse(order.provider.disPrice.toString()) ?? 0;
total = pricePerUnit * (order.quantity);
// Add tax
if (Constant.taxList.isNotEmpty) {
for (var tax in Constant.taxList) {
total += Constant.getTaxValue(amount: total.toString(), taxModel: tax);
}
}
// Admin commission
double adminComm = 0.0;
if ((order.adminCommission ?? '0') != '0' && (order.adminCommissionType ?? '').isNotEmpty) {
if (order.adminCommissionType!.toLowerCase() == 'percentage' || order.adminCommissionType!.toLowerCase() == 'percent') {
adminComm = (total * (double.tryParse(order.adminCommission!) ?? 0)) / 100;
} else {
adminComm = double.tryParse(order.adminCommission!) ?? 0;
}
}
// Refund customer wallet if not COD
if ((order.payment_method).toLowerCase() != 'cod') {
await FireStoreUtils.setWalletTransaction(
WalletTransactionModel(
id: Constant.getUuid(),
serviceType: 'ondemand-service',
amount: total,
date: Timestamp.now(),
paymentMethod: 'wallet',
transactionUser: 'customer',
userId: Constant.userModel?.id,
isTopup: true,
orderId: order.id,
note: 'Booking Amount Refund',
paymentStatus: "success".tr,
),
);
// Deduct from provider if accepted
if (order.status == Constant.orderAccepted) {
await FireStoreUtils.setWalletTransaction(
WalletTransactionModel(
id: Constant.getUuid(),
serviceType: 'ondemand-service',
amount: total,
date: Timestamp.now(),
paymentMethod: 'wallet',
transactionUser: 'provider',
userId: order.provider.author ?? '',
isTopup: false,
orderId: order.id,
note: 'Booking Amount Refund',
paymentStatus: "success".tr,
),
);
}
}
// Refund admin commission
if (order.status == Constant.orderAccepted && adminComm > 0) {
await FireStoreUtils.setWalletTransaction(
WalletTransactionModel(
id: Constant.getUuid(),
serviceType: 'ondemand-service',
amount: adminComm,
date: Timestamp.now(),
paymentMethod: 'wallet',
transactionUser: 'provider',
userId: order.provider.author ?? '',
isTopup: true,
orderId: order.id,
note: 'Admin commission refund',
paymentStatus: "success".tr,
),
);
}
// Update order status & reason
order.status = Constant.orderCancelled;
order.reason = cancelBookingController.value.text;
await FireStoreUtils.updateOnDemandOrder(order); // Ensure this completes
// Notify provider
final provider = await FireStoreUtils.getUserProfile(order.provider.author ?? '');
if (provider != null) {
Map<String, dynamic> payload = {"type": 'provider_order', "orderId": order.id};
await SendNotification.sendFcmMessage(Constant.bookingPlaced, provider.fcmToken ?? '', payload);
}
ShowToastDialog.closeLoader();
Get.back();
ShowToastDialog.showToast("Booking cancelled successfully".tr);
} catch (e, st) {
log("Cancel error: $e\n$st");
ShowToastDialog.closeLoader();
ShowToastDialog.showToast("Something went wrong".tr);
}
}
}

View File

@@ -0,0 +1,169 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../constant/collection_name.dart';
import '../models/onprovider_order_model.dart';
import '../models/provider_serivce_model.dart';
import '../models/rating_model.dart';
import '../models/worker_model.dart';
import '../models/user_model.dart';
import '../service/fire_store_utils.dart';
import '../constant/constant.dart';
import '../themes/show_toast_dialog.dart';
class OnDemandReviewController extends GetxController {
final Rxn<OnProviderOrderModel> order = Rxn<OnProviderOrderModel>();
final RxString reviewFor = "".obs;
final Rxn<RatingModel> ratingModel = Rxn<RatingModel>();
final RxDouble ratings = 0.0.obs;
final TextEditingController comment = TextEditingController();
final Rxn<UserModel> provider = Rxn<UserModel>();
final Rxn<ProviderServiceModel> providerServiceModel = Rxn<ProviderServiceModel>();
final Rxn<WorkerModel> workerModel = Rxn<WorkerModel>();
final RxInt providerReviewCount = 0.obs;
final RxDouble providerReviewSum = 0.0.obs;
final RxInt serviceReviewCount = 0.obs;
final RxDouble serviceReviewSum = 0.0.obs;
final RxInt workerReviewCount = 0.obs;
final RxDouble workerReviewSum = 0.0.obs;
final FirebaseFirestore firestore = FirebaseFirestore.instance;
@override
void onInit() {
super.onInit();
final args = Get.arguments;
if (args != null) {
order.value = args['order'];
reviewFor.value = args['reviewFor'];
getReview();
}
}
void getReview() async {
// Get existing rating
if (reviewFor.value == "Provider") {
RatingModel? value = await FireStoreUtils.getReviewsByProviderID(order.value!.id, order.value!.provider.author.toString());
if (value != null) {
ratingModel.value = value;
ratings.value = value.rating ?? 0.0;
comment.text = value.comment ?? '';
}
} else {
RatingModel? value = await FireStoreUtils.getReviewsByWorkerID(order.value!.id, order.value!.workerId.toString());
if (value != null) {
ratingModel.value = value;
ratings.value = value.rating ?? 0.0;
comment.text = value.comment ?? '';
}
}
// Worker review logic
if (reviewFor.value == "Worker") {
WorkerModel? value = await FireStoreUtils.getWorker(order.value!.workerId.toString());
if (value != null) {
workerModel.value = value;
final int existingCount = (value.reviewsCount ?? 0).toInt();
final double existingSum = (value.reviewsSum ?? 0.0).toDouble();
final double oldRating = ratingModel.value?.rating ?? 0.0;
workerReviewCount.value = ratingModel.value != null ? (existingCount - 1) : existingCount;
workerReviewSum.value = ratingModel.value != null ? (existingSum - oldRating) : existingSum;
}
}
// Provider & service review logic
else {
UserModel? user = await FireStoreUtils.getUserProfile(order.value!.provider.author.toString());
if (user != null) {
provider.value = user;
final int existingCount = int.tryParse(user.reviewsCount?.toString() ?? '0') ?? 0;
final double existingSum = double.tryParse(user.reviewsSum?.toString() ?? '0.0') ?? 0.0;
final double oldRating = ratingModel.value?.rating ?? 0.0;
providerReviewCount.value = ratingModel.value != null ? (existingCount - 1) : existingCount;
providerReviewSum.value = ratingModel.value != null ? (existingSum - oldRating) : existingSum;
}
ProviderServiceModel? service = await FireStoreUtils.getCurrentProvider(order.value!.provider.id.toString());
if (service != null) {
providerServiceModel.value = service;
final int existingCount = (service.reviewsCount ?? 0).toInt();
final double existingSum = (service.reviewsSum ?? 0.0).toDouble();
final double oldRating = ratingModel.value?.rating ?? 0.0;
serviceReviewCount.value = ratingModel.value != null ? (existingCount - 1) : existingCount;
serviceReviewSum.value = ratingModel.value != null ? (existingSum - oldRating) : existingSum;
}
}
}
void submitReview() async {
if (reviewFor.value == "Provider") {
await _providerReviewSubmit();
} else {
await _workerReviewSubmit();
}
}
Future<void> _providerReviewSubmit() async {
ShowToastDialog.showLoader("Submit in...".tr);
providerServiceModel.value!.reviewsCount = serviceReviewCount.value + 1;
providerServiceModel.value!.reviewsSum = serviceReviewSum.value + ratings.value;
// Convert to string only if your model field is String
provider.value!.reviewsCount = (providerReviewCount.value + 1).toString();
provider.value!.reviewsSum = (providerReviewSum.value + ratings.value).toString();
RatingModel rate = RatingModel(
id: ratingModel.value?.id ?? firestore.collection(CollectionName.itemsReview).doc().id,
productId: ratingModel.value?.productId ?? order.value!.provider.id,
comment: comment.text,
photos: ratingModel.value?.photos ?? [],
rating: ratings.value,
orderId: ratingModel.value?.orderId ?? order.value!.id,
vendorId: ratingModel.value?.vendorId ?? order.value!.provider.author.toString(),
customerId: Constant.userModel?.id,
uname: '${Constant.userModel?.firstName ?? ''} ${Constant.userModel?.lastName ?? ''}',
profile: Constant.userModel?.profilePictureURL,
createdAt: Timestamp.now(),
);
await FireStoreUtils.updateReviewById(rate);
await FireStoreUtils.updateUser(provider.value!);
await FireStoreUtils.updateProvider(providerServiceModel.value!);
ShowToastDialog.closeLoader();
Get.back(result: true);
}
Future<void> _workerReviewSubmit() async {
ShowToastDialog.showLoader("Submit in...".tr);
workerModel.value!.reviewsCount = workerReviewCount.value + 1;
workerModel.value!.reviewsSum = workerReviewSum.value + ratings.value;
RatingModel rate = RatingModel(
id: ratingModel.value?.id ?? firestore.collection(CollectionName.itemsReview).doc().id,
productId: ratingModel.value?.productId ?? order.value!.provider.id,
comment: comment.text,
photos: ratingModel.value?.photos ?? [],
rating: ratings.value,
orderId: ratingModel.value?.orderId ?? order.value!.id,
driverId: ratingModel.value?.driverId ?? order.value!.workerId.toString(),
customerId: Constant.userModel?.id,
uname: '${Constant.userModel?.firstName ?? ''} ${Constant.userModel?.lastName ?? ''}',
profile: Constant.userModel?.profilePictureURL,
createdAt: Timestamp.now(),
);
await FireStoreUtils.updateReviewById(rate);
await FireStoreUtils.updateWorker(workerModel.value!);
ShowToastDialog.closeLoader();
Get.back(result: true);
}
}

View File

@@ -0,0 +1,51 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/models/cart_product_model.dart';
import 'package:customer/models/order_model.dart';
import '../service/cart_provider.dart';
import '../service/fire_store_utils.dart';
import 'package:get/get.dart';
class OrderController extends GetxController {
RxList<OrderModel> allList = <OrderModel>[].obs;
RxList<OrderModel> inProgressList = <OrderModel>[].obs;
RxList<OrderModel> deliveredList = <OrderModel>[].obs;
RxList<OrderModel> rejectedList = <OrderModel>[].obs;
RxList<OrderModel> cancelledList = <OrderModel>[].obs;
RxBool isLoading = true.obs;
@override
void onInit() {
// TODO: implement onInit
getOrder();
super.onInit();
}
Future<void> getOrder() async {
if (Constant.userModel != null) {
await FireStoreUtils.getAllOrder().then((value) {
allList.value = value;
rejectedList.value = allList.where((p0) => p0.status == Constant.orderRejected).toList();
inProgressList.value =
allList
.where(
(p0) => p0.status == Constant.orderAccepted || p0.status == Constant.driverPending || p0.status == Constant.orderShipped || p0.status == Constant.orderInTransit,
)
.toList();
deliveredList.value = allList.where((p0) => p0.status == Constant.orderCompleted).toList();
cancelledList.value = allList.where((p0) => p0.status == Constant.orderCancelled).toList();
});
}
isLoading.value = false;
}
final CartProvider cartProvider = CartProvider();
void addToCart({required CartProductModel cartProductModel}) {
cartProvider.addToCart(Get.context!, cartProductModel, cartProductModel.quantity!);
update();
}
}

View File

@@ -0,0 +1,77 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/models/cart_product_model.dart';
import 'package:customer/models/order_model.dart';
import 'package:get/get.dart';
import '../service/cart_provider.dart';
class OrderDetailsController extends GetxController {
RxBool isLoading = true.obs;
@override
void onInit() {
// TODO: implement onInit
getArgument();
super.onInit();
}
Rx<OrderModel> orderModel = OrderModel().obs;
Future<void> getArgument() async {
dynamic argumentData = Get.arguments;
if (argumentData != null) {
orderModel.value = argumentData['orderModel'];
}
calculatePrice();
update();
}
RxDouble subTotal = 0.0.obs;
RxDouble specialDiscountAmount = 0.0.obs;
RxDouble taxAmount = 0.0.obs;
RxDouble totalAmount = 0.0.obs;
Future<void> calculatePrice() async {
subTotal.value = 0.0;
specialDiscountAmount.value = 0.0;
taxAmount.value = 0.0;
totalAmount.value = 0.0;
for (var element in orderModel.value.products!) {
if (double.parse(element.discountPrice.toString()) <= 0) {
subTotal.value = subTotal.value +
double.parse(element.price.toString()) * double.parse(element.quantity.toString()) +
(double.parse(element.extrasPrice.toString()) * double.parse(element.quantity.toString()));
} else {
subTotal.value = subTotal.value +
double.parse(element.discountPrice.toString()) * double.parse(element.quantity.toString()) +
(double.parse(element.extrasPrice.toString()) * double.parse(element.quantity.toString()));
}
}
if (orderModel.value.specialDiscount != null && orderModel.value.specialDiscount!['special_discount'] != null) {
specialDiscountAmount.value = double.parse(orderModel.value.specialDiscount!['special_discount'].toString());
}
if (orderModel.value.taxSetting != null) {
for (var element in orderModel.value.taxSetting!) {
taxAmount.value = taxAmount.value +
Constant.calculateTax(amount: (subTotal.value - double.parse(orderModel.value.discount.toString()) - specialDiscountAmount.value).toString(), taxModel: element);
}
}
totalAmount.value = (subTotal.value - double.parse(orderModel.value.discount.toString()) - specialDiscountAmount.value) +
taxAmount.value +
double.parse(orderModel.value.deliveryCharge.toString()) +
double.parse(orderModel.value.tipAmount.toString());
isLoading.value = false;
}
final CartProvider cartProvider = CartProvider();
void addToCart({required CartProductModel cartProductModel}) {
cartProvider.addToCart(Get.context!, cartProductModel, cartProductModel.quantity!);
update();
}
}

View File

@@ -0,0 +1,43 @@
import 'dart:async';
import 'package:customer/models/order_model.dart';
import 'package:get/get.dart';
import '../service/database_helper.dart';
class OrderPlacingController extends GetxController {
RxBool isLoading = true.obs;
@override
void onInit() {
// TODO: implement onInit
getArgument();
startTimer();
super.onInit();
}
Rx<OrderModel> orderModel = OrderModel().obs;
Future<void> getArgument() async {
DatabaseHelper.instance.deleteAllCartProducts();
dynamic argumentData = Get.arguments;
if (argumentData != null) {
orderModel.value = argumentData['orderModel'];
}
isLoading.value = false;
update();
}
Timer? timer;
RxInt counter = 0.obs;
RxBool isPlacing = false.obs;
void startTimer() {
timer = Timer.periodic(const Duration(seconds: 1), (timer) {
if (counter.value == 3) {
timer.cancel();
isPlacing.value = true;
}
counter++;
});
}
}

View File

@@ -0,0 +1,36 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:flutter_osm_plugin/flutter_osm_plugin.dart';
import 'package:get/get.dart';
import 'package:shared_preferences/shared_preferences.dart';
class OsmSearchPlaceController extends GetxController {
Rx<TextEditingController> searchTxtController = TextEditingController().obs;
RxList<SearchInfo> suggestionsList = <SearchInfo>[].obs;
@override
void onInit() {
super.onInit();
searchTxtController.value.addListener(() {
_onChanged();
});
}
void _onChanged() {
fetchAddress(searchTxtController.value.text);
}
Future<void> fetchAddress(text) async {
log(":: fetchAddress :: $text");
try {
String locale = 'en';
SharedPreferences sp = await SharedPreferences.getInstance();
if (sp.getString("languageCode") != null || sp.getString("languageCode")?.isNotEmpty == true) {
locale = sp.getString("languageCode") ?? "en";
}
suggestionsList.value = await addressSuggestion(text, locale: locale);
} catch (e) {
log(e.toString());
}
}
}

View File

@@ -0,0 +1,131 @@
import 'package:customer/screen_ui/location_enable_screens/location_permission_screen.dart';
import 'package:customer/themes/show_toast_dialog.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../constant/constant.dart';
import '../models/user_model.dart';
import '../screen_ui/auth_screens/login_screen.dart';
import '../screen_ui/auth_screens/sign_up_screen.dart';
import '../screen_ui/service_home_screen/service_list_screen.dart';
import '../service/fire_store_utils.dart';
import '../utils/notification_service.dart';
class OtpVerifyController extends GetxController {
/// Use a normal controller (NOT obs)
final Rx<TextEditingController> otpController = TextEditingController().obs;
/// Reactive Strings
final RxString countryCode = "".obs;
final RxString phoneNumber = "".obs;
final RxString verificationId = "".obs;
RxInt resendToken = 0.obs;
final FirebaseAuth _auth = FirebaseAuth.instance;
@override
void onInit() {
super.onInit();
final args = Get.arguments ?? {};
countryCode.value = args['countryCode'] ?? "";
phoneNumber.value = args['phoneNumber'] ?? "";
verificationId.value = args['verificationId'] ?? "";
}
Future<bool> sendOTP() async {
await FirebaseAuth.instance.verifyPhoneNumber(
phoneNumber: countryCode.value + phoneNumber.value,
verificationCompleted: (PhoneAuthCredential credential) {},
verificationFailed: (FirebaseAuthException e) {},
codeSent: (String verificationId0, int? resendToken0) async {
verificationId.value = verificationId0;
resendToken.value = resendToken0!;
ShowToastDialog.showToast("OTP sent".tr);
},
timeout: const Duration(seconds: 25),
forceResendingToken: resendToken.value,
codeAutoRetrievalTimeout: (String verificationId0) {
verificationId0 = verificationId.value;
},
);
return true;
}
void verifyOtp() async {
if (otpController.value.text.length != 6) {
ShowToastDialog.showToast("Enter valid 6-digit OTP".tr);
return;
}
try {
ShowToastDialog.showLoader("Verifying OTP...".tr);
final credential = PhoneAuthProvider.credential(verificationId: verificationId.value, smsCode: otpController.value.text.trim());
final fcmToken = await NotificationService.getToken();
final result = await _auth.signInWithCredential(credential);
if (result.additionalUserInfo?.isNewUser == true) {
final userModel = UserModel(id: result.user!.uid, countryCode: countryCode.value, phoneNumber: phoneNumber.value, fcmToken: fcmToken, active: true);
ShowToastDialog.closeLoader();
Get.to(() => const SignUpScreen(), arguments: {'type': 'mobileNumber', 'userModel': userModel});
return;
}
final exists = await FireStoreUtils.userExistOrNot(result.user!.uid);
ShowToastDialog.closeLoader();
if (!exists) {
final userModel = UserModel(id: result.user!.uid, countryCode: countryCode.value, phoneNumber: phoneNumber.value, fcmToken: fcmToken);
Get.off(() => const SignUpScreen(), arguments: {'type': 'mobileNumber', 'userModel': userModel});
return;
}
final userModel = await FireStoreUtils.getUserProfile(result.user!.uid);
if (userModel == null || userModel.role != 'customer') {
await _auth.signOut();
Get.offAll(() => const LoginScreen());
return;
}
if (userModel.active == false) {
ShowToastDialog.showToast("This user is disabled".tr);
await _auth.signOut();
Get.offAll(() => const LoginScreen());
return;
}
userModel.fcmToken = fcmToken;
await FireStoreUtils.updateUser(userModel);
if (userModel.shippingAddress?.isNotEmpty ?? false) {
final defaultAddress = userModel.shippingAddress!.firstWhere((e) => e.isDefault == true, orElse: () => userModel.shippingAddress!.first);
Constant.selectedLocation = defaultAddress;
Get.offAll(() => const ServiceListScreen());
} else {
Get.offAll(() => const LocationPermissionScreen());
}
} catch (e) {
ShowToastDialog.closeLoader();
ShowToastDialog.showToast("Invalid OTP or Verification Failed".tr);
}
}
String maskPhoneNumber(String phone) {
if (phone.length < 4) return phone;
final first = phone.substring(0, 2);
final last = phone.substring(phone.length - 2);
return "$first*** ***$last";
}
@override
void dispose() {
otpController.value.dispose();
// TODO: implement dispose
super.dispose();
}
}

View File

@@ -0,0 +1,29 @@
import 'package:customer/models/coupon_model.dart';
import 'package:customer/service/fire_store_utils.dart';
import 'package:get/get.dart';
class ParcelCouponController extends GetxController{
@override
void onInit() {
// TODO: implement onInit
getData();
super.onInit();
}
void getData(){
getCouponCode();
}
RxBool isLoading = true.obs;
RxList<CouponModel> cabCouponList = <CouponModel>[].obs;
Future<void> getCouponCode() async {
await FireStoreUtils.getParcelCoupon().then((value) {
cabCouponList.value = value;
// Handle the retrieved coupon code
});
print("cabCouponList ${cabCouponList.length}");
isLoading.value = false;
}
}

View File

@@ -0,0 +1,35 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/screen_ui/multi_vendor_service/profile_screen/profile_screen.dart';
import 'package:customer/screen_ui/multi_vendor_service/wallet_screen/wallet_screen.dart';
import 'package:customer/screen_ui/parcel_service/home_parcel_screen.dart';
import 'package:customer/service/fire_store_utils.dart';
import 'package:get/get.dart';
import '../screen_ui/parcel_service/my_booking_screen.dart';
class ParcelDashboardController extends GetxController {
RxInt selectedIndex = 0.obs;
RxList pageList = [].obs;
@override
void onInit() {
getTaxList();
if (Constant.walletSetting == false) {
pageList.value = [const HomeParcelScreen(), const MyBookingScreen(), const ProfileScreen()];
} else {
pageList.value = [const HomeParcelScreen(), const MyBookingScreen(), const WalletScreen(), const ProfileScreen()];
}
super.onInit();
}
Future<void> getTaxList() async {
await FireStoreUtils.getTaxList(Constant.sectionConstantModel!.id).then((value) {
if (value != null) {
Constant.taxList = value;
}
});
}
DateTime? currentBackPressTime;
RxBool canPopNow = false.obs;
}

View File

@@ -0,0 +1,140 @@
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import '../constant/constant.dart';
import '../models/parcel_order_model.dart';
import '../models/wallet_transaction_model.dart';
import '../screen_ui/multi_vendor_service/wallet_screen/wallet_screen.dart';
import '../service/fire_store_utils.dart';
import '../themes/show_toast_dialog.dart';
class ParcelMyBookingController extends GetxController {
RxBool isLoading = true.obs;
RxList<ParcelOrderModel> parcelOrder = <ParcelOrderModel>[].obs;
RxString selectedTab = "New".obs;
RxList<String> tabTitles = ["New", "In Transit", "Delivered", "Cancelled"].obs;
StreamSubscription<List<ParcelOrderModel>>? _parcelSubscription;
@override
void onInit() {
super.onInit();
listenParcelOrders();
}
void selectTab(String tab) {
selectedTab.value = tab;
}
/// Start listening to orders live. Cancel previous subscription first.
void listenParcelOrders() {
isLoading.value = true;
if (Constant.userModel == null) {
isLoading.value = false;
return;
}
_parcelSubscription?.cancel();
_parcelSubscription = FireStoreUtils.listenParcelOrders().listen(
(orders) {
parcelOrder.assignAll(orders);
isLoading.value = false;
},
onError: (err) {
isLoading.value = false;
// optionally handle error
},
);
}
/// Return filtered list for a specific tab title
List<ParcelOrderModel> getOrdersForTab(String tab) {
switch (tab) {
case "New":
return parcelOrder.where((order) => ["Order Placed"].contains(order.status)).toList();
case "In Transit":
return parcelOrder.where((order) => ["Order Accepted", "Driver Accepted", "Driver Pending", "Order Shipped", "In Transit"].contains(order.status)).toList();
case "Delivered":
return parcelOrder.where((order) => ["Order Completed"].contains(order.status)).toList();
case "Cancelled":
return parcelOrder.where((order) => ["Order Rejected", "Order Cancelled", "Driver Rejected"].contains(order.status)).toList();
default:
return [];
}
}
/// Old helper (optional)
List<ParcelOrderModel> get filteredParcelOrders => getOrdersForTab(selectedTab.value);
String formatDate(Timestamp timestamp) {
final dateTime = timestamp.toDate();
return DateFormat("dd MMM yyyy, hh:mm a").format(dateTime);
}
Future<void> cancelParcelOrder(ParcelOrderModel order) async {
try {
isLoading.value = true;
if (order.status != Constant.orderPlaced) {
ShowToastDialog.showToast("You can only cancel before pickup.".tr);
return;
}
order.status = Constant.orderCancelled;
await FireStoreUtils.parcelOrderPlace(order);
listenParcelOrders();
if (order.paymentMethod?.toLowerCase() != "cod") {
double totalTax = 0.0;
final taxSettings = order.taxSetting ?? [];
for (var element in taxSettings) {
totalTax += Constant.calculateTax(amount: (double.parse(order.subTotal.toString()) - double.parse(order.discount.toString())).toString(), taxModel: element);
}
double subTotal = double.parse(order.subTotal.toString()) - double.parse(order.discount.toString());
double refundAmount = subTotal + totalTax;
WalletTransactionModel walletTransaction = WalletTransactionModel(
id: Constant.getUuid(),
amount: refundAmount,
date: Timestamp.now(),
paymentMethod: PaymentGateway.wallet.name,
transactionUser: "customer",
userId: FireStoreUtils.getCurrentUid(),
isTopup: true,
// refund
orderId: order.id,
note: "Refund for cancelled parcel order",
paymentStatus: "success",
serviceType: Constant.parcelServiceType,
);
// Save wallet transaction
await FireStoreUtils.setWalletTransaction(walletTransaction);
// Update wallet balance
await FireStoreUtils.updateUserWallet(amount: refundAmount.toString(), userId: FireStoreUtils.getCurrentUid());
}
ShowToastDialog.showToast("Order cancelled successfully".tr);
} catch (e) {
ShowToastDialog.showToast("${'Failed to cancel order:'.tr} $e".tr);
} finally {
isLoading.value = false;
}
}
@override
void onClose() {
_parcelSubscription?.cancel();
super.onClose();
}
}

View File

@@ -0,0 +1,983 @@
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'dart:math' as maths;
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:customer/models/coupon_model.dart';
import 'package:customer/models/wallet_transaction_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter_paypal/flutter_paypal.dart';
import 'package:flutter_stripe/flutter_stripe.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import 'package:image_picker/image_picker.dart';
import 'package:intl/intl.dart';
import 'package:razorpay_flutter/razorpay_flutter.dart';
import 'package:uuid/uuid.dart';
import '../../../models/parcel_order_model.dart';
import '../constant/constant.dart';
import '../models/payment_model/cod_setting_model.dart';
import '../models/payment_model/flutter_wave_model.dart';
import '../models/payment_model/mercado_pago_model.dart';
import '../models/payment_model/mid_trans.dart';
import '../models/payment_model/orange_money.dart';
import '../models/payment_model/pay_fast_model.dart';
import '../models/payment_model/pay_stack_model.dart';
import '../models/payment_model/paypal_model.dart';
import '../models/payment_model/paytm_model.dart';
import '../models/payment_model/razorpay_model.dart';
import '../models/payment_model/stripe_model.dart';
import '../models/payment_model/wallet_setting_model.dart';
import '../models/payment_model/xendit.dart';
import '../models/user_model.dart';
import '../payment/MercadoPagoScreen.dart';
import '../payment/PayFastScreen.dart';
import '../payment/getPaytmTxtToken.dart';
import '../payment/midtrans_screen.dart';
import '../payment/orangePayScreen.dart';
import '../payment/paystack/pay_stack_screen.dart';
import '../payment/paystack/pay_stack_url_model.dart';
import '../payment/paystack/paystack_url_genrater.dart';
import '../payment/stripe_failed_model.dart';
import '../payment/xenditModel.dart';
import '../payment/xenditScreen.dart';
import '../screen_ui/multi_vendor_service/wallet_screen/wallet_screen.dart';
import '../screen_ui/parcel_service/order_successfully_placed.dart';
import '../service/fire_store_utils.dart';
import '../themes/app_them_data.dart';
import '../themes/show_toast_dialog.dart';
import '../utils/preferences.dart';
class ParcelOrderConfirmationController extends GetxController {
RxBool isLoading = true.obs;
final Rx<ParcelOrderModel> parcelOrder = ParcelOrderModel().obs;
final RxList<XFile> images = <XFile>[].obs;
final RxString paymentBy = "Receiver".obs;
RxString selectedPaymentMethod = ''.obs;
RxBool isOrderPlaced = false.obs;
RxDouble subTotal = 0.0.obs;
RxDouble discount = 0.0.obs;
RxDouble taxAmount = 0.0.obs;
RxDouble totalAmount = 0.0.obs;
Rx<TextEditingController> couponController = TextEditingController().obs;
Rx<UserModel> userModel = UserModel().obs;
@override
void onInit() {
super.onInit();
getArgument();
}
Rx<CouponModel> selectedCouponModel = CouponModel().obs;
Future<void> getArgument() async {
final dynamic args = Get.arguments;
if (args != null) {
parcelOrder.value = args['parcelOrder'];
images.value = List<XFile>.from(args['images'] ?? []);
calculatePrice();
}
userModel.value = Constant.userModel!;
await fetchCoupons();
await getPaymentSettings();
isLoading.value = false;
update();
}
void calculatePrice() {
subTotal.value = 0;
discount.value = 0;
taxAmount.value = 0;
subTotal.value = double.tryParse(parcelOrder.value.subTotal ?? '0') ?? 0.0;
if (selectedCouponModel.value.id != null) {
discount.value = Constant.calculateDiscount(amount: subTotal.value.toString(), offerModel: selectedCouponModel.value);
}
for (var element in Constant.taxList) {
taxAmount.value = (taxAmount.value + Constant.calculateTax(amount: (subTotal.value - discount.value).toString(), taxModel: element));
}
print("Tax: ${taxAmount.value}");
print("Discount: ${discount.value}");
totalAmount.value = (subTotal.value - discount.value) + taxAmount.value;
}
RxList<CouponModel> couponList = <CouponModel>[].obs;
Future<void> fetchCoupons() async {
try {
await FireStoreUtils.getParcelCoupon().then((value) {
couponList.value = value;
});
} catch (e) {
print("Error fetching coupons: $e");
}
}
String formatDate(Timestamp timestamp) {
final dateTime = timestamp.toDate();
return DateFormat("dd MMM yyyy, hh:mm a").format(dateTime);
}
Future<void> placeOrder() async {
ShowToastDialog.showLoader("Please wait...".tr);
try {
List<String> parcelImages = [];
if (images.isNotEmpty) {
for (var image in images) {
final upload = await FireStoreUtils.uploadChatImageToFireStorage(File(image.path), Get.context!);
parcelImages.add(upload.url);
}
}
parcelOrder.value.parcelImages = parcelImages;
parcelOrder.value.discount = discount.value.toString();
parcelOrder.value.discountType = selectedCouponModel.value.discountType.toString();
parcelOrder.value.discountLabel = selectedCouponModel.value.code.toString();
parcelOrder.value.adminCommission = Constant.sectionConstantModel?.adminCommision?.amount?.toString();
parcelOrder.value.adminCommissionType = Constant.sectionConstantModel?.adminCommision?.commissionType;
parcelOrder.value.status = Constant.orderPlaced;
parcelOrder.value.createdAt = Timestamp.now();
parcelOrder.value.author = userModel.value;
parcelOrder.value.authorID = FireStoreUtils.getCurrentUid();
parcelOrder.value.paymentMethod = paymentBy.value == "Receiver" ? "cod" : selectedPaymentMethod.value;
parcelOrder.value.paymentCollectByReceiver = paymentBy.value == "Receiver";
parcelOrder.value.senderZoneId = Constant.getZoneId(parcelOrder.value.senderLatLong!.latitude ?? 0.0, parcelOrder.value.senderLatLong!.longitude ?? 0.0);
parcelOrder.value.receiverZoneId = Constant.getZoneId(parcelOrder.value.receiverLatLong!.latitude ?? 0.0, parcelOrder.value.receiverLatLong!.longitude ?? 0.0);
if (selectedPaymentMethod.value == PaymentGateway.wallet.name) {
WalletTransactionModel transactionModel = WalletTransactionModel(
id: Constant.getUuid(),
amount: double.parse(totalAmount.value.toString()),
date: Timestamp.now(),
paymentMethod: PaymentGateway.wallet.name,
transactionUser: "customer",
userId: FireStoreUtils.getCurrentUid(),
isTopup: false,
orderId: parcelOrder.value.id,
note: "Parcel Amount debited",
paymentStatus: "success",
serviceType: Constant.parcelServiceType,
);
await FireStoreUtils.setWalletTransaction(transactionModel).then((value) async {
if (value == true) {
await FireStoreUtils.updateUserWallet(amount: "-${totalAmount.value.toString()}", userId: FireStoreUtils.getCurrentUid());
}
});
}
await FireStoreUtils.parcelOrderPlace(parcelOrder.value).then((value) async {
ShowToastDialog.closeLoader();
ShowToastDialog.showToast("Order placed successfully".tr);
Get.offAll(() => OrderSuccessfullyPlaced(), arguments: {'parcelOrder': parcelOrder.value});
await FireStoreUtils.sendParcelBookEmail(orderModel: parcelOrder.value);
});
} catch (e) {
ShowToastDialog.closeLoader();
ShowToastDialog.showToast("Something went wrong. Please try again.".tr);
}
}
Rx<WalletSettingModel> walletSettingModel = WalletSettingModel().obs;
Rx<CodSettingModel> cashOnDeliverySettingModel = CodSettingModel().obs;
Rx<PayFastModel> payFastModel = PayFastModel().obs;
Rx<MercadoPagoModel> mercadoPagoModel = MercadoPagoModel().obs;
Rx<PayPalModel> payPalModel = PayPalModel().obs;
Rx<StripeModel> stripeModel = StripeModel().obs;
Rx<FlutterWaveModel> flutterWaveModel = FlutterWaveModel().obs;
Rx<PayStackModel> payStackModel = PayStackModel().obs;
Rx<PaytmModel> paytmModel = PaytmModel().obs;
Rx<RazorPayModel> razorPayModel = RazorPayModel().obs;
Rx<MidTrans> midTransModel = MidTrans().obs;
Rx<OrangeMoney> orangeMoneyModel = OrangeMoney().obs;
Rx<Xendit> xenditModel = Xendit().obs;
Future<void> getPaymentSettings() async {
await FireStoreUtils.getPaymentSettingsData().then((value) {
stripeModel.value = StripeModel.fromJson(jsonDecode(Preferences.getString(Preferences.stripeSettings)));
payPalModel.value = PayPalModel.fromJson(jsonDecode(Preferences.getString(Preferences.paypalSettings)));
payStackModel.value = PayStackModel.fromJson(jsonDecode(Preferences.getString(Preferences.payStack)));
mercadoPagoModel.value = MercadoPagoModel.fromJson(jsonDecode(Preferences.getString(Preferences.mercadoPago)));
flutterWaveModel.value = FlutterWaveModel.fromJson(jsonDecode(Preferences.getString(Preferences.flutterWave)));
paytmModel.value = PaytmModel.fromJson(jsonDecode(Preferences.getString(Preferences.paytmSettings)));
payFastModel.value = PayFastModel.fromJson(jsonDecode(Preferences.getString(Preferences.payFastSettings)));
razorPayModel.value = RazorPayModel.fromJson(jsonDecode(Preferences.getString(Preferences.razorpaySettings)));
midTransModel.value = MidTrans.fromJson(jsonDecode(Preferences.getString(Preferences.midTransSettings)));
orangeMoneyModel.value = OrangeMoney.fromJson(jsonDecode(Preferences.getString(Preferences.orangeMoneySettings)));
xenditModel.value = Xendit.fromJson(jsonDecode(Preferences.getString(Preferences.xenditSettings)));
walletSettingModel.value = WalletSettingModel.fromJson(jsonDecode(Preferences.getString(Preferences.walletSettings)));
cashOnDeliverySettingModel.value = CodSettingModel.fromJson(jsonDecode(Preferences.getString(Preferences.codSettings)));
if (walletSettingModel.value.isEnabled == true) {
selectedPaymentMethod.value = PaymentGateway.wallet.name;
} else if (cashOnDeliverySettingModel.value.isEnabled == true) {
selectedPaymentMethod.value = PaymentGateway.cod.name;
} else if (stripeModel.value.isEnabled == true) {
selectedPaymentMethod.value = PaymentGateway.stripe.name;
} else if (payPalModel.value.isEnabled == true) {
selectedPaymentMethod.value = PaymentGateway.paypal.name;
} else if (payStackModel.value.isEnable == true) {
selectedPaymentMethod.value = PaymentGateway.payStack.name;
} else if (mercadoPagoModel.value.isEnabled == true) {
selectedPaymentMethod.value = PaymentGateway.mercadoPago.name;
} else if (flutterWaveModel.value.isEnable == true) {
selectedPaymentMethod.value = PaymentGateway.flutterWave.name;
} else if (payFastModel.value.isEnable == true) {
selectedPaymentMethod.value = PaymentGateway.payFast.name;
} else if (razorPayModel.value.isEnabled == true) {
selectedPaymentMethod.value = PaymentGateway.razorpay.name;
} else if (midTransModel.value.enable == true) {
selectedPaymentMethod.value = PaymentGateway.midTrans.name;
} else if (orangeMoneyModel.value.enable == true) {
selectedPaymentMethod.value = PaymentGateway.orangeMoney.name;
} else if (xenditModel.value.enable == true) {
selectedPaymentMethod.value = PaymentGateway.xendit.name;
}
Stripe.publishableKey = stripeModel.value.clientpublishableKey.toString();
Stripe.merchantIdentifier = 'eMart Customer';
Stripe.instance.applySettings();
setRef();
razorPay.on(Razorpay.EVENT_PAYMENT_SUCCESS, handlePaymentSuccess);
razorPay.on(Razorpay.EVENT_EXTERNAL_WALLET, handleExternalWaller);
razorPay.on(Razorpay.EVENT_PAYMENT_ERROR, handlePaymentError);
});
}
// Strip
Future<void> stripeMakePayment({required String amount}) async {
log(double.parse(amount).toStringAsFixed(0));
try {
Map<String, dynamic>? paymentIntentData = await createStripeIntent(amount: amount);
log("stripe Responce====>$paymentIntentData");
if (paymentIntentData!.containsKey("error")) {
Get.back();
ShowToastDialog.showToast("Something went wrong, please contact admin.".tr);
} else {
await Stripe.instance.initPaymentSheet(
paymentSheetParameters: SetupPaymentSheetParameters(
paymentIntentClientSecret: paymentIntentData['client_secret'],
allowsDelayedPaymentMethods: false,
googlePay: const PaymentSheetGooglePay(merchantCountryCode: 'US', testEnv: true, currencyCode: "USD"),
customFlow: true,
style: ThemeMode.system,
appearance: PaymentSheetAppearance(colors: PaymentSheetAppearanceColors(primary: AppThemeData.primary300)),
merchantDisplayName: 'GoRide',
),
);
displayStripePaymentSheet(amount: amount);
}
} catch (e, s) {
log("$e \n$s");
ShowToastDialog.showToast("exception:$e \n$s");
}
}
Future<void> displayStripePaymentSheet({required String amount}) async {
try {
await Stripe.instance.presentPaymentSheet().then((value) {
ShowToastDialog.showToast("Payment successfully".tr);
placeOrder();
});
} on StripeException catch (e) {
var lo1 = jsonEncode(e);
var lo2 = jsonDecode(lo1);
StripePayFailedModel lom = StripePayFailedModel.fromJson(lo2);
ShowToastDialog.showToast(lom.error.message);
} catch (e) {
ShowToastDialog.showToast(e.toString());
}
}
Future createStripeIntent({required String amount}) async {
try {
Map<String, dynamic> body = {
'amount': ((double.parse(amount) * 100).round()).toString(),
'currency': "USD",
'payment_method_types[]': 'card',
"description": "Strip Payment",
"shipping[name]": Constant.userModel?.fullName(),
"shipping[address][line1]": "510 Townsend St",
"shipping[address][postal_code]": "98140",
"shipping[address][city]": "San Francisco",
"shipping[address][state]": "CA",
"shipping[address][country]": "US",
};
var stripeSecret = stripeModel.value.stripeSecret;
var response = await http.post(
Uri.parse('https://api.stripe.com/v1/payment_intents'),
body: body,
headers: {'Authorization': 'Bearer $stripeSecret', 'Content-Type': 'application/x-www-form-urlencoded'},
);
return jsonDecode(response.body);
} catch (e) {
log(e.toString());
}
}
//mercadoo
Future<Null> mercadoPagoMakePayment({required BuildContext context, required String amount}) async {
final headers = {'Authorization': 'Bearer ${mercadoPagoModel.value.accessToken}', 'Content-Type': 'application/json'};
final body = jsonEncode({
"items": [
{
"title": "Test",
"description": "Test Payment",
"quantity": 1,
"currency_id": "BRL", // or your preferred currency
"unit_price": double.parse(amount),
},
],
"payer": {"email": Constant.userModel?.email},
"back_urls": {"failure": "${Constant.globalUrl}payment/failure", "pending": "${Constant.globalUrl}payment/pending", "success": "${Constant.globalUrl}payment/success"},
"auto_return": "approved",
// Automatically return after payment is approved
});
final response = await http.post(Uri.parse("https://api.mercadopago.com/checkout/preferences"), headers: headers, body: body);
if (response.statusCode == 200 || response.statusCode == 201) {
final data = jsonDecode(response.body);
Get.to(MercadoPagoScreen(initialURl: data['init_point']))!.then((value) {
if (value) {
ShowToastDialog.showToast("Payment Successful!!".tr);
placeOrder();
} else {
ShowToastDialog.showToast("Payment UnSuccessful!!".tr);
}
});
} else {
print('Error creating preference: ${response.body}');
return null;
}
}
//Paypal
void paypalPaymentSheet(String amount, BuildContext context) {
// ✅ Ensure amount format is correct (e.g. 10.00)
final formattedAmount = double.parse(amount).toStringAsFixed(2);
Navigator.of(context).push(
MaterialPageRoute(
builder:
(BuildContext context) => UsePaypal(
sandboxMode: payPalModel.value.isLive == true ? false : true,
clientId: payPalModel.value.paypalClient ?? '',
secretKey: payPalModel.value.paypalSecret ?? '',
returnURL: "com.emart.customer://paypalpay",
cancelURL: "com.emart.customer://paypalcancel",
transactions: [
{
"amount": {
"total": formattedAmount,
"currency": "USD",
"details": {"subtotal": formattedAmount},
},
},
],
note: "Contact us for any questions on your order.",
onSuccess: (Map params) async {
debugPrint("✅ PayPal Payment Success: $params");
placeOrder();
ShowToastDialog.showToast("Payment Successful!!".tr);
},
onError: (error) {
debugPrint("❌ PayPal Payment Error: $error");
Get.back();
ShowToastDialog.showToast("Payment UnSuccessful!!".tr);
},
onCancel: (params) {
debugPrint("⚠️ PayPal Payment Canceled: $params");
Get.back();
ShowToastDialog.showToast("Payment UnSuccessful!!".tr);
},
),
),
);
}
// void paypalPaymentSheet(String amount, context) {
// Navigator.of(context).push(
// MaterialPageRoute(
// builder:
// (BuildContext context) => UsePaypal(
// sandboxMode: payPalModel.value.isLive == true ? false : true,
// clientId: payPalModel.value.paypalClient ?? '',
// secretKey: payPalModel.value.paypalSecret ?? '',
// returnURL: "https://success.emart.com/return",
// cancelURL: "https://cancel.emart.com/cancel",
// // returnURL: "com.emart.customer://paypalpay",
// // cancelURL: "com.emart.customer://paypalpay",
// transactions: [
// {
// "amount": {
// "total": amount,
// "currency": "USD",
// "details": {"subtotal": amount},
// },
// },
// ],
// note: "Contact us for any questions on your order.",
// onSuccess: (Map params) async {
// placeOrder();
// ShowToastDialog.showToast("Payment Successful!!".tr);
// },
// onError: (error) {
// Get.back();
// ShowToastDialog.showToast("Payment UnSuccessful!!".tr);
// },
// onCancel: (params) {
// Get.back();
// ShowToastDialog.showToast("Payment UnSuccessful!!".tr);
// },
// ),
// ),
// );
// }
///PayStack Payment Method
Future<void> payStackPayment(String totalAmount) async {
// Convert to int (kobo/cents)
int amountInCents = (double.parse(totalAmount) * 100).round();
await PayStackURLGen.payStackURLGen(
amount: amountInCents.toString(), //integer string
currency: "ZAR",
secretKey: payStackModel.value.secretKey.toString(),
userModel: Constant.userModel!,
).then((value) async {
if (value != null) {
PayStackUrlModel payStackModel0 = value;
Get.to(
PayStackScreen(
secretKey: payStackModel.value.secretKey.toString(),
callBackUrl: payStackModel.value.callbackURL.toString(),
initialURl: payStackModel0.data.authorizationUrl,
amount: totalAmount,
reference: payStackModel0.data.reference,
),
)!.then((value) {
if (value) {
ShowToastDialog.showToast("Payment Successful!!".tr);
placeOrder();
} else {
ShowToastDialog.showToast("Payment UnSuccessful!!".tr);
}
});
} else {
ShowToastDialog.showToast("Something went wrong, please contact admin.".tr);
}
});
}
///flutter wave Payment Method
Future<void> flutterWaveInitiatePayment({required BuildContext context, required String amount}) async {
setRef(); // make sure you generate reference
final url = Uri.parse('https://api.flutterwave.com/v3/payments');
final headers = {'Authorization': 'Bearer ${flutterWaveModel.value.secretKey}', 'Content-Type': 'application/json'};
final body = jsonEncode({
"tx_ref": _ref,
"amount": amount,
"currency": "NGN",
"redirect_url": "${Constant.globalUrl}payment/success",
"payment_options": "ussd, card, barter, payattitude",
"customer": {"email": Constant.userModel?.email.toString(), "phonenumber": Constant.userModel?.phoneNumber, "name": Constant.userModel?.fullName()},
"customizations": {"title": "Payment for Services", "description": "Payment for XYZ services"},
});
final response = await http.post(url, headers: headers, body: body);
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
Get.to(MercadoPagoScreen(initialURl: data['data']['link']))!.then((value) async {
bool isVerified = await verifyFlutterWavePayment(_ref!);
if (isVerified) {
ShowToastDialog.showToast("Payment Successful!!".tr);
placeOrder();
} else {
ShowToastDialog.showToast("Payment Unsuccessful!!".tr);
Get.back();
}
});
} else {
debugPrint('Payment initialization failed: ${response.body}');
}
}
Future<bool> verifyFlutterWavePayment(String txRef) async {
try {
final url = Uri.parse("https://api.flutterwave.com/v3/transactions/verify_by_reference?tx_ref=$txRef");
final headers = {'Authorization': 'Bearer ${flutterWaveModel.value.secretKey}', 'Content-Type': 'application/json'};
final response = await http.get(url, headers: headers);
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
if (data['status'] == 'success' && data['data']['status'] == 'successful') {
return true; // ✅ Payment confirmed
}
}
return false; // ❌ Payment not verified
} catch (e) {
debugPrint("Error verifying payment: $e");
return false;
}
}
String? _ref;
void setRef() {
maths.Random numRef = maths.Random();
int year = DateTime.now().year;
int refNumber = numRef.nextInt(20000);
if (Platform.isAndroid) {
_ref = "AndroidRef$year$refNumber";
} else if (Platform.isIOS) {
_ref = "IOSRef$year$refNumber";
}
}
// payFast
void payFastPayment({required BuildContext context, required String amount}) {
PayStackURLGen.getPayHTML(payFastSettingData: payFastModel.value, amount: amount.toString(), userModel: Constant.userModel!).then((String? value) async {
bool isDone = await Get.to(PayFastScreen(htmlData: value!, payFastSettingData: payFastModel.value));
if (isDone) {
Get.back();
ShowToastDialog.showToast("Payment successfully".tr);
placeOrder();
} else {
Get.back();
ShowToastDialog.showToast("Payment Failed".tr);
}
});
}
///Paytm payment function
Future<void> getPaytmCheckSum(context, {required double amount}) async {
final String orderId = DateTime.now().millisecondsSinceEpoch.toString();
String getChecksum = "${Constant.globalUrl}payments/getpaytmchecksum";
final response = await http.post(
Uri.parse(getChecksum),
headers: {},
body: {"mid": paytmModel.value.paytmMID.toString(), "order_id": orderId, "key_secret": paytmModel.value.pAYTMMERCHANTKEY.toString()},
);
final data = jsonDecode(response.body);
await verifyCheckSum(checkSum: data["code"], amount: amount, orderId: orderId).then((value) {
initiatePayment(amount: amount, orderId: orderId).then((value) {
String callback = "";
if (paytmModel.value.isSandboxEnabled == true) {
callback = "${callback}https://securegw-stage.paytm.in/theia/paytmCallback?ORDER_ID=$orderId";
} else {
callback = "${callback}https://securegw.paytm.in/theia/paytmCallback?ORDER_ID=$orderId";
}
GetPaymentTxtTokenModel result = value;
startTransaction(context, txnTokenBy: result.body.txnToken ?? '', orderId: orderId, amount: amount, callBackURL: callback, isStaging: paytmModel.value.isSandboxEnabled);
});
});
}
Future<void> startTransaction(context, {required String txnTokenBy, required orderId, required double amount, required callBackURL, required isStaging}) async {
// try {
// var response = AllInOneSdk.startTransaction(
// paytmModel.value.paytmMID.toString(),
// orderId,
// amount.toString(),
// txnTokenBy,
// callBackURL,
// isStaging,
// true,
// true,
// );
//
// response.then((value) {
// if (value!["RESPMSG"] == "Txn Success") {
// print("txt done!!");
// ShowToastDialog.showToast("Payment Successful!!");
// placeOrder();
// }
// }).catchError((onError) {
// if (onError is PlatformException) {
// Get.back();
//
// ShowToastDialog.showToast(onError.message.toString());
// } else {
// log("======>>2");
// Get.back();
// ShowToastDialog.showToast(onError.message.toString());
// }
// });
// } catch (err) {
// Get.back();
// ShowToastDialog.showToast(err.toString());
// }
}
Future verifyCheckSum({required String checkSum, required double amount, required orderId}) async {
String getChecksum = "${Constant.globalUrl}payments/validatechecksum";
final response = await http.post(
Uri.parse(getChecksum),
headers: {},
body: {"mid": paytmModel.value.paytmMID.toString(), "order_id": orderId, "key_secret": paytmModel.value.pAYTMMERCHANTKEY.toString(), "checksum_value": checkSum},
);
final data = jsonDecode(response.body);
return data['status'];
}
Future<GetPaymentTxtTokenModel> initiatePayment({required double amount, required String orderId}) async {
String initiateURL = "${Constant.globalUrl}payments/initiatepaytmpayment";
String callback =
(paytmModel.value.isSandboxEnabled ?? false) ? "https://securegw-stage.paytm.in/theia/paytmCallback?ORDER_ID=$orderId" : "https://securegw.paytm.in/theia/paytmCallback?ORDER_ID=$orderId";
print("INITIATE PAYMENT CALL:");
print("MID: ${paytmModel.value.paytmMID}");
print("OrderId: $orderId");
print("Amount: $amount");
print("Env: ${(paytmModel.value.isSandboxEnabled ?? false) ? "STAGING" : "LIVE"}");
final response = await http.post(
Uri.parse(initiateURL),
body: {
"mid": paytmModel.value.paytmMID ?? "",
"order_id": orderId,
"key_secret": paytmModel.value.pAYTMMERCHANTKEY ?? "",
"amount": amount.toStringAsFixed(0), // Paytm requires integer
"currency": "INR",
"callback_url": callback,
"custId": FireStoreUtils.getCurrentUid(),
"issandbox": (paytmModel.value.isSandboxEnabled ?? false) ? "1" : "0",
},
);
log("Paytm Initiate Response: ${response.body}");
final data = jsonDecode(response.body);
if (data["body"]["txnToken"] == null || data["body"]["txnToken"].toString().isEmpty) {
Get.back();
ShowToastDialog.showToast("Something went wrong, please contact admin.".tr);
}
return GetPaymentTxtTokenModel.fromJson(data);
}
// Future<GetPaymentTxtTokenModel> initiatePayment({required double amount, required orderId}) async {
// String initiateURL = "${Constant.globalUrl}payments/initiatepaytmpayment";
// String callback = "";
// if (paytmModel.value.isSandboxEnabled == true) {
// callback = "${callback}https://securegw-stage.paytm.in/theia/paytmCallback?ORDER_ID=$orderId";
// } else {
// callback = "${callback}https://securegw.paytm.in/theia/paytmCallback?ORDER_ID=$orderId";
// }
// final response = await http.post(
// Uri.parse(initiateURL),
// headers: {},
// body: {
// "mid": paytmModel.value.paytmMID,
// "order_id": orderId,
// "key_secret": paytmModel.value.pAYTMMERCHANTKEY,
// "amount": amount.toString(),
// "currency": "INR",
// "callback_url": callback,
// "custId": FireStoreUtils.getCurrentUid(),
// "issandbox": paytmModel.value.isSandboxEnabled == true ? "1" : "2",
// },
// );
// log(response.body);
// final data = jsonDecode(response.body);
// if (data["body"]["txnToken"] == null || data["body"]["txnToken"].toString().isEmpty) {
// Get.back();
// ShowToastDialog.showToast("something went wrong, please contact admin.".tr);
// }
// return GetPaymentTxtTokenModel.fromJson(data);
// }
///RazorPay payment function
final Razorpay razorPay = Razorpay();
void openCheckout({required amount, required orderId}) async {
var options = {
'key': razorPayModel.value.razorpayKey,
'amount': amount * 100,
'name': 'GoRide',
'order_id': orderId,
"currency": "INR",
'description': 'wallet Topup',
'retry': {'enabled': true, 'max_count': 1},
'send_sms_hash': true,
'prefill': {'contact': Constant.userModel?.phoneNumber, 'email': Constant.userModel?.email},
'external': {
'wallets': ['paytm'],
},
};
try {
razorPay.open(options);
} catch (e) {
debugPrint('Error: $e');
}
}
void handlePaymentSuccess(PaymentSuccessResponse response) {
Get.back();
ShowToastDialog.showToast("Payment Successful!!".tr);
placeOrder();
}
void handleExternalWaller(ExternalWalletResponse response) {
Get.back();
ShowToastDialog.showToast("Payment Processing!! via".tr);
}
void handlePaymentError(PaymentFailureResponse response) {
Get.back();
ShowToastDialog.showToast("Payment Failed!!".tr);
}
bool isCurrentDateInRange(DateTime startDate, DateTime endDate) {
final currentDate = DateTime.now();
return currentDate.isAfter(startDate) && currentDate.isBefore(endDate);
}
//Midtrans payment
Future<void> midtransMakePayment({required String amount, required BuildContext context}) async {
await createPaymentLink(amount: amount).then((url) {
ShowToastDialog.closeLoader();
if (url != '') {
Get.to(() => MidtransScreen(initialURl: url))!.then((value) {
if (value == true) {
ShowToastDialog.showToast("Payment Successful!!".tr);
placeOrder();
} else {
ShowToastDialog.showToast("Payment Unsuccessful!!".tr);
}
});
}
});
}
Future<String> createPaymentLink({required var amount}) async {
var ordersId = const Uuid().v1();
final url = Uri.parse(midTransModel.value.isSandbox! ? 'https://api.sandbox.midtrans.com/v1/payment-links' : 'https://api.midtrans.com/v1/payment-links');
final response = await http.post(
url,
headers: {'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': generateBasicAuthHeader(midTransModel.value.serverKey!)},
body: jsonEncode({
'transaction_details': {'order_id': ordersId, 'gross_amount': double.parse(amount.toString()).toInt()},
'usage_limit': 2,
"callbacks": {"finish": "https://www.google.com?merchant_order_id=$ordersId"},
}),
);
if (response.statusCode == 200 || response.statusCode == 201) {
final responseData = jsonDecode(response.body);
return responseData['payment_url'];
} else {
ShowToastDialog.showToast("something went wrong, please contact admin.".tr);
return '';
}
}
String generateBasicAuthHeader(String apiKey) {
String credentials = '$apiKey:';
String base64Encoded = base64Encode(utf8.encode(credentials));
return 'Basic $base64Encoded';
}
// 🟠 ORANGE MONEY PAYMENT INTEGRATION
static String accessToken = '';
static String payToken = '';
static String orderId = '';
static String amount = '';
Future<void> orangeMakePayment({required String amount, required BuildContext context}) async {
reset();
var id = const Uuid().v4();
debugPrint('🟩 Starting OrangePay Payment...');
debugPrint('💰 Amount: $amount | 🆔 Order ID: $id');
ShowToastDialog.showLoader("Initializing payment...".tr);
var paymentURL = await fetchToken(context: context, orderId: id, amount: amount, currency: 'USD');
ShowToastDialog.closeLoader();
if (paymentURL.toString().isNotEmpty) {
debugPrint('✅ Payment URL fetched successfully: $paymentURL');
Get.to(() => OrangeMoneyScreen(initialURl: paymentURL, accessToken: accessToken, amount: amount, orangePay: orangeMoneyModel.value, orderId: orderId, payToken: payToken))?.then((value) async {
if (value == true) {
ShowToastDialog.showToast("Payment Successful!!".tr);
debugPrint('🎉 Payment Successful for Order ID: $orderId');
if (Get.isBottomSheetOpen ?? false) Get.back();
await placeOrder();
} else {
ShowToastDialog.showToast("Payment Unsuccessful!!".tr);
debugPrint('⚠️ Payment flow closed without success.');
if (Get.isBottomSheetOpen ?? false) Get.back();
}
});
} else {
ShowToastDialog.showToast("Payment Unsuccessful!!".tr);
if (Get.isBottomSheetOpen ?? false) Get.back();
}
}
Future fetchToken({required String orderId, required String currency, required BuildContext context, required String amount}) async {
const String apiUrl = 'https://api.orange.com/oauth/v3/token';
final Map<String, String> requestBody = {'grant_type': 'client_credentials'};
debugPrint('🔐 Fetching access token from Orange API...');
debugPrint('📡 POST $apiUrl');
final response = await http.post(
Uri.parse(apiUrl),
headers: {'Authorization': "Basic ${orangeMoneyModel.value.auth!}", 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json'},
body: requestBody,
);
debugPrint('🔍 Response Code: ${response.statusCode}');
debugPrint('📨 Response Body: ${response.body}');
if (response.statusCode == 200) {
final Map<String, dynamic> responseData = jsonDecode(response.body);
accessToken = responseData['access_token'];
debugPrint('✅ Access Token Received: $accessToken');
return await webpayment(context: context, amountData: amount, currency: currency, orderIdData: orderId);
} else {
debugPrint('❌ Failed to fetch access token.');
ShowToastDialog.showToast("Something went wrong, please contact admin.".tr);
return '';
}
}
Future webpayment({required String orderIdData, required BuildContext context, required String currency, required String amountData}) async {
orderId = orderIdData;
amount = amountData;
String apiUrl = orangeMoneyModel.value.isSandbox == true ? 'https://api.orange.com/orange-money-webpay/dev/v1/webpayment' : 'https://api.orange.com/orange-money-webpay/cm/v1/webpayment';
// ✅ Ensure amount formatted correctly
String formattedAmount = double.parse(amountData).toStringAsFixed(2);
Map<String, String> requestBody = {
"merchant_key": orangeMoneyModel.value.merchantKey ?? '',
"currency": orangeMoneyModel.value.isSandbox == true ? "OUV" : currency,
"order_id": orderId,
"amount": formattedAmount,
"reference": 'Y-Note Test',
"lang": "en",
"return_url": orangeMoneyModel.value.returnUrl!.toString(),
"cancel_url": orangeMoneyModel.value.cancelUrl!.toString(),
"notif_url": orangeMoneyModel.value.notifyUrl ?? '',
};
debugPrint('💳 Creating Web Payment...');
debugPrint('📡 POST $apiUrl');
debugPrint('📦 Request Body: ${jsonEncode(requestBody)}');
final response = await http.post(
Uri.parse(apiUrl),
headers: {'Authorization': 'Bearer $accessToken', 'Content-Type': 'application/json', 'Accept': 'application/json'},
body: json.encode(requestBody),
);
debugPrint('🔍 Response Code: ${response.statusCode}');
debugPrint('📨 Response Body: ${response.body}');
if (response.statusCode == 201) {
final Map<String, dynamic> responseData = jsonDecode(response.body);
if (responseData['message'] == 'OK') {
payToken = responseData['pay_token'];
debugPrint('✅ Payment Token: $payToken');
debugPrint('🌍 Payment URL: ${responseData['payment_url']}');
return responseData['payment_url'];
} else {
debugPrint('⚠️ Unexpected message: ${responseData['message']}');
return '';
}
} else {
debugPrint('❌ Payment request failed.');
ShowToastDialog.showToast("Something went wrong, please contact admin.".tr);
return '';
}
}
static void reset() {
accessToken = '';
payToken = '';
orderId = '';
amount = '';
debugPrint('🌀 OrangePay reset completed.');
}
//XenditPayment
Future<void> xenditPayment(context, amount) async {
await createXenditInvoice(amount: amount).then((model) {
ShowToastDialog.closeLoader();
if (model.id != null) {
Get.to(() => XenditScreen(initialURl: model.invoiceUrl ?? '', transId: model.id ?? '', apiKey: xenditModel.value.apiKey!.toString()))!.then((value) {
if (value == true) {
ShowToastDialog.showToast("Payment Successful!!".tr);
placeOrder();
();
} else {
ShowToastDialog.showToast("Payment Unsuccessful!!".tr);
}
});
}
});
}
Future<XenditModel> createXenditInvoice({required var amount}) async {
const url = 'https://api.xendit.co/v2/invoices';
var headers = {
'Content-Type': 'application/json',
'Authorization': generateBasicAuthHeader(xenditModel.value.apiKey!.toString()),
// 'Cookie': '__cf_bm=yERkrx3xDITyFGiou0bbKY1bi7xEwovHNwxV1vCNbVc-1724155511-1.0.1.1-jekyYQmPCwY6vIJ524K0V6_CEw6O.dAwOmQnHtwmaXO_MfTrdnmZMka0KZvjukQgXu5B.K_6FJm47SGOPeWviQ',
};
final body = jsonEncode({
'external_id': const Uuid().v1(),
'amount': amount,
'payer_email': 'customer@domain.com',
'description': 'Test - VA Successful invoice payment',
'currency': 'IDR', //IDR, PHP, THB, VND, MYR
});
try {
final response = await http.post(Uri.parse(url), headers: headers, body: body);
if (response.statusCode == 200 || response.statusCode == 201) {
XenditModel model = XenditModel.fromJson(jsonDecode(response.body));
return model;
} else {
return XenditModel();
}
} catch (e) {
return XenditModel();
}
}
}

View File

@@ -0,0 +1,139 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:customer/models/rating_model.dart';
import 'package:customer/models/wallet_transaction_model.dart';
import 'package:customer/screen_ui/multi_vendor_service/wallet_screen/wallet_screen.dart';
import 'package:customer/themes/show_toast_dialog.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import '../constant/constant.dart';
import '../models/parcel_category.dart';
import '../models/parcel_order_model.dart';
import '../models/user_model.dart';
import '../service/fire_store_utils.dart';
class ParcelOrderDetailsController extends GetxController {
Rx<ParcelOrderModel> parcelOrder = ParcelOrderModel().obs;
RxList<ParcelCategory> parcelCategory = <ParcelCategory>[].obs;
RxBool isLoading = false.obs;
Rx<UserModel?> driverUser = Rx<UserModel?>(null);
Rx<RatingModel> ratingModel = RatingModel().obs;
@override
void onInit() {
super.onInit();
final args = Get.arguments;
if (args != null && args is ParcelOrderModel) {
parcelOrder.value = args;
setStatusHistoryFromString(parcelOrder.value);
}
loadParcelCategories();
calculateTotalAmount();
fetchDriverDetails();
}
RxDouble subTotal = 0.0.obs;
RxDouble discount = 0.0.obs;
RxDouble taxAmount = 0.0.obs;
RxDouble totalAmount = 0.0.obs;
void calculateTotalAmount() {
taxAmount = 0.0.obs;
discount = 0.0.obs;
subTotal.value = double.parse(parcelOrder.value.subTotal.toString());
discount.value = double.parse(parcelOrder.value.discount ?? '0.0');
for (var element in parcelOrder.value.taxSetting!) {
taxAmount.value = (taxAmount.value + Constant.calculateTax(amount: (subTotal.value - discount.value).toString(), taxModel: element));
}
totalAmount.value = (subTotal.value - discount.value) + taxAmount.value;
update();
}
Future<void> fetchDriverDetails() async {
if (parcelOrder.value.driverId != null) {
await FireStoreUtils.getUserProfile(parcelOrder.value.driverId ?? '').then((value) {
if (value != null) {
driverUser.value = value;
}
});
await FireStoreUtils.getReviewsbyID(parcelOrder.value.id.toString()).then((value) {
if (value != null) {
ratingModel.value = value;
}
});
}
}
void setStatusHistoryFromString(ParcelOrderModel order) {
final steps = ["Order Placed", "Driver Accepted", "Pickup Done", "In Transit", "Delivered"];
final history = <ParcelStatus>[];
DateTime baseTime = order.createdAt?.toDate() ?? DateTime.now();
int minutesGap = 30;
for (int i = 0; i < steps.length; i++) {
final step = steps[i];
history.add(ParcelStatus(status: step, time: baseTime.add(Duration(minutes: i * minutesGap))));
if (step == order.status) break;
}
order.statusHistory = history;
}
Future<void> cancelParcelOrder() async {
ShowToastDialog.showLoader("Cancelling order...".tr);
parcelOrder.value.status = Constant.orderCancelled;
if (parcelOrder.value.paymentMethod?.toLowerCase() != "cod") {
WalletTransactionModel walletTransaction = WalletTransactionModel(
id: Constant.getUuid(),
amount: totalAmount.value,
date: Timestamp.now(),
paymentMethod: PaymentGateway.wallet.name,
transactionUser: "customer",
userId: FireStoreUtils.getCurrentUid(),
isTopup: true,
orderId: parcelOrder.value.id,
note: "Refund for cancelled parcel order",
paymentStatus: "success",
serviceType: Constant.parcelServiceType,
);
// Save wallet transaction
await FireStoreUtils.setWalletTransaction(walletTransaction);
// Update wallet balance
await FireStoreUtils.updateUserWallet(amount: totalAmount.value.toString(), userId: FireStoreUtils.getCurrentUid());
}
await FireStoreUtils.parcelOrderPlace(parcelOrder.value);
ShowToastDialog.closeLoader();
ShowToastDialog.showToast("Order cancelled successfully".tr);
Get.back(result: true);
}
void loadParcelCategories() async {
isLoading.value = true;
final categories = await FireStoreUtils.getParcelServiceCategory();
parcelCategory.value = categories;
isLoading.value = false;
}
String formatDate(Timestamp timestamp) {
final dateTime = timestamp.toDate();
return DateFormat("dd MMM yyyy, hh:mm a").format(dateTime);
}
ParcelCategory? getSelectedCategory() {
try {
return parcelCategory.firstWhere((cat) => cat.title?.toLowerCase().trim() == parcelOrder.value.parcelType?.toLowerCase().trim(), orElse: () => ParcelCategory());
} catch (e) {
return null;
}
}
}

View File

@@ -0,0 +1,135 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:customer/models/parcel_order_model.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../models/rating_model.dart';
import '../models/user_model.dart';
import '../service/fire_store_utils.dart';
import '../constant/constant.dart';
import '../themes/show_toast_dialog.dart';
class ParcelReviewController extends GetxController {
RxBool isLoading = true.obs;
/// Order from arguments
final Rx<ParcelOrderModel?> order = Rx<ParcelOrderModel?>(null);
/// Rating data
final Rx<RatingModel?> ratingModel = Rx<RatingModel?>(null);
final RxDouble ratings = 0.0.obs;
final Rx<TextEditingController> comment = TextEditingController().obs;
/// Driver (to be reviewed)
final Rx<UserModel?> driverUser = Rx<UserModel?>(null);
/// Review stats
final RxInt futureCount = 0.obs;
final RxInt futureSum = 0.obs;
@override
void onInit() {
super.onInit();
final args = Get.arguments;
if (args != null && args['order'] != null) {
order.value = args['order'] as ParcelOrderModel;
getReview();
}
}
/// Fetch old review + driver stats
Future<void> getReview() async {
await FireStoreUtils.getReviewsbyID(order.value?.id ?? "").then((value) {
if (value != null) {
ratingModel.value = value;
ratings.value = value.rating ?? 0;
comment.value.text = value.comment ?? "";
}
});
await FireStoreUtils.getUserProfile(order.value?.driverId ?? '').then((value) {
if (value != null) {
driverUser.value = value;
final int userReviewsCount = int.tryParse(driverUser.value!.reviewsCount?.toString() ?? "0") ?? 0;
final int userReviewsSum = int.tryParse(driverUser.value!.reviewsSum?.toString() ?? "0") ?? 0;
if (ratingModel.value != null) {
final int oldRating = ratingModel.value?.rating?.toInt() ?? 0;
futureCount.value = userReviewsCount - 1;
futureSum.value = userReviewsSum - oldRating;
} else {
futureCount.value = userReviewsCount;
futureSum.value = userReviewsSum;
}
}
});
isLoading.value = false;
}
/// Save / update review
Future<void> submitReview() async {
if (comment.value.text.trim().isEmpty || ratings.value == 0) {
ShowToastDialog.showToast("Please provide rating and comment".tr);
return;
}
ShowToastDialog.showLoader("Submit in...".tr);
final user = await FireStoreUtils.getUserProfile(order.value?.driverId ?? '');
if (user != null) {
user.reviewsCount = (futureCount.value + 1).toString();
user.reviewsSum = (futureSum.value + ratings.value.toInt()).toString();
}
if (ratingModel.value != null && ratingModel.value!.id!.isNotEmpty) {
/// Update existing review
final updatedRating = RatingModel(
id: ratingModel.value!.id,
comment: comment.value.text,
photos: ratingModel.value?.photos ?? [],
rating: ratings.value,
orderId: ratingModel.value!.orderId,
driverId: ratingModel.value!.driverId,
customerId: ratingModel.value!.customerId,
vendorId: ratingModel.value?.vendorId,
uname: "${Constant.userModel?.firstName ?? ''} ${Constant.userModel?.lastName ?? ''}",
profile: Constant.userModel?.profilePictureURL,
createdAt: Timestamp.now(),
);
await FireStoreUtils.updateReviewById(updatedRating);
if (user != null) {
await FireStoreUtils.updateUser(user);
}
} else {
/// New review
final newRating = RatingModel(
id: Constant.getUuid(),
comment: comment.value.text,
photos: [],
rating: ratings.value,
orderId: order.value?.id,
driverId: order.value?.driverId.toString(),
customerId: Constant.userModel?.id,
uname: "${Constant.userModel?.firstName ?? ''} ${Constant.userModel?.lastName ?? ''}",
profile: Constant.userModel?.profilePictureURL,
createdAt: Timestamp.now(),
);
await FireStoreUtils.updateReviewById(newRating);
if (user != null) {
await FireStoreUtils.updateUser(user);
}
}
ShowToastDialog.closeLoader();
Get.back(result: true);
}
@override
void onClose() {
comment.value.dispose();
super.onClose();
}
}

View File

@@ -0,0 +1,40 @@
import 'package:customer/controllers/on_demand_home_controller.dart';
import 'package:get/get.dart';
import '../models/provider_serivce_model.dart';
import '../models/user_model.dart';
import '../service/fire_store_utils.dart';
class ProviderController extends GetxController {
RxList<ProviderServiceModel> providerList = <ProviderServiceModel>[].obs;
final Rxn<UserModel> userModel = Rxn<UserModel>();
RxBool isLoading = true.obs;
late final String providerId;
Rx<OnDemandHomeController> onDemandHomeController = Get.put(OnDemandHomeController()).obs;
@override
void onInit() {
super.onInit();
//Get providerId from arguments
providerId = Get.arguments['providerId'];
getProvider();
getAuthor();
}
void getProvider() async {
FireStoreUtils.getProviderServiceByProviderId(providerId: providerId).then((catValue) {
providerList.value = catValue;
});
isLoading.value = false;
}
Future<void> getAuthor() async {
final user = await FireStoreUtils.getUserProfile(providerId);
if (user != null) {
userModel.value = user;
}
}
}

View File

@@ -0,0 +1,194 @@
import 'dart:developer';
import 'dart:io';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:customer/constant/constant.dart';
import 'package:customer/models/order_model.dart';
import 'package:customer/models/product_model.dart';
import 'package:customer/models/vendor_category_model.dart';
import 'package:customer/models/vendor_model.dart';
import '../models/rating_model.dart';
import '../models/review_attribute_model.dart';
import '../service/fire_store_utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:image_picker/image_picker.dart';
import '../themes/show_toast_dialog.dart';
class RateProductController extends GetxController {
RxBool isLoading = true.obs;
@override
void onInit() {
// TODO: implement onInit
getArgument();
super.onInit();
}
Rx<TextEditingController> commentController = TextEditingController().obs;
Rx<OrderModel> orderModel = OrderModel().obs;
RxString productId = "".obs;
Rx<RatingModel> ratingModel = RatingModel().obs;
Rx<ProductModel> productModel = ProductModel().obs;
Rx<VendorModel> vendorModel = VendorModel().obs;
Rx<VendorCategoryModel> vendorCategoryModel = VendorCategoryModel().obs;
RxList<ReviewAttributeModel> reviewAttributeList = <ReviewAttributeModel>[].obs;
RxDouble ratings = 0.0.obs;
RxMap<String, dynamic> reviewAttribute = <String, dynamic>{}.obs;
RxMap<String, dynamic> reviewProductAttributes = <String, dynamic>{}.obs;
RxDouble vendorReviewSum = 0.0.obs;
RxDouble vendorReviewCount = 0.0.obs;
RxDouble productReviewSum = 0.0.obs;
RxDouble productReviewCount = 0.0.obs;
Future<void> getArgument() async {
dynamic argumentData = Get.arguments;
if (argumentData != null) {
orderModel.value = argumentData['orderModel'];
productId.value = argumentData['productId'];
await FireStoreUtils.getOrderReviewsByID(orderModel.value.id.toString(), productId.value).then((value) {
if (value != null) {
ratingModel.value = value;
ratings.value = value.rating ?? 0.0;
commentController.value.text = value.comment.toString();
reviewAttribute.value = value.reviewAttributes!;
images.addAll(value.photos ?? []);
}
});
await FireStoreUtils.getProductById(productId.value.split('~').first).then((value) {
if (value != null) {
productModel.value = value;
if (ratingModel.value.id != null && ratingModel.value.id!.isNotEmpty) {
productReviewCount.value = value.reviewsCount! - 1;
productReviewSum.value = value.reviewsSum! - ratings.value;
if (value.reviewAttributes != null) {
value.reviewAttributes!.forEach((key, value) {
ReviewsAttribute reviewsAttributeModel = ReviewsAttribute.fromJson(value);
reviewsAttributeModel.reviewsCount = reviewsAttributeModel.reviewsCount! - 1;
reviewsAttributeModel.reviewsSum = reviewsAttributeModel.reviewsSum! - reviewAttribute[key];
reviewProductAttributes.addEntries([MapEntry(key, reviewsAttributeModel.toJson())]);
});
}
} else {
productReviewCount.value = double.parse(value.reviewsCount.toString());
productReviewSum.value = double.parse(value.reviewsSum.toString());
if (value.reviewAttributes != null) {
reviewProductAttributes.value = value.reviewAttributes!;
}
}
}
});
await FireStoreUtils.getVendorById(productModel.value.vendorID.toString()).then((value) {
if (value != null) {
vendorModel.value = value;
if (ratingModel.value.id != null && ratingModel.value.id!.isNotEmpty) {
vendorReviewCount.value = value.reviewsCount! - 1;
vendorReviewSum.value = value.reviewsSum! - ratings.value;
} else {
vendorReviewCount.value = double.parse(value.reviewsCount.toString());
vendorReviewSum.value = double.parse(value.reviewsSum.toString());
}
}
});
await FireStoreUtils.getVendorCategoryByCategoryId(productModel.value.categoryID.toString()).then((value) async {
if (value != null) {
vendorCategoryModel.value = value;
for (var element in vendorCategoryModel.value.reviewAttributes!) {
await FireStoreUtils.getVendorReviewAttribute(element).then((value) {
reviewAttributeList.add(value!);
});
}
}
});
}
isLoading.value = false;
}
Future<void> saveRating() async {
if (ratings.value != 0.0) {
ShowToastDialog.showLoader("Please wait...".tr);
productModel.value.reviewsCount = productReviewCount.value + 1;
productModel.value.reviewsSum = productReviewSum.value + ratings.value;
productModel.value.reviewAttributes = reviewProductAttributes;
vendorModel.value.reviewsCount = vendorReviewCount.value + 1;
vendorModel.value.reviewsSum = vendorReviewSum.value + ratings.value;
if (reviewProductAttributes.isEmpty) {
reviewAttribute.forEach((key, value) {
ReviewsAttribute reviewsAttributeModel = ReviewsAttribute(reviewsCount: 1, reviewsSum: value);
reviewProductAttributes.addEntries([MapEntry(key, reviewsAttributeModel.toJson())]);
});
} else {
reviewProductAttributes.forEach((key, value) {
ReviewsAttribute reviewsAttributeModel = ReviewsAttribute.fromJson(value);
reviewsAttributeModel.reviewsCount = reviewsAttributeModel.reviewsCount! + 1;
reviewsAttributeModel.reviewsSum = reviewsAttributeModel.reviewsSum! + reviewAttribute[key];
reviewProductAttributes.addEntries([MapEntry(key, reviewsAttributeModel.toJson())]);
});
}
for (int i = 0; i < images.length; i++) {
if (images[i].runtimeType == XFile) {
String url = await Constant.uploadUserImageToFireStorage(File(images[i].path), "profileImage/${FireStoreUtils.getCurrentUid()}", File(images[i].path).path.split('/').last);
images.removeAt(i);
images.insert(i, url);
}
}
RatingModel ratingProduct = RatingModel(
productId: productId.value,
comment: commentController.value.text,
photos: images,
rating: ratings.value,
customerId: FireStoreUtils.getCurrentUid(),
id: ratingModel.value.id != null && ratingModel.value.id!.isNotEmpty ? ratingModel.value.id : Constant.getUuid(),
orderId: orderModel.value.id,
vendorId: productModel.value.vendorID,
createdAt: Timestamp.now(),
uname: Constant.userModel!.fullName(),
profile: Constant.userModel!.profilePictureURL,
reviewAttributes: reviewAttribute,
);
print("vendor model");
log(vendorModel.value.toJson().toString());
await FireStoreUtils.updateReviewById(ratingProduct);
print("Rating Saved");
print(ratingProduct.toJson());
await FireStoreUtils.updateVendor(vendorModel.value);
await FireStoreUtils.setProduct(productModel.value);
ShowToastDialog.closeLoader();
ShowToastDialog.showToast("Rating saved successfully.".tr);
Get.back();
} else {
ShowToastDialog.showToast("Please add rate for food item.".tr);
}
}
final ImagePicker _imagePicker = ImagePicker();
RxList images = <dynamic>[].obs;
Future pickFile({required ImageSource source}) async {
try {
XFile? image = await _imagePicker.pickImage(source: source);
if (image == null) return;
images.add(image);
Get.back();
} on PlatformException catch (e) {
ShowToastDialog.showToast("Failed to Pick : \n $e");
}
}
}

View File

@@ -0,0 +1,13 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class RedeemGiftCardController extends GetxController {
Rx<TextEditingController> giftCodeController = TextEditingController().obs;
Rx<TextEditingController> giftPinController = TextEditingController().obs;
@override
void onInit() {
// TODO: implement onInit
super.onInit();
}
}

View File

@@ -0,0 +1,25 @@
import 'package:customer/models/referral_model.dart';
import '../service/fire_store_utils.dart';
import 'package:get/get.dart';
class ReferFriendController extends GetxController {
Rx<ReferralModel> referralModel = ReferralModel().obs;
RxBool isLoading = true.obs;
@override
void onInit() {
// TODO: implement onInit
getData();
super.onInit();
}
Future<void> getData() async {
await FireStoreUtils.getReferralUserBy().then((value) {
if (value != null) {
referralModel.value = value;
}
});
isLoading.value = false;
}
}

View File

@@ -0,0 +1,89 @@
import 'dart:math' as maths;
import 'package:customer/constant/constant.dart';
import 'package:customer/models/coupon_model.dart';
import 'package:customer/models/rental_order_model.dart';
import 'package:customer/service/fire_store_utils.dart';
import 'package:customer/themes/show_toast_dialog.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../screen_ui/rental_service/rental_dashboard_screen.dart';
import 'cab_rental_dashboard_controllers.dart';
class RentalConformationController extends GetxController {
RxBool isLoading = false.obs;
Rx<RentalOrderModel> rentalOrderModel = RentalOrderModel().obs;
Rx<TextEditingController> couponController = TextEditingController().obs;
@override
void onInit() {
getArguments();
fetchCoupons();
super.onInit();
}
void getArguments() {
final args = Get.arguments;
if (args.containsKey('rentalOrderModel') && args['rentalOrderModel'] is RentalOrderModel) {
rentalOrderModel.value = args['rentalOrderModel'] as RentalOrderModel;
calculateAmount();
} else {
debugPrint('No rental order found in arguments or invalid format.');
}
isLoading.value = false;
}
RxDouble subTotal = 0.0.obs;
RxDouble discount = 0.0.obs;
RxDouble taxAmount = 0.0.obs;
RxDouble totalAmount = 0.0.obs;
Rx<CouponModel> selectedCouponModel = CouponModel().obs;
void calculateAmount() {
subTotal.value = 0.0;
discount.value = 0.0;
taxAmount.value = 0.0;
totalAmount.value = 0.0;
subTotal.value = double.tryParse(rentalOrderModel.value.subTotal ?? '0') ?? 0.0;
if (selectedCouponModel.value.id != null) {
discount.value = Constant.calculateDiscount(amount: subTotal.value.toString(), offerModel: selectedCouponModel.value);
}
for (var element in rentalOrderModel.value.taxSetting ?? []) {
taxAmount.value = (taxAmount.value + Constant.calculateTax(amount: (subTotal.value - discount.value).toString(), taxModel: element));
}
totalAmount.value = subTotal.value - discount.value + taxAmount.value;
}
RxList<CouponModel> couponList = <CouponModel>[].obs;
Future<void> fetchCoupons() async {
try {
await FireStoreUtils.getRentalCoupon().then((value) {
couponList.value = value;
});
} catch (e) {
print("Error fetching coupons: $e");
}
}
Future<void> placeOrder() async {
ShowToastDialog.showLoader("Placing booking...".tr);
rentalOrderModel.value.discount = discount.value.toString();
rentalOrderModel.value.couponCode = selectedCouponModel.value.code;
rentalOrderModel.value.couponId = selectedCouponModel.value.id;
rentalOrderModel.value.subTotal = subTotal.value.toString();
rentalOrderModel.value.otpCode = (maths.Random().nextInt(9000) + 1000).toString();
await FireStoreUtils.rentalOrderPlace(rentalOrderModel.value).then((value) async {
ShowToastDialog.closeLoader();
ShowToastDialog.showToast("Order placed successfully".tr);
Get.offAll(const RentalDashboardScreen());
CabRentalDashboardControllers controller = Get.put(CabRentalDashboardControllers());
controller.selectedIndex.value = 1;
// Get.back();
});
}
}

View File

@@ -0,0 +1,28 @@
import 'package:customer/models/coupon_model.dart';
import 'package:customer/service/fire_store_utils.dart';
import 'package:get/get.dart';
class RentalCouponController extends GetxController{
@override
void onInit() {
// TODO: implement onInit
getData();
super.onInit();
}
void getData(){
getCouponCode();
}
RxBool isLoading = true.obs;
RxList<CouponModel> cabCouponList = <CouponModel>[].obs;
Future<void> getCouponCode() async {
await FireStoreUtils.getRentalCoupon().then((value) {
cabCouponList.value = value;
});
print("cabCouponList ${cabCouponList.length}");
isLoading.value = false;
}
}

View File

@@ -0,0 +1,237 @@
import 'dart:convert';
import 'dart:developer';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:customer/models/rental_order_model.dart';
import 'package:customer/models/rental_package_model.dart';
import 'package:customer/models/user_model.dart';
import 'package:customer/models/vendor_model.dart';
import 'package:customer/screen_ui/rental_service/rental_conformation_screen.dart';
import 'package:customer/widget/geoflutterfire/src/geoflutterfire.dart';
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart' as latlong;
import '../constant/constant.dart';
import '../models/payment_model/cod_setting_model.dart';
import '../models/payment_model/flutter_wave_model.dart';
import '../models/payment_model/mercado_pago_model.dart';
import '../models/payment_model/mid_trans.dart';
import '../models/payment_model/orange_money.dart';
import '../models/payment_model/pay_fast_model.dart';
import '../models/payment_model/pay_stack_model.dart';
import '../models/payment_model/paypal_model.dart';
import '../models/payment_model/paytm_model.dart';
import '../models/payment_model/razorpay_model.dart';
import '../models/payment_model/stripe_model.dart';
import '../models/payment_model/wallet_setting_model.dart';
import '../models/payment_model/xendit.dart';
import '../models/rental_vehicle_type.dart';
import '../screen_ui/multi_vendor_service/wallet_screen/wallet_screen.dart';
import '../service/fire_store_utils.dart';
import '../themes/show_toast_dialog.dart';
import '../utils/preferences.dart';
import '../utils/utils.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart' as gmaps;
class RentalHomeController extends GetxController {
RxBool isLoading = false.obs;
// Location input
final Rx<TextEditingController> sourceTextEditController = TextEditingController().obs;
// Selected date
Rx<DateTime> selectedDate = DateTime.now().obs;
// Vehicle list + selected vehicle
RxList<RentalVehicleType> vehicleTypes = <RentalVehicleType>[].obs;
Rx<RentalVehicleType?> selectedVehicleType = Rx<RentalVehicleType?>(null);
RxList<RentalPackageModel> rentalPackages = <RentalPackageModel>[].obs;
Rx<RentalPackageModel?> selectedPackage = Rx<RentalPackageModel?>(null);
Rx<UserModel> userModel = UserModel().obs;
final RxString selectedPaymentMethod = ''.obs;
final Rx<gmaps.LatLng> departureLatLong = gmaps.LatLng(0.0, 0.0).obs;
final Rx<latlong.LatLng> departureLatLongOsm = latlong.LatLng(0.0, 0.0).obs;
@override
void onInit() {
super.onInit();
if (Constant.userModel != null) {
userModel.value = Constant.userModel!;
}
getVehicleType();
fetchCurrentLocation();
}
void fetchCurrentLocation() async {
try {
Position? position = await Utils.getCurrentLocation();
if (position != null) {
Constant.currentLocation = position;
// Set default coordinates for Google or OSM
departureLatLong.value = gmaps.LatLng(position.latitude, position.longitude);
departureLatLongOsm.value = latlong.LatLng(position.latitude, position.longitude);
// Get readable address
String address = await Utils.getAddressFromCoordinates(position.latitude, position.longitude);
sourceTextEditController.value.text = address;
}
} catch (e) {
ShowToastDialog.showToast("Unable to fetch current location".tr);
}
}
/// Fetch Vehicle Types
Future<void> getVehicleType() async {
isLoading.value = true;
await FireStoreUtils.getRentalVehicleType().then((value) async {
vehicleTypes.value = value;
if (vehicleTypes.isNotEmpty) {
selectedVehicleType.value = vehicleTypes[0];
await getRentalPackage();
}
});
await getPaymentSettings();
isLoading.value = false;
}
/// Date Picker
Future<void> pickDate(BuildContext context) async {
final DateTime? picked = await showDatePicker(context: context, initialDate: selectedDate.value, firstDate: DateTime.now(), lastDate: DateTime(2100));
if (picked != null) {
selectedDate.value = picked;
}
}
Future<void> getRentalPackage() async {
await FireStoreUtils.getRentalPackage(selectedVehicleType.value!.id.toString()).then((value) {
rentalPackages.value = value;
if (rentalPackages.isNotEmpty) {
selectedPackage.value = rentalPackages[0];
}
});
}
void completeOrder() {
DestinationLocation sourceLocation = DestinationLocation(
latitude: Constant.selectedMapType == 'osm' ? departureLatLongOsm.value.latitude : departureLatLong.value.latitude,
longitude: Constant.selectedMapType == 'osm' ? departureLatLongOsm.value.longitude : departureLatLong.value.longitude,
);
print("=====>");
print(sourceTextEditController.value.text);
RentalOrderModel rentalOrderModel = RentalOrderModel();
rentalOrderModel.id = Constant.getUuid();
rentalOrderModel.authorID = userModel.value.id;
rentalOrderModel.author = userModel.value;
rentalOrderModel.rentalVehicleType = selectedVehicleType.value;
rentalOrderModel.vehicleId = selectedVehicleType.value!.id;
rentalOrderModel.sectionId = Constant.sectionConstantModel!.id;
rentalOrderModel.sourceLocationName = sourceTextEditController.value.text;
rentalOrderModel.bookingDateTime = Timestamp.fromDate(selectedDate.value);
rentalOrderModel.paymentMethod = selectedPaymentMethod.value;
rentalOrderModel.paymentStatus = false;
rentalOrderModel.status = Constant.orderPlaced;
rentalOrderModel.subTotal = selectedPackage.value!.baseFare;
rentalOrderModel.rentalPackageModel = selectedPackage.value;
rentalOrderModel.taxSetting = Constant.taxList;
rentalOrderModel.createdAt = Timestamp.now();
rentalOrderModel.sourceLocation = sourceLocation;
rentalOrderModel.adminCommission = Constant.sectionConstantModel!.adminCommision!.amount;
rentalOrderModel.adminCommissionType = Constant.sectionConstantModel!.adminCommision!.commissionType;
rentalOrderModel.sourcePoint = G(
geopoint: GeoPoint(sourceLocation.latitude ?? 0.0, sourceLocation.longitude ?? 0.0),
geohash: Geoflutterfire().point(latitude: sourceLocation.latitude ?? 0.0, longitude: sourceLocation.longitude ?? 0.0).hash,
);
rentalOrderModel.zoneId = Constant.getZoneId(sourceLocation.latitude ?? 0.0, sourceLocation.longitude ?? 0.0);
log(rentalOrderModel.toJson().toString());
Get.back();
Get.back();
Get.to(() => RentalConformationScreen(), arguments: {"rentalOrderModel": rentalOrderModel});
}
void setDepartureMarker(double lat, double lng) {
if (Constant.selectedMapType == 'osm') {
departureLatLongOsm.value = latlong.LatLng(lat, lng);
} else {
departureLatLong.value = gmaps.LatLng(lat, lng);
}
}
// final Rx<LatLng> departureLatLong = const LatLng(0.0, 0.0).obs;
// final Rx<latlong.LatLng> departureLatLongOsm = latlong.LatLng(0.0, 0.0).obs;
// void setDepartureMarker(double lat, double long) {
// if (Constant.selectedMapType == 'osm') {
// departureLatLongOsm.value = latlong.LatLng(lat, long);
// } else {
// departureLatLong.value = LatLng(lat, long);
// }
// }
Rx<WalletSettingModel> walletSettingModel = WalletSettingModel().obs;
Rx<CodSettingModel> cashOnDeliverySettingModel = CodSettingModel().obs;
Rx<PayFastModel> payFastModel = PayFastModel().obs;
Rx<MercadoPagoModel> mercadoPagoModel = MercadoPagoModel().obs;
Rx<PayPalModel> payPalModel = PayPalModel().obs;
Rx<StripeModel> stripeModel = StripeModel().obs;
Rx<FlutterWaveModel> flutterWaveModel = FlutterWaveModel().obs;
Rx<PayStackModel> payStackModel = PayStackModel().obs;
Rx<PaytmModel> paytmModel = PaytmModel().obs;
Rx<RazorPayModel> razorPayModel = RazorPayModel().obs;
Rx<MidTrans> midTransModel = MidTrans().obs;
Rx<OrangeMoney> orangeMoneyModel = OrangeMoney().obs;
Rx<Xendit> xenditModel = Xendit().obs;
Future<void> getPaymentSettings() async {
await FireStoreUtils.getPaymentSettingsData().then((value) {
stripeModel.value = StripeModel.fromJson(jsonDecode(Preferences.getString(Preferences.stripeSettings)));
payPalModel.value = PayPalModel.fromJson(jsonDecode(Preferences.getString(Preferences.paypalSettings)));
payStackModel.value = PayStackModel.fromJson(jsonDecode(Preferences.getString(Preferences.payStack)));
mercadoPagoModel.value = MercadoPagoModel.fromJson(jsonDecode(Preferences.getString(Preferences.mercadoPago)));
flutterWaveModel.value = FlutterWaveModel.fromJson(jsonDecode(Preferences.getString(Preferences.flutterWave)));
paytmModel.value = PaytmModel.fromJson(jsonDecode(Preferences.getString(Preferences.paytmSettings)));
payFastModel.value = PayFastModel.fromJson(jsonDecode(Preferences.getString(Preferences.payFastSettings)));
razorPayModel.value = RazorPayModel.fromJson(jsonDecode(Preferences.getString(Preferences.razorpaySettings)));
midTransModel.value = MidTrans.fromJson(jsonDecode(Preferences.getString(Preferences.midTransSettings)));
orangeMoneyModel.value = OrangeMoney.fromJson(jsonDecode(Preferences.getString(Preferences.orangeMoneySettings)));
xenditModel.value = Xendit.fromJson(jsonDecode(Preferences.getString(Preferences.xenditSettings)));
walletSettingModel.value = WalletSettingModel.fromJson(jsonDecode(Preferences.getString(Preferences.walletSettings)));
cashOnDeliverySettingModel.value = CodSettingModel.fromJson(jsonDecode(Preferences.getString(Preferences.codSettings)));
if (walletSettingModel.value.isEnabled == true) {
selectedPaymentMethod.value = PaymentGateway.wallet.name;
} else if (cashOnDeliverySettingModel.value.isEnabled == true) {
selectedPaymentMethod.value = PaymentGateway.cod.name;
} else if (stripeModel.value.isEnabled == true) {
selectedPaymentMethod.value = PaymentGateway.stripe.name;
} else if (payPalModel.value.isEnabled == true) {
selectedPaymentMethod.value = PaymentGateway.paypal.name;
} else if (payStackModel.value.isEnable == true) {
selectedPaymentMethod.value = PaymentGateway.payStack.name;
} else if (mercadoPagoModel.value.isEnabled == true) {
selectedPaymentMethod.value = PaymentGateway.mercadoPago.name;
} else if (flutterWaveModel.value.isEnable == true) {
selectedPaymentMethod.value = PaymentGateway.flutterWave.name;
} else if (payFastModel.value.isEnable == true) {
selectedPaymentMethod.value = PaymentGateway.payFast.name;
} else if (razorPayModel.value.isEnabled == true) {
selectedPaymentMethod.value = PaymentGateway.razorpay.name;
} else if (midTransModel.value.enable == true) {
selectedPaymentMethod.value = PaymentGateway.midTrans.name;
} else if (orangeMoneyModel.value.enable == true) {
selectedPaymentMethod.value = PaymentGateway.orangeMoney.name;
} else if (xenditModel.value.enable == true) {
selectedPaymentMethod.value = PaymentGateway.xendit.name;
}
});
}
}

View File

@@ -0,0 +1,904 @@
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'dart:math' as maths;
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:customer/models/payment_model/cod_setting_model.dart';
import 'package:customer/models/payment_model/flutter_wave_model.dart';
import 'package:customer/models/payment_model/mercado_pago_model.dart';
import 'package:customer/models/payment_model/mid_trans.dart';
import 'package:customer/models/payment_model/orange_money.dart';
import 'package:customer/models/payment_model/pay_fast_model.dart';
import 'package:customer/models/payment_model/pay_stack_model.dart';
import 'package:customer/models/payment_model/paypal_model.dart';
import 'package:customer/models/payment_model/paytm_model.dart';
import 'package:customer/models/payment_model/razorpay_model.dart';
import 'package:customer/models/payment_model/stripe_model.dart';
import 'package:customer/models/payment_model/wallet_setting_model.dart';
import 'package:customer/models/payment_model/xendit.dart';
import 'package:customer/models/rating_model.dart';
import 'package:customer/models/rental_order_model.dart';
import 'package:customer/models/wallet_transaction_model.dart';
import 'package:customer/payment/MercadoPagoScreen.dart';
import 'package:customer/payment/PayFastScreen.dart';
import 'package:customer/payment/getPaytmTxtToken.dart';
import 'package:customer/payment/midtrans_screen.dart';
import 'package:customer/payment/orangePayScreen.dart';
import 'package:customer/payment/paystack/pay_stack_screen.dart';
import 'package:customer/payment/paystack/pay_stack_url_model.dart';
import 'package:customer/payment/paystack/paystack_url_genrater.dart';
import 'package:customer/payment/stripe_failed_model.dart';
import 'package:customer/payment/xenditModel.dart';
import 'package:customer/payment/xenditScreen.dart';
import 'package:customer/screen_ui/multi_vendor_service/wallet_screen/wallet_screen.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:customer/themes/show_toast_dialog.dart';
import 'package:customer/utils/preferences.dart';
import 'package:flutter/material.dart';
import 'package:flutter_paypal/flutter_paypal.dart';
import 'package:flutter_stripe/flutter_stripe.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import 'package:razorpay_flutter/razorpay_flutter.dart';
import 'package:uuid/uuid.dart';
import '../constant/constant.dart';
import '../models/tax_model.dart';
import '../models/user_model.dart';
import '../service/fire_store_utils.dart';
class RentalOrderDetailsController extends GetxController {
Rx<RentalOrderModel> order = RentalOrderModel().obs;
RxBool isLoading = true.obs;
Rx<UserModel?> driverUser = Rx<UserModel?>(null);
RxDouble subTotal = 0.0.obs;
RxDouble discount = 0.0.obs;
RxDouble taxAmount = 0.0.obs;
RxDouble totalAmount = 0.0.obs;
RxDouble extraKilometerCharge = 0.0.obs;
RxDouble extraMinutesCharge = 0.0.obs;
final RxString selectedPaymentMethod = ''.obs;
Rx<RatingModel> ratingModel = RatingModel().obs;
@override
void onInit() {
getData();
super.onInit();
}
Future<void> getData() async {
final args = Get.arguments;
if (args != null) {
order.value = args as RentalOrderModel;
calculateTotalAmount();
await fetchDriverDetails();
await getPaymentSettings();
}
isLoading.value = false;
}
Future<void> fetchDriverDetails() async {
if (order.value.driverId != null) {
await FireStoreUtils.getUserProfile(order.value.driverId ?? '').then((value) {
if (value != null) {
driverUser.value = value;
}
});
await FireStoreUtils.getReviewsbyID(order.value.id.toString()).then((value) {
if (value != null) {
ratingModel.value = value;
}
});
}
}
String getExtraKm() {
try {
final double start = double.tryParse(order.value.startKitoMetersReading ?? '0') ?? 0.0;
final double end = double.tryParse(order.value.endKitoMetersReading ?? '0') ?? 0.0;
final double included = double.tryParse(order.value.rentalPackageModel?.includedDistance?.toString() ?? '0') ?? 0.0;
// Calculate extra km safely
final double extra = (end - start - included);
final double validExtra = extra > 0 ? extra : 0;
return "${validExtra.toStringAsFixed(2)} ${Constant.distanceType}";
} catch (e) {
return "0 ${Constant.distanceType}";
}
}
///Safe calculation after order is loaded
void calculateTotalAmount() {
try {
subTotal.value = double.tryParse(order.value.subTotal?.toString() ?? "0") ?? 0.0;
discount.value = double.tryParse(order.value.discount?.toString() ?? "0") ?? 0.0;
taxAmount.value = 0.0;
if (order.value.endTime != null) {
DateTime start = order.value.startTime!.toDate();
DateTime end = order.value.endTime!.toDate();
// Total rented minutes
int totalMinutes = end.difference(start).inMinutes;
int includedMinutes = (int.tryParse(order.value.rentalPackageModel?.includedHours.toString() ?? "0") ?? 0) * 60;
if (totalMinutes > includedMinutes) {
int extraMinutes = totalMinutes - includedMinutes;
double minuteFare = double.tryParse(order.value.rentalPackageModel?.extraMinuteFare?.toString() ?? "0") ?? 0.0;
extraMinutesCharge.value = extraMinutes * minuteFare;
} else {
extraMinutesCharge.value = 0;
}
}
if (order.value.startKitoMetersReading != null && order.value.endKitoMetersReading != null) {
double startKm = double.tryParse(order.value.startKitoMetersReading?.toString() ?? "0") ?? 0.0;
double endKm = double.tryParse(order.value.endKitoMetersReading?.toString() ?? "0") ?? 0.0;
if (endKm > startKm) {
double totalKm = endKm - startKm;
if (totalKm > double.parse(order.value.rentalPackageModel!.includedDistance!)) {
totalKm = totalKm - double.parse(order.value.rentalPackageModel!.includedDistance!);
double extraKmRate = double.tryParse(order.value.rentalPackageModel?.extraKmFare?.toString() ?? "0") ?? 0.0;
extraKilometerCharge.value = totalKm * extraKmRate;
}
}
}
subTotal.value = subTotal.value + extraKilometerCharge.value + extraMinutesCharge.value;
if (order.value.taxSetting != null) {
for (var element in order.value.taxSetting!) {
taxAmount.value += Constant.calculateTax(amount: (subTotal.value - discount.value).toString(), taxModel: element);
}
}
totalAmount.value = (subTotal.value - discount.value) + taxAmount.value;
} catch (e) {
ShowToastDialog.showToast("${'Failed to calculate total:'.tr} $e");
}
}
Future<void> completeOrder() async {
if (selectedPaymentMethod.value == PaymentGateway.cod.name) {
order.value.paymentMethod = selectedPaymentMethod.value;
await FireStoreUtils.rentalOrderPlace(order.value).then((value) {
ShowToastDialog.showToast("Payment method changed".tr);
Get.back();
Get.back();
});
} else {
order.value.paymentStatus = true;
order.value.paymentMethod = selectedPaymentMethod.value;
if (selectedPaymentMethod.value == PaymentGateway.wallet.name) {
WalletTransactionModel transactionModel = WalletTransactionModel(
id: Constant.getUuid(),
amount: double.parse(totalAmount.toString()),
date: Timestamp.now(),
paymentMethod: PaymentGateway.wallet.name,
transactionUser: "customer",
userId: FireStoreUtils.getCurrentUid(),
isTopup: false,
orderId: order.value.id,
note: "Rental Amount debited",
paymentStatus: "success",
serviceType: Constant.parcelServiceType,
);
await FireStoreUtils.setWalletTransaction(transactionModel).then((value) async {
if (value == true) {
await FireStoreUtils.updateUserWallet(amount: "-${totalAmount.toString()}", userId: FireStoreUtils.getCurrentUid());
}
});
}
await FireStoreUtils.rentalOrderPlace(order.value).then((value) {
ShowToastDialog.showToast("Payment successfully".tr);
Get.back();
Get.back();
});
}
}
Future<void> cancelRentalRequest(RentalOrderModel order, {List<TaxModel>? taxList}) async {
try {
isLoading.value = true;
order.status = Constant.orderCancelled;
await FireStoreUtils.rentalOrderPlace(order);
if (order.paymentMethod?.toLowerCase() != "cod") {
double totalTax = 0.0;
if (taxList != null) {
for (var element in taxList) {
totalTax += Constant.calculateTax(amount: (double.parse(order.subTotal.toString()) - double.parse(order.discount.toString())).toString(), taxModel: element);
}
}
double subTotal = double.parse(order.subTotal.toString()) - double.parse(order.discount.toString());
double refundAmount = subTotal + totalTax;
WalletTransactionModel walletTransaction = WalletTransactionModel(
id: Constant.getUuid(),
amount: refundAmount,
date: Timestamp.now(),
paymentMethod: PaymentGateway.wallet.name,
transactionUser: "customer",
userId: FireStoreUtils.getCurrentUid(),
isTopup: true,
// refund
orderId: order.id,
note: "Refund for cancelled booking",
paymentStatus: "success",
serviceType: Constant.parcelServiceType,
);
await FireStoreUtils.setWalletTransaction(walletTransaction);
await FireStoreUtils.updateUserWallet(amount: refundAmount.toString(), userId: FireStoreUtils.getCurrentUid());
}
ShowToastDialog.showToast("Booking cancelled successfully".tr);
Get.back();
} catch (e) {
ShowToastDialog.showToast("${'Failed to cancel booking:'.tr} $e".tr);
} finally {
isLoading.value = false;
}
}
Rx<WalletSettingModel> walletSettingModel = WalletSettingModel().obs;
Rx<CodSettingModel> cashOnDeliverySettingModel = CodSettingModel().obs;
Rx<PayFastModel> payFastModel = PayFastModel().obs;
Rx<MercadoPagoModel> mercadoPagoModel = MercadoPagoModel().obs;
Rx<PayPalModel> payPalModel = PayPalModel().obs;
Rx<StripeModel> stripeModel = StripeModel().obs;
Rx<FlutterWaveModel> flutterWaveModel = FlutterWaveModel().obs;
Rx<PayStackModel> payStackModel = PayStackModel().obs;
Rx<PaytmModel> paytmModel = PaytmModel().obs;
Rx<RazorPayModel> razorPayModel = RazorPayModel().obs;
Rx<MidTrans> midTransModel = MidTrans().obs;
Rx<OrangeMoney> orangeMoneyModel = OrangeMoney().obs;
Rx<Xendit> xenditModel = Xendit().obs;
Future<void> getPaymentSettings() async {
await FireStoreUtils.getPaymentSettingsData().then((value) {
stripeModel.value = StripeModel.fromJson(jsonDecode(Preferences.getString(Preferences.stripeSettings)));
payPalModel.value = PayPalModel.fromJson(jsonDecode(Preferences.getString(Preferences.paypalSettings)));
payStackModel.value = PayStackModel.fromJson(jsonDecode(Preferences.getString(Preferences.payStack)));
mercadoPagoModel.value = MercadoPagoModel.fromJson(jsonDecode(Preferences.getString(Preferences.mercadoPago)));
flutterWaveModel.value = FlutterWaveModel.fromJson(jsonDecode(Preferences.getString(Preferences.flutterWave)));
paytmModel.value = PaytmModel.fromJson(jsonDecode(Preferences.getString(Preferences.paytmSettings)));
payFastModel.value = PayFastModel.fromJson(jsonDecode(Preferences.getString(Preferences.payFastSettings)));
razorPayModel.value = RazorPayModel.fromJson(jsonDecode(Preferences.getString(Preferences.razorpaySettings)));
midTransModel.value = MidTrans.fromJson(jsonDecode(Preferences.getString(Preferences.midTransSettings)));
orangeMoneyModel.value = OrangeMoney.fromJson(jsonDecode(Preferences.getString(Preferences.orangeMoneySettings)));
xenditModel.value = Xendit.fromJson(jsonDecode(Preferences.getString(Preferences.xenditSettings)));
walletSettingModel.value = WalletSettingModel.fromJson(jsonDecode(Preferences.getString(Preferences.walletSettings)));
cashOnDeliverySettingModel.value = CodSettingModel.fromJson(jsonDecode(Preferences.getString(Preferences.codSettings)));
if (walletSettingModel.value.isEnabled == true) {
selectedPaymentMethod.value = PaymentGateway.wallet.name;
} else if (cashOnDeliverySettingModel.value.isEnabled == true) {
selectedPaymentMethod.value = PaymentGateway.cod.name;
} else if (stripeModel.value.isEnabled == true) {
selectedPaymentMethod.value = PaymentGateway.stripe.name;
} else if (payPalModel.value.isEnabled == true) {
selectedPaymentMethod.value = PaymentGateway.paypal.name;
} else if (payStackModel.value.isEnable == true) {
selectedPaymentMethod.value = PaymentGateway.payStack.name;
} else if (mercadoPagoModel.value.isEnabled == true) {
selectedPaymentMethod.value = PaymentGateway.mercadoPago.name;
} else if (flutterWaveModel.value.isEnable == true) {
selectedPaymentMethod.value = PaymentGateway.flutterWave.name;
} else if (payFastModel.value.isEnable == true) {
selectedPaymentMethod.value = PaymentGateway.payFast.name;
} else if (razorPayModel.value.isEnabled == true) {
selectedPaymentMethod.value = PaymentGateway.razorpay.name;
} else if (midTransModel.value.enable == true) {
selectedPaymentMethod.value = PaymentGateway.midTrans.name;
} else if (orangeMoneyModel.value.enable == true) {
selectedPaymentMethod.value = PaymentGateway.orangeMoney.name;
} else if (xenditModel.value.enable == true) {
selectedPaymentMethod.value = PaymentGateway.xendit.name;
}
Stripe.publishableKey = stripeModel.value.clientpublishableKey.toString();
Stripe.merchantIdentifier = 'eMart Customer';
Stripe.instance.applySettings();
setRef();
razorPay.on(Razorpay.EVENT_PAYMENT_SUCCESS, handlePaymentSuccess);
razorPay.on(Razorpay.EVENT_EXTERNAL_WALLET, handleExternalWaller);
razorPay.on(Razorpay.EVENT_PAYMENT_ERROR, handlePaymentError);
});
}
// Strip
Future<void> stripeMakePayment({required String amount}) async {
log(double.parse(amount).toStringAsFixed(0));
try {
Map<String, dynamic>? paymentIntentData = await createStripeIntent(amount: amount);
log("stripe Responce====>$paymentIntentData");
if (paymentIntentData!.containsKey("error")) {
Get.back();
ShowToastDialog.showToast("Something went wrong, please contact admin.".tr);
} else {
await Stripe.instance.initPaymentSheet(
paymentSheetParameters: SetupPaymentSheetParameters(
paymentIntentClientSecret: paymentIntentData['client_secret'],
allowsDelayedPaymentMethods: false,
googlePay: const PaymentSheetGooglePay(merchantCountryCode: 'US', testEnv: true, currencyCode: "USD"),
customFlow: true,
style: ThemeMode.system,
appearance: PaymentSheetAppearance(colors: PaymentSheetAppearanceColors(primary: AppThemeData.primary300)),
merchantDisplayName: 'GoRide',
),
);
displayStripePaymentSheet(amount: amount);
}
} catch (e, s) {
log("$e \n$s");
ShowToastDialog.showToast("exception:$e \n$s");
}
}
Future<void> displayStripePaymentSheet({required String amount}) async {
try {
await Stripe.instance.presentPaymentSheet().then((value) {
ShowToastDialog.showToast("Payment successfully".tr);
completeOrder();
});
} on StripeException catch (e) {
var lo1 = jsonEncode(e);
var lo2 = jsonDecode(lo1);
StripePayFailedModel lom = StripePayFailedModel.fromJson(lo2);
ShowToastDialog.showToast(lom.error.message);
} catch (e) {
ShowToastDialog.showToast(e.toString());
}
}
Future createStripeIntent({required String amount}) async {
try {
Map<String, dynamic> body = {
'amount': ((double.parse(amount) * 100).round()).toString(),
'currency': "USD",
'payment_method_types[]': 'card',
"description": "Strip Payment",
"shipping[name]": Constant.userModel!.fullName(),
"shipping[address][line1]": "510 Townsend St",
"shipping[address][postal_code]": "98140",
"shipping[address][city]": "San Francisco",
"shipping[address][state]": "CA",
"shipping[address][country]": "US",
};
var stripeSecret = stripeModel.value.stripeSecret;
var response = await http.post(
Uri.parse('https://api.stripe.com/v1/payment_intents'),
body: body,
headers: {'Authorization': 'Bearer $stripeSecret', 'Content-Type': 'application/x-www-form-urlencoded'},
);
return jsonDecode(response.body);
} catch (e) {
log(e.toString());
}
}
//mercadoo
Future<Null> mercadoPagoMakePayment({required BuildContext context, required String amount}) async {
final headers = {'Authorization': 'Bearer ${mercadoPagoModel.value.accessToken}', 'Content-Type': 'application/json'};
final body = jsonEncode({
"items": [
{
"title": "Test",
"description": "Test Payment",
"quantity": 1,
"currency_id": "BRL", // or your preferred currency
"unit_price": double.parse(amount),
},
],
"payer": {"email": Constant.userModel!.email},
"back_urls": {"failure": "${Constant.globalUrl}payment/failure", "pending": "${Constant.globalUrl}payment/pending", "success": "${Constant.globalUrl}payment/success"},
"auto_return": "approved",
// Automatically return after payment is approved
});
final response = await http.post(Uri.parse("https://api.mercadopago.com/checkout/preferences"), headers: headers, body: body);
if (response.statusCode == 200 || response.statusCode == 201) {
final data = jsonDecode(response.body);
Get.to(MercadoPagoScreen(initialURl: data['init_point']))!.then((value) {
if (value) {
ShowToastDialog.showToast("Payment Successful!!".tr);
completeOrder();
} else {
ShowToastDialog.showToast("Payment UnSuccessful!!".tr);
}
});
} else {
print('Error creating preference: ${response.body}');
return null;
}
}
//Paypal
void paypalPaymentSheet(String amount, context) {
Navigator.of(context).push(
MaterialPageRoute(
builder:
(BuildContext context) => UsePaypal(
sandboxMode: payPalModel.value.isLive == true ? false : true,
clientId: payPalModel.value.paypalClient ?? '',
secretKey: payPalModel.value.paypalSecret ?? '',
returnURL: "com.parkme://paypalpay",
cancelURL: "com.parkme://paypalpay",
transactions: [
{
"amount": {
"total": amount,
"currency": "USD",
"details": {"subtotal": amount},
},
},
],
note: "Contact us for any questions on your order.",
onSuccess: (Map params) async {
completeOrder();
ShowToastDialog.showToast("Payment Successful!!".tr);
},
onError: (error) {
Get.back();
ShowToastDialog.showToast("Payment UnSuccessful!!".tr);
},
onCancel: (params) {
Get.back();
ShowToastDialog.showToast("Payment UnSuccessful!!".tr);
},
),
),
);
}
///PayStack Payment Method
Future<void> payStackPayment(String totalAmount) async {
await PayStackURLGen.payStackURLGen(
amount: (double.parse(totalAmount) * 100).toString(),
currency: "ZAR",
secretKey: payStackModel.value.secretKey.toString(),
userModel: Constant.userModel!,
).then((value) async {
if (value != null) {
PayStackUrlModel payStackModel0 = value;
Get.to(
PayStackScreen(
secretKey: payStackModel.value.secretKey.toString(),
callBackUrl: payStackModel.value.callbackURL.toString(),
initialURl: payStackModel0.data.authorizationUrl,
amount: totalAmount,
reference: payStackModel0.data.reference,
),
)!.then((value) {
if (value) {
ShowToastDialog.showToast("Payment Successful!!".tr);
completeOrder();
} else {
ShowToastDialog.showToast("Payment UnSuccessful!!".tr);
}
});
} else {
ShowToastDialog.showToast("Something went wrong, please contact admin.".tr);
}
});
}
//flutter wave Payment Method
Future<Null> flutterWaveInitiatePayment({required BuildContext context, required String amount}) async {
final url = Uri.parse('https://api.flutterwave.com/v3/payments');
final headers = {'Authorization': 'Bearer ${flutterWaveModel.value.secretKey}', 'Content-Type': 'application/json'};
final body = jsonEncode({
"tx_ref": _ref,
"amount": amount,
"currency": "NGN",
"redirect_url": "${Constant.globalUrl}payment/success",
"payment_options": "ussd, card, barter, payattitude",
"customer": {
"email": Constant.userModel!.email.toString(),
"phonenumber": Constant.userModel!.phoneNumber, // Add a real phone number
"name": Constant.userModel!.fullName(), // Add a real customer name
},
"customizations": {"title": "Payment for Services", "description": "Payment for XYZ services"},
});
final response = await http.post(url, headers: headers, body: body);
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
Get.to(MercadoPagoScreen(initialURl: data['data']['link']))!.then((value) {
if (value) {
ShowToastDialog.showToast("Payment Successful!!".tr);
completeOrder();
} else {
ShowToastDialog.showToast("Payment UnSuccessful!!".tr);
}
});
} else {
print('Payment initialization failed: ${response.body}');
return null;
}
}
String? _ref;
void setRef() {
maths.Random numRef = maths.Random();
int year = DateTime.now().year;
int refNumber = numRef.nextInt(20000);
if (Platform.isAndroid) {
_ref = "AndroidRef$year$refNumber";
} else if (Platform.isIOS) {
_ref = "IOSRef$year$refNumber";
}
}
// payFast
void payFastPayment({required BuildContext context, required String amount}) {
PayStackURLGen.getPayHTML(payFastSettingData: payFastModel.value, amount: amount.toString(), userModel: Constant.userModel!).then((String? value) async {
bool isDone = await Get.to(PayFastScreen(htmlData: value!, payFastSettingData: payFastModel.value));
if (isDone) {
Get.back();
ShowToastDialog.showToast("Payment successfully".tr);
completeOrder();
} else {
Get.back();
ShowToastDialog.showToast("Payment Failed".tr);
}
});
}
///Paytm payment function
Future<void> getPaytmCheckSum(context, {required double amount}) async {
// final String orderId = DateTime.now().millisecondsSinceEpoch.toString();
// String getChecksum = "${Constant.globalUrl}payments/getpaytmchecksum";
//
// final response = await http.post(
// Uri.parse(getChecksum),
// headers: {},
// body: {"mid": paytmModel.value.paytmMID.toString(), "order_id": orderId, "key_secret": paytmModel.value.pAYTMMERCHANTKEY.toString()},
// );
//
// final data = jsonDecode(response.body);
// await verifyCheckSum(checkSum: data["code"], amount: amount, orderId: orderId).then((value) {
// initiatePayment(amount: amount, orderId: orderId).then((value) {
// String callback = "";
// if (paytmModel.value.isSandboxEnabled == true) {
// callback = "${callback}https://securegw-stage.paytm.in/theia/paytmCallback?ORDER_ID=$orderId";
// } else {
// callback = "${callback}https://securegw.paytm.in/theia/paytmCallback?ORDER_ID=$orderId";
// }
//
// GetPaymentTxtTokenModel result = value;
// startTransaction(context, txnTokenBy: result.body.txnToken, orderId: orderId, amount: amount, callBackURL: callback, isStaging: paytmModel.value.isSandboxEnabled);
// });
// });
}
Future<void> startTransaction(context, {required String txnTokenBy, required orderId, required double amount, required callBackURL, required isStaging}) async {
// try {
// var response = AllInOneSdk.startTransaction(
// paytmModel.value.paytmMID.toString(),
// orderId,
// amount.toString(),
// txnTokenBy,
// callBackURL,
// isStaging,
// true,
// true,
// );
//
// response.then((value) {
// if (value!["RESPMSG"] == "Txn Success") {
// print("txt done!!");
// ShowToastDialog.showToast("Payment Successful!!");
// completeOrder();
// }
// }).catchError((onError) {
// if (onError is PlatformException) {
// Get.back();
//
// ShowToastDialog.showToast(onError.message.toString());
// } else {
// log("======>>2");
// Get.back();
// ShowToastDialog.showToast(onError.message.toString());
// }
// });
// } catch (err) {
// Get.back();
// ShowToastDialog.showToast(err.toString());
// }
}
Future verifyCheckSum({required String checkSum, required double amount, required orderId}) async {
String getChecksum = "${Constant.globalUrl}payments/validatechecksum";
final response = await http.post(
Uri.parse(getChecksum),
headers: {},
body: {"mid": paytmModel.value.paytmMID.toString(), "order_id": orderId, "key_secret": paytmModel.value.pAYTMMERCHANTKEY.toString(), "checksum_value": checkSum},
);
final data = jsonDecode(response.body);
return data['status'];
}
Future<GetPaymentTxtTokenModel> initiatePayment({required double amount, required orderId}) async {
String initiateURL = "${Constant.globalUrl}payments/initiatepaytmpayment";
String callback = "";
if (paytmModel.value.isSandboxEnabled == true) {
callback = "${callback}https://securegw-stage.paytm.in/theia/paytmCallback?ORDER_ID=$orderId";
} else {
callback = "${callback}https://securegw.paytm.in/theia/paytmCallback?ORDER_ID=$orderId";
}
final response = await http.post(
Uri.parse(initiateURL),
headers: {},
body: {
"mid": paytmModel.value.paytmMID,
"order_id": orderId,
"key_secret": paytmModel.value.pAYTMMERCHANTKEY,
"amount": amount.toString(),
"currency": "INR",
"callback_url": callback,
"custId": FireStoreUtils.getCurrentUid(),
"issandbox": paytmModel.value.isSandboxEnabled == true ? "1" : "2",
},
);
log(response.body);
final data = jsonDecode(response.body);
if (data["body"]["txnToken"] == null || data["body"]["txnToken"].toString().isEmpty) {
Get.back();
ShowToastDialog.showToast("something went wrong, please contact admin.".tr);
}
return GetPaymentTxtTokenModel.fromJson(data);
}
///RazorPay payment function
final Razorpay razorPay = Razorpay();
void openCheckout({required amount, required orderId}) async {
var options = {
'key': razorPayModel.value.razorpayKey,
'amount': amount * 100,
'name': 'GoRide',
'order_id': orderId,
"currency": "INR",
'description': 'wallet Topup',
'retry': {'enabled': true, 'max_count': 1},
'send_sms_hash': true,
'prefill': {'contact': Constant.userModel!.phoneNumber, 'email': Constant.userModel!.email},
'external': {
'wallets': ['paytm'],
},
};
try {
razorPay.open(options);
} catch (e) {
debugPrint('Error: $e');
}
}
void handlePaymentSuccess(PaymentSuccessResponse response) {
Get.back();
ShowToastDialog.showToast("Payment Successful!!".tr);
completeOrder();
}
void handleExternalWaller(ExternalWalletResponse response) {
Get.back();
ShowToastDialog.showToast("Payment Processing!! via".tr);
}
void handlePaymentError(PaymentFailureResponse response) {
Get.back();
ShowToastDialog.showToast("Payment Failed!!".tr);
}
bool isCurrentDateInRange(DateTime startDate, DateTime endDate) {
final currentDate = DateTime.now();
return currentDate.isAfter(startDate) && currentDate.isBefore(endDate);
}
//Midtrans payment
Future<void> midtransMakePayment({required String amount, required BuildContext context}) async {
await createPaymentLink(amount: amount).then((url) {
ShowToastDialog.closeLoader();
if (url != '') {
Get.to(() => MidtransScreen(initialURl: url))!.then((value) {
if (value == true) {
ShowToastDialog.showToast("Payment Successful!!".tr);
completeOrder();
} else {
ShowToastDialog.showToast("Payment Unsuccessful!!".tr);
}
});
}
});
}
Future<String> createPaymentLink({required var amount}) async {
var ordersId = const Uuid().v1();
final url = Uri.parse(midTransModel.value.isSandbox! ? 'https://api.sandbox.midtrans.com/v1/payment-links' : 'https://api.midtrans.com/v1/payment-links');
final response = await http.post(
url,
headers: {'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': generateBasicAuthHeader(midTransModel.value.serverKey!)},
body: jsonEncode({
'transaction_details': {'order_id': ordersId, 'gross_amount': double.parse(amount.toString()).toInt()},
'usage_limit': 2,
"callbacks": {"finish": "https://www.google.com?merchant_order_id=$ordersId"},
}),
);
if (response.statusCode == 200 || response.statusCode == 201) {
final responseData = jsonDecode(response.body);
return responseData['payment_url'];
} else {
ShowToastDialog.showToast("something went wrong, please contact admin.".tr);
return '';
}
}
String generateBasicAuthHeader(String apiKey) {
String credentials = '$apiKey:';
String base64Encoded = base64Encode(utf8.encode(credentials));
return 'Basic $base64Encoded';
}
//Orangepay payment
static String accessToken = '';
static String payToken = '';
static String orderId = '';
static String amount = '';
Future<void> orangeMakePayment({required String amount, required BuildContext context}) async {
reset();
var id = const Uuid().v4();
var paymentURL = await fetchToken(context: context, orderId: id, amount: amount, currency: 'USD');
ShowToastDialog.closeLoader();
if (paymentURL.toString() != '') {
Get.to(() => OrangeMoneyScreen(initialURl: paymentURL, accessToken: accessToken, amount: amount, orangePay: orangeMoneyModel.value, orderId: orderId, payToken: payToken))!.then((value) {
if (value == true) {
ShowToastDialog.showToast("Payment Successful!!".tr);
completeOrder();
();
}
});
} else {
ShowToastDialog.showToast("Payment Unsuccessful!!".tr);
}
}
Future fetchToken({required String orderId, required String currency, required BuildContext context, required String amount}) async {
String apiUrl = 'https://api.orange.com/oauth/v3/token';
Map<String, String> requestBody = {'grant_type': 'client_credentials'};
var response = await http.post(
Uri.parse(apiUrl),
headers: <String, String>{'Authorization': "Basic ${orangeMoneyModel.value.auth!}", 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json'},
body: requestBody,
);
// Handle the response
if (response.statusCode == 200) {
Map<String, dynamic> responseData = jsonDecode(response.body);
accessToken = responseData['access_token'];
// ignore: use_build_context_synchronously
return await webpayment(context: context, amountData: amount, currency: currency, orderIdData: orderId);
} else {
ShowToastDialog.showToast("Something went wrong, please contact admin.".tr);
return '';
}
}
Future webpayment({required String orderIdData, required BuildContext context, required String currency, required String amountData}) async {
orderId = orderIdData;
amount = amountData;
String apiUrl = orangeMoneyModel.value.isSandbox! == true ? 'https://api.orange.com/orange-money-webpay/dev/v1/webpayment' : 'https://api.orange.com/orange-money-webpay/cm/v1/webpayment';
Map<String, String> requestBody = {
"merchant_key": orangeMoneyModel.value.merchantKey ?? '',
"currency": orangeMoneyModel.value.isSandbox == true ? "OUV" : currency,
"order_id": orderId,
"amount": amount,
"reference": 'Y-Note Test',
"lang": "en",
"return_url": orangeMoneyModel.value.returnUrl!.toString(),
"cancel_url": orangeMoneyModel.value.cancelUrl!.toString(),
"notif_url": orangeMoneyModel.value.notifUrl!.toString(),
};
var response = await http.post(
Uri.parse(apiUrl),
headers: <String, String>{'Authorization': 'Bearer $accessToken', 'Content-Type': 'application/json', 'Accept': 'application/json'},
body: json.encode(requestBody),
);
// Handle the response
if (response.statusCode == 201) {
Map<String, dynamic> responseData = jsonDecode(response.body);
if (responseData['message'] == 'OK') {
payToken = responseData['pay_token'];
return responseData['payment_url'];
} else {
return '';
}
} else {
ShowToastDialog.showToast("Something went wrong, please contact admin.".tr);
return '';
}
}
static void reset() {
accessToken = '';
payToken = '';
amount = '';
}
//XenditPayment
Future<void> xenditPayment(context, amount) async {
await createXenditInvoice(amount: amount).then((model) {
ShowToastDialog.closeLoader();
if (model.id != null) {
Get.to(() => XenditScreen(initialURl: model.invoiceUrl ?? '', transId: model.id ?? '', apiKey: xenditModel.value.apiKey!.toString()))!.then((value) {
if (value == true) {
ShowToastDialog.showToast("Payment Successful!!".tr);
completeOrder();
} else {
ShowToastDialog.showToast("Payment Unsuccessful!!".tr);
}
});
}
});
}
Future<XenditModel> createXenditInvoice({required var amount}) async {
const url = 'https://api.xendit.co/v2/invoices';
var headers = {
'Content-Type': 'application/json',
'Authorization': generateBasicAuthHeader(xenditModel.value.apiKey!.toString()),
// 'Cookie': '__cf_bm=yERkrx3xDITyFGiou0bbKY1bi7xEwovHNwxV1vCNbVc-1724155511-1.0.1.1-jekyYQmPCwY6vIJ524K0V6_CEw6O.dAwOmQnHtwmaXO_MfTrdnmZMka0KZvjukQgXu5B.K_6FJm47SGOPeWviQ',
};
final body = jsonEncode({
'external_id': const Uuid().v1(),
'amount': amount,
'payer_email': 'customer@domain.com',
'description': 'Test - VA Successful invoice payment',
'currency': 'IDR', //IDR, PHP, THB, VND, MYR
});
try {
final response = await http.post(Uri.parse(url), headers: headers, body: body);
if (response.statusCode == 200 || response.statusCode == 201) {
XenditModel model = XenditModel.fromJson(jsonDecode(response.body));
return model;
} else {
return XenditModel();
}
} catch (e) {
return XenditModel();
}
}
}

View File

@@ -0,0 +1,137 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../constant/collection_name.dart';
import '../models/rating_model.dart';
import '../models/rental_order_model.dart';
import '../models/user_model.dart';
import '../service/fire_store_utils.dart';
import '../constant/constant.dart';
import '../themes/show_toast_dialog.dart';
class RentalReviewController extends GetxController {
RxBool isLoading = true.obs;
/// Order from arguments
final Rx<RentalOrderModel?> order = Rx<RentalOrderModel?>(null);
/// Rating data
final Rx<RatingModel?> ratingModel = Rx<RatingModel?>(null);
final RxDouble ratings = 0.0.obs;
final Rx<TextEditingController> comment = TextEditingController().obs;
/// Driver (to be reviewed)
final Rx<UserModel?> driverUser = Rx<UserModel?>(null);
/// Review stats
final RxInt futureCount = 0.obs;
final RxInt futureSum = 0.obs;
@override
void onInit() {
super.onInit();
final args = Get.arguments;
if (args != null && args['order'] != null) {
order.value = args['order'] as RentalOrderModel;
getReview();
}
}
/// Fetch old review + driver stats
Future<void> getReview() async {
await FireStoreUtils.getReviewsbyID(order.value?.id ?? "").then((value) {
if (value != null) {
ratingModel.value = value;
ratings.value = value.rating ?? 0;
comment.value.text = value.comment ?? "";
}
});
await FireStoreUtils.getUserProfile(order.value?.driverId ?? '').then((value) {
if (value != null) {
driverUser.value = value;
final int userReviewsCount = int.tryParse(driverUser.value!.reviewsCount?.toString() ?? "0") ?? 0;
final int userReviewsSum = int.tryParse(driverUser.value!.reviewsSum?.toString() ?? "0") ?? 0;
if (ratingModel.value != null) {
final int oldRating = ratingModel.value?.rating?.toInt() ?? 0;
futureCount.value = userReviewsCount - 1;
futureSum.value = userReviewsSum - oldRating;
} else {
futureCount.value = userReviewsCount;
futureSum.value = userReviewsSum;
}
}
});
isLoading.value = false;
}
/// Save / update review
Future<void> submitReview() async {
if (comment.value.text.trim().isEmpty || ratings.value == 0) {
ShowToastDialog.showToast("Please provide rating and comment".tr);
return;
}
ShowToastDialog.showLoader("Submit in...".tr);
final user = await FireStoreUtils.getUserProfile(order.value?.driverId ?? '');
if (user != null) {
user.reviewsCount = (futureCount.value + 1).toString();
user.reviewsSum = (futureSum.value + ratings.value.toInt()).toString();
}
if (ratingModel.value != null) {
/// Update existing review
final updatedRating = RatingModel(
id: ratingModel.value!.id,
comment: comment.value.text,
photos: ratingModel.value?.photos ?? [],
rating: ratings.value,
orderId: ratingModel.value!.orderId,
driverId: ratingModel.value!.driverId,
customerId: ratingModel.value!.customerId,
vendorId: ratingModel.value?.vendorId,
uname: "${Constant.userModel?.firstName ?? ''} ${Constant.userModel?.lastName ?? ''}",
profile: Constant.userModel?.profilePictureURL,
createdAt: Timestamp.now(),
);
await FireStoreUtils.updateReviewById(updatedRating);
if (user != null) {
await FireStoreUtils.updateUser(user);
}
} else {
/// New review
final docRef = FireStoreUtils.fireStore.collection(CollectionName.itemsReview).doc();
final newRating = RatingModel(
id: docRef.id,
comment: comment.value.text,
photos: [],
rating: ratings.value,
orderId: order.value?.id,
driverId: order.value?.driverId.toString(),
customerId: Constant.userModel?.id,
uname: "${Constant.userModel?.firstName ?? ''} ${Constant.userModel?.lastName ?? ''}",
profile: Constant.userModel?.profilePictureURL,
createdAt: Timestamp.now(),
);
await FireStoreUtils.updateReviewById(newRating);
if (user != null) {
await FireStoreUtils.updateUser(user);
}
}
ShowToastDialog.closeLoader();
Get.back(result: true);
}
@override
void onClose() {
comment.value.dispose();
super.onClose();
}
}

View File

@@ -0,0 +1,300 @@
import 'dart:async';
import 'dart:developer';
import 'package:customer/constant/constant.dart';
import 'package:customer/models/brands_model.dart';
import 'package:customer/models/cart_product_model.dart';
import 'package:customer/models/coupon_model.dart';
import 'package:customer/models/favourite_item_model.dart';
import 'package:customer/models/favourite_model.dart';
import 'package:customer/models/product_model.dart';
import 'package:customer/models/vendor_category_model.dart';
import 'package:customer/models/vendor_model.dart';
import '../models/attributes_model.dart';
import '../service/cart_provider.dart';
import '../service/fire_store_utils.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
class RestaurantDetailsController extends GetxController {
Rx<TextEditingController> searchEditingController = TextEditingController().obs;
RxBool isLoading = true.obs;
Rx<PageController> pageController = PageController().obs;
RxInt currentPage = 0.obs;
RxBool isVag = false.obs;
RxBool isNonVag = false.obs;
RxBool isMenuOpen = false.obs;
RxList<FavouriteModel> favouriteList = <FavouriteModel>[].obs;
RxList<FavouriteItemModel> favouriteItemList = <FavouriteItemModel>[].obs;
RxList<ProductModel> allProductList = <ProductModel>[].obs;
RxList<ProductModel> productList = <ProductModel>[].obs;
RxList<VendorCategoryModel> vendorCategoryList = <VendorCategoryModel>[].obs;
RxList<CouponModel> couponList = <CouponModel>[].obs;
@override
void onInit() {
// TODO: implement onInit
getArgument();
super.onInit();
}
void animateSlider() {
if (vendorModel.value.photos != null && vendorModel.value.photos!.isNotEmpty) {
Timer.periodic(const Duration(seconds: 2), (Timer timer) {
if (currentPage < vendorModel.value.photos!.length - 1) {
currentPage++;
} else {
currentPage.value = 0;
}
if (pageController.value.hasClients) {
pageController.value.animateToPage(currentPage.value, duration: const Duration(milliseconds: 300), curve: Curves.easeIn);
}
});
}
}
Rx<VendorModel> vendorModel = VendorModel().obs;
final CartProvider cartProvider = CartProvider();
Future<void> getArgument() async {
cartProvider.cartStream.listen((event) async {
cartItem.clear();
cartItem.addAll(event);
});
dynamic argumentData = Get.arguments;
if (argumentData != null) {
vendorModel.value = argumentData['vendorModel'];
}
animateSlider();
statusCheck();
await getProduct();
isLoading.value = false;
await getFavouriteList();
update();
}
RxList<BrandsModel> brandList = <BrandsModel>[].obs;
Future<void> getProduct() async {
await FireStoreUtils.getProductByVendorId(vendorModel.value.id.toString()).then((value) {
if ((Constant.isSubscriptionModelApplied == true || vendorModel.value.adminCommission?.isEnabled == true) && vendorModel.value.subscriptionPlan != null) {
if (vendorModel.value.subscriptionPlan?.itemLimit == '-1') {
allProductList.value = value;
productList.value = value;
} else {
int selectedProduct =
value.length < int.parse(vendorModel.value.subscriptionPlan?.itemLimit ?? '0') ? (value.isEmpty ? 0 : (value.length)) : int.parse(vendorModel.value.subscriptionPlan?.itemLimit ?? '0');
allProductList.value = value.sublist(0, selectedProduct);
productList.value = value.sublist(0, selectedProduct);
}
} else {
allProductList.value = value;
productList.value = value;
}
});
for (var element in productList) {
await FireStoreUtils.getVendorCategoryById(element.categoryID.toString()).then((value) {
if (value != null) {
vendorCategoryList.add(value);
}
});
}
await FireStoreUtils.getBrandList().then((value) {
brandList.value = value;
});
var seen = <String>{};
vendorCategoryList.value = vendorCategoryList.where((element) => seen.add(element.id.toString())).toList();
}
void searchProduct(String name) {
if (name.isEmpty) {
productList.clear();
productList.addAll(allProductList);
} else {
isVag.value = false;
isNonVag.value = false;
productList.value = allProductList.where((p0) => p0.name!.toLowerCase().contains(name.toLowerCase())).toList();
}
update();
}
void filterRecord() {
if (isVag.value == true && isNonVag.value == true) {
productList.value = allProductList.where((p0) => p0.nonveg == true || p0.nonveg == false).toList();
} else if (isVag.value == true && isNonVag.value == false) {
productList.value = allProductList.where((p0) => p0.nonveg == false).toList();
} else if (isVag.value == false && isNonVag.value == true) {
productList.value = allProductList.where((p0) => p0.nonveg == true).toList();
} else if (isVag.value == false && isNonVag.value == false) {
productList.value = allProductList.where((p0) => p0.nonveg == true || p0.nonveg == false).toList();
}
}
Future<List<ProductModel>> getProductByCategory(VendorCategoryModel vendorCategoryModel) async {
return productList.where((p0) => p0.categoryID == vendorCategoryModel.id).toList();
}
Future<void> getFavouriteList() async {
if (Constant.userModel != null) {
await FireStoreUtils.getFavouriteRestaurant().then((value) {
favouriteList.value = value;
});
await FireStoreUtils.getFavouriteItem().then((value) {
favouriteItemList.value = value;
});
await FireStoreUtils.getOfferByVendorId(vendorModel.value.id.toString()).then((value) {
couponList.value = value;
});
}
await getAttributeData();
update();
}
RxBool isOpen = false.obs;
void statusCheck() {
final now = DateTime.now();
var day = DateFormat('EEEE', 'en_US').format(now);
var date = DateFormat('dd-MM-yyyy').format(now);
for (var element in vendorModel.value.workingHours ?? []) {
if (day == element.day.toString()) {
if (element.timeslot!.isNotEmpty) {
for (var element in element.timeslot!) {
var start = DateFormat("dd-MM-yyyy HH:mm").parse("$date ${element.from}");
var end = DateFormat("dd-MM-yyyy HH:mm").parse("$date ${element.to}");
if (isCurrentDateInRange(start, end)) {
isOpen.value = true;
}
}
}
}
}
}
String getBrandName(String brandId) {
String brandName = '';
for (var element in brandList) {
if (element.id == brandId) {
brandName = element.title ?? '';
}
}
return brandName;
}
bool isCurrentDateInRange(DateTime startDate, DateTime endDate) {
print(startDate);
print(endDate);
final currentDate = DateTime.now();
print(currentDate);
return currentDate.isAfter(startDate) && currentDate.isBefore(endDate);
}
RxList<AttributesModel> attributesList = <AttributesModel>[].obs;
RxList selectedVariants = [].obs;
RxList selectedIndexVariants = [].obs;
RxList selectedIndexArray = [].obs;
RxList selectedAddOns = [].obs;
RxInt quantity = 1.obs;
String calculatePrice(ProductModel productModel) {
String mainPrice = "0";
String variantPrice = "0";
String adOnsPrice = "0";
if (productModel.itemAttribute != null) {
if (productModel.itemAttribute!.variants!.where((element) => element.variantSku == selectedVariants.join('-')).isNotEmpty) {
variantPrice = Constant.productCommissionPrice(
vendorModel.value,
productModel.itemAttribute!.variants!.where((element) => element.variantSku == selectedVariants.join('-')).first.variantPrice ?? '0',
);
}
} else {
String price = Constant.productCommissionPrice(vendorModel.value, productModel.price.toString());
String disPrice = double.parse(productModel.disPrice.toString()) <= 0 ? "0" : Constant.productCommissionPrice(vendorModel.value, productModel.disPrice.toString());
if (double.parse(disPrice) <= 0) {
variantPrice = price;
} else {
variantPrice = disPrice;
}
}
for (int i = 0; i < productModel.addOnsPrice!.length; i++) {
if (selectedAddOns.contains(productModel.addOnsTitle![i]) == true) {
adOnsPrice = (double.parse(adOnsPrice.toString()) + double.parse(Constant.productCommissionPrice(vendorModel.value, productModel.addOnsPrice![i].toString()))).toString();
}
}
adOnsPrice = (quantity.value * double.parse(adOnsPrice)).toString();
mainPrice = ((double.parse(variantPrice.toString()) * double.parse(quantity.value.toString())) + double.parse(adOnsPrice.toString())).toString();
return mainPrice;
}
Future<void> getAttributeData() async {
await FireStoreUtils.getAttributes().then((value) {
if (value != null) {
attributesList.value = value;
}
});
}
Future<void> addToCart({required ProductModel productModel, required String price, required String discountPrice, required bool isIncrement, required int quantity, VariantInfo? variantInfo}) async {
CartProductModel cartProductModel = CartProductModel();
String adOnsPrice = "0";
for (int i = 0; i < productModel.addOnsPrice!.length; i++) {
if (selectedAddOns.contains(productModel.addOnsTitle![i]) == true && productModel.addOnsPrice![i] != '0') {
adOnsPrice = (double.parse(adOnsPrice.toString()) + double.parse(Constant.productCommissionPrice(vendorModel.value, productModel.addOnsPrice![i].toString()))).toString();
}
}
if (variantInfo != null) {
cartProductModel.id = "${productModel.id!}~${variantInfo.variantId.toString()}";
cartProductModel.name = productModel.name!;
cartProductModel.photo = productModel.photo!;
cartProductModel.categoryId = productModel.categoryID!;
cartProductModel.price = price;
cartProductModel.discountPrice = discountPrice;
cartProductModel.vendorID = vendorModel.value.id;
cartProductModel.quantity = quantity;
cartProductModel.variantInfo = variantInfo;
cartProductModel.extrasPrice = adOnsPrice;
cartProductModel.extras = selectedAddOns.isEmpty ? [] : selectedAddOns;
} else {
cartProductModel.id = productModel.id!;
cartProductModel.name = productModel.name!;
cartProductModel.photo = productModel.photo!;
cartProductModel.categoryId = productModel.categoryID!;
cartProductModel.price = price;
cartProductModel.discountPrice = discountPrice;
cartProductModel.vendorID = vendorModel.value.id;
cartProductModel.quantity = quantity;
cartProductModel.variantInfo = VariantInfo();
cartProductModel.extrasPrice = adOnsPrice;
cartProductModel.extras = selectedAddOns.isEmpty ? [] : selectedAddOns;
}
if (isIncrement) {
await cartProvider.addToCart(Get.context!, cartProductModel, quantity);
} else {
await cartProvider.removeFromCart(cartProductModel, quantity);
}
log("===> new ${cartItem.length}");
update();
}
}

View File

@@ -0,0 +1,49 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/models/favourite_model.dart';
import 'package:customer/models/vendor_model.dart';
import '../service/fire_store_utils.dart';
import 'package:get/get.dart';
class RestaurantListController extends GetxController {
RxBool isLoading = true.obs;
RxList<VendorModel> vendorList = <VendorModel>[].obs;
RxList<VendorModel> vendorSearchList = <VendorModel>[].obs;
RxString title = "Restaurants".obs;
RxList<FavouriteModel> favouriteList = <FavouriteModel>[].obs;
@override
void onInit() {
// TODO: implement onInit
getArgument();
super.onInit();
}
Future<void> getArgument() async {
dynamic argumentData = Get.arguments;
if (argumentData != null) {
vendorList.value = argumentData['vendorList'];
vendorSearchList.value = argumentData['vendorList'];
title.value = argumentData['title'] ?? "Restaurants";
}
await getFavouriteRestaurant();
isLoading.value = false;
}
Future<void> getFavouriteRestaurant() async {
if (Constant.userModel != null) {
await FireStoreUtils.getFavouriteRestaurant().then((value) {
favouriteList.value = value;
});
}
}
@override
void dispose() {
vendorSearchList.clear();
super.dispose();
}
}

View File

@@ -0,0 +1,36 @@
import 'package:customer/models/rating_model.dart';
import 'package:customer/models/vendor_model.dart';
import '../service/fire_store_utils.dart';
import 'package:get/get.dart';
class ReviewListController extends GetxController {
RxBool isLoading = true.obs;
@override
void onInit() {
// TODO: implement onInit
getArgument();
super.onInit();
}
Rx<VendorModel> vendorModel = VendorModel().obs;
RxList<RatingModel> ratingList = <RatingModel>[].obs;
void getArgument() {
dynamic argumentData = Get.arguments;
if (argumentData != null) {
vendorModel.value = argumentData['vendorModel'];
getAllReview();
}
isLoading.value = false;
}
Future<void> getAllReview() async {
await FireStoreUtils.getVendorReviews(vendorModel.value.id.toString()).then(
(value) {
ratingList.value = value;
},
);
update();
}
}

View File

@@ -0,0 +1,26 @@
import 'package:customer/models/vendor_model.dart';
import '../service/fire_store_utils.dart';
import 'package:get/get.dart';
class ScanQrCodeController extends GetxController {
@override
void onInit() {
// TODO: implement onInit
getData();
super.onInit();
}
RxList<VendorModel> allNearestRestaurant = <VendorModel>[].obs;
void getData() {
FireStoreUtils.getAllNearestRestaurant().listen((event) async {
allNearestRestaurant.addAll(event);
});
}
@override
void dispose() {
// TODO: implement dispose
super.dispose();
}
}

View File

@@ -0,0 +1,72 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/models/product_model.dart';
import 'package:customer/models/vendor_model.dart';
import '../service/fire_store_utils.dart';
import 'package:get/get.dart';
class SearchScreenController extends GetxController {
@override
void onInit() {
// TODO: implement onInit
getArgument();
super.onInit();
}
RxBool isLoading = true.obs;
RxList<VendorModel> vendorList = <VendorModel>[].obs;
RxList<VendorModel> vendorSearchList = <VendorModel>[].obs;
RxList<ProductModel> productList = <ProductModel>[].obs;
RxList<ProductModel> productSearchList = <ProductModel>[].obs;
Future<void> getArgument() async {
dynamic argumentData = Get.arguments;
if (argumentData != null) {
vendorList.value = argumentData['vendorList'];
productList.clear();
}
isLoading.value = false;
for (var element in vendorList) {
await FireStoreUtils.getProductByVendorId(element.id.toString()).then((value) {
if ((Constant.isSubscriptionModelApplied == true || element.adminCommission?.isEnabled == true) && element.subscriptionPlan != null) {
if (element.subscriptionPlan?.itemLimit == '-1') {
productList.addAll(value);
} else {
int selectedProduct =
value.length < int.parse(element.subscriptionPlan?.itemLimit ?? '0') ? (value.isEmpty ? 0 : (value.length)) : int.parse(element.subscriptionPlan?.itemLimit ?? '0');
productList.addAll(value.sublist(0, selectedProduct));
}
} else {
productList.addAll(value);
}
});
}
}
void onSearchTextChanged(String text) {
if (text.isEmpty) {
return;
}
vendorSearchList.clear();
productSearchList.clear();
for (var element in vendorList) {
if (element.title!.toLowerCase().contains(text.toLowerCase())) {
vendorSearchList.add(element);
}
}
for (var element in productList) {
if (element.name!.toLowerCase().contains(text.toLowerCase())) {
productSearchList.add(element);
}
}
}
@override
void dispose() {
vendorSearchList.clear();
productSearchList.clear();
super.dispose();
}
}

View File

@@ -0,0 +1,180 @@
import 'package:customer/models/section_model.dart';
import 'package:customer/screen_ui/cab_service_screens/cab_dashboard_screen.dart';
import 'package:customer/screen_ui/ecommarce/dash_board_e_commerce_screen.dart';
import 'package:customer/screen_ui/parcel_service/parcel_dashboard_screen.dart';
import 'package:customer/screen_ui/rental_service/rental_dashboard_screen.dart';
import 'package:customer/service/cart_provider.dart';
import 'package:customer/service/database_helper.dart';
import 'package:customer/service/fire_store_utils.dart';
import 'package:customer/constant/constant.dart';
import 'package:customer/models/user_model.dart';
import 'package:customer/models/currency_model.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:customer/themes/round_button_fill.dart';
import 'package:customer/themes/show_toast_dialog.dart';
import 'package:firebase_auth/firebase_auth.dart' as auth;
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../screen_ui/auth_screens/login_screen.dart';
import '../screen_ui/multi_vendor_service/dash_board_screens/dash_board_screen.dart';
import '../screen_ui/on_demand_service/on_demand_dashboard_screen.dart';
import '../service/notification_service.dart';
class ServiceListController extends GetxController {
var isLoading = false.obs;
var serviceListBanner = <dynamic>[].obs;
var sectionList = <SectionModel>[].obs;
var currencyData = CurrencyModel().obs;
@override
void onInit() {
super.onInit();
loadData();
}
Future<void> loadData() async {
isLoading.value = true;
// fetch currency
CurrencyModel? currency = await FireStoreUtils.getCurrency();
currencyData.value = currency ?? CurrencyModel(id: "", code: "USD", decimal: 2, isactive: true, name: "US Dollar", symbol: "\$", symbolatright: false);
// Load sections
List<SectionModel> sections = await FireStoreUtils.getSections();
sectionList.assignAll(sections);
await FireStoreUtils.getSectionBannerList().then((value) {
serviceListBanner.assignAll(value);
});
await getZone();
isLoading.value = false;
}
Future<void> getZone() async {
await FireStoreUtils.getZone().then((value) {
if (value != null) {
Constant.zoneList = value;
}
});
}
Future<void> onServiceTap(BuildContext context, SectionModel sectionModel) async {
try {
ShowToastDialog.showLoader("Please wait...".tr);
Constant.sectionConstantModel = sectionModel;
AppThemeData.primary300 = Color(int.tryParse(sectionModel.color?.replaceFirst("#", "0xff") ?? '') ?? 0xff2196F3);
if (auth.FirebaseAuth.instance.currentUser != null) {
String uid = auth.FirebaseAuth.instance.currentUser!.uid;
UserModel? user = await FireStoreUtils.getUserProfile(uid);
if (user != null && user.role == Constant.userRoleCustomer) {
user.fcmToken = await NotificationService.getToken();
await FireStoreUtils.updateUser(user);
ShowToastDialog.closeLoader();
await _navigate(sectionModel);
} else {
ShowToastDialog.closeLoader();
Get.offAll(() => const LoginScreen());
}
} else {
ShowToastDialog.closeLoader();
await _navigate(sectionModel);
}
} catch (e) {
print("Error during service tap: $e");
ShowToastDialog.closeLoader();
}
}
Future<void> _navigate(SectionModel sectionModel) async {
await FireStoreUtils.getTaxList(sectionModel.id ?? "").then((value) {
if (value != null) {
Constant.taxList = value;
}
});
if (sectionModel.serviceTypeFlag == "ecommerce-service" || sectionModel.serviceTypeFlag == "delivery-service") {
if (cartItem.isNotEmpty) {
showAlertDialog(Get.context!, UserModel(), sectionModel);
} else {
if (sectionModel.serviceTypeFlag == "ecommerce-service") {
Get.to(DashBoardEcommerceScreen());
} else if (sectionModel.serviceTypeFlag == "cab-service") {
Get.to(CabDashboardScreen());
} else if (sectionModel.serviceTypeFlag == "rental-service") {
Get.to(RentalDashboardScreen());
} else if (sectionModel.serviceTypeFlag == "parcel_delivery") {
Get.to(ParcelDashboardScreen());
} else if (sectionModel.serviceTypeFlag == "ondemand-service") {
Get.to(OnDemandDashboardScreen());
} else {
Get.to(() => DashBoardScreen());
}
}
} else {
if (sectionModel.serviceTypeFlag == "ecommerce-service") {
Get.to(DashBoardEcommerceScreen());
} else if (sectionModel.serviceTypeFlag == "cab-service") {
Get.to(CabDashboardScreen());
} else if (sectionModel.serviceTypeFlag == "rental-service") {
Get.to(RentalDashboardScreen());
} else if (sectionModel.serviceTypeFlag == "parcel_delivery") {
Get.to(ParcelDashboardScreen());
} else if (sectionModel.serviceTypeFlag == "ondemand-service") {
Get.to(OnDemandDashboardScreen());
} else {
Get.to(() => DashBoardScreen());
}
}
}
final CartProvider cartProvider = CartProvider();
void showAlertDialog(BuildContext context, UserModel user, SectionModel sectionModel) {
Get.defaultDialog(
title: "Alert!",
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text("If you select this Section/Service, your previously added items will be removed from the cart.".tr, textAlign: TextAlign.center),
const SizedBox(height: 20),
Row(
children: [
Expanded(
child: RoundedButtonFill(
height: 5.5,
title: "Cancel".tr,
onPress: () {
Get.back();
},
color: AppThemeData.grey900,
textColor: AppThemeData.surface,
),
),
const SizedBox(width: 12),
Expanded(
child: RoundedButtonFill(
title: "OK".tr,
height: 5.5,
onPress: () async {
DatabaseHelper.instance.deleteAllCartProducts();
cartProvider.clearDatabase();
Get.back();
if (sectionModel.serviceTypeFlag == "ecommerce-service") {
Get.off(() => DashBoardEcommerceScreen());
} else {
Get.to(() => DashBoardScreen());
}
},
color: AppThemeData.primary300,
textColor: AppThemeData.surface,
),
),
],
),
],
),
actions: [], // 👈 keep this empty since we put buttons in content
);
}
}

View File

@@ -0,0 +1,195 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:customer/models/user_model.dart';
import 'package:customer/screen_ui/location_enable_screens/location_permission_screen.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:firebase_auth/firebase_auth.dart' as auth;
import '../constant/constant.dart';
import '../models/referral_model.dart';
import '../screen_ui/service_home_screen/service_list_screen.dart';
import '../service/fire_store_utils.dart';
import '../themes/show_toast_dialog.dart';
import '../utils/notification_service.dart';
class SignUpController extends GetxController {
Rx<TextEditingController> firstNameController = TextEditingController().obs;
Rx<TextEditingController> lastNameController = TextEditingController().obs;
Rx<TextEditingController> emailController = TextEditingController().obs;
Rx<TextEditingController> mobileController = TextEditingController().obs;
Rx<TextEditingController> countryCodeController = TextEditingController(text: Constant.defaultCountryCode).obs;
Rx<TextEditingController> passwordController = TextEditingController().obs;
Rx<TextEditingController> confirmPasswordController = TextEditingController().obs;
Rx<TextEditingController> referralController = TextEditingController().obs;
final FocusNode emailFocusNode = FocusNode();
final FocusNode passwordFocusNode = FocusNode();
// State
final RxBool isLoading = false.obs;
final auth.FirebaseAuth _firebaseAuth = auth.FirebaseAuth.instance;
RxString type = "email".obs;
Rx<UserModel> userModel = UserModel().obs;
RxBool passwordVisible = true.obs;
RxBool conformPasswordVisible = true.obs;
@override
void onInit() {
super.onInit();
getArgument();
}
void getArgument() {
final args = Get.arguments;
type.value = args?['type'] ?? 'email';
userModel.value = args?['userModel'] ?? UserModel();
//Pre-fill fields for Google/Apple signup
if (type.value == "google" || type.value == "apple") {
firstNameController.value.text = userModel.value.firstName ?? "";
lastNameController.value.text = userModel.value.lastName ?? "";
emailController.value.text = userModel.value.email ?? "";
}
//mobile number signup
if (type.value == "mobileNumber") {
mobileController.value.text = userModel.value.phoneNumber ?? "";
countryCodeController.value.text = userModel.value.countryCode ?? "";
}
}
/// Main Sign-Up Trigger
void signUp() async {
debugPrint("SIGNUP CALLED!");
try {
if (!_validateInputs()) return;
ShowToastDialog.showLoader("Creating account...".tr);
if (type.value == "mobileNumber") {
await _signUpWithMobile();
} else {
await _signUpWithEmail();
}
ShowToastDialog.closeLoader();
} catch (e, st) {
ShowToastDialog.closeLoader();
debugPrint("SIGNUP OUTER EXCEPTION: $e\n$st");
ShowToastDialog.showToast("${'signup_failed'.tr}: $e");
}
}
/// Validation Logic
bool _validateInputs() {
if (firstNameController.value.text.isEmpty) {
ShowToastDialog.showToast("Please enter first name".tr);
return false;
} else if (lastNameController.value.text.isEmpty) {
ShowToastDialog.showToast("Please enter last name".tr);
return false;
} else if (emailController.value.text.isEmpty || !emailController.value.text.isEmail) {
ShowToastDialog.showToast("Please enter a valid email address".tr);
return false;
} else if (mobileController.value.text.isEmpty) {
ShowToastDialog.showToast("Please enter a valid phone number".tr);
return false;
} else if (passwordController.value.text.length < 6) {
ShowToastDialog.showToast("Password must be at least 6 characters".tr);
return false;
} else if (passwordController.value.text != confirmPasswordController.value.text) {
ShowToastDialog.showToast("Password and Confirm password do not match".tr);
return false;
}
return true;
}
/// Email Sign-up Flow
Future<void> _signUpWithEmail() async {
try {
final credential = await _firebaseAuth.createUserWithEmailAndPassword(email: emailController.value.text.trim(), password: passwordController.value.text.trim());
if (credential.user != null) {
final newUser = await _buildUserModel(credential.user?.uid ?? '');
await _handleReferral(newUser.id ?? '');
await FireStoreUtils.updateUser(newUser);
// appController.currentUser.value = newUser;
_navigateBasedOnAddress(newUser);
}
} on auth.FirebaseAuthException catch (e) {
debugPrint("FirebaseAuthException caught: code=${e.code}, message=${e.message}");
if (e.code == 'email-already-in-use') {
ShowToastDialog.showToast("Email already in use".tr);
} else if (e.code == 'weak-password') {
ShowToastDialog.showToast("Password is too weak".tr);
} else if (e.code == 'invalid-email') {
ShowToastDialog.showToast("Invalid email address".tr);
} else {
ShowToastDialog.showToast(e.message ?? "signup_failed".tr);
}
} catch (e) {
debugPrint("Something went wrong: ${e.toString()}");
ShowToastDialog.showToast("${'something_went_wrong'.tr}: ${e.toString()}");
}
}
/// Mobile Sign-up Flow
Future<void> _signUpWithMobile() async {
debugPrint("Signup with mobile called...");
try {
final uid = FireStoreUtils.getCurrentUid();
userModel.value = await _buildUserModel(uid);
await _handleReferral(uid);
await FireStoreUtils.updateUser(userModel.value);
_navigateBasedOnAddress(userModel.value);
} catch (e) {
ShowToastDialog.showToast("${'signup_failed'.tr}: $e");
}
}
/// Construct UserModel
Future<UserModel> _buildUserModel(String uid) async {
final fcmToken = await NotificationService.getToken();
return UserModel(
id: uid,
firstName: firstNameController.value.text.trim(),
lastName: lastNameController.value.text.trim(),
email: emailController.value.text.trim().toLowerCase(),
phoneNumber: mobileController.value.text.trim(),
countryCode: countryCodeController.value.text.trim(),
fcmToken: fcmToken,
active: true,
createdAt: Timestamp.now(),
role: Constant.userRoleCustomer,
);
}
/// Handle Referral Logic
Future<void> _handleReferral(String userId) async {
final referralCode = referralController.value.text.trim();
final referralBy = referralCode.isNotEmpty ? (await FireStoreUtils.getReferralUserByCode(referralCode))?.id ?? '' : '';
final referral = ReferralModel(id: userId, referralBy: referralBy, referralCode: Constant.getReferralCode());
await FireStoreUtils.referralAdd(referral);
}
/// Navigate Based on Shipping Address
void _navigateBasedOnAddress(UserModel user) {
if (user.shippingAddress?.isNotEmpty == true) {
final defaultAddress = user.shippingAddress!.firstWhere((e) => e.isDefault == true, orElse: () => user.shippingAddress!.first);
/// Save the default address to global constant
Constant.selectedLocation = defaultAddress;
Get.offAll(() => const ServiceListScreen());
} else {
Get.offAll(() => const LocationPermissionScreen());
}
}
}

View File

@@ -0,0 +1,67 @@
import 'dart:async';
import 'dart:developer';
import 'package:customer/constant/constant.dart';
import 'package:customer/models/user_model.dart';
import 'package:customer/screen_ui/maintenance_mode_screen/maintenance_mode_screen.dart';
import 'package:customer/screen_ui/service_home_screen/service_list_screen.dart';
import 'package:customer/utils/notification_service.dart';
import 'package:customer/utils/preferences.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:get/get.dart';
import '../screen_ui/auth_screens/login_screen.dart';
import '../screen_ui/location_enable_screens/location_permission_screen.dart';
import '../screen_ui/on_boarding_screen/on_boarding_screen.dart';
import '../service/fire_store_utils.dart';
class SplashController extends GetxController {
@override
void onInit() {
Timer(const Duration(seconds: 3), () => redirectScreen());
super.onInit();
}
Future<void> redirectScreen() async {
if (Constant.isMaintenanceModeForCustomer == true) {
Get.offAll(const MaintenanceModeScreen());
return;
}
if (Preferences.getBoolean(Preferences.isFinishOnBoardingKey) == false) {
Get.offAll(const OnboardingScreen());
} else {
bool isLogin = await FireStoreUtils.isLogin();
if (isLogin == true) {
await FireStoreUtils.getUserProfile(FireStoreUtils.getCurrentUid()).then((value) async {
if (value != null) {
UserModel userModel = value;
log(userModel.toJson().toString());
if (userModel.role == Constant.userRoleCustomer) {
if (userModel.active == true) {
userModel.fcmToken = await NotificationService.getToken();
await FireStoreUtils.updateUser(userModel);
if (userModel.shippingAddress != null && userModel.shippingAddress!.isNotEmpty) {
if (userModel.shippingAddress!.where((element) => element.isDefault == true).isNotEmpty) {
Constant.selectedLocation = userModel.shippingAddress!.where((element) => element.isDefault == true).single;
} else {
Constant.selectedLocation = userModel.shippingAddress!.first;
}
Get.offAll(const ServiceListScreen());
} else {
Get.offAll(const LocationPermissionScreen());
}
} else {
await FirebaseAuth.instance.signOut();
Get.offAll(const LoginScreen());
}
} else {
await FirebaseAuth.instance.signOut();
Get.offAll(const LoginScreen());
}
}
});
} else {
await FirebaseAuth.instance.signOut();
Get.offAll(const LoginScreen());
}
}
}
}

View File

@@ -0,0 +1,27 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../utils/preferences.dart';
class ThemeController extends GetxController {
RxBool isDark = false.obs;
@override
void onInit() {
super.onInit();
loadTheme();
}
void loadTheme() {
// Use safe getBoolean from Preferences
isDark.value = Preferences.getBoolean(Preferences.themKey);
}
void toggleTheme() {
isDark.value = !isDark.value;
Preferences.setBoolean(Preferences.themKey, isDark.value);
}
ThemeMode get themeMode => isDark.value ? ThemeMode.dark : ThemeMode.light;
}

View File

@@ -0,0 +1,31 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/models/vendor_category_model.dart';
import '../service/fire_store_utils.dart';
import 'package:get/get.dart';
class ViewAllCategoryController extends GetxController {
RxBool isLoading = true.obs;
RxList<VendorCategoryModel> vendorCategoryModel = <VendorCategoryModel>[].obs;
@override
void onInit() {
// TODO: implement onInit
getCategoryData();
super.onInit();
}
Future<void> getCategoryData() async {
await FireStoreUtils.getVendorCategory().then((value) {
vendorCategoryModel.value = value;
});
if (Constant.restaurantList != null) {
List<String> usedCategoryIds = Constant.restaurantList!.expand((vendor) => vendor.categoryID ?? []).whereType<String>().toSet().toList();
vendorCategoryModel.value = vendorCategoryModel.where((category) => usedCategoryIds.contains(category.id)).toList();
}
isLoading.value = false;
}
}

View File

@@ -0,0 +1,83 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/on_demand_home_controller.dart';
import 'package:get/get.dart';
import 'package:flutter/material.dart';
import '../models/favorite_ondemand_service_model.dart';
import '../models/provider_serivce_model.dart';
import '../service/fire_store_utils.dart';
class ViewAllPopularServiceController extends GetxController {
RxList<ProviderServiceModel> providerList = <ProviderServiceModel>[].obs;
RxList<ProviderServiceModel> allProviderList = <ProviderServiceModel>[].obs;
RxBool isLoading = true.obs;
Rx<OnDemandHomeController> onDemandHomeController = Get.find<OnDemandHomeController>().obs;
final OnDemandHomeController onDemandController = Get.find<OnDemandHomeController>();
Rx<TextEditingController> searchTextFiledController = TextEditingController().obs;
RxList<FavouriteOndemandServiceModel> lstFav = <FavouriteOndemandServiceModel>[].obs;
@override
void onInit() {
super.onInit();
getData();
}
Future<void> getData() async {
isLoading.value = true;
await FireStoreUtils.getProviderFuture()
.then((providerServiceList) {
Set<String?> uniqueAuthorIds = providerServiceList.map((service) => service.author).toSet();
List<String?> listOfUniqueProviders = uniqueAuthorIds.toList();
List<ProviderServiceModel> filteredProviders = [];
for (var provider in listOfUniqueProviders) {
List<ProviderServiceModel> filteredList = providerServiceList.where((service) => service.author == provider).toList();
filteredList.sort((a, b) => a.createdAt!.compareTo(b.createdAt!));
for (int index = 0; index < filteredList.length; index++) {
final service = filteredList[index];
if (Constant.isSubscriptionModelApplied == true || Constant.sectionConstantModel?.adminCommision?.isEnabled == true) {
if (service.subscriptionPlan?.itemLimit == "-1") {
filteredProviders.add(service);
} else {
if (index < int.parse(service.subscriptionPlan?.itemLimit ?? '0')) {
filteredProviders.add(service);
}
}
} else {
filteredProviders.add(service);
}
}
}
allProviderList.value = filteredProviders;
providerList.value = filteredProviders;
isLoading.value = false;
})
.catchError((e) {
print("Provider error: $e");
isLoading.value = false;
});
if (Constant.userModel != null) {
await FireStoreUtils.getFavouritesServiceList(FireStoreUtils.getCurrentUid()).then((value) {
lstFav.value = value;
});
}
isLoading.value = false;
}
void getFilterData(String value) {
if (value.isNotEmpty) {
providerList.value = allProviderList.where((e) => e.title!.toLowerCase().contains(value.toLowerCase()) || e.title!.startsWith(value)).toList();
} else {
providerList.assignAll(allProviderList);
}
}
}

View File

@@ -0,0 +1,65 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/on_demand_home_controller.dart';
import 'package:get/get.dart';
import '../models/provider_serivce_model.dart';
import '../service/fire_store_utils.dart';
class ViewCategoryServiceController extends GetxController {
RxBool isLoading = true.obs;
RxList<ProviderServiceModel> providerList = <ProviderServiceModel>[].obs;
RxString categoryId = "".obs, categoryTitle = "".obs;
Rx<OnDemandHomeController> onDemandHomeController = Get.find<OnDemandHomeController>().obs;
@override
void onInit() {
super.onInit();
final args = Get.arguments as Map<String, dynamic>;
categoryId.value = args['categoryId'] ?? "";
categoryTitle.value = args['categoryTitle'] ?? "";
getData();
}
Future<void> getData() async {
providerList.clear();
isLoading.value = true;
List<ProviderServiceModel> providerServiceList = await FireStoreUtils.getProviderFuture(categoryId: categoryId.value);
List<String?> uniqueAuthId = providerServiceList.map((service) => service.author).toList();
List<String?> uniqueServiceId = providerServiceList.map((service) => service.id).toList();
List<ProviderServiceModel> filterByItemLimit = <ProviderServiceModel>[];
List<String?> uniqueId = <String>[];
if ((Constant.isSubscriptionModelApplied == true || Constant.sectionConstantModel!.adminCommision?.isEnabled == true)) {
for (var authUser in uniqueAuthId) {
List<ProviderServiceModel> listofAllServiceByAuth = await FireStoreUtils.getAllProviderServiceByAuthorId(authUser!);
for (int i = 0; i < listofAllServiceByAuth.length; i++) {
if (listofAllServiceByAuth[i].subscriptionPlan?.itemLimit != null &&
(i < int.parse(listofAllServiceByAuth[i].subscriptionPlan?.itemLimit ?? '0') || listofAllServiceByAuth[i].subscriptionPlan?.itemLimit == '-1')) {
if (uniqueServiceId.contains(listofAllServiceByAuth[i].id)) {
filterByItemLimit.add(listofAllServiceByAuth[i]);
}
}
}
for (var service in filterByItemLimit) {
for (var unique in uniqueServiceId) {
if (service.id == unique && !uniqueId.contains(service.id) && service.subscriptionTotalOrders != '0') {
uniqueId.add(service.id);
providerList.add(service);
}
}
}
}
} else {
providerList.addAll(providerServiceList);
}
isLoading.value = false;
}
}

View File

@@ -0,0 +1,601 @@
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'dart:math' as maths;
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:customer/constant/constant.dart';
import 'package:customer/models/payment_model/flutter_wave_model.dart';
import 'package:customer/models/payment_model/mercado_pago_model.dart';
import 'package:customer/models/payment_model/mid_trans.dart';
import 'package:customer/models/payment_model/orange_money.dart';
import 'package:customer/models/payment_model/pay_fast_model.dart';
import 'package:customer/models/payment_model/pay_stack_model.dart';
import 'package:customer/models/payment_model/paypal_model.dart';
import 'package:customer/models/payment_model/razorpay_model.dart';
import 'package:customer/models/payment_model/stripe_model.dart';
import 'package:customer/models/payment_model/xendit.dart';
import 'package:customer/models/user_model.dart';
import 'package:customer/models/wallet_transaction_model.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:flutter_paypal/flutter_paypal.dart';
import 'package:flutter_stripe/flutter_stripe.dart';
import 'package:razorpay_flutter/razorpay_flutter.dart';
import '../payment/MercadoPagoScreen.dart';
import '../payment/PayFastScreen.dart';
import '../payment/midtrans_screen.dart';
import '../payment/orangePayScreen.dart';
import '../payment/paystack/pay_stack_screen.dart';
import '../payment/paystack/pay_stack_url_model.dart';
import '../payment/paystack/paystack_url_genrater.dart';
import '../payment/stripe_failed_model.dart';
import '../payment/xenditModel.dart';
import '../payment/xenditScreen.dart';
import '../service/fire_store_utils.dart';
import 'package:customer/utils/preferences.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import 'package:uuid/uuid.dart';
import '../themes/show_toast_dialog.dart';
class WalletController extends GetxController {
RxBool isLoading = true.obs;
Rx<TextEditingController> topUpAmountController = TextEditingController().obs;
RxList<WalletTransactionModel> walletTransactionList = <WalletTransactionModel>[].obs;
Rx<UserModel> userModel = UserModel().obs;
RxString selectedPaymentMethod = "".obs;
@override
void onInit() {
// TODO: implement onInit
getPaymentSettings();
getWalletTransaction();
super.onInit();
}
Rx<PayFastModel> payFastModel = PayFastModel().obs;
Rx<MercadoPagoModel> mercadoPagoModel = MercadoPagoModel().obs;
Rx<PayPalModel> payPalModel = PayPalModel().obs;
Rx<StripeModel> stripeModel = StripeModel().obs;
Rx<FlutterWaveModel> flutterWaveModel = FlutterWaveModel().obs;
Rx<PayStackModel> payStackModel = PayStackModel().obs;
Rx<RazorPayModel> razorPayModel = RazorPayModel().obs;
Rx<MidTrans> midTransModel = MidTrans().obs;
Rx<OrangeMoney> orangeMoneyModel = OrangeMoney().obs;
Rx<Xendit> xenditModel = Xendit().obs;
Future<void> getPaymentSettings() async {
await FireStoreUtils.getPaymentSettingsData().then((value) {
payFastModel.value = PayFastModel.fromJson(jsonDecode(Preferences.getString(Preferences.payFastSettings)));
mercadoPagoModel.value = MercadoPagoModel.fromJson(jsonDecode(Preferences.getString(Preferences.mercadoPago)));
payPalModel.value = PayPalModel.fromJson(jsonDecode(Preferences.getString(Preferences.paypalSettings)));
stripeModel.value = StripeModel.fromJson(jsonDecode(Preferences.getString(Preferences.stripeSettings)));
flutterWaveModel.value = FlutterWaveModel.fromJson(jsonDecode(Preferences.getString(Preferences.flutterWave)));
payStackModel.value = PayStackModel.fromJson(jsonDecode(Preferences.getString(Preferences.payStack)));
razorPayModel.value = RazorPayModel.fromJson(jsonDecode(Preferences.getString(Preferences.razorpaySettings)));
midTransModel.value = MidTrans.fromJson(jsonDecode(Preferences.getString(Preferences.midTransSettings)));
orangeMoneyModel.value = OrangeMoney.fromJson(json.decode(Preferences.getString(Preferences.orangeMoneySettings)));
xenditModel.value = Xendit.fromJson(jsonDecode(Preferences.getString(Preferences.xenditSettings)));
Stripe.publishableKey = stripeModel.value.clientpublishableKey.toString();
Stripe.merchantIdentifier = 'GoRide';
Stripe.instance.applySettings();
setRef();
razorPay.on(Razorpay.EVENT_PAYMENT_SUCCESS, handlePaymentSuccess);
razorPay.on(Razorpay.EVENT_EXTERNAL_WALLET, handleExternalWaller);
razorPay.on(Razorpay.EVENT_PAYMENT_ERROR, handlePaymentError);
});
}
Future<void> getWalletTransaction() async {
if (Constant.userModel != null) {
await FireStoreUtils.getWalletTransaction().then((value) {
if (value != null) {
walletTransactionList.value = value;
}
});
await FireStoreUtils.getUserProfile(FireStoreUtils.getCurrentUid()).then((value) {
if (value != null) {
userModel.value = value;
}
});
}
isLoading.value = false;
}
Future<void> walletTopUp() async {
WalletTransactionModel transactionModel = WalletTransactionModel(
id: Constant.getUuid(),
amount: double.parse(topUpAmountController.value.text),
date: Timestamp.now(),
paymentMethod: selectedPaymentMethod.value,
transactionUser: "user",
userId: FireStoreUtils.getCurrentUid(),
isTopup: true,
note: "Wallet Top-up",
paymentStatus: "success",
);
await FireStoreUtils.setWalletTransaction(transactionModel).then((value) async {
if (value == true) {
await FireStoreUtils.updateUserWallet(amount: topUpAmountController.value.text, userId: FireStoreUtils.getCurrentUid()).then((value) {
getWalletTransaction();
Get.back();
});
}
});
ShowToastDialog.showToast("Amount Top-up successfully".tr);
}
// Strip
Future<void> stripeMakePayment({required String amount}) async {
log(double.parse(amount).toStringAsFixed(0));
try {
Map<String, dynamic>? paymentIntentData = await createStripeIntent(amount: amount);
log("stripe Responce====>$paymentIntentData");
if (paymentIntentData!.containsKey("error")) {
Get.back();
ShowToastDialog.showToast("Something went wrong, please contact admin.".tr);
} else {
await Stripe.instance.initPaymentSheet(
paymentSheetParameters: SetupPaymentSheetParameters(
paymentIntentClientSecret: paymentIntentData['client_secret'],
allowsDelayedPaymentMethods: false,
googlePay: const PaymentSheetGooglePay(merchantCountryCode: 'US', testEnv: true, currencyCode: "USD"),
customFlow: true,
style: ThemeMode.system,
appearance: PaymentSheetAppearance(colors: PaymentSheetAppearanceColors(primary: AppThemeData.primary300)),
merchantDisplayName: 'GoRide',
),
);
displayStripePaymentSheet(amount: amount);
}
} catch (e, s) {
log("$e \n$s");
ShowToastDialog.showToast("exception:$e \n$s");
}
}
Future<void> displayStripePaymentSheet({required String amount}) async {
try {
await Stripe.instance.presentPaymentSheet().then((value) {
ShowToastDialog.showToast("Payment successfully".tr);
walletTopUp();
});
} on StripeException catch (e) {
var lo1 = jsonEncode(e);
var lo2 = jsonDecode(lo1);
StripePayFailedModel lom = StripePayFailedModel.fromJson(lo2);
ShowToastDialog.showToast(lom.error.message);
} catch (e) {
ShowToastDialog.showToast(e.toString());
}
}
Future createStripeIntent({required String amount}) async {
try {
Map<String, dynamic> body = {
'amount': ((double.parse(amount) * 100).round()).toString(),
'currency': "USD",
'payment_method_types[]': 'card',
"description": "Strip Payment",
"shipping[name]": userModel.value.fullName(),
"shipping[address][line1]": "510 Townsend St",
"shipping[address][postal_code]": "98140",
"shipping[address][city]": "San Francisco",
"shipping[address][state]": "CA",
"shipping[address][country]": "US",
};
var stripeSecret = stripeModel.value.stripeSecret;
var response = await http.post(
Uri.parse('https://api.stripe.com/v1/payment_intents'),
body: body,
headers: {'Authorization': 'Bearer $stripeSecret', 'Content-Type': 'application/x-www-form-urlencoded'},
);
return jsonDecode(response.body);
} catch (e) {
log(e.toString());
}
}
//mercadoo
Future<Null> mercadoPagoMakePayment({required BuildContext context, required String amount}) async {
final headers = {'Authorization': 'Bearer ${mercadoPagoModel.value.accessToken}', 'Content-Type': 'application/json'};
final body = jsonEncode({
"items": [
{
"title": "Test",
"description": "Test Payment",
"quantity": 1,
"currency_id": "BRL", // or your preferred currency
"unit_price": double.parse(amount),
},
],
"payer": {"email": userModel.value.email},
"back_urls": {"failure": "${Constant.globalUrl}payment/failure", "pending": "${Constant.globalUrl}payment/pending", "success": "${Constant.globalUrl}payment/success"},
"auto_return": "approved", // Automatically return after payment is approved
});
final response = await http.post(Uri.parse("https://api.mercadopago.com/checkout/preferences"), headers: headers, body: body);
if (response.statusCode == 200 || response.statusCode == 201) {
final data = jsonDecode(response.body);
Get.to(MercadoPagoScreen(initialURl: data['init_point']))!.then((value) {
if (value) {
ShowToastDialog.showToast("Payment Successful!!".tr);
walletTopUp();
} else {
ShowToastDialog.showToast("Payment UnSuccessful!!".tr);
}
});
} else {
ShowToastDialog.showToast("Something want wrong please contact administrator".tr);
print('Error creating preference: ${response.body}');
return null;
}
}
void paypalPaymentSheet(String amount, context) {
Navigator.of(context).push(
MaterialPageRoute(
builder:
(BuildContext context) => UsePaypal(
sandboxMode: payPalModel.value.isLive == true ? false : true,
clientId: payPalModel.value.paypalClient ?? '',
secretKey: payPalModel.value.paypalSecret ?? '',
returnURL: "com.parkme://paypalpay",
cancelURL: "com.parkme://paypalpay",
transactions: [
{
"amount": {
"total": amount,
"currency": "USD",
"details": {"subtotal": amount},
},
},
],
note: "Contact us for any questions on your order.",
onSuccess: (Map params) async {
walletTopUp();
ShowToastDialog.showToast("Payment Successful!!".tr);
},
onError: (error) {
Get.back();
ShowToastDialog.showToast("Payment UnSuccessful!!".tr);
},
onCancel: (params) {
Get.back();
ShowToastDialog.showToast("Payment UnSuccessful!!".tr);
},
),
),
);
}
///PayStack Payment Method
Future<void> payStackPayment(String totalAmount) async {
await PayStackURLGen.payStackURLGen(amount: (double.parse(totalAmount) * 100).toString(), currency: "ZAR", secretKey: payStackModel.value.secretKey.toString(), userModel: userModel.value).then((
value,
) async {
if (value != null) {
PayStackUrlModel payStackModel0 = value;
Get.to(
PayStackScreen(
secretKey: payStackModel.value.secretKey.toString(),
callBackUrl: payStackModel.value.callbackURL.toString(),
initialURl: payStackModel0.data.authorizationUrl,
amount: totalAmount,
reference: payStackModel0.data.reference,
),
)!.then((value) {
if (value) {
ShowToastDialog.showToast("Payment Successful!!".tr);
walletTopUp();
} else {
ShowToastDialog.showToast("Payment UnSuccessful!!".tr);
}
});
} else {
ShowToastDialog.showToast("Something went wrong, please contact admin.".tr);
}
});
}
//flutter wave Payment Method
Future<Null> flutterWaveInitiatePayment({required BuildContext context, required String amount}) async {
final url = Uri.parse('https://api.flutterwave.com/v3/payments');
final headers = {'Authorization': 'Bearer ${flutterWaveModel.value.secretKey}', 'Content-Type': 'application/json'};
final body = jsonEncode({
"tx_ref": _ref,
"amount": amount,
"currency": "NGN",
"redirect_url": "${Constant.globalUrl}payment/success",
"payment_options": "ussd, card, barter, payattitude",
"customer": {
"email": userModel.value.email.toString(),
"phonenumber": userModel.value.phoneNumber, // Add a real phone number
"name": userModel.value.fullName(), // Add a real customer name
},
"customizations": {"title": "Payment for Services", "description": "Payment for XYZ services"},
});
final response = await http.post(url, headers: headers, body: body);
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
Get.to(MercadoPagoScreen(initialURl: data['data']['link']))!.then((value) {
if (value) {
ShowToastDialog.showToast("Payment Successful!!".tr);
walletTopUp();
} else {
ShowToastDialog.showToast("Payment UnSuccessful!!".tr);
}
});
} else {
print('Payment initialization failed: ${response.body}');
return null;
}
}
String? _ref;
void setRef() {
maths.Random numRef = maths.Random();
int year = DateTime.now().year;
int refNumber = numRef.nextInt(20000);
if (Platform.isAndroid) {
_ref = "AndroidRef$year$refNumber";
} else if (Platform.isIOS) {
_ref = "IOSRef$year$refNumber";
}
}
// payFast
void payFastPayment({required BuildContext context, required String amount}) {
PayStackURLGen.getPayHTML(payFastSettingData: payFastModel.value, amount: amount.toString(), userModel: userModel.value).then((String? value) async {
bool isDone = await Get.to(PayFastScreen(htmlData: value!, payFastSettingData: payFastModel.value));
if (isDone) {
Get.back();
ShowToastDialog.showToast("Payment successfully".tr);
walletTopUp();
} else {
Get.back();
ShowToastDialog.showToast("Payment Failed".tr);
}
});
}
///RazorPay payment function
final Razorpay razorPay = Razorpay();
void openCheckout({required amount, required orderId}) async {
var options = {
'key': razorPayModel.value.razorpayKey,
'amount': amount * 100,
'name': 'GoRide',
'order_id': orderId,
"currency": "INR",
'description': 'wallet Topup',
'retry': {'enabled': true, 'max_count': 1},
'send_sms_hash': true,
'prefill': {'contact': userModel.value.phoneNumber, 'email': userModel.value.email},
'external': {
'wallets': ['paytm'],
},
};
try {
razorPay.open(options);
} catch (e) {
debugPrint('Error: $e');
}
}
void handlePaymentSuccess(PaymentSuccessResponse response) {
ShowToastDialog.showToast("Payment Successful!!".tr);
walletTopUp();
}
void handleExternalWaller(ExternalWalletResponse response) {
Get.back();
ShowToastDialog.showToast("Payment Processing!! via".tr);
}
void handlePaymentError(PaymentFailureResponse response) {
Get.back();
ShowToastDialog.showToast("Payment Failed!!".tr);
}
//Midtrans payment
Future<void> midtransMakePayment({required String amount, required BuildContext context}) async {
await createPaymentLink(amount: amount).then((url) {
ShowToastDialog.closeLoader();
if (url != '') {
Get.to(() => MidtransScreen(initialURl: url))!.then((value) {
if (value == true) {
ShowToastDialog.showToast("Payment Successful!!".tr);
walletTopUp();
} else {
ShowToastDialog.showToast("Payment Unsuccessful!!".tr);
}
});
}
});
}
Future<String> createPaymentLink({required var amount}) async {
var ordersId = const Uuid().v1();
final url = Uri.parse(midTransModel.value.isSandbox! ? 'https://api.sandbox.midtrans.com/v1/payment-links' : 'https://api.midtrans.com/v1/payment-links');
final response = await http.post(
url,
headers: {'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': generateBasicAuthHeader(midTransModel.value.serverKey!)},
body: jsonEncode({
'transaction_details': {'order_id': ordersId, 'gross_amount': double.parse(amount.toString()).toInt()},
'usage_limit': 2,
"callbacks": {"finish": "https://www.google.com?merchant_order_id=$ordersId"},
}),
);
if (response.statusCode == 200 || response.statusCode == 201) {
final responseData = jsonDecode(response.body);
return responseData['payment_url'];
} else {
ShowToastDialog.showToast("something went wrong, please contact admin.".tr);
return '';
}
}
String generateBasicAuthHeader(String apiKey) {
String credentials = '$apiKey:';
String base64Encoded = base64Encode(utf8.encode(credentials));
return 'Basic $base64Encoded';
}
//Orangepay payment
static String accessToken = '';
static String payToken = '';
static String orderId = '';
static String amount = '';
Future<void> orangeMakePayment({required String amount, required BuildContext context}) async {
reset();
var id = const Uuid().v4();
var paymentURL = await fetchToken(context: context, orderId: id, amount: amount, currency: 'USD');
ShowToastDialog.closeLoader();
if (paymentURL.toString() != '') {
Get.to(() => OrangeMoneyScreen(initialURl: paymentURL, accessToken: accessToken, amount: amount, orangePay: orangeMoneyModel.value, orderId: orderId, payToken: payToken))!.then((value) {
if (value == true) {
ShowToastDialog.showToast("Payment Successful!!".tr);
walletTopUp();
}
});
} else {
ShowToastDialog.showToast("Payment Unsuccessful!!".tr);
}
}
Future fetchToken({required String orderId, required String currency, required BuildContext context, required String amount}) async {
String apiUrl = 'https://api.orange.com/oauth/v3/token';
Map<String, String> requestBody = {'grant_type': 'client_credentials'};
var response = await http.post(
Uri.parse(apiUrl),
headers: <String, String>{'Authorization': "Basic ${orangeMoneyModel.value.auth!}", 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json'},
body: requestBody,
);
if (response.statusCode == 200) {
Map<String, dynamic> responseData = jsonDecode(response.body);
accessToken = responseData['access_token'];
return await webpayment(context: context, amountData: amount, currency: currency, orderIdData: orderId);
} else {
ShowToastDialog.showToast("Something went wrong, please contact admin.".tr);
return '';
}
}
Future webpayment({required String orderIdData, required BuildContext context, required String currency, required String amountData}) async {
orderId = orderIdData;
amount = amountData;
String apiUrl = orangeMoneyModel.value.isSandbox! == true ? 'https://api.orange.com/orange-money-webpay/dev/v1/webpayment' : 'https://api.orange.com/orange-money-webpay/cm/v1/webpayment';
Map<String, String> requestBody = {
"merchant_key": orangeMoneyModel.value.merchantKey ?? '',
"currency": orangeMoneyModel.value.isSandbox == true ? "OUV" : currency,
"order_id": orderId,
"amount": amount,
"reference": 'Y-Note Test',
"lang": "en",
"return_url": orangeMoneyModel.value.returnUrl!.toString(),
"cancel_url": orangeMoneyModel.value.cancelUrl!.toString(),
"notif_url": orangeMoneyModel.value.notifUrl!.toString(),
};
var response = await http.post(
Uri.parse(apiUrl),
headers: <String, String>{'Authorization': 'Bearer $accessToken', 'Content-Type': 'application/json', 'Accept': 'application/json'},
body: json.encode(requestBody),
);
print(response.statusCode);
print(response.body);
// Handle the response
if (response.statusCode == 201) {
Map<String, dynamic> responseData = jsonDecode(response.body);
if (responseData['message'] == 'OK') {
payToken = responseData['pay_token'];
return responseData['payment_url'];
} else {
return '';
}
} else {
ShowToastDialog.showToast("Something went wrong, please contact admin.".tr);
return '';
}
}
static void reset() {
accessToken = '';
payToken = '';
orderId = '';
amount = '';
}
//XenditPayment
Future<void> xenditPayment(context, amount) async {
await createXenditInvoice(amount: amount).then((model) {
ShowToastDialog.closeLoader();
if (model.id != null) {
Get.to(() => XenditScreen(initialURl: model.invoiceUrl ?? '', transId: model.id ?? '', apiKey: xenditModel.value.apiKey!.toString()))!.then((value) {
if (value == true) {
ShowToastDialog.showToast("Payment Successful!!".tr);
walletTopUp();
} else {
ShowToastDialog.showToast("Payment Unsuccessful!!".tr);
}
});
}
});
}
Future<XenditModel> createXenditInvoice({required var amount}) async {
const url = 'https://api.xendit.co/v2/invoices';
var headers = {
'Content-Type': 'application/json',
'Authorization': generateBasicAuthHeader(xenditModel.value.apiKey!.toString()),
// 'Cookie': '__cf_bm=yERkrx3xDITyFGiou0bbKY1bi7xEwovHNwxV1vCNbVc-1724155511-1.0.1.1-jekyYQmPCwY6vIJ524K0V6_CEw6O.dAwOmQnHtwmaXO_MfTrdnmZMka0KZvjukQgXu5B.K_6FJm47SGOPeWviQ',
};
final body = jsonEncode({
'external_id': const Uuid().v1(),
'amount': amount,
'payer_email': 'customer@domain.com',
'description': 'Test - VA Successful invoice payment',
'currency': 'IDR', //IDR, PHP, THB, VND, MYR
});
try {
final response = await http.post(Uri.parse(url), headers: headers, body: body);
if (response.statusCode == 200 || response.statusCode == 201) {
XenditModel model = XenditModel.fromJson(jsonDecode(response.body));
return model;
} else {
return XenditModel();
}
} catch (e) {
return XenditModel();
}
}
}