Browse Source

feat: 实现订单支付流程

main
Your Name 3 weeks ago committed by Your Name (aider)
parent
commit
855d30555d
  1. 408
      app/screens/previewOrder/perviewOrder.tsx

408
app/screens/previewOrder/perviewOrder.tsx

@ -1,31 +1,41 @@
import { View, Text, TouchableOpacity, StyleSheet, TextInput } from "react-native"; import {View, Text, TouchableOpacity, StyleSheet, TextInput, ScrollView, Alert} from "react-native";
import useCreateOrderStore from "../../store/createOrder"; import useCreateOrderStore from "../../store/createOrder";
import BackIcon from "../../components/BackIcon"; import BackIcon from "../../components/BackIcon";
import { useNavigation } from "@react-navigation/native"; import { useNavigation, useRoute, RouteProp } from "@react-navigation/native";
import { NativeStackNavigationProp } from "@react-navigation/native-stack";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import useUserStore from "../../store/user";
import {
Order
} from "../../services/api/orders";
import { payApi } from "../../services/api/payApi";
// 假设的图标组件 - 请替换为您的实际图标
const AddressIcon = ({ size, color }) => <Text style={{fontSize: size, color: color}}>📍</Text>; // 示例图标 // Define the param list for navigation
const AmountIcon = ({ size, color }) => <Text style={{fontSize: size, color: color}}>💰</Text>; // 示例图标 type RootStackParamList = {
const ShippingIcon = ({ size, color }) => <Text style={{fontSize: size, color: color}}>🚚</Text>; // 示例图标 PreviewOrder: { data: Order, payMethod: string ,currency:string,amount:number};
const WhatsAppIcon = ({ size, color }) => <Text style={{fontSize: size, color: color}}>💬</Text>; // 示例图标 Pay: { payUrl:string };
const UserIcon = ({ size, color }) => <Text style={{fontSize: size, color: color}}>👤</Text>; // 示例图标 - 姓名 };
const PhoneIcon = ({ size, color }) => <Text style={{fontSize: size, color: color}}>📞</Text>; // 示例图标 - 手机
const GlobeIcon = ({ size, color }) => <Text style={{fontSize: size, color: color}}>🌍</Text>; // 示例图标 - 国家
export const PreviewOrder = () => { export const PreviewOrder = () => {
const {orderData, setOrderData} = useCreateOrderStore(); const {orderData, setOrderData} = useCreateOrderStore();
const navigation = useNavigation(); const navigation = useNavigation<NativeStackNavigationProp<RootStackParamList>>();
const [phoneNumber, setPhoneNumber] = useState(""); const [phoneNumber, setPhoneNumber] = useState("");
const [showPhoneInput, setShowPhoneInput] = useState(false); const [showPhoneInput, setShowPhoneInput] = useState(false);
const route = useRoute<RouteProp<RootStackParamList, 'PreviewOrder'>>();
const [loading, setLoading] = useState(false);
const {user} = useUserStore();
useEffect(() => { useEffect(() => {
if (orderData?.payment_method === "Brainnel Pay(Mobile Money)") { if (!user.user_id){
return Alert.alert('检查您未登录')
}
if (route.params.payMethod === "Brainnel Pay(Mobile Money)") {
setShowPhoneInput(true); setShowPhoneInput(true);
} else { } else {
setShowPhoneInput(false); setShowPhoneInput(false);
} }
}, [orderData?.payment_method]); }, [route.params.payMethod, user.user_id]);
const handleSubmit = () => { const handleSubmit = () => {
if (showPhoneInput && !phoneNumber) { if (showPhoneInput && !phoneNumber) {
@ -33,98 +43,143 @@ export const PreviewOrder = () => {
console.log("Phone number is required for Mobile Money"); console.log("Phone number is required for Mobile Money");
return; return;
} }
if (showPhoneInput) { console.log(route.params.currency);
setOrderData({ ...orderData, mobile_money_phone: phoneNumber });
const data = {
order_id: route.params.data.order_id,
method: route.params.payMethod,
currency: route.params.currency || route.params.data.currency,
amount: route.params.data.actual_amount,
} }
setLoading(true);
payApi.getPayInfo(data).then((res) => {
if(res.success){``
navigation.navigate('Pay', {
payUrl:res.payment_url
})
}
}).catch((err) => {
setLoading(false);
Alert.alert('支付失败')
}).finally(() => {
setLoading(false);
})
console.log("orderData", orderData); // navigation.navigate('Pay', {
// Add your submission logic here // orderId: user.user_id,
// payMethod: route.params.payMethod,
// })
} }
return ( return (
<View style={styles.container}> <View style={styles.container}>
<View style={styles.titleContainer}> <View style={styles.titleContainer}>
<View style={styles.backIconContainer}> <View style={styles.backIconContainer}>
<TouchableOpacity onPress={() => navigation.goBack()}> <TouchableOpacity onPress={() => navigation.goBack()}>
<BackIcon size={20} /> <BackIcon size={20} />
</TouchableOpacity> </TouchableOpacity>
</View>
<Text style={styles.titleHeading}></Text>
</View> </View>
<Text style={styles.titleHeading}></Text> <ScrollView style={styles.scrollContainer}>
</View> <View style={styles.mainContent}>
{/* Payment Method Section */}
<View style={styles.section}>
<Text style={styles.sectionTitle}></Text>
<View style={styles.paymentMethodContainer}>
<Text style={styles.paymentMethodText}>{route.params.payMethod}</Text>
</View>
{/* Payment Details */} {showPhoneInput && (
<View style={styles.section}> <View style={styles.phoneInputContainer}>
<Text style={styles.phoneInputLabel}></Text>
<TextInput
style={styles.phoneInput}
value={phoneNumber}
onChangeText={setPhoneNumber}
placeholder="输入手机号码"
keyboardType="phone-pad"
/>
</View>
)}
</View>
{showPhoneInput && ( {/* Order Info Section */}
<View style={styles.phoneInputContainer}> <View style={styles.section}>
<Text style={styles.phoneInputLabel}></Text> <Text style={styles.sectionTitle}></Text>
<TextInput
style={styles.phoneInput} <View style={styles.infoRow}>
value={phoneNumber} <Text style={styles.infoLabel}></Text>
onChangeText={setPhoneNumber} <Text style={styles.infoValue}>{route.params?.data.receiver_name || "N/A"}</Text>
placeholder="输入手机号码" </View>
keyboardType="phone-pad"
/> <View style={styles.infoRow}>
</View> <Text style={styles.infoLabel}></Text>
)} <Text style={styles.infoValue}>{route.params?.data.receiver_phone || "N/A"}</Text>
</View>
{route.params.data?.whatsapp_number && (
<View style={styles.infoRow}>
<Text style={styles.infoLabel}>WhatsApp</Text>
<Text style={styles.infoValue}>{route.params.data?.whatsapp_number}</Text>
</View>
)}
<View style={styles.infoRow}>
<Text style={styles.infoLabel}></Text>
<Text style={styles.infoValue}>{route.params?.data.receiver_country || "N/A"}</Text>
</View>
<View style={styles.infoRow}>
<Text style={styles.infoLabel}></Text>
<Text style={styles.infoValue}>{route.params?.data.receiver_address || "N/A"}</Text>
</View>
<View style={styles.infoRow}>
<Text style={styles.infoLabel}></Text>
<Text style={styles.infoValue}>
{route.params.data.actual_amount || "N/A"} {user.currency}
</Text>
</View>
<View style={styles.infoRow}>
<Text style={styles.infoLabel}></Text>
<Text style={styles.infoValue}>
{(route.params.data.domestic_shipping_fee + route.params.data.shipping_fee).toFixed(2) || "N/A"} {user.currency}
</Text>
</View>
</View>
{/* Order Details Card */} {/* Order Summary Section */}
<View style={styles.card}> <View style={styles.section}>
<Text style={styles.cardTitle}></Text> <Text style={styles.sectionTitle}></Text>
<View style={styles.detailRow}> <View style={styles.totalRow}>
<UserIcon size={18} color="#555555" /> <Text style={styles.totalLabel}></Text>
<Text style={styles.detailLabel}>:</Text> <Text style={styles.totalValue}>
<Text style={styles.detailValue}>{orderData?.receiver_name || "N/A"}</Text> {route.params.amount} {route.params.currency}
</View> </Text>
<View style={styles.detailRow}> </View>
<PhoneIcon size={18} color="#555555" />
<Text style={styles.detailLabel}>:</Text>
<Text style={styles.detailValue}>{orderData?.receiver_phone || "N/A"}</Text>
</View>
{orderData?.whatsapp_phone && (
<View style={styles.detailRow}>
<WhatsAppIcon size={18} color="#555555" />
<Text style={styles.detailLabel}>WhatsApp:</Text>
<Text style={styles.detailValue}>{orderData.whatsapp_phone}</Text>
</View> </View>
)}
<View style={styles.detailRow}>
<GlobeIcon size={18} color="#555555" />
<Text style={styles.detailLabel}>:</Text>
<Text style={styles.detailValue}>{orderData?.country || "N/A"}</Text>
</View>
<View style={styles.detailRow}>
<AddressIcon size={18} color="#555555" />
<Text style={styles.detailLabel}>:</Text>
<Text style={styles.detailValue} numberOfLines={2} ellipsizeMode="tail">{orderData?.address || "请选择地址"}</Text>
</View>
<View style={styles.detailRow}>
<AmountIcon size={18} color="#555555" />
<Text style={styles.detailLabel}>:</Text>
<Text style={styles.detailValue}>{orderData?.amount ? `${orderData.currency || ''} ${orderData.amount}` : "N/A"}</Text>
</View> </View>
<View style={styles.detailRow}> </ScrollView>
<ShippingIcon size={18} color="#555555" />
<Text style={styles.detailLabel}>:</Text>
<Text style={styles.detailValue}>{orderData?.shipping_fee ? `${orderData.currency || ''} ${orderData.shipping_fee}` : "N/A"}</Text>
</View>
</View>
</View>
<View style={styles.submitButtonContainer}> <View style={styles.submitButtonContainer}>
<TouchableOpacity <TouchableOpacity
style={[ style={[
styles.primaryButtonStyle, styles.primaryButtonStyle,
(!showPhoneInput || (showPhoneInput && phoneNumber)) ? {} : styles.disabledButtonStyle (!showPhoneInput || (showPhoneInput && phoneNumber)) ? {} : styles.disabledButtonStyle
]} ]}
onPress={handleSubmit} onPress={handleSubmit}
disabled={showPhoneInput && !phoneNumber} disabled={showPhoneInput && !phoneNumber}
> >
<Text style={styles.buttonText}></Text> <Text style={styles.buttonText}></Text>
</TouchableOpacity> </TouchableOpacity>
</View> </View>
</View> </View>
); );
}; };
@ -132,154 +187,149 @@ export const PreviewOrder = () => {
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
flex: 1, flex: 1,
backgroundColor: "white", backgroundColor: "#f7f7fa",
},
scrollContainer: {
flex: 1,
},
mainContent: {
padding: 16,
}, },
submitButtonContainer: { submitButtonContainer: {
paddingRight: 11, paddingHorizontal: 16,
paddingLeft: 11, paddingVertical: 20,
marginTop: 20, backgroundColor: "white",
marginBottom: 20, borderTopWidth: 1,
borderTopColor: "#eaeaea",
}, },
primaryButtonStyle: { primaryButtonStyle: {
width: "100%", width: "100%",
height: 50, height: 50,
justifyContent: "center", justifyContent: "center",
alignItems: "center", alignItems: "center",
fontWeight: "600",
fontSize: 16,
lineHeight: 22,
fontFamily: "PingFang SC",
color: "white",
backgroundColor: "#002fa7", backgroundColor: "#002fa7",
borderWidth: 0,
borderRadius: 25, borderRadius: 25,
shadowColor: "#002fa7",
shadowOffset: { width: 0, height: 3 },
shadowOpacity: 0.2,
shadowRadius: 5,
elevation: 5,
}, },
buttonText: { buttonText: {
color: "white", color: "white",
fontWeight: "600", fontWeight: "600",
fontSize: 16, fontSize: 16,
lineHeight: 22,
fontFamily: "PingFang SC",
},
selectedCountryText: {
padding: 0,
margin: 0,
fontWeight: "500",
fontSize: 16,
lineHeight: 22,
fontFamily: "PingFang SC", fontFamily: "PingFang SC",
color: "#646472",
}, },
disabledButtonStyle: { disabledButtonStyle: {
backgroundColor: "#ccc", backgroundColor: "#c0c0c0",
shadowOpacity: 0,
}, },
titleContainer: { titleContainer: {
width: "100%", width: "100%",
padding: 15, padding: 16,
flexDirection: "row", flexDirection: "row",
alignItems: "center", alignItems: "center",
justifyContent: "center", justifyContent: "center",
position: "relative", position: "relative",
backgroundColor: "#fff", backgroundColor: "#fff",
borderBottomWidth: 1,
borderBottomColor: "#eaeaea",
}, },
backIconContainer: { backIconContainer: {
position: "absolute", position: "absolute",
left: 15, left: 16,
backgroundColor: "#fff",
}, },
titleHeading: { titleHeading: {
fontWeight: "600", fontWeight: "600",
fontSize: 20, fontSize: 18,
lineHeight: 22, color: "#333",
fontFamily: "PingFang SC", fontFamily: "PingFang SC",
color: "black",
}, },
// Order Summary Styles
section: { section: {
backgroundColor: "#fff", backgroundColor: "#fff",
borderRadius: 8, borderRadius: 12,
paddingHorizontal: 15, padding: 16,
marginTop: 15, marginBottom: 16,
paddingVertical: 12, shadowColor: "#000",
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.05,
shadowRadius: 2,
elevation: 2,
}, },
sectionTitle: { sectionTitle: {
fontSize: 17,
fontWeight: "600", fontWeight: "600",
fontSize: 16,
marginBottom: 8,
color: "#333", color: "#333",
marginBottom: 16,
fontFamily: "PingFang SC",
},
paymentMethodContainer: {
backgroundColor: "#f8f8f8",
padding: 12,
borderRadius: 8,
borderLeftWidth: 4,
borderLeftColor: "#002fa7",
}, },
paymentMethod: { paymentMethodText: {
fontSize: 15, fontSize: 15,
color: "#666", color: "#333",
marginBottom: 10, fontFamily: "PingFang SC",
}, },
phoneInputContainer: { phoneInputContainer: {
marginTop: 10, marginTop: 16,
marginBottom: 10,
}, },
phoneInputLabel: { phoneInputLabel: {
fontSize: 14, fontSize: 15,
color: "#333", color: "#666",
marginBottom: 5, marginBottom: 8,
fontFamily: "PingFang SC",
}, },
phoneInput: { phoneInput: {
borderWidth: 1, borderWidth: 1,
borderColor: "#ccc", borderColor: "#ddd",
borderRadius: 8, borderRadius: 8,
padding: 10, padding: 12,
fontSize: 16, fontSize: 15,
backgroundColor: "#f9f9f9", backgroundColor: "#fff",
},
// Card styles for order details
card: {
// backgroundColor: "#ffffff", // Removed, will inherit from section or be transparent
// borderRadius: 12, // Removed rounded corners
// marginHorizontal: 15, // Removed to align with parent section padding
marginTop: 10, // Adjusted margin from the top element
// padding: 18, // Removed inner padding, rely on section or detailRow padding
// shadowColor: "#000", // Removed shadow
// shadowOffset: {
// width: 0,
// height: 2,
// },
// shadowOpacity: 0.1,
// shadowRadius: 3.84,
// elevation: 5, // Removed elevation
// borderWidth: 1, // Removed border
// borderColor: "#e0e0e0", // Removed border color
},
cardTitle: {
fontSize: 18,
fontWeight: "bold",
color: "#333333",
marginBottom: 15, // Space below the title
fontFamily: "PingFang SC", fontFamily: "PingFang SC",
}, },
detailRow: { infoRow: {
flexDirection: "row", flexDirection: "row",
// justifyContent: "space-between", // We'll handle spacing differently with icons justifyContent: "space-between",
alignItems: "center", // Align items vertically centered paddingVertical: 12,
paddingVertical: 10, // Adjusted vertical padding
borderBottomWidth: 1, borderBottomWidth: 1,
borderBottomColor: "#f0f0f0", // Lighter separator line borderBottomColor: "#f0f0f0",
}, },
detailLabel: { infoLabel: {
fontSize: 15, fontSize: 15,
color: "#555555", // Slightly lighter label color color: "#666",
fontFamily: "PingFang SC", fontFamily: "PingFang SC",
fontWeight: '500',
marginLeft: 8, // Add margin to the left of the label, after the icon
marginRight: 10, // Space between label and value
flex: 1, // Allow label to take available space before value
}, },
detailValue: { infoValue: {
fontSize: 15, fontSize: 15,
color: "#333333", // Darker value color color: "#333",
textAlign: "right",
maxWidth: "60%",
fontFamily: "PingFang SC",
},
totalRow: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
paddingVertical: 12,
},
totalLabel: {
fontSize: 16,
fontWeight: "600",
color: "#333",
fontFamily: "PingFang SC",
},
totalValue: {
fontSize: 20,
fontWeight: "700",
color: "#002fa7",
fontFamily: "PingFang SC", fontFamily: "PingFang SC",
textAlign: 'right',
flexShrink: 1, // Allow text to shrink and wrap
}, },
}); });

Loading…
Cancel
Save