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,59 +1,68 @@
plugins {
id "com.android.application"
id "kotlin-android"
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id "dev.flutter.flutter-gradle-plugin"
}
def localProperties = new Properties()
def localPropertiesFile = rootProject.file("local.properties")
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader("UTF-8") { reader ->
localProperties.load(reader)
}
// Load key.properties for signing release builds
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file("key.properties")
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
def flutterVersionCode = localProperties.getProperty("flutter.versionCode")
if (flutterVersionCode == null) {
flutterVersionCode = "1"
}
def flutterVersionName = localProperties.getProperty("flutter.versionName")
if (flutterVersionName == null) {
flutterVersionName = "1.0"
}
// Flutter versioning
def flutterVersionCode = 12
def flutterVersionName = "1.0.4"
android {
namespace = "com.frontshop.userapp"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
compileSdk = 35
ndkVersion = "26.1.10909125" // Optional; only if you use native code
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
defaultConfig {
applicationId = "com.frontshop.userapp"
minSdk = 23
targetSdk = 35
versionCode = flutterVersionCode
versionName = flutterVersionName
}
defaultConfig
{
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.frontshop.userapp"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdk = flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion
versionCode = flutterVersionCode.toInteger()
versionName = flutterVersionName
signingConfigs {
release {
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
}
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig = signingConfigs.debug
signingConfig = signingConfigs.release
minifyEnabled false
shrinkResources false
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
coreLibraryDesugaringEnabled true
}
kotlinOptions {
jvmTarget = '1.8'
}
}
flutter {
source = "../.."
}
dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'
// ✅ PhonePe SDK - ensure this matches your maven URL from root `build.gradle`
}

View File

@@ -2,34 +2,34 @@
xmlns:tools="http://schemas.android.com/tools"
package="com.frontshop.userapp">
<!-- Permissions -->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.CALL_PHONE"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<application
android:requestLegacyExternalStorage="true"
<application
android:requestLegacyExternalStorage="true"
android:enableOnBackInvokedCallback="true"
android:label="Frontshop"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="high_importance_channel"/>
<!-- Firebase notification channel -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="high_importance_channel"/>
<!-- Main activity -->
<activity
android:name=".MainActivity"
android:exported="true"
@@ -39,69 +39,76 @@
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<!-- Flutter splash theme -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"/>
<!-- PhonePe / Deeplink support -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="frontshop"
android:host="payment-response"
android:pathPrefix="/" />
</intent-filter>
<!-- Main launcher -->
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Razorpay Tokenizer -->
<activity
android:name="com.razorpay.RzpTokeniserSdkFlutter.TokeniserSdkActivity"
android:theme="@style/Theme.AppCompat.NoActionBar"
android:exported="false" />
<!-- Firebase Messaging -->
<service
android:name="io.flutter.plugins.firebase.messaging.FlutterFirebaseMessagingService"
android:exported="true"
tools:replace="android:exported">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
<!-- Firebase Maps API Key -->
<meta-data android:name="com.google.android.geo.API_KEY"
android:value="AIzaSyAi3_Dls63iGs7Nccgdm-4FkS0rhT03-4U"/>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
android:value="AIzaSyAi3_Dls63iGs7Nccgdm-4FkS0rhT03-4U"/>
<!-- Flutter embedding -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
<service
android:name="io.flutter.plugins.firebase.messaging.FlutterFirebaseMessagingService"
android:exported="true"
tools:replace="android:exported">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
</application>
<!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility and
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
<!-- Queries for package visibility -->
<queries>
<intent>
<action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/>
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="sms" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="tel" />
</intent>
<intent>
<action android:name="android.support.customtabs.action.CustomTabsService" />
</intent>
</queries>
<!-- Provide required visibility configuration for API level 30 and above -->
<queries>
<!-- If your app checks for SMS support -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="sms" />
</intent>
<!-- If your app checks for call support -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="tel" />
</intent>
<!-- If your application checks for inAppBrowserView launch mode support -->
<intent>
<action android:name="android.support.customtabs.action.CustomTabsService" />
</intent>
</queries>
</manifest>

View File

@@ -1,5 +1,3 @@
package com.frontshop.userapp
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity()
import io.flutter.embedding.android.FlutterFragmentActivity
class MainActivity : FlutterFragmentActivity()

View File

@@ -2,14 +2,18 @@ allprojects {
repositories {
google()
mavenCentral()
// ✅ Required for PhonePe SDK
maven {
url "https://phonepe.mycloudrepo.io/public/repositories/phonepe-intentsdk-android"
}
}
}
rootProject.buildDir = "../build"
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(":app")
}

View File

@@ -2,4 +2,5 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip

View File

@@ -18,7 +18,7 @@ pluginManagement {
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "7.3.0" apply false
id "com.android.application" version "8.6.0" apply false
id "org.jetbrains.kotlin.android" version "1.9.10" apply false
}

View File

@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '12.0'
# platform :ios, '13.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

View File

@@ -45,5 +45,21 @@
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>tez</string>
<string>phonepe</string>
<string>paytmmp</string>
<string>bhim</string>
</array>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>frontshop</string> <!-- 👈 must match `appSchema` -->
</array>
</dict>
</array>
</dict>
</plist>

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),
),
),
);
}
}

View File

@@ -7,12 +7,16 @@
#include "generated_plugin_registrant.h"
#include <file_selector_linux/file_selector_plugin.h>
#include <gtk/gtk_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
g_autoptr(FlPluginRegistrar) gtk_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "GtkPlugin");
gtk_plugin_register_with_registrar(gtk_registrar);
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);

View File

@@ -4,6 +4,7 @@
list(APPEND FLUTTER_PLUGIN_LIST
file_selector_linux
gtk
url_launcher_linux
)

View File

@@ -5,6 +5,7 @@
import FlutterMacOS
import Foundation
import app_links
import connectivity_plus
import file_selector_macos
import firebase_core
@@ -15,10 +16,11 @@ import geolocator_apple
import package_info_plus
import path_provider_foundation
import shared_preferences_foundation
import sqflite
import sqflite_darwin
import url_launcher_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin"))
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@ name: grocery_app
description: "CustomerApp"
publish_to: 'none'
version: 1.0.0+1
version: 1.0.3+25
environment:
sdk: '>=3.4.0 <4.0.0'
@@ -24,16 +24,21 @@ dependencies:
flutter_toggle_tab: ^1.4.1
dotted_border: ^2.1.0
go_router: ^14.2.0
uuid: ^4.0.0
phonepe_payment_sdk: ^3.0.0
google_maps_flutter:
geolocator:
geocoding:
flutter_google_places:
google_api_headers:
app_links: ^3.0.0
google_places_flutter: ^2.1.0
google_api_headers: ^4.4.1
fluttertoast:
flutter_rating_stars: ^1.1.0
url_launcher: ^6.3.1
skeletonizer: ^1.4.3
art_sweetalert: ^0.0.5
blur:
flutter_rating_bar:
change_app_package_name: ^1.5.0
@@ -75,6 +80,10 @@ dependencies:
image_picker: any
shimmer: ^3.0.0
flutter_inappwebview: ^6.0.0
razorpay_flutter: ^1.3.7
http: ^1.1.0
dev_dependencies:

View File

@@ -1,30 +0,0 @@
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility in the flutter_test package. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:grocery_app/main.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(const MyApplication());
// Verify that our counter starts at 0.
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
// Tap the '+' icon and trigger a frame.
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
// Verify that our counter has incremented.
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
});
}

View File

@@ -6,20 +6,26 @@
#include "generated_plugin_registrant.h"
#include <app_links/app_links_plugin_c_api.h>
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
#include <file_selector_windows/file_selector_windows.h>
#include <firebase_core/firebase_core_plugin_c_api.h>
#include <flutter_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.h>
#include <geolocator_windows/geolocator_windows.h>
#include <permission_handler_windows/permission_handler_windows_plugin.h>
#include <url_launcher_windows/url_launcher_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
AppLinksPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("AppLinksPluginCApi"));
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
FileSelectorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FileSelectorWindows"));
FirebaseCorePluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FirebaseCorePluginCApi"));
FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterInappwebviewWindowsPluginCApi"));
GeolocatorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("GeolocatorWindows"));
PermissionHandlerWindowsPluginRegisterWithRegistrar(

View File

@@ -3,9 +3,11 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
app_links
connectivity_plus
file_selector_windows
firebase_core
flutter_inappwebview_windows
geolocator_windows
permission_handler_windows
url_launcher_windows