BaaS_Driver_Android_App/app/payments/payEmi.tsx

440 lines
11 KiB
TypeScript

import React, { useEffect, useState } from "react";
import {
View,
Text,
TouchableOpacity,
StyleSheet,
SafeAreaView,
Alert,
Share,
Linking,
BackHandler,
} from "react-native";
import * as FileSystem from "expo-file-system";
import * as MediaLibrary from "expo-media-library";
import * as Sharing from "expo-sharing";
import { Image } from "expo-image";
import { useSelector, useDispatch } from "react-redux";
import { RootState } from "@/store";
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";
import { useSnackbar } from "@/contexts/Snackbar";
import DownloadIcon from "@/assets/icons/download.svg";
import { payments } from "@/constants/config";
import { useRouter } from "expo-router";
import { useSocket } from "@/contexts/SocketContext";
const UpiPaymentScreen = () => {
const dispatch = useDispatch();
const paymentOrder = useSelector(
(state: RootState) => state.payments.paymentOrder
);
const router = useRouter();
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);
dispatch(
setPaymentOrder({
...paymentOrder,
...data,
})
);
offPaymentConfirmation();
disconnect();
router.replace("/payments/Confirmation");
};
onPaymentConfirmation(handlePaymentConfirmation);
const backAction = () => {
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
);
return () => {
offPaymentConfirmation();
backHandler.remove();
};
}, [onPaymentConfirmation, offPaymentConfirmation, router]);
const formatAmount = (amount: number): string => {
return `${amount.toLocaleString("en-IN")}`;
};
const isPaymentExpired = (): boolean => {
const expiryDate = new Date(paymentOrder.expiry_date);
const currentDate = new Date();
return currentDate > expiryDate;
};
const getExpiryTime = (): string => {
const expiryDate = new Date(paymentOrder.expiry_date);
return expiryDate.toLocaleString("en-IN");
};
const getUpiUrl = (): string => {
const upiString = paymentOrder.ppayment_link;
const upiMatch = upiString.match(/upi_string=([^&]+)/);
if (upiMatch) {
return decodeURIComponent(upiMatch[1]);
}
return `upi://pay?pa=${paymentOrder.upi_handle}&am=${paymentOrder.amount}&cu=INR&tr=${paymentOrder.order_id}`;
};
const payUsingUpiApp = async (): Promise<void> => {
try {
if (isPaymentExpired()) {
showSnackbar(payments.LINK_EXPIRED, "error");
return;
}
const upiUrl = getUpiUrl();
console.log("Opening UPI URL:", upiUrl);
dispatch(updatePaymentStatus("processing"));
const canOpenUrl = await Linking.canOpenURL(upiUrl);
if (canOpenUrl) {
await Linking.openURL(upiUrl);
} else {
Alert.alert(
"UPI App Required",
"Please install a UPI-enabled app like PhonePe, Paytm, Google Pay, or BHIM to make payments.",
[
{
text: "Share Payment Details",
onPress: () => sharePaymentDetails(),
},
{ text: "Cancel", style: "cancel" },
]
);
}
} catch (error) {
console.error("Error opening UPI app:", error);
dispatch(updatePaymentStatus("failed"));
Alert.alert(
"Error",
"Unable to open UPI app. Would you like to share payment details instead?",
[
{
text: "Share Details",
onPress: () => sharePaymentDetails(),
},
{ text: "Cancel", style: "cancel" },
]
);
}
};
const sharePaymentDetails = async (): Promise<void> => {
try {
const shareMessage =
`Payment Request\n\n` +
`Amount: ${formatAmount(paymentOrder.amount)}\n` +
`UPI ID: ${paymentOrder.upi_handle}\n` +
`Order ID: ${paymentOrder.order_id}\n` +
`Transaction ID: ${paymentOrder.transaction_id}\n` +
`Expires: ${getExpiryTime()}\n\n` +
`UPI Link: ${getUpiUrl()}`;
await Share.share({
message: shareMessage,
title: "UPI Payment Details",
});
} catch (error) {
Alert.alert("Error", "Failed to share payment details");
}
};
const shareQR = async (): Promise<void> => {
try {
if (await Sharing.isAvailableAsync()) {
await Share.share({
message: `Pay ${formatAmount(
paymentOrder.amount
)} using this QR code`,
url: paymentOrder.qr_code_url,
});
} else {
Alert.alert("Error", "Sharing is not available on this device");
}
} catch (error) {
Alert.alert("Error", "Failed to share QR code");
}
};
const downloadQR = async (): Promise<void> => {
try {
const { status } = await MediaLibrary.requestPermissionsAsync();
if (status !== "granted") {
Alert.alert(
"Permission Required",
"Please grant permission to save images"
);
return;
}
const fileUri =
FileSystem.documentDirectory + `qr-${paymentOrder.order_id}.png`;
const downloadResult = await FileSystem.downloadAsync(
paymentOrder.qr_code_url,
fileUri
);
if (downloadResult.status === 200) {
const asset = await MediaLibrary.createAssetAsync(downloadResult.uri);
await MediaLibrary.createAlbumAsync("Payment QR Codes", asset, false);
Alert.alert("Success", "QR code saved to gallery");
} else {
Alert.alert("Error", "Failed to download QR code");
}
} catch (error) {
Alert.alert("Error", "Failed to download QR code");
}
};
return (
<SafeAreaView style={styles.container}>
<Header title="Pay EMI" showBackButton={false} />
<View style={styles.content}>
<View style={styles.qrFrame}>
<View style={styles.amountSection}>
<Text style={styles.amountLabel}>Amount to be paid</Text>
<Text style={styles.amount}>
{formatAmount(paymentOrder.amount)}
</Text>
</View>
<View style={styles.qrCodeContainer}>
<Image
source={{ uri: paymentOrder.qr_code_url }}
style={styles.qrCode}
contentFit="contain"
/>
</View>
<View style={styles.buttonsContainer}>
<TouchableOpacity onPress={shareQR} style={styles.secondaryButton}>
<ShareIcon />
<Text style={styles.secondaryButtonText}>Share QR</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={downloadQR}
style={styles.secondaryButton}
>
<DownloadIcon />
<Text style={styles.secondaryButtonText}>Download QR</Text>
</TouchableOpacity>
</View>
</View>
<TouchableOpacity
onPress={payUsingUpiApp}
style={[styles.primaryButton, { marginBottom: insets.bottom || 20 }]}
>
<Text style={[styles.primaryButtonText]}>Pay using UPI app</Text>
</TouchableOpacity>
</View>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#F3F5F8",
},
loadingContainer: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
loadingText: {
marginTop: 16,
fontSize: 16,
color: "#253342",
},
header: {
flexDirection: "row",
alignItems: "center",
paddingHorizontal: 16,
paddingVertical: 12,
backgroundColor: "#FFFFFF",
},
backButton: {
padding: 8,
marginRight: 8,
},
headerTitle: {
fontSize: 18,
fontWeight: "600",
color: "#253342",
},
content: {
flex: 1,
justifyContent: "space-between",
paddingHorizontal: 16,
paddingBottom: 16,
},
qrFrame: {
backgroundColor: "#FCFCFC",
borderRadius: 8,
padding: 16,
alignItems: "center",
marginTop: 16,
},
amountSection: {
alignItems: "center",
marginBottom: 16,
},
amountLabel: {
fontSize: 14,
color: "#253342",
textAlign: "center",
marginBottom: 4,
},
amount: {
fontSize: 20,
fontWeight: "600",
color: "#253342",
textAlign: "center",
marginBottom: 8,
},
statusBadge: {
paddingHorizontal: 12,
paddingVertical: 4,
borderRadius: 12,
},
statusText: {
fontSize: 12,
fontWeight: "600",
color: "#FFFFFF",
},
qrCodeContainer: {
width: 200,
height: 200,
justifyContent: "center",
alignItems: "center",
marginBottom: 16,
borderWidth: 2,
borderColor: "#E5E9F0",
padding: 12,
borderRadius: 8,
},
qrCode: {
width: "100%",
height: "100%",
},
upiIdSection: {
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
marginBottom: 12,
padding: 8,
backgroundColor: "#F3F5F8",
borderRadius: 4,
},
upiIdText: {
fontSize: 14,
color: "#253342",
marginRight: 8,
fontWeight: "500",
},
expirySection: {
alignItems: "center",
marginBottom: 16,
},
expiryLabel: {
fontSize: 12,
color: "#6C757D",
marginBottom: 2,
},
expiryTime: {
fontSize: 14,
color: "#253342",
fontWeight: "500",
},
buttonsContainer: {
flexDirection: "row",
gap: 8,
width: "100%",
},
secondaryButton: {
flex: 1,
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
paddingVertical: 10,
paddingHorizontal: 16,
backgroundColor: "#F3F5F8",
borderColor: "#D8DDE7",
borderWidth: 1,
borderRadius: 4,
gap: 4,
},
secondaryButtonText: {
fontSize: 14,
fontWeight: "500",
color: "#253342",
},
primaryButton: {
backgroundColor: "#00876F",
paddingVertical: 12,
paddingHorizontal: 16,
borderRadius: 4,
alignItems: "center",
},
primaryButtonText: {
fontSize: 14,
fontWeight: "500",
color: "#FCFCFC",
},
disabledButton: {
backgroundColor: "#D8DDE7",
},
disabledButtonText: {
color: "#6C757D",
},
});
export default UpiPaymentScreen;