Browse Source

充值

main
Your Name 2 weeks ago
parent
commit
497d9810f8
  1. 3
      app/locales/en/translation.json
  2. 3
      app/locales/fr/translation.json
  3. 168
      app/screens/BalanceScreen/BalanceScreen.tsx
  4. 5
      app/screens/BalanceScreen/RechargeScreen.tsx
  5. 86
      app/screens/previewOrder/PaymentMethod.tsx
  6. 4
      app/screens/previewOrder/perviewOrder.tsx
  7. 10
      app/screens/productStatus/OrderDatails.tsx
  8. 23
      app/services/api/payApi.ts

3
app/locales/en/translation.json

@ -401,5 +401,8 @@
"pay": "PAY",
"payment_failed": "Payment failed, please try again"
}
},
"pay":{
"payment_Insufficient": "Insufficient balance"
}
}

3
app/locales/fr/translation.json

@ -397,5 +397,8 @@
"pay": "PAYER",
"payment_failed": "Paiement échoué, veuillez réessayer"
}
},
"pay":{
"payment_Insufficient": "Solde insuffisant"
}
}

168
app/screens/BalanceScreen/BalanceScreen.tsx

@ -1,5 +1,6 @@
// 余额管理
import React, { useState, useEffect } from "react";
import React, { useState, useCallback, useRef } from "react";
import {
View,
Text,
@ -11,7 +12,11 @@ import {
SafeAreaView,
StatusBar,
Platform,
FlatList,
ActivityIndicator,
} from "react-native";
import { useFocusEffect } from "@react-navigation/native";
import fontSize from "../../utils/fontsizeUtils";
import widthUtils from "../../utils/widthUtils";
import { useNavigation } from "@react-navigation/native";
@ -20,7 +25,7 @@ import { RootStackParamList } from "../../navigation/types";
import RechargeScreen from "./RechargeScreen";
import BackIcon from "../../components/BackIcon";
import useUserStore from "../../store/user";
import { rechargeHistory, payApi } from "../../services/api/payApi";
import { Transaction, payApi } from "../../services/api/payApi";
import { useTranslation } from "react-i18next";
type BalanceScreenNavigationProp = NativeStackNavigationProp<
@ -33,7 +38,11 @@ export const BalanceScreen = () => {
const { user } = useUserStore();
const navigation = useNavigation<BalanceScreenNavigationProp>();
const [isModalVisible, setIsModalVisible] = useState(false);
const [rechargeHistory, setRechargeHistory] = useState<rechargeHistory[]>([]);
const [rechargeHistory, setRechargeHistory] = useState<Transaction[]>([]);
const [currentPage, setCurrentPage] = useState(1);
const [loading, setLoading] = useState(false);
const [hasMore, setHasMore] = useState(true);
const pageSize = 10;
const handleOpenModal = () => {
setIsModalVisible(true);
@ -43,13 +52,84 @@ export const BalanceScreen = () => {
setIsModalVisible(false);
};
useEffect(() => {
const fetchRechargeHistory = async () => {
const response = await payApi.getRechargeHistory();
setRechargeHistory(response);
const fetchRechargeHistory = async (page: number, refresh = false) => {
if (loading || (!hasMore && !refresh)) return;
try {
setLoading(true);
const response = await payApi.getTransactionHistory(page, pageSize);
if (response.items.length < pageSize) {
setHasMore(false);
}
if (refresh) {
setRechargeHistory(response.items);
} else {
setRechargeHistory(prev => [...prev, ...response.items]);
}
setCurrentPage(page);
} catch (error) {
console.error("Failed to fetch transaction history:", error);
} finally {
setLoading(false);
}
};
const handleLoadMore = () => {
if (!loading && hasMore) {
fetchRechargeHistory(currentPage + 1);
}
};
useFocusEffect(
useCallback(() => {
fetchRechargeHistory(1, true);
}, [])
);
const renderTransactionItem = ({ item }: { item: Transaction }) => (
<View style={styles.transactionHistoryList} key={item.transaction_id}>
<View style={styles.transactionDetailsPanel}>
<View style={styles.transactionDetailsRow}>
<Text style={styles.transactionDescriptionBold}>
{item.type}
</Text>
<Text
style={[
styles.transactionAmountDisplay,
{
color:
Number(item.amount) < 0
? "#0035a4"
: "#ff5217",
},
]}
>
{item.amount} {item.currency}
</Text>
</View>
<View style={styles.transactionInfoRow}>
<Text style={styles.transactionDate}>
{item.timestamp}
</Text>
<Text style={styles.shipmentReference}>
{item.type}
</Text>
</View>
</View>
</View>
);
const renderFooter = () => {
if (!loading) return null;
return (
<View style={styles.loaderContainer}>
<ActivityIndicator size="small" color="#FF8000" />
</View>
);
};
fetchRechargeHistory();
}, []);
return (
<SafeAreaView style={styles.safeArea}>
@ -62,18 +142,28 @@ export const BalanceScreen = () => {
>
<BackIcon size={fontSize(22)} />
</TouchableOpacity>
<Text style={styles.headerTitle}>{t('balance.screen.title')}</Text>
<Text style={styles.headerTitle}>{t("balance.screen.title")}</Text>
<View style={styles.placeholder} />
</View>
<ScrollView style={styles.container}>
<View style={styles.container}>
<FlatList
data={rechargeHistory}
renderItem={renderTransactionItem}
keyExtractor={(item) => item.transaction_id.toString()}
onEndReached={handleLoadMore}
onEndReachedThreshold={0.5}
ListFooterComponent={renderFooter}
ListHeaderComponent={() => (
<View style={styles.balanceCard}>
<View style={styles.sunriseGradientContainer}>
<View style={styles.timeContainer}>
<View style={styles.timeContainer2}></View>
</View>
<View style={styles.balanceWidget}>
<Text style={styles.balanceText}>{t('balance.screen.balance_card')}</Text>
<Text style={styles.balanceText}>
{t("balance.screen.balance_card")}
</Text>
<Image
source={require("../../../assets/img/image_11d1b9c.png")}
style={styles.balanceImage}
@ -89,21 +179,9 @@ export const BalanceScreen = () => {
</View>
<View style={styles.totalSoldInfoContainer}>
<Text style={styles.totalSoldeText}>
{t('balance.screen.total_balance')} ({user?.currency})
</Text>
{/* <View style={styles.totalBalanceInfoContainer}>
<View style={styles.verticalCenteredTextBox}>
<Text style={styles.highlightedText}>50,000</Text>
<Text style={styles.expirationInfo}>
Bientôt expiré (FCFA)
{t("balance.screen.total_balance")} ({user?.currency})
</Text>
</View>
<View style={styles.deadlineInfoContainer}>
<Text style={styles.deadlineText}>2025-05-09</Text>
<Text style={styles.expirationInfo}>Date limite</Text>
</View>
</View> */}
</View>
</View>
<View
style={[
@ -115,45 +193,25 @@ export const BalanceScreen = () => {
style={styles.button}
onPress={handleOpenModal}
>
<Text style={styles.buttonText}>{t('balance.screen.recharge_now')}</Text>
<Text style={styles.buttonText}>
{t("balance.screen.recharge_now")}
</Text>
</TouchableOpacity>
</View>
</View>
<View style={styles.transactionDetailsContainer}>
<View style={styles.balanceDetailContainer}>
<Text style={styles.balanceDetailTitle}>{t('balance.screen.balance_detail')}</Text>
<View style={styles.transactionDetailsContainer1}>
{rechargeHistory.map((item) => (
<View style={styles.transactionHistoryList} key={item.recharge_id}>
{/* Repeated Transaction Details */}
<View style={styles.transactionDetailsPanel}>
<View style={styles.transactionDetailsRow}>
<Text style={styles.transactionDescriptionBold}>
{item.payment_method}
</Text>
<Text style={styles.transactionAmountDisplay}>
{item.amount} {item.currency}
</Text>
</View>
<View style={styles.transactionInfoRow}>
<Text style={styles.transactionDate}>
{item.create_time}
<Text style={styles.balanceDetailTitle}>
{t("balance.screen.balance_detail")}
</Text>
<Text style={styles.shipmentReference}>
{item.transaction_id}
</Text>
</View>
</View>
{/* Additional transaction panels can be added here */}
</View>
))}
</View>
</View>
</View>
</View>
)}
/>
</View>
</ScrollView>
<Modal
animationType="slide"
@ -364,7 +422,6 @@ const styles = StyleSheet.create({
transactionAmountDisplay: {
fontSize: fontSize(24),
fontWeight: "bold",
color: "#002fa7",
},
transactionInfoRow: {
flexDirection: "row",
@ -411,4 +468,9 @@ const styles = StyleSheet.create({
borderTopLeftRadius: 20,
borderTopRightRadius: 20,
},
loaderContainer: {
paddingVertical: 20,
alignItems: 'center',
justifyContent: 'center',
},
});

5
app/screens/BalanceScreen/RechargeScreen.tsx

@ -83,7 +83,10 @@ const RechargeScreen = ({ onClose }: RechargeScreenProps) => {
});
payApi.getCountryPaymentMethods().then((res) => {
setPaymentMethods(res.current_country_methods);
const currentCountryMethods = res.current_country_methods.filter(
(method) => method.key !== "balance"
);
setPaymentMethods(currentCountryMethods);
});
}, []);

86
app/screens/previewOrder/PaymentMethod.tsx

@ -97,6 +97,8 @@ const PaymentMethodItem = ({
isPaypalExpanded?: boolean;
}) => {
const { t } = useTranslation();
const { user } = useUserStore();
return (
<View>
<TouchableOpacity style={styles.cardContainer} onPress={onSelect}>
@ -111,8 +113,8 @@ const PaymentMethodItem = ({
<Text style={styles.balanceText}>
{t("balance.recharge.balance_remaining") || "Balance remaining"}
{"\n"}
{useUserStore().user.balance}
{useUserStore().user.currency}
{user.balance}
{user.currency}
</Text>
</View>
) : (
@ -458,7 +460,7 @@ export const PaymentMethod = () => {
createOrderData.items = items;
createOrderData.payment_method = selectedPayment;
createOrderData.total_amount =
selectedPayment === "Paypal"
selectedPayment === "paypal"
? convertedAmount.reduce(
(acc, item) => acc + item.converted_amount,
0
@ -471,7 +473,7 @@ export const PaymentMethod = () => {
).toFixed(2)
);
createOrderData.actual_amount =
selectedPayment === "Paypal"
selectedPayment === "paypal"
? convertedAmount.reduce(
(acc, item) => acc + item.converted_amount,
0
@ -484,15 +486,15 @@ export const PaymentMethod = () => {
).toFixed(2)
);
createOrderData.currency =
selectedPayment === "Paypal" ? selectedCurrency : user.currency;
selectedPayment === "paypal" ? selectedCurrency : user.currency;
createOrderData.domestic_shipping_fee =
selectedPayment === "Paypal"
selectedPayment === "paypal"
? convertedAmount.find(
(item) => item.item_key === "domestic_shipping_fee"
)?.converted_amount || 0
: orderData?.domestic_shipping_fee;
createOrderData.shipping_fee =
selectedPayment === "Paypal"
selectedPayment === "paypal"
? convertedAmount.find((item) => item.item_key === "shipping_fee")
?.converted_amount || 0
: orderData?.shipping_fee;
@ -502,7 +504,7 @@ export const PaymentMethod = () => {
pay_method: selectedPayment,
offline_payment: currentTab === "offline" ? 0 : 1,
all_price:
selectedPayment === "Paypal"
selectedPayment === "paypal"
? convertedAmount.reduce(
(acc, item) => acc + item.converted_amount,
0
@ -564,9 +566,9 @@ export const PaymentMethod = () => {
data: res,
payMethod: selectedPayment,
currency:
selectedPayment === "Paypal" ? selectedCurrency : user.currency,
selectedPayment === "paypal" ? selectedCurrency : user.currency,
amount:
selectedPayment === "Paypal"
selectedPayment === "paypal"
? convertedAmount.reduce(
(acc, item) => acc + item.converted_amount,
0
@ -731,12 +733,12 @@ export const PaymentMethod = () => {
<Text>{t("payment.product_total")}</Text>
<View>
<Text>
{selectedPayment === "Paypal"
{selectedPayment === "paypal"
? convertedAmount.find(
(item) => item.item_key === "total_amount"
)?.converted_amount || 0
: previewOrder?.total_amount || 0}{" "}
{selectedPayment === "Paypal"
{selectedPayment === "paypal"
? selectedCurrency === "USD"
? "USD"
: "EUR"
@ -748,12 +750,12 @@ export const PaymentMethod = () => {
<Text>{t("payment.domestic_shipping")}</Text>
<View>
<Text>
{selectedPayment === "Paypal"
{selectedPayment === "paypal"
? convertedAmount.find(
(item) => item.item_key === "domestic_shipping_fee"
)?.converted_amount || 0
: orderData?.domestic_shipping_fee || 0}{" "}
{selectedPayment === "Paypal"
{selectedPayment === "paypal"
? selectedCurrency === "USD"
? "USD"
: "EUR"
@ -765,12 +767,12 @@ export const PaymentMethod = () => {
<Text>{t("payment.international_shipping")}</Text>
<View>
<Text>
{selectedPayment === "Paypal"
{selectedPayment === "paypal"
? convertedAmount.find(
(item) => item.item_key === "shipping_fee"
)?.converted_amount || 0
: orderData?.shipping_fee || 0}{" "}
{selectedPayment === "Paypal"
{selectedPayment === "paypal"
? selectedCurrency === "USD"
? "USD"
: "EUR"
@ -791,7 +793,7 @@ export const PaymentMethod = () => {
>
{t("payment.total")}
</Text>
{selectedPayment === "Paypal" &&
{selectedPayment === "paypal" &&
selectedCurrency !== user.currency && (
<View style={{ flexDirection: "row" }}>
<Text
@ -827,7 +829,7 @@ export const PaymentMethod = () => {
</Text>
</View>
)}
{selectedPayment === "Paypal" &&
{selectedPayment === "paypal" &&
selectedCurrency === user.currency && (
<View style={{ flexDirection: "row" }}>
<Text
@ -847,7 +849,7 @@ export const PaymentMethod = () => {
</Text>
</View>
)}
{selectedPayment !== "Paypal" && (
{selectedPayment !== "paypal" && (
<Text
style={{
fontSize: fontSize(18),
@ -998,8 +1000,8 @@ const styles = StyleSheet.create({
color: "#666",
},
radioButton: {
width: 20,
height: 20,
width: widthUtils(20, 20).width,
height: widthUtils(20, 20).height,
borderRadius: 10,
borderWidth: 1,
borderColor: "#CCCCCC",
@ -1007,8 +1009,8 @@ const styles = StyleSheet.create({
alignItems: "center",
},
radioInner: {
width: 12,
height: 12,
width: widthUtils(12, 12).width,
height: widthUtils(12, 12).height,
borderRadius: 6,
backgroundColor: "transparent",
},
@ -1186,7 +1188,7 @@ const styles = StyleSheet.create({
},
primaryButtonStyle: {
width: "100%",
height: 50,
height: widthUtils(50, 50).height,
justifyContent: "center",
alignItems: "center",
fontWeight: "600",
@ -1318,7 +1320,7 @@ const styles = StyleSheet.create({
},
submitButton: {
width: "100%",
height: 50,
height: widthUtils(50, 50).height,
justifyContent: "center",
alignItems: "center",
backgroundColor: "#FF5100",
@ -1379,56 +1381,65 @@ const styles = StyleSheet.create({
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
padding: 15,
paddingHorizontal: 15,
paddingVertical: 10,
backgroundColor: "#F8F8F8",
borderRadius: 8,
marginBottom: 10,
minHeight: 60,
},
leftInfo: {
flexDirection: "row",
alignItems: "center",
flex: 1,
},
blueBox: {
borderRadius: 4,
justifyContent: "center",
alignItems: "center",
marginRight: 10,
width: widthUtils(30, 80).width,
height: widthUtils(30, 30).height,
backgroundColor: "#3955f6",
},
operatorImage: {
width: 60,
height: 22,
width: widthUtils(30, 80).width,
height: widthUtils(30, 30).height,
borderRadius: 4,
resizeMode: "contain",
},
balanceText: {
fontSize: fontSize(14),
fontWeight: "500",
color: "#030303",
marginLeft: 10,
lineHeight: 18,
},
iconRow: {
flexDirection: "row",
alignItems: "center",
flex: 1,
},
imageContainer: {
width: 40,
height: 40,
borderRadius: 4,
marginRight: 10,
flexDirection: "column",
alignItems: "flex-start",
},
mobileMoneyTextContainer: {
flexDirection: "row",
alignItems: "center",
marginTop: 5,
},
mobileMoneyImgContainer: {
width: 60,
height: 22,
width: widthUtils(30, 80).width,
height: widthUtils(30, 30).height,
borderWidth: 0,
marginRight: 5,
},
mobileMoneyImg: {
width: 60,
height: 22,
width: widthUtils(30, 80).width,
height: widthUtils(30, 30).height,
borderWidth: 0,
resizeMode: "contain",
},
mobileMoneyText: {
fontSize: fontSize(16),
@ -1441,6 +1452,7 @@ const styles = StyleSheet.create({
height: fontSize(24),
justifyContent: "center",
alignItems: "center",
marginLeft: 10,
},
checkmarkContainer: {
position: "absolute",

4
app/screens/previewOrder/perviewOrder.tsx

@ -203,7 +203,7 @@ export const PreviewOrder = () => {
</Text>
</View>
<View style={styles.infoRow}>
{/* <View style={styles.infoRow}>
<Text style={styles.infoLabel}>{t("order.preview.order_amount")}</Text>
<Text style={styles.infoValue}>
{route.params.amount || "N/A"} {route.params.data.currency}
@ -219,7 +219,7 @@ export const PreviewOrder = () => {
).toFixed(2) || "N/A"}{" "}
{route.params.data.currency}
</Text>
</View>
</View> */}
</View>
{/* Order Summary Section */}

10
app/screens/productStatus/OrderDatails.tsx

@ -285,6 +285,8 @@ export const OrderDetails = () => {
navigation.navigate("Pay", {
payUrl: res.payment_url,
});
}else{
Alert.alert(t("error"), t("pay.payment_failed"));
}
})
.catch((err) => {
@ -622,22 +624,22 @@ export const OrderDetails = () => {
<Text style={styles.orderIdText1}>
</Text>
</View> */}
{/* <View style={styles.orderId}>
<View style={styles.orderId}>
<Text style={styles.orderIdText}>
{t("order.platform_shipping")}
</Text>
<Text style={styles.orderIdText1}>
{orderDetails.domestic_shipping_fee}
</Text>
</View> */}
{/* <View style={styles.orderId}>
</View>
<View style={styles.orderId}>
<Text style={styles.orderIdText}>
{t("order.international_shipping")}
</Text>
<Text style={styles.orderIdText1}>
{orderDetails.shipping_fee}
</Text>
</View> */}
</View>
<View style={styles.dottedLine}></View>
<View style={styles.orderId}>
<Text style={styles.TotalText}>{t("order.total")}</Text>

23
app/services/api/payApi.ts

@ -89,6 +89,24 @@ export interface RechargeRecommendAmountResponse {
currency: string;
}
export interface Transaction {
transaction_id: string;
type: "order_payment"; // Assuming 'order_payment' is the only possible type for this dataset
amount: number;
currency: string;
description: string;
timestamp: string; // Could also be `Date` if you plan to parse it immediately
related_order_no: string;
}
export interface TransactionsResponse {
items: Transaction[];
total: number;
page: number;
page_size: number;
}
export const payApi = {
// 获取当前国家支付方式
getCountryPaymentMethods: () => {
@ -124,4 +142,9 @@ export const payApi = {
getRechargeRecommendAmount: () => {
return apiService.get<RechargeRecommendAmountResponse>('/api/recharge/recommended-amounts/');
},
// 获取流水
getTransactionHistory: (page:number,page_size:number) => {
return apiService.get<TransactionsResponse>(`/api/users/me/transactions/?page=${page}&page_size=${page_size}`);
},
};

Loading…
Cancel
Save