|
|
import React, { useState, useEffect } from "react"; |
|
|
import { |
|
|
View, |
|
|
Text, |
|
|
TouchableOpacity, |
|
|
StyleSheet, |
|
|
ScrollView, |
|
|
Image, |
|
|
Alert, |
|
|
ActivityIndicator, |
|
|
} from "react-native"; |
|
|
import { payApi, PaymentMethodsResponse } from "../../services/api/payApi"; |
|
|
import fontSize from "../../utils/fontsizeUtils"; |
|
|
import BackIcon from "../../components/BackIcon"; |
|
|
import { useNavigation } from "@react-navigation/native"; |
|
|
import widthUtils from "../../utils/widthUtils"; |
|
|
import useOrderStore from "../../store/order"; |
|
|
import useCreateOrderStore from "../../store/createOrder"; |
|
|
import { useRoute, RouteProp } from "@react-navigation/native"; |
|
|
import useUserStore from "../../store/user"; |
|
|
import { |
|
|
ordersApi, |
|
|
OrderData, |
|
|
AddressDataItem, |
|
|
DomesticShippingFeeData, |
|
|
} from "../../services/api/orders"; |
|
|
|
|
|
// Define route params type |
|
|
type PaymentMethodRouteParams = { |
|
|
countryCode?: string; |
|
|
}; |
|
|
|
|
|
interface PaymentOption { |
|
|
id: string; |
|
|
label: string; |
|
|
icon: string; |
|
|
value?: string | string[]; |
|
|
} |
|
|
|
|
|
interface PaymentTab { |
|
|
id: string; |
|
|
label: string; |
|
|
options: PaymentOption[]; |
|
|
} |
|
|
|
|
|
interface PaymentMethodProps { |
|
|
onSelectPayment?: (paymentMethod: string) => void; |
|
|
selectedPayment?: string | null; |
|
|
} |
|
|
|
|
|
const PaymentMethodItem = ({ |
|
|
option, |
|
|
isSelected, |
|
|
onSelect, |
|
|
}: { |
|
|
option: PaymentOption; |
|
|
isSelected: boolean; |
|
|
onSelect: () => void; |
|
|
}) => ( |
|
|
<TouchableOpacity |
|
|
style={[styles.paymentOption, isSelected && styles.paymentSelected]} |
|
|
onPress={onSelect} |
|
|
> |
|
|
<View style={styles.paymentContent}> |
|
|
<View style={styles.defaultPaymentContainer}> |
|
|
<Text style={styles.paymentIcon}>{option.icon}</Text> |
|
|
<Text style={styles.paymentLabel}>{option.label}</Text> |
|
|
</View> |
|
|
{Array.isArray(option.value) && option.value.length > 0 && ( |
|
|
<View style={styles.operatorContainer}> |
|
|
{option.value.map((op: string) => ( |
|
|
<View key={op} style={styles.operatorBox}> |
|
|
<Text style={styles.operatorText}>{op}</Text> |
|
|
</View> |
|
|
))} |
|
|
</View> |
|
|
)} |
|
|
</View> |
|
|
<View style={styles.radioButton}> |
|
|
<View |
|
|
style={[styles.radioInner, isSelected && styles.radioInnerSelected]} |
|
|
/> |
|
|
</View> |
|
|
</TouchableOpacity> |
|
|
); |
|
|
|
|
|
export const PaymentMethod = () => { |
|
|
const [tabs, setTabs] = useState<PaymentTab[]>([ |
|
|
{ |
|
|
id: "online", |
|
|
label: "Online Payment", |
|
|
options: [], |
|
|
}, |
|
|
{ |
|
|
id: "offline", |
|
|
label: "Offline Payment", |
|
|
options: [], |
|
|
}, |
|
|
]); |
|
|
const [currentTab, setCurrentTab] = useState("online"); |
|
|
const [paymentMethods, setPaymentMethods] = |
|
|
useState<PaymentMethodsResponse>(); |
|
|
const [selectedPayment, setSelectedPayment] = useState<string | null>(null); |
|
|
const navigation = useNavigation(); |
|
|
const route = |
|
|
useRoute<RouteProp<Record<string, PaymentMethodRouteParams>, string>>(); |
|
|
const [expanded, setExpanded] = useState(false); |
|
|
const order = useOrderStore((state) => state.order); |
|
|
const [previewOrder, setPreviewOrder] = useState<OrderData>(); |
|
|
const [loading, setLoading] = useState(false); |
|
|
const state = useUserStore(); |
|
|
|
|
|
const { items,orderData } = useCreateOrderStore(); |
|
|
const toggleExpanded = () => { |
|
|
setExpanded(!expanded); |
|
|
}; |
|
|
|
|
|
const onSelectPayment = (paymentId: string) => { |
|
|
setSelectedPayment(paymentId); |
|
|
}; |
|
|
|
|
|
const getPaymentMethods = async () => { |
|
|
try { |
|
|
const response = await payApi.getCountryPaymentMethods(); |
|
|
setPaymentMethods(response); |
|
|
// 设置默认支付方式选项 |
|
|
setTabs([ |
|
|
{ |
|
|
id: "online", |
|
|
label: "Online Payment", |
|
|
options: response.current_country_methods.map((method) => ({ |
|
|
id: method.key, |
|
|
label: method.key, |
|
|
icon: getPaymentIcon(method.key), |
|
|
value: method.value, |
|
|
})), |
|
|
}, |
|
|
{ |
|
|
id: "offline", |
|
|
label: "Offline Payment", |
|
|
options: [], |
|
|
}, |
|
|
]); |
|
|
} catch (error) { |
|
|
console.error("获取支付方式失败:", error); |
|
|
} |
|
|
}; |
|
|
|
|
|
const getPaymentIcon = (key: string): string => { |
|
|
switch (key) { |
|
|
case "Brainnel Pay(Mobile Money)": |
|
|
return "💳"; |
|
|
case "Wave": |
|
|
return "💸"; |
|
|
case "Paypal": |
|
|
return "🅿️"; |
|
|
case "Bank Card Payment": |
|
|
return "💳"; |
|
|
default: |
|
|
return "💰"; |
|
|
} |
|
|
}; |
|
|
|
|
|
useEffect(() => { |
|
|
getPaymentMethods(); |
|
|
}, []); |
|
|
|
|
|
useEffect(() => { |
|
|
setLoading(true); |
|
|
if (route.params?.countryCode) { |
|
|
const data = { |
|
|
country_code: route.params.countryCode, |
|
|
items: items, |
|
|
}; |
|
|
ordersApi |
|
|
.getOrders(data) |
|
|
.then((res) => { |
|
|
setPreviewOrder(res); |
|
|
setLoading(false); |
|
|
}) |
|
|
.catch(() => { |
|
|
setLoading(false); |
|
|
Alert.alert("Error", "Failed to get preview order"); |
|
|
}); |
|
|
} |
|
|
}, [route.params?.countryCode]); |
|
|
|
|
|
return ( |
|
|
<> |
|
|
<View style={styles.titleContainer}> |
|
|
<View style={styles.backIconContainer}> |
|
|
<TouchableOpacity onPress={() => navigation.goBack()}> |
|
|
<BackIcon size={20} /> |
|
|
</TouchableOpacity> |
|
|
</View> |
|
|
|
|
|
<Text style={styles.titleHeading}>Payment Method</Text> |
|
|
</View> |
|
|
|
|
|
{loading ? ( |
|
|
<View style={styles.loadingContainer}> |
|
|
<ActivityIndicator size="large" color="#f77f3a" /> |
|
|
</View> |
|
|
) : ( |
|
|
<ScrollView style={styles.container}> |
|
|
<View style={styles.sectionHeader}> |
|
|
<Text style={styles.sectionIcon}>💳</Text> |
|
|
<Text style={styles.sectionTitle}>Payment Method</Text> |
|
|
</View> |
|
|
|
|
|
{/* 选项卡 */} |
|
|
<View style={styles.tabContainer}> |
|
|
{tabs.map((tab) => ( |
|
|
<TouchableOpacity |
|
|
key={tab.id} |
|
|
style={[ |
|
|
styles.tabButton, |
|
|
currentTab === tab.id && styles.tabButtonActive, |
|
|
]} |
|
|
onPress={() => setCurrentTab(tab.id)} |
|
|
> |
|
|
<Text |
|
|
style={[ |
|
|
styles.tabText, |
|
|
currentTab === tab.id && styles.tabTextActive, |
|
|
]} |
|
|
> |
|
|
{tab.label} |
|
|
</Text> |
|
|
</TouchableOpacity> |
|
|
))} |
|
|
</View> |
|
|
|
|
|
{/* 支付选项 */} |
|
|
<View style={styles.paymentOptions}> |
|
|
{tabs |
|
|
.find((tab) => tab.id === currentTab) |
|
|
?.options.map((option) => ( |
|
|
<PaymentMethodItem |
|
|
key={option.id} |
|
|
option={option} |
|
|
isSelected={selectedPayment === option.id} |
|
|
onSelect={() => onSelectPayment(option.id)} |
|
|
/> |
|
|
))} |
|
|
</View> |
|
|
|
|
|
{/* Order Summary Section */} |
|
|
<View style={styles.section}> |
|
|
<View style={styles.section1}> |
|
|
<View style={styles.sectionHeader1}> |
|
|
<Text style={styles.sectionIcon1}>📦</Text> |
|
|
<Text style={styles.sectionTitle1}>Order Summary</Text> |
|
|
</View> |
|
|
|
|
|
<View style={styles.setOrderContent}> |
|
|
<Text style={styles.noCouponsMessage}> |
|
|
Products({previewOrder?.items?.length || 0} items) |
|
|
</Text> |
|
|
<TouchableOpacity onPress={toggleExpanded}> |
|
|
<Text style={styles.sectionAction}> |
|
|
{expanded ? "Hide Details" : "View Details"} |
|
|
</Text> |
|
|
</TouchableOpacity> |
|
|
</View> |
|
|
<View |
|
|
style={[ |
|
|
styles.orderItems, |
|
|
expanded && styles.orderItemsExpanded, |
|
|
]} |
|
|
> |
|
|
{previewOrder?.items?.map((item) => ( |
|
|
<View key={item.sku_id} style={styles.orderItem}> |
|
|
{item.sku_image_url ? ( |
|
|
<Image |
|
|
source={{ uri: item.sku_image_url }} |
|
|
style={styles.itemImage} |
|
|
/> |
|
|
) : ( |
|
|
<View style={styles.itemImagePlaceholder} /> |
|
|
)} |
|
|
|
|
|
<View style={styles.itemDetails}> |
|
|
<Text style={styles.itemName} numberOfLines={2}> |
|
|
{item.product_name} |
|
|
</Text> |
|
|
{item.sku_attributes?.map((attribute) => ( |
|
|
<Text |
|
|
style={styles.itemVariant} |
|
|
key={attribute?.attribute_value} |
|
|
numberOfLines={1} |
|
|
> |
|
|
{attribute?.attribute_name}:{" "} |
|
|
{attribute?.attribute_value} |
|
|
</Text> |
|
|
))} |
|
|
<Text style={styles.itemQuantity}> |
|
|
Qty: {item.quantity} |
|
|
</Text> |
|
|
</View> |
|
|
|
|
|
<View style={styles.itemPrices}> |
|
|
<Text style={styles.itemPrice}>${item?.total_price}</Text> |
|
|
</View> |
|
|
</View> |
|
|
))} |
|
|
</View> |
|
|
</View> |
|
|
</View> |
|
|
|
|
|
<View style={styles.priceBox}> |
|
|
<View style={styles.priceBox1}> |
|
|
<Text>Subtotal</Text> |
|
|
<View> |
|
|
<Text>{previewOrder?.total_amount || 0}</Text> |
|
|
</View> |
|
|
</View> |
|
|
<View style={styles.priceBox1}> |
|
|
<Text>Domestic Shipping</Text> |
|
|
<View> |
|
|
<Text>{previewOrder?.shipping_fee || 0}</Text> |
|
|
</View> |
|
|
</View> |
|
|
|
|
|
<View style={styles.priceBox1}> |
|
|
<Text>Estimated International Shipping</Text> |
|
|
<View> |
|
|
<Text>{orderData.transport_type === 1 ?previewOrder?.shipping_fee_sea : previewOrder?.shipping_fee_air || 0}</Text> |
|
|
</View> |
|
|
</View> |
|
|
</View> |
|
|
|
|
|
{/* 实际支付金额 */} |
|
|
<View style={styles.actualPaymentBox}> |
|
|
<View style={styles.actualPaymentBox1}> |
|
|
<Text |
|
|
style={{ |
|
|
fontSize: fontSize(18), |
|
|
fontWeight: "600", |
|
|
color: "#000", |
|
|
}} |
|
|
> |
|
|
Total |
|
|
</Text> |
|
|
<Text |
|
|
style={{ |
|
|
fontSize: fontSize(18), |
|
|
fontWeight: "600", |
|
|
color: "#ff6000", |
|
|
}} |
|
|
> |
|
|
${previewOrder?.actual_amount?.toFixed(2)} |
|
|
</Text> |
|
|
</View> |
|
|
</View> |
|
|
</ScrollView> |
|
|
)} |
|
|
</> |
|
|
); |
|
|
}; |
|
|
|
|
|
const styles = StyleSheet.create({ |
|
|
container: { |
|
|
flex: 1, |
|
|
backgroundColor: "#fff", |
|
|
}, |
|
|
sectionHeader: { |
|
|
flexDirection: "row", |
|
|
alignItems: "center", |
|
|
marginBottom: 15, |
|
|
paddingHorizontal: 15, |
|
|
}, |
|
|
sectionIcon: { |
|
|
fontSize: fontSize(20), |
|
|
marginRight: 8, |
|
|
}, |
|
|
sectionTitle: { |
|
|
fontSize: fontSize(18), |
|
|
fontWeight: "600", |
|
|
color: "#000", |
|
|
}, |
|
|
tabContainer: { |
|
|
flexDirection: "row", |
|
|
marginBottom: 15, |
|
|
borderBottomWidth: 1, |
|
|
borderBottomColor: "#EEEEEE", |
|
|
paddingHorizontal: 15, |
|
|
}, |
|
|
tabButton: { |
|
|
paddingVertical: 10, |
|
|
paddingHorizontal: 15, |
|
|
marginRight: 10, |
|
|
}, |
|
|
tabButtonActive: { |
|
|
borderBottomWidth: 2, |
|
|
borderBottomColor: "#FF5100", |
|
|
}, |
|
|
tabText: { |
|
|
fontSize: fontSize(16), |
|
|
color: "#666", |
|
|
}, |
|
|
tabTextActive: { |
|
|
color: "#FF5100", |
|
|
fontWeight: "500", |
|
|
}, |
|
|
paymentOptions: { |
|
|
marginBottom: 15, |
|
|
paddingHorizontal: 15, |
|
|
}, |
|
|
paymentOption: { |
|
|
flexDirection: "row", |
|
|
alignItems: "center", |
|
|
justifyContent: "space-between", |
|
|
padding: 15, |
|
|
backgroundColor: "#F8F8F8", |
|
|
borderRadius: 8, |
|
|
marginBottom: 10, |
|
|
}, |
|
|
paymentSelected: { |
|
|
backgroundColor: "#FFF0E8", |
|
|
borderWidth: 1, |
|
|
borderColor: "#FF5100", |
|
|
}, |
|
|
paymentContent: { |
|
|
flex: 1, |
|
|
}, |
|
|
defaultPaymentContainer: { |
|
|
flexDirection: "row", |
|
|
alignItems: "center", |
|
|
}, |
|
|
paymentIcon: { |
|
|
fontSize: fontSize(24), |
|
|
marginRight: 8, |
|
|
}, |
|
|
paymentLabel: { |
|
|
fontSize: fontSize(16), |
|
|
fontWeight: "500", |
|
|
}, |
|
|
operatorContainer: { |
|
|
flexDirection: "row", |
|
|
flexWrap: "wrap", |
|
|
marginTop: 8, |
|
|
}, |
|
|
operatorBox: { |
|
|
backgroundColor: "#EEEEEE", |
|
|
paddingVertical: 4, |
|
|
paddingHorizontal: 8, |
|
|
borderRadius: 4, |
|
|
marginRight: 8, |
|
|
marginBottom: 4, |
|
|
}, |
|
|
operatorText: { |
|
|
fontSize: fontSize(12), |
|
|
color: "#666", |
|
|
}, |
|
|
radioButton: { |
|
|
width: 20, |
|
|
height: 20, |
|
|
borderRadius: 10, |
|
|
borderWidth: 1, |
|
|
borderColor: "#CCCCCC", |
|
|
justifyContent: "center", |
|
|
alignItems: "center", |
|
|
}, |
|
|
radioInner: { |
|
|
width: 12, |
|
|
height: 12, |
|
|
borderRadius: 6, |
|
|
backgroundColor: "transparent", |
|
|
}, |
|
|
radioInnerSelected: { |
|
|
backgroundColor: "#FF5100", |
|
|
}, |
|
|
titleContainer: { |
|
|
width: "100%", |
|
|
padding: 15, |
|
|
flexDirection: "row", |
|
|
alignItems: "center", |
|
|
justifyContent: "center", |
|
|
position: "relative", |
|
|
marginBottom: 10, |
|
|
}, |
|
|
backIconContainer: { |
|
|
position: "absolute", |
|
|
left: 15, |
|
|
}, |
|
|
titleHeading: { |
|
|
fontWeight: "600", |
|
|
fontSize: 20, |
|
|
lineHeight: 22, |
|
|
fontFamily: "PingFang SC", |
|
|
color: "black", |
|
|
}, |
|
|
// Order Summary Styles |
|
|
section: { |
|
|
backgroundColor: "#fff", |
|
|
borderRadius: 8, |
|
|
paddingHorizontal: 15, |
|
|
marginTop: 15, |
|
|
}, |
|
|
section1: { |
|
|
backgroundColor: "#fff", |
|
|
borderRadius: 8, |
|
|
overflow: "hidden", |
|
|
}, |
|
|
sectionHeader1: { |
|
|
flexDirection: "row", |
|
|
alignItems: "center", |
|
|
paddingTop: 12, |
|
|
paddingBottom: 12, |
|
|
borderBottomWidth: 1, |
|
|
borderBottomColor: "#f5f5f5", |
|
|
}, |
|
|
sectionIcon1: { |
|
|
fontSize: fontSize(18), |
|
|
marginRight: 10, |
|
|
color: "#666", |
|
|
}, |
|
|
sectionTitle1: { |
|
|
fontSize: fontSize(15), |
|
|
fontWeight: "500", |
|
|
flex: 1, |
|
|
}, |
|
|
setOrderContent: { |
|
|
flexDirection: "row", |
|
|
justifyContent: "space-between", |
|
|
alignItems: "center", |
|
|
paddingTop: 12, |
|
|
paddingBottom: 12, |
|
|
paddingHorizontal: 12, |
|
|
}, |
|
|
sectionAction: { |
|
|
color: "#ff6000", |
|
|
fontSize: fontSize(13), |
|
|
fontWeight: "500", |
|
|
}, |
|
|
noCouponsMessage: { |
|
|
color: "#888", |
|
|
fontSize: fontSize(13), |
|
|
}, |
|
|
orderItems: { |
|
|
maxHeight: 0, |
|
|
overflow: "hidden", |
|
|
}, |
|
|
orderItemsExpanded: { |
|
|
maxHeight: 1000, // Arbitrary large number to accommodate all items |
|
|
}, |
|
|
orderItem: { |
|
|
flexDirection: "row", |
|
|
padding: 16, |
|
|
borderBottomWidth: 1, |
|
|
borderBottomColor: "#f5f5f5", |
|
|
}, |
|
|
itemImage: { |
|
|
width: widthUtils(70, 70).width, |
|
|
height: widthUtils(70, 70).height, |
|
|
borderRadius: 6, |
|
|
marginRight: 12, |
|
|
borderWidth: 1, |
|
|
borderColor: "#eee", |
|
|
}, |
|
|
itemImagePlaceholder: { |
|
|
width: widthUtils(70, 70).width, |
|
|
height: widthUtils(70, 70).height, |
|
|
borderRadius: 6, |
|
|
marginRight: 12, |
|
|
borderWidth: 1, |
|
|
borderColor: "#eee", |
|
|
backgroundColor: "#f1f1f1", |
|
|
}, |
|
|
itemDetails: { |
|
|
flex: 1, |
|
|
}, |
|
|
itemName: { |
|
|
fontSize: fontSize(14), |
|
|
lineHeight: 18, |
|
|
}, |
|
|
itemVariant: { |
|
|
fontSize: fontSize(12), |
|
|
color: "#666", |
|
|
backgroundColor: "#f7f7f7", |
|
|
paddingVertical: 3, |
|
|
paddingHorizontal: 6, |
|
|
borderRadius: 4, |
|
|
marginTop: 6, |
|
|
alignSelf: "flex-start", |
|
|
}, |
|
|
itemQuantity: { |
|
|
fontSize: fontSize(12), |
|
|
color: "#666", |
|
|
marginTop: 4, |
|
|
}, |
|
|
itemPrices: { |
|
|
alignItems: "flex-end", |
|
|
fontSize: fontSize(13), |
|
|
color: "#555", |
|
|
}, |
|
|
itemPrice: { |
|
|
fontWeight: "600", |
|
|
color: "#ff6000", |
|
|
fontSize: fontSize(15), |
|
|
marginBottom: 5, |
|
|
}, |
|
|
priceBox: { |
|
|
borderRadius: 10, |
|
|
marginTop: 15, |
|
|
paddingHorizontal: 15, |
|
|
}, |
|
|
priceBox1: { |
|
|
justifyContent: "space-between", |
|
|
flexDirection: "row", |
|
|
alignItems: "center", |
|
|
padding: 12, |
|
|
borderBottomWidth: 1, |
|
|
borderBottomColor: "#f5f5f5", |
|
|
}, |
|
|
actualPaymentBox: { |
|
|
padding: 12, |
|
|
borderRadius: 6, |
|
|
backgroundColor: "#fff8f4", |
|
|
marginTop: 15, |
|
|
marginHorizontal: 15, |
|
|
marginBottom: 20, |
|
|
}, |
|
|
actualPaymentBox1: { |
|
|
flexDirection: "row", |
|
|
justifyContent: "space-between", |
|
|
alignItems: "center", |
|
|
}, |
|
|
loadingContainer: { |
|
|
flex: 1, |
|
|
justifyContent: "center", |
|
|
alignItems: "center", |
|
|
}, |
|
|
});
|
|
|
|