Code committed

This commit is contained in:
2025-08-08 22:21:49 +05:30
parent f572983146
commit 9abcd609bf
22 changed files with 992 additions and 417 deletions

View File

@@ -1,11 +1,11 @@
class APIURL {
static const BASE_URL = "http://210.89.44.183:3333/xam/";
static const BASE_URL = "https://www.mv.frontshopemporium.in/xam/";
static const String sendOtp = "${BASE_URL}auth/send-otp/customer";
static const String verifyOtp = "${BASE_URL}auth/verify-otp/customer";
static const String loginOtp = "${BASE_URL}auth/login/customer";
static const String login = "${BASE_URL}auth/login/vendor";
static const String customerRegister = "${BASE_URL}auth/register/customer";
static const String getAllProduct = "${BASE_URL}products";
static const String getAllProduct = "${BASE_URL}products?order=home";
static const String getProductDetails = "${BASE_URL}products/";
static const String getBanners = "${BASE_URL}banners";
static const String customerLogOut = "${BASE_URL}auth/logout/customer";
@@ -39,6 +39,8 @@ class APIURL {
static const String getProduct = "${BASE_URL}products";
static const String getCategoryByLevel = "${BASE_URL}categories/by-level/1";
static const String getMe = "${BASE_URL}auth/me";
static const String distanctByProduct = "${BASE_URL}distance/delivery-charges-by-distance";
static const String createProduct = "${BASE_URL}products";
@@ -50,6 +52,7 @@ class APIURL {
static const String updateStatus = "${BASE_URL}orders/items/";
static const String checkAddress = "${BASE_URL}distance/by-address/";
static const String productReview = "${BASE_URL}products/";
static const String upDateDeviceToken = "${BASE_URL}devices/register";

View File

@@ -377,75 +377,66 @@ class AddtocartProvider extends ChangeNotifier {
////////////////////////////COD ////////////
Future<void> paymentCODOrder(
BuildContext context,
double subtotal,
int deliverCharge,
String couponId,
String addressId,
) async {
Future<bool> paymentCODOrder(
BuildContext context,
double subtotal,
int deliverCharge,
String couponId,
String addressId,
) async {
ispaymentLoader = true;
notifyListeners();
var data;
if (couponId.isNotEmpty) {
data = {
"addressId": addressId,
"paymentMethod": "COD",
"paymentStatus": "PENDING",
"orderStatus": "PENDING",
"subtotal": subtotal,
"deliveryCharge": deliverCharge,
"transactionId": "phonepe_transaction_123",
"couponId": couponId
};
} else {
data = {
"addressId": addressId,
"paymentMethod": "COD",
"paymentStatus": "PENDING",
"orderStatus": "PENDING",
"subtotal": subtotal,
"deliveryCharge": deliverCharge,
};
}
print("kjfhxgkljfhg ${data}");
var data = {
"addressId": addressId,
"paymentMethod": "COD",
"paymentStatus": "PENDING",
"orderStatus": "PENDING",
"subtotal": subtotal,
"deliveryCharge": deliverCharge,
};
if (couponId.isNotEmpty) {
data["couponId"] = couponId;
data["transactionId"] = "phonepe_transaction_123";
}
try {
var result = await _homeRepo.paymentCODOrder(data);
return result.fold(
(error) {
(error) {
Fluttertoast.showToast(
msg: "${error.message}",
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.BOTTOM,
backgroundColor: Colors.green,
backgroundColor: Colors.red,
textColor: Colors.white,
fontSize: 14.0,
);
ispaymentLoader = false;
notifyListeners();
return false;
},
(response) {
context.clearAndPush(routePath: MyRoutes.SUCCESSPAYMENT);
(response) {
ispaymentLoader = false;
notifyListeners();
return true;
},
);
} catch (e) {
ispaymentLoader = false;
notifyListeners();
Fluttertoast.showToast(
msg: "${e}",
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.BOTTOM,
backgroundColor: Colors.green,
backgroundColor: Colors.red,
textColor: Colors.white,
fontSize: 14.0,
);
notifyListeners();
return false;
}
}

View File

@@ -1,13 +1,17 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:grocery_app/src/common_widget/name_text_field.dart';
import 'package:grocery_app/src/common_widget/network_image.dart';
import 'package:grocery_app/src/logic/provider/addTocart_provider.dart';
import 'package:grocery_app/src/ui/payment/phonepe_payment.dart';
import 'package:grocery_app/utils/constants/assets_constant.dart';
import 'package:grocery_app/utils/constants/color_constant.dart';
import 'package:grocery_app/utils/extensions/uicontext.dart';
import 'package:provider/provider.dart';
import '../payment/razorpay_payment.dart';
class CardCheckoutScreen extends StatefulWidget {
int deliveryCharge;
// String currency;
@@ -15,7 +19,7 @@ class CardCheckoutScreen extends StatefulWidget {
// String name;
// String phone;
// String email;
// String userId;
String userId;
String cartId;
String addressId;
// String remarks;
@@ -30,7 +34,7 @@ class CardCheckoutScreen extends StatefulWidget {
// required this.name,
// required this.phone,
// required this.email,
// required this.userId,
required this.userId,
required this.cartId,
required this.addressId,
// required this.remarks,
@@ -84,7 +88,7 @@ class _CardCheckoutScreenState extends State<CardCheckoutScreen> {
),
Expanded(
child: InkWell(
onTap: () {
/* onTap: () {
if (paymentProvider.selectedPaymentMethod == "Online") {
print("dsjfkhkdfhgdkfghdfg");
paymentProvider.orderPaymnet(
@@ -93,7 +97,8 @@ class _CardCheckoutScreenState extends State<CardCheckoutScreen> {
widget.cartId,
widget.addressId,
widget.couponId!);
} else {
}
else {
paymentProvider.paymentCODOrder(
context,
widget.originalAmount,
@@ -102,6 +107,68 @@ class _CardCheckoutScreenState extends State<CardCheckoutScreen> {
widget.addressId,
);
}
},*/
onTap: (){
if(paymentProvider.selectedPaymentMethod!=null) {
if (paymentProvider.selectedPaymentMethod ==
"razorpay") {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
RazorpayPaymentScreen(
amount: widget.originalAmount +
widget.deliveryCharge,
cartId: widget.cartId,
addressId: widget.addressId,
couponId: widget.couponId,
userId: widget.userId,
),
),
);
} else if (paymentProvider.selectedPaymentMethod ==
"phonepe") {
// Navigate to PhonePe screen (optional for now)
// Replace this when you implement PhonePe
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
PhonePePaymentScreen(
amount: widget.originalAmount +
widget.deliveryCharge,
cartId: widget.cartId,
addressId: widget.addressId,
//couponId: widget.couponId,
userId: widget.userId,
),
),
);
/* ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("PhonePe integration coming soon")),
);*/
} /*
else {
paymentProvider.paymentCODOrder(
context,
widget.originalAmount,
widget.deliveryCharge,
widget.couponId ?? "",
widget.addressId,
);
}*/
}else
{
Fluttertoast.showToast(
msg: "Select Payment method",
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.BOTTOM,
backgroundColor: Colors.green,
textColor: Colors.white,
fontSize: 14.0,
);
}
},
child: Container(
height: 50,
@@ -173,16 +240,34 @@ class _CardCheckoutScreenState extends State<CardCheckoutScreen> {
),
SizedBox(height: 0),
ListTile(
/* ListTile(
leading: Icon(Icons.payment, color: Colors.blue),
title: Text("Online Payment"),
trailing: paymentProvider.selectedPaymentMethod == "Online"
? Icon(Icons.check_circle, color: Colors.green)
: null,
onTap: () {
paymentProvider.selectPaymentMethod("Online");
// paymentProvider.selectPaymentMethod("Online");
// Navigator.pop(context);
},
),*/
RadioListTile<String>(
value: "razorpay",
groupValue: paymentProvider.selectedPaymentMethod,
title: const Text("Razorpay Online Payment"),
onChanged: (value) {
paymentProvider.selectPaymentMethod(value!);
},
),
RadioListTile<String>(
value: "phonepe",
groupValue: paymentProvider.selectedPaymentMethod,
title: const Text("PhonePe Online Payment"),
onChanged: (value) {
paymentProvider.selectPaymentMethod(value!);
},
),
// Cash on Delivery (COD) Option
@@ -404,3 +489,4 @@ class _CardCheckoutScreenState extends State<CardCheckoutScreen> {
);
}
}

View File

@@ -1,3 +1,5 @@
import 'dart:convert';
import 'package:carousel_slider/carousel_slider.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
@@ -15,6 +17,7 @@ import 'package:grocery_app/src/ui/widgets/elevated_button.dart';
import 'package:grocery_app/utils/constants/color_constant.dart';
import 'package:grocery_app/utils/constants/shared_pref_utils.dart';
import 'package:grocery_app/utils/extensions/uicontext.dart';
import 'package:http/http.dart' as http;
import 'package:provider/provider.dart';
import 'package:skeletonizer/skeletonizer.dart';
import 'package:url_launcher/url_launcher.dart';
@@ -1006,6 +1009,8 @@ class _MycartState extends State<Mycart> {
);
}
void _showAddressBottomSheet(BuildContext context) {
showModalBottomSheet(
context: context,
@@ -1140,6 +1145,67 @@ class AddressBottomSheet extends StatefulWidget {
}
class _AddressBottomSheetState extends State<AddressBottomSheet> {
Future<String?> fetchDeliveryMessage() async {
const String url = 'https://www.mv.frontshopemporium.in/xam/distance/delivery-charges-by-distance';
try {
final response = await http.get(Uri.parse(url), headers: {
"Content-Type": "application/json",
"authorization": "Bearer ${await SharedPrefUtils.getToken()}"
});
print('continue response');
print(response.statusCode);
print(response.body);
if (response.statusCode == 200) {
final Map<String, dynamic> data = json.decode(response.body);
print(data);
final List<dynamic> nonDeliverableProducts = data['nonDeliverableProducts'] ?? [];
if (nonDeliverableProducts.isNotEmpty) {
final String reason = nonDeliverableProducts[0]['reason'];
print(reason);
return reason;
} else {
return '';
}
} else {
return 'Failed with status: ${response.statusCode}';
}
} catch (e) {
return 'Error: $e';
}
}
Future<String?> setDefaultAddress(String addressId) async {
String url = 'https://www.mv.frontshopemporium.in/xam/user/addresses/${addressId}/set-default';
try {
final response = await http.put(Uri.parse(url), headers: {
"Content-Type": "application/json",
"authorization": "Bearer ${await SharedPrefUtils.getToken()}"
});
print('address response');
print(response.statusCode);
print(response.body);
if (response.statusCode == 200) {
/* final Map<String, dynamic> data = json.decode(response.body);
print(data);
final List<dynamic> nonDeliverableProducts = data['nonDeliverableProducts'] ?? [];
if (nonDeliverableProducts.isNotEmpty) {
final String reason = nonDeliverableProducts[0]['reason'];
return reason;
} else {
return 'No non-deliverable products found';
}*/
} else {
return 'Failed with status: ${response.statusCode}';
}
} catch (e) {
return 'Error: $e';
}
}
@override
Widget build(BuildContext context) {
return Padding(
@@ -1186,8 +1252,8 @@ class _AddressBottomSheetState extends State<AddressBottomSheet> {
Consumer<AddtocartProvider>(
builder: (context, paymentProvider, child) {
return ElevatedButton.icon(
onPressed: () {
if (paymentProvider.selectedAddress.isNotEmpty) {
onPressed: () async{
/* if (paymentProvider.selectedAddress.isNotEmpty) {
if (paymentProvider.isDeliverable) {
Navigator.pop(context);
Navigator.of(context).push(MaterialPageRoute(
@@ -1200,7 +1266,7 @@ class _AddressBottomSheetState extends State<AddressBottomSheet> {
// name: paymentProvider.selecteUserName,
// phone: paymentProvider.selecteUserPhone,
// email: paymentProvider.selecteEmail,
// userId: paymentProvider.allitem.userId!,
userId: paymentProvider.allitem.userId!,
cartId: paymentProvider.allitem.id!,
addressId: paymentProvider.selectedAddress,
// remarks: paymentProvider.selecteUserName,
@@ -1229,17 +1295,97 @@ class _AddressBottomSheetState extends State<AddressBottomSheet> {
textColor: Colors.white,
fontSize: 14.0,
);
}*/
// Call your API and get the message
String? message = await fetchDeliveryMessage(); // Use the function from earlier
if (message != null && message.isNotEmpty) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Alert"),
content: Text(message),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop(); // Close the dialog
},
child: Text("OK"),
),
],
);
},
);
} else {
Navigator.pop(context);
Navigator.of(context).push(MaterialPageRoute(
builder: (context) {
return CardCheckoutScreen(
deliveryCharge: paymentProvider.getdeliverycharge,
originalAmount: paymentProvider.grandPrice,
userId: paymentProvider.allitem.userId!,
cartId: paymentProvider.allitem.id!,
addressId: paymentProvider.selectedAddress,
couponId: paymentProvider.couponId,
);
},
));
}
},
/*
// Call your API and get the message
await paymentProvider.paymentCODOrder(
context,
paymentProvider.grandPrice,
paymentProvider.getdeliverycharge,
paymentProvider.couponId ?? "",
paymentProvider.selectedAddress,
);
*/
/* if (message != null && message.isNotEmpty) {
Fluttertoast.showToast(
msg: message,
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.BOTTOM,
backgroundColor: Colors.red,
textColor: Colors.white,
fontSize: 14.0,
);
} else {*/
/*
Navigator.pop(context);
Navigator.of(context).push(MaterialPageRoute(
builder: (context) {
return CardCheckoutScreen(
deliveryCharge: paymentProvider.getdeliverycharge,
originalAmount: paymentProvider.grandPrice,
userId: paymentProvider.allitem.userId!,
cartId: paymentProvider.allitem.id!,
addressId: paymentProvider.selectedAddress,
couponId: paymentProvider.couponId,
);
},
));
// }
},*/
label: Text(
"Continue",
style: TextStyle(color: Colors.white, fontSize: 16),
),
style: ElevatedButton.styleFrom(
backgroundColor: paymentProvider.isDeliverable
backgroundColor:Colors.green,
/*backgroundColor: paymentProvider.isDeliverable
? Colors.green
: Colors.grey,
minimumSize: Size(double.infinity, 50),
*/minimumSize: Size(double.infinity, 50),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
),
@@ -1278,12 +1424,13 @@ class _AddressBottomSheetState extends State<AddressBottomSheet> {
activeColor: Colors.green,
onChanged: (value) {
addressProvider.checkAddress(context, value);
setDefaultAddress(address.id);
addressProvider.selectAddress(
value.toString(),
address.phoneNumber,
address.name,
address.user!.email);
},
),
title: Text(

View File

@@ -59,7 +59,7 @@ class _MyOrderScreenState extends State<MyOrderScreen> {
width: 20,
child: InkWell(
onTap: () {
context.clearAndPush(routePath: MyRoutes.BOTTOMNAV);
context.clearAndPush(routePath: MyRoutes.HOME);
},
child: SvgPicture.asset(
APPASSETS.back,

View File

@@ -0,0 +1,271 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:http/http.dart' as http;
import 'package:phonepe_payment_sdk/phonepe_payment_sdk.dart';
import 'package:uuid/uuid.dart';
import 'dart:convert';
import 'dart:async';
import 'package:app_links/app_links.dart';
import '../../../utils/constants/shared_pref_utils.dart';
import '../../core/routes/routes.dart';
import '../myOrder/my_order.dart';
class PhonePePaymentScreen extends StatefulWidget {
final double amount;
final String cartId;
final String addressId;
final String userId;
const PhonePePaymentScreen({
required this.amount,
required this.cartId,
required this.addressId,
required this.userId,
super.key,
});
@override
State<PhonePePaymentScreen> createState() => _PhonePePaymentScreenState();
}
class _PhonePePaymentScreenState extends State<PhonePePaymentScreen> {
String _statusMessage = "Initiating payment...";
bool _isLoading = true;
final AppLinks _appLinks = AppLinks();
StreamSubscription<Uri>? _linkSub;
@override
void initState() {
super.initState();
_listenForRedirect();
_initiateAndStartPayment();
}
@override
void dispose() {
_linkSub?.cancel();
super.dispose();
}
void _listenForRedirect() {
print("Listening for redirect...");
_linkSub = _appLinks.uriLinkStream.listen((Uri uri) {
print("Deep link received: $uri");
final transactionId = uri.queryParameters['transactionId'];
final status = uri.queryParameters['status'];
if (transactionId != null && status == "SUCCESS") {
//_verifyPayment(transactionId);
} else if (status == "CANCEL") {
setState(() {
_statusMessage = "Payment cancelled by user.";
_isLoading = false;
});
Navigator.pop(context, false); // 👈 Return failure
} else {
setState(() {
_statusMessage = "Payment failed or unknown status.";
_isLoading = false;
});
Navigator.pop(context, false); // 👈 Return failure
}
}, onError: (err) {
print("Deep link error: $err");
setState(() {
_statusMessage = "Error handling redirect: $err";
_isLoading = false;
});
Navigator.pop(context, false); // 👈 Return failure
});
}
Future<void> _initiateAndStartPayment() async {
try {
final uri = Uri.parse("https://www.mv.frontshopemporium.in/xam/payment/phonepe/sdk/initiate");
print("Calling backend initiate API...");
final response = await http.post(
uri,
headers: {
"Content-Type": "application/json",
"authorization": "Bearer ${await SharedPrefUtils.getToken()}",
},
body: jsonEncode({
"amount": widget.amount,
"cartId": widget.cartId,
"addressId": widget.addressId,
}),
);
print("Response code: ${response.statusCode}");
print("Response body: ${response.body}");
if (response.statusCode != 200 && response.statusCode != 201) {
setState(() {
_statusMessage = "Failed to initiate payment.";
_isLoading = false;
});
return;
}
final data = jsonDecode(response.body);
final String? orderId = data['orderId'];
final String? merchantId = data['merchantId'];
final String? token = data['token'];
const String environment = "PRODUCTION";
const String appSchema = "frontshop";
final String flowId = 'FLWID_${Uuid().v4()}';
if (orderId == null || merchantId == null || token == null) {
print("Invalid response from backend. Missing fields.");
setState(() {
_statusMessage = "Invalid response from backend.";
_isLoading = false;
});
return;
}
print("Initializing SDK...");
PhonePePaymentSdk.init(environment, merchantId, flowId, true);
final payload = {
"orderId": orderId,
"merchantId": merchantId,
"token": token,
"paymentMode": {"type": "PAY_PAGE"},
};
String request = jsonEncode(payload);
print("Starting transaction with payload: $request");
final result = await PhonePePaymentSdk.startTransaction(request, appSchema);
print("Transaction result: $result");
if (result != null) {
final status = result['status']?.toString() ?? '';
final error = result['error']?.toString() ?? '';
if (status == 'SUCCESS') {
final txnId = data['merchantOrderId']?? '';
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("Payment Successful!")),
);
_verifyPayment(txnId);
context.push(MyRoutes.MYORDER);
} else if (status == 'CANCELLED') {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("Payment Cancelled by User")),
);
setState(() {
_statusMessage = "Payment cancelled.";
_isLoading = false;
});
Navigator.pop(context, false);
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Payment Failed: $error")),
);
setState(() {
_statusMessage = "Payment failed.";
_isLoading = false;
});
Navigator.pop(context, false);
}
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("No response from PhonePe")),
);
setState(() {
_statusMessage = "No response.";
_isLoading = false;
});
Navigator.pop(context, false);
}
} catch (e) {
print("Error during payment: $e");
setState(() {
_statusMessage = "Error: ${e.toString()}";
_isLoading = false;
});
Navigator.pop(context, false); // 👈 Exit with failure
}
}
Future<void> _verifyPayment(String transactionId) async {
try {
final verifyUri = Uri.parse(
"https://mv.frontshopemporium.in/xam/payment/phonepe/sdk/status/$transactionId",
);
final response = await http.get(
verifyUri,
headers: {
"Content-Type": "application/json",
"authorization": "Bearer ${await SharedPrefUtils.getToken()}",
},
);
print("Status check response: ${response.body}");
if (response.statusCode == 200) {
final statusData = jsonDecode(response.body);
final paymentStatus = statusData['status'];
if (paymentStatus == "SUCCESS") {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("Order Placed Successfully")),
);
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (_) => MyOrderScreen()),
(route) => false,
);
}
else {
setState(() {
_statusMessage = "Payment verification failed: $paymentStatus";
_isLoading = false;
});
Navigator.pop(context, false); // ❌ Return failure
}
} else {
setState(() {
_statusMessage = "Failed to verify payment.";
_isLoading = false;
});
Navigator.pop(context, false);
}
} catch (e) {
print("Verification error: $e");
setState(() {
_statusMessage = "Verification error: ${e.toString()}";
_isLoading = false;
});
Navigator.pop(context, false); // ❌ Return failure
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("PhonePe Payment")),
body: Center(
child: _isLoading
? const CircularProgressIndicator()
: Text(
_statusMessage,
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 16),
),
),
);
}
}