diff --git a/app/payments/Confirmation.tsx b/app/payments/Confirmation.tsx
index 17e9921..3fc5dbf 100644
--- a/app/payments/Confirmation.tsx
+++ b/app/payments/Confirmation.tsx
@@ -1,23 +1,127 @@
import React from "react";
import { View, Text, StyleSheet, TouchableOpacity } from "react-native";
import { useRouter } from "expo-router";
+import { useSelector } from "react-redux";
+import { RootState } from "@/store/rootReducer";
+import Header from "@/components/common/Header";
+import CheckCircle from "@/assets/icons/check_circle.svg";
+import { useSafeAreaInsets } from "react-native-safe-area-context";
const PaymentConfirmationScreen = () => {
const router = useRouter();
+ const { paymentOrder, due_amount } = useSelector(
+ (state: RootState) => state.payments
+ );
+
+ const insets = useSafeAreaInsets();
+
+ // Format amount with currency
+ const formatAmount = (amount: number | null) => {
+ if (!amount) return "₹0";
+ return `₹${amount.toLocaleString("en-IN")}`;
+ };
+
+ // Format date
+ const formatDate = (dateString?: string) => {
+ if (!dateString)
+ return new Date().toLocaleString("en-IN", {
+ day: "numeric",
+ month: "long",
+ year: "numeric",
+ hour: "numeric",
+ minute: "2-digit",
+ hour12: true,
+ weekday: "long",
+ });
+
+ return new Date(dateString).toLocaleString("en-IN", {
+ day: "numeric",
+ month: "long",
+ year: "numeric",
+ hour: "numeric",
+ minute: "2-digit",
+ hour12: true,
+ weekday: "long",
+ });
+ };
return (
- Payment Successful!
-
- Thank you for your payment. Your transaction has been completed.
-
+
- router.push("/dashboard")} // Or wherever you want to go
- >
- Go to Dashboard
-
+
+
+
+
+
+
+
+ {formatAmount(paymentOrder?.amount || due_amount)}
+
+
+
+ Payment successful
+
+ {formatDate(paymentOrder?.transaction_date)}
+
+
+
+
+
+
+
+ Transaction Details
+
+
+ Payment mode
+
+ {paymentOrder?.payment_mode?.[0] || "UPI"}
+
+
+
+ Paid to
+
+ {paymentOrder?.upi_handle || "randomupiid@vecpay"}
+
+
+
+
+ Paid by
+
+ {paymentOrder?.paid_by_upi_handle || "amar.kesari@vecpay"}
+
+
+
+
+ Order ID
+
+ {paymentOrder?.order_id || "1000516861984940"}
+
+
+
+
+ Transaction ID
+
+ {paymentOrder?.transaction_id ||
+ paymentOrder?.transaction_order_id ||
+ "1000516861984940"}
+
+
+
+ RRN
+
+ {paymentOrder?.payment_reference_id || "1000516861984940"}
+
+
+
+
+ router.replace("/(tabs)/payments")}
+ >
+ OK
+
+
);
};
@@ -27,32 +131,92 @@ export default PaymentConfirmationScreen;
const styles = StyleSheet.create({
container: {
flex: 1,
+ backgroundColor: "#f3f4f6", // Light gray background
+ },
+ contentFrame: {
+ flex: 1,
+ paddingHorizontal: 16,
+ paddingBottom: 16,
+ justifyContent: "space-between",
+ },
+ qrFrame: {
+ backgroundColor: "#fcfcfc",
+ borderRadius: 8,
+ padding: 16,
+ paddingVertical: 32,
+ },
+ paymentStatusContainer: {
+ alignItems: "center",
+ },
+ successIconContainer: {
+ marginBottom: 16,
+ alignItems: "center",
+ },
+ successIcon: {
+ // Icon styling handled by Ionicons
+ },
+ amountText: {
+ fontSize: 20,
+ fontWeight: "600",
+ color: "#252a34",
+ textAlign: "center",
+ marginBottom: 16,
+ },
+ statusContainer: {
+ alignItems: "center",
+ gap: 4,
+ },
+ statusText: {
+ fontSize: 14,
+ color: "#252a34",
+ textAlign: "center",
+ },
+ dateText: {
+ fontSize: 14,
+ color: "#252a34",
+ textAlign: "center",
+ },
+ divider: {
+ height: 1,
+ backgroundColor: "#e5e9f0",
+ marginVertical: 24,
+ },
+ transactionContainer: {
+ gap: 8,
+ },
+ sectionHeader: {
+ fontSize: 14,
+ fontWeight: "600",
+ color: "#252a34",
+ },
+ detailRow: {
+ flexDirection: "row",
+ justifyContent: "space-between",
+ alignItems: "flex-start",
+ paddingVertical: 2,
+ },
+ detailLabel: {
+ fontSize: 14,
+ color: "#252a34",
+ flex: 1,
+ },
+ detailValue: {
+ fontSize: 14,
+ fontWeight: "600",
+ color: "#252a34",
+ textAlign: "left",
+ flex: 1,
+ },
+ primaryButton: {
+ backgroundColor: "#008761",
+ height: 40,
+ borderRadius: 4,
justifyContent: "center",
alignItems: "center",
- padding: 24,
- backgroundColor: "#f0f4f7",
- },
- title: {
- fontSize: 28,
- fontWeight: "bold",
- marginBottom: 12,
- color: "#2e7d32",
- },
- message: {
- fontSize: 16,
- textAlign: "center",
- marginBottom: 30,
- color: "#555",
- },
- button: {
- backgroundColor: "#2e7d32",
- paddingVertical: 14,
- paddingHorizontal: 30,
- borderRadius: 8,
},
buttonText: {
- color: "white",
- fontSize: 16,
- fontWeight: "600",
+ color: "#fcfcfc",
+ fontSize: 14,
+ fontWeight: "500",
},
});
diff --git a/app/payments/payEmi.tsx b/app/payments/payEmi.tsx
index 312cc6e..1d58361 100644
--- a/app/payments/payEmi.tsx
+++ b/app/payments/payEmi.tsx
@@ -16,7 +16,7 @@ import * as Sharing from "expo-sharing";
import { Image } from "expo-image";
import { useSelector, useDispatch } from "react-redux";
import { RootState } from "@/store";
-import { updatePaymentStatus } from "@/store/paymentSlice";
+import { setPaymentOrder, updatePaymentStatus } from "@/store/paymentSlice";
import Header from "@/components/common/Header";
import ShareIcon from "@/assets/icons/share.svg";
import { useSafeAreaInsets } from "react-native-safe-area-context";
@@ -34,83 +34,74 @@ const UpiPaymentScreen = () => {
);
const router = useRouter();
- const { onPaymentConfirmation, offPaymentConfirmation } = useSocket();
- const [isListening, setIsListening] = useState(false);
+ const { onPaymentConfirmation, offPaymentConfirmation, disconnect } =
+ useSocket();
const { showSnackbar } = useSnackbar();
const insets = useSafeAreaInsets();
useEffect(() => {
+ let backPressCount = 0;
+ let backPressTimer: NodeJS.Timeout | null = null;
const handlePaymentConfirmation = (data: any) => {
console.log("Payment confirmation received:", data);
- Alert.alert(
- "Payment Successful!",
- "Your payment has been confirmed successfully.",
- [
- {
- text: "Continue",
- onPress: () => {
- router.replace("/payments/Confirmation");
- },
- },
- ],
- { cancelable: false }
+ dispatch(
+ setPaymentOrder({
+ ...paymentOrder,
+ ...data,
+ })
);
+
+ offPaymentConfirmation();
+ disconnect();
+
+ router.replace("/payments/Confirmation");
};
onPaymentConfirmation(handlePaymentConfirmation);
- setIsListening(true);
const backAction = () => {
- Alert.alert(
- "Cancel Payment?",
- "Are you sure you want to cancel this payment?",
- [
- {
- text: "No",
- onPress: () => null,
- style: "cancel",
- },
- {
- text: "Yes",
- onPress: () => {
- offPaymentConfirmation();
- router.back();
- },
- },
- ]
- );
- return true;
+ if (backPressCount === 0) {
+ backPressCount++;
+ console.log("Press back again to cancel payment");
+ showSnackbar("Press back again to cancel payment", "success");
+
+ backPressTimer = setTimeout(() => {
+ backPressCount = 0;
+ }, 2000);
+
+ return true;
+ } else {
+ if (backPressTimer) clearTimeout(backPressTimer);
+ offPaymentConfirmation();
+ disconnect();
+ router.back();
+ return true;
+ }
};
const backHandler = BackHandler.addEventListener(
"hardwareBackPress",
backAction
);
-
- // Cleanup on unmount
return () => {
offPaymentConfirmation();
- setIsListening(false);
backHandler.remove();
};
}, [onPaymentConfirmation, offPaymentConfirmation, router]);
- // Format amount with currency symbol
const formatAmount = (amount: number): string => {
return `₹${amount.toLocaleString("en-IN")}`;
};
- // Check if payment is expired
const isPaymentExpired = (): boolean => {
const expiryDate = new Date(paymentOrder.expiry_date);
const currentDate = new Date();
return currentDate > expiryDate;
};
- // Get formatted expiry time
const getExpiryTime = (): string => {
const expiryDate = new Date(paymentOrder.expiry_date);
return expiryDate.toLocaleString("en-IN");
diff --git a/app/payments/selectAmount.tsx b/app/payments/selectAmount.tsx
index 442aaba..4f6ab45 100644
--- a/app/payments/selectAmount.tsx
+++ b/app/payments/selectAmount.tsx
@@ -140,6 +140,8 @@ const SelectAmountScreen = () => {
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleSubmit}
+ validateOnChange={true}
+ validateOnBlur={true}
>
{({
values,
@@ -169,7 +171,19 @@ const SelectAmountScreen = () => {
const isPayButtonEnabled = () => {
if (values.paymentType === "due") return true;
- return isValid && dirty && values.customAmount;
+
+ // For custom amount, check if it's valid and meets minimum requirement
+ if (values.paymentType === "custom") {
+ const amount = parseFloat(values.customAmount);
+ return (
+ values.customAmount &&
+ !isNaN(amount) &&
+ amount >= payments.MIN_AMOUNT &&
+ !errors.customAmount
+ );
+ }
+
+ return false;
};
return (
diff --git a/assets/icons/check_circle.svg b/assets/icons/check_circle.svg
new file mode 100644
index 0000000..980c3a2
--- /dev/null
+++ b/assets/icons/check_circle.svg
@@ -0,0 +1,8 @@
+
diff --git a/store/paymentSlice.ts b/store/paymentSlice.ts
index f6b334b..9991d75 100644
--- a/store/paymentSlice.ts
+++ b/store/paymentSlice.ts
@@ -2,15 +2,20 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { MyPlan } from "@/app/(tabs)/payments";
-// Define the payment order interface based on your API response
interface PaymentOrder {
- amount: number;
- expiry_date: string;
+ amount: number; // kept as number for math
+ id?: number; // optional if not always present
+ expiry_date?: string;
order_id: string;
- ppayment_link: string;
- qr_code_url: string;
- status: string;
- transaction_id: string;
+ ppayment_link?: string;
+ qr_code_url?: string;
+ status: string; // "confirmed", "pending", etc.
+ transaction_id?: string;
+ transaction_order_id?: string;
+ transaction_date?: string;
+ payment_mode?: string[]; // e.g. ["UPI"]
+ payment_reference_id?: string;
+ paid_by_upi_handle?: string;
upi_handle: string;
}