Handle Payment Confirmation
parent
5b89bebe0b
commit
1ef44669e1
|
|
@ -72,7 +72,6 @@ interface PaymentHistoryResponse {
|
|||
}
|
||||
|
||||
const formatPaymentDate = (dateString: string) => {
|
||||
console.log(dateString.split(",")[0], "datestring");
|
||||
try {
|
||||
return dateString.split(",")[0];
|
||||
} catch {
|
||||
|
|
@ -82,7 +81,6 @@ const formatPaymentDate = (dateString: string) => {
|
|||
|
||||
// Format time for payment history
|
||||
const formatPaymentTime = (dateString: string) => {
|
||||
console.log(dateString, "formattime");
|
||||
try {
|
||||
// Expected: "12 Aug 2025, 12:38:23 pm"
|
||||
const [datePart, timePart] = dateString.split(",");
|
||||
|
|
@ -452,7 +450,6 @@ export default function PaymentsTabScreen() {
|
|||
)}
|
||||
</View>
|
||||
</View>
|
||||
<Overlay isUploading={isLoading} />
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,12 +7,9 @@ import {
|
|||
SafeAreaView,
|
||||
Alert,
|
||||
Share,
|
||||
Clipboard,
|
||||
Linking,
|
||||
ActivityIndicator,
|
||||
BackHandler,
|
||||
} from "react-native";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import * as FileSystem from "expo-file-system";
|
||||
import * as MediaLibrary from "expo-media-library";
|
||||
import * as Sharing from "expo-sharing";
|
||||
|
|
@ -43,23 +40,11 @@ const UpiPaymentScreen = () => {
|
|||
const { showSnackbar } = useSnackbar();
|
||||
|
||||
const insets = useSafeAreaInsets();
|
||||
if (!paymentOrder) {
|
||||
return (
|
||||
<SafeAreaView style={styles.container}>
|
||||
<View style={styles.loadingContainer}>
|
||||
<ActivityIndicator size="large" color="#00876F" />
|
||||
<Text style={styles.loadingText}>Loading payment details...</Text>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
// Set up payment confirmation listener
|
||||
const handlePaymentConfirmation = (data: any) => {
|
||||
console.log("Payment confirmation received:", data);
|
||||
|
||||
// Show success message
|
||||
Alert.alert(
|
||||
"Payment Successful!",
|
||||
"Your payment has been confirmed successfully.",
|
||||
|
|
@ -75,11 +60,9 @@ const UpiPaymentScreen = () => {
|
|||
);
|
||||
};
|
||||
|
||||
// Start listening for payment confirmation
|
||||
onPaymentConfirmation(handlePaymentConfirmation);
|
||||
setIsListening(true);
|
||||
|
||||
// Handle Android back button
|
||||
const backAction = () => {
|
||||
Alert.alert(
|
||||
"Cancel Payment?",
|
||||
|
|
@ -120,11 +103,6 @@ const UpiPaymentScreen = () => {
|
|||
return `₹${amount.toLocaleString("en-IN")}`;
|
||||
};
|
||||
|
||||
// Extract numeric amount from the amount
|
||||
const getNumericAmount = (): string => {
|
||||
return paymentOrder.amount.toString();
|
||||
};
|
||||
|
||||
// Check if payment is expired
|
||||
const isPaymentExpired = (): boolean => {
|
||||
const expiryDate = new Date(paymentOrder.expiry_date);
|
||||
|
|
@ -261,23 +239,9 @@ const UpiPaymentScreen = () => {
|
|||
}
|
||||
};
|
||||
|
||||
const getStatusColor = (status: string): string => {
|
||||
switch (status) {
|
||||
case "completed":
|
||||
case "success":
|
||||
return "#00876F";
|
||||
case "failed":
|
||||
return "#E74C3C";
|
||||
case "processing":
|
||||
return "#F39C12";
|
||||
default:
|
||||
return "#6C757D";
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.container}>
|
||||
<Header title="Pay EMI" showBackButton={true} />
|
||||
<Header title="Pay EMI" showBackButton={false} />
|
||||
|
||||
<View style={styles.content}>
|
||||
<View style={styles.qrFrame}>
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@ import { BASE_URL, payments } from "@/constants/config";
|
|||
import api from "@/services/axiosClient";
|
||||
import { setPaymentOrder } from "@/store/paymentSlice";
|
||||
import { Overlay } from "@/components/common/Overlay";
|
||||
import { useRouter } from "expo-router";
|
||||
import { useFocusEffect, useRouter } from "expo-router";
|
||||
import { useSocket } from "@/contexts/SocketContext";
|
||||
import { useSnackbar } from "@/contexts/Snackbar";
|
||||
|
||||
// Validation schema
|
||||
const validationSchema = Yup.object().shape({
|
||||
paymentType: Yup.string().required("Please select a payment option"),
|
||||
customAmount: Yup.string().when("paymentType", {
|
||||
|
|
@ -47,11 +47,12 @@ const validationSchema = Yup.object().shape({
|
|||
});
|
||||
|
||||
const SelectAmountScreen = () => {
|
||||
// Fetch due amount from Redux
|
||||
const dueAmount = useSelector(
|
||||
(state: RootState) => state.payments?.due_amount || 0
|
||||
);
|
||||
|
||||
const { showSnackbar } = useSnackbar();
|
||||
|
||||
const router = useRouter();
|
||||
const [isFetching, setIsFetching] = useState<boolean>(false);
|
||||
|
||||
|
|
@ -64,41 +65,72 @@ const SelectAmountScreen = () => {
|
|||
customAmount: "",
|
||||
};
|
||||
|
||||
const existingPaymentOrder = useSelector(
|
||||
(state: RootState) => state.payments?.paymentOrder
|
||||
);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
useFocusEffect(
|
||||
React.useCallback(() => {
|
||||
console.log(
|
||||
"SelectAmountScreen focused - clearing paymentOrder and remounting"
|
||||
);
|
||||
// Clear the payment order
|
||||
dispatch(setPaymentOrder(null));
|
||||
setIsFetching(false);
|
||||
}, [dispatch])
|
||||
);
|
||||
|
||||
const handleSubmit = async (values: any) => {
|
||||
setIsFetching(true);
|
||||
const paymentAmount =
|
||||
values.paymentType === "due"
|
||||
? dueAmount
|
||||
: parseFloat(values.customAmount);
|
||||
console.log(values, "values");
|
||||
|
||||
try {
|
||||
let orderData = existingPaymentOrder;
|
||||
|
||||
if (
|
||||
existingPaymentOrder &&
|
||||
existingPaymentOrder.amount === paymentAmount
|
||||
) {
|
||||
console.log(
|
||||
"Order for current amount already exists, using existing order"
|
||||
);
|
||||
orderData = existingPaymentOrder;
|
||||
} else {
|
||||
console.log("Creating new order for amount:", paymentAmount);
|
||||
const res = await api.post(`/api/v1/create-order`, {
|
||||
amount: paymentAmount,
|
||||
});
|
||||
|
||||
console.log(res.data, "response from select amount");
|
||||
if (res.data && res.data.success) {
|
||||
dispatch(setPaymentOrder(res.data.data));
|
||||
try {
|
||||
await registerTransaction(res.data.data.transaction_id);
|
||||
console.log("Transaction registered successfully");
|
||||
|
||||
// Navigate to payment screen
|
||||
router.push("/payments/payEmi");
|
||||
} catch (socketError) {
|
||||
throw socketError;
|
||||
}
|
||||
if (res.data && res.data.success) {
|
||||
orderData = res.data.data;
|
||||
dispatch(setPaymentOrder(orderData));
|
||||
} else {
|
||||
throw new Error("Failed to create order");
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await registerTransaction(orderData?.transaction_id);
|
||||
console.log("Transaction registered successfully");
|
||||
|
||||
router.push("/payments/payEmi");
|
||||
} catch (socketError) {
|
||||
console.error("Socket connection failed:", socketError);
|
||||
throw socketError;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err, "Error in creating order.");
|
||||
showSnackbar("Something went wrong.", "error");
|
||||
} finally {
|
||||
setIsFetching(false);
|
||||
}
|
||||
|
||||
console.log("Payment Amount:", paymentAmount);
|
||||
};
|
||||
|
||||
|
|
@ -263,7 +295,6 @@ const SelectAmountScreen = () => {
|
|||
</View>
|
||||
</ScrollView>
|
||||
|
||||
{/* Pay Button */}
|
||||
<View style={styles.buttonContainer}>
|
||||
<TouchableOpacity
|
||||
style={[
|
||||
|
|
|
|||
|
|
@ -183,4 +183,8 @@ export const issueConfig = [
|
|||
export const payments = {
|
||||
MIN_AMOUNT: 1,
|
||||
LINK_EXPIRED: "Payment link expired",
|
||||
SOCKET_CONNECTION_TIMEOUT_IN_SECS: 5,
|
||||
REGISTER_TRANSACTION_EMIT_EVENT_NAME: "register-transaction",
|
||||
REGISTER_TRANSACTION_LISTEN_EVENT_NAME: "registration-ack",
|
||||
PAYMENT_CONFIRMATION_EVENT_NAME: "register-transaction",
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,7 +10,9 @@ import SocketService from "../services/paymentSocket";
|
|||
|
||||
interface SocketContextType {
|
||||
isConnected: boolean;
|
||||
registerTransaction: (transactionId: string) => Promise<void>;
|
||||
registerTransaction: (
|
||||
transactionId: string | null | undefined
|
||||
) => Promise<void>;
|
||||
onPaymentConfirmation: (callback: (data: any) => void) => void;
|
||||
offPaymentConfirmation: () => void;
|
||||
disconnect: () => void;
|
||||
|
|
@ -26,25 +28,6 @@ interface SocketProviderProps {
|
|||
export const SocketProvider: React.FC<SocketProviderProps> = ({ children }) => {
|
||||
const [isConnected, setIsConnected] = useState(false);
|
||||
|
||||
// useEffect(() => {
|
||||
// const initSocket = async () => {
|
||||
// try {
|
||||
// await SocketService.connect();
|
||||
// setIsConnected(true);
|
||||
// } catch (error) {
|
||||
// console.error("Failed to connect socket:", error);
|
||||
// setIsConnected(false);
|
||||
// }
|
||||
// };
|
||||
|
||||
// initSocket();
|
||||
|
||||
// return () => {
|
||||
// SocketService.disconnect();
|
||||
// setIsConnected(false);
|
||||
// };
|
||||
// }, []);
|
||||
|
||||
const connectSocket = async () => {
|
||||
try {
|
||||
await SocketService.connect();
|
||||
|
|
@ -56,7 +39,9 @@ export const SocketProvider: React.FC<SocketProviderProps> = ({ children }) => {
|
|||
}
|
||||
};
|
||||
|
||||
const registerTransaction = async (transactionId: string) => {
|
||||
const registerTransaction = async (
|
||||
transactionId: string | null | undefined
|
||||
) => {
|
||||
try {
|
||||
if (!transactionId) {
|
||||
throw new Error("Transaction Id missing in register transaction");
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
// services/socketService.ts
|
||||
import { PAYMENT_SOCKET_BASE_URL, STORAGE_KEYS } from "@/constants/config";
|
||||
import {
|
||||
PAYMENT_SOCKET_BASE_URL,
|
||||
payments,
|
||||
STORAGE_KEYS,
|
||||
} from "@/constants/config";
|
||||
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||
import io, { Socket } from "socket.io-client";
|
||||
|
||||
|
|
@ -66,19 +70,17 @@ class SocketService {
|
|||
return;
|
||||
}
|
||||
|
||||
const timeoutMs = 5000;
|
||||
const timeoutSecs = payments.SOCKET_CONNECTION_TIMEOUT_IN_SECS * 1000;
|
||||
|
||||
const timeoutId = setTimeout(() => {
|
||||
socket.off("registration-ack", onRegistrationAck);
|
||||
reject(new Error("Timeout: No registration-ack received"));
|
||||
}, timeoutMs);
|
||||
}, timeoutSecs);
|
||||
|
||||
const onRegistrationAck = (data: any) => {
|
||||
console.log("inside onRegisterAck");
|
||||
if (data.transactionId === transactionId) {
|
||||
clearTimeout(timeoutId);
|
||||
// socket.off("registration-ack", onRegistrationAck);
|
||||
|
||||
socket.off("registration-ack", onRegistrationAck);
|
||||
if (data.success) {
|
||||
resolve();
|
||||
} else {
|
||||
|
|
@ -87,33 +89,30 @@ class SocketService {
|
|||
}
|
||||
};
|
||||
|
||||
socket.on("registration-ack", onRegistrationAck);
|
||||
|
||||
socket.on("register-transaction", this.onPaymentConfirm);
|
||||
socket.onAny((eventName, ...args) => {
|
||||
console.log(eventName, "eventname", JSON.stringify(args), "✅✅✅✅✅");
|
||||
//register the transaction with the specific 'transactionId'
|
||||
socket.emit(payments.REGISTER_TRANSACTION_EMIT_EVENT_NAME, {
|
||||
transactionId,
|
||||
});
|
||||
|
||||
socket.emit("register-transaction", { transactionId });
|
||||
socket.on(
|
||||
payments.REGISTER_TRANSACTION_LISTEN_EVENT_NAME,
|
||||
onRegistrationAck
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private onPaymentConfirm(eventname: string, ...args: any[]): void {
|
||||
console.log(eventname, "eventName", JSON.stringify(args));
|
||||
}
|
||||
|
||||
//payment confirmation is received on 'register-transaction' eventName
|
||||
public onPaymentConfirmation(callback: (data: any) => void): void {
|
||||
if (!this.socket) {
|
||||
console.error("Socket not connected");
|
||||
return;
|
||||
}
|
||||
|
||||
this.socket.on("register-transaction", callback);
|
||||
this.socket.on(payments.PAYMENT_CONFIRMATION_EVENT_NAME, callback);
|
||||
}
|
||||
|
||||
public offPaymentConfirmation(): void {
|
||||
if (this.socket) {
|
||||
this.socket.off("payment-confirmation");
|
||||
this.socket.off(payments.PAYMENT_CONFIRMATION_EVENT_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue