461 lines
17 KiB
TypeScript
461 lines
17 KiB
TypeScript
import ReturnArrowSvg from '@/assets/icons/svg/returnArrow.svg';
|
|
import StarSvg from '@/assets/icons/svg/whiteStart.svg';
|
|
import CheckSvg from '@/assets/icons/svg/yes.svg';
|
|
import PrivacyModal from '@/components/owner/qualification/privacy';
|
|
import Normal from '@/components/owner/rights/normal';
|
|
import Premium, { PayItem } from '@/components/owner/rights/premium';
|
|
import ProRights from '@/components/owner/rights/proRights';
|
|
import { createOrder, createPayment, getPAy, isOrderExpired, payFailure, payProcessing, paySuccess } from '@/components/owner/rights/utils';
|
|
import { ThemedText } from '@/components/ThemedText';
|
|
import { CreateOrder } from '@/types/personal-info';
|
|
import { useLocalSearchParams, useRouter } from "expo-router";
|
|
import { useEffect, useState } from 'react';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { ActivityIndicator, Image, Platform, ScrollView, StyleSheet, TouchableOpacity, View } from 'react-native';
|
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
|
|
// 根据平台动态导入 expo-iap
|
|
let useIAP: any, requestPurchase: any, getPurchaseHistories: any;
|
|
if (Platform.OS !== 'web') {
|
|
const iap = require('expo-iap');
|
|
useIAP = iap.useIAP;
|
|
requestPurchase = iap.requestPurchase;
|
|
getPurchaseHistories = iap.getPurchaseHistories;
|
|
} else {
|
|
// 为 Web 端提供 mock 实现
|
|
useIAP = () => ({ connected: false });
|
|
requestPurchase = async () => { console.log('IAP is not available on web.'); };
|
|
getPurchaseHistories = async () => [];
|
|
}
|
|
|
|
export default function Rights() {
|
|
const insets = useSafeAreaInsets();
|
|
const router = useRouter();
|
|
const { t } = useTranslation();
|
|
const {
|
|
connected,
|
|
requestProducts,
|
|
ErrorCode
|
|
} = useIAP();
|
|
const { pro } = useLocalSearchParams<{
|
|
credit: string;
|
|
pro: string;
|
|
}>();
|
|
// 用户勾选协议
|
|
const [agree, setAgree] = useState<boolean>(false);
|
|
// 用户选择购买的loading
|
|
const [confirmLoading, setConfirmLoading] = useState<boolean>(false);
|
|
// 选择购买方式
|
|
const [payChoice, setPayChoice] = useState<'ApplePay'>('ApplePay');
|
|
// 普通用户,会员
|
|
const [userType, setUserType] = useState<'normal' | 'premium'>('normal');
|
|
|
|
// 选择权益方式
|
|
const [payType, setPayType] = useState<string>('');
|
|
|
|
// 用户协议弹窗打开
|
|
const [showTerms, setShowTerms] = useState<boolean>(false);
|
|
|
|
// 调接口获取支付信息
|
|
const [premiumPay, setPremiumPay] = useState<PayItem[]>();
|
|
const [loading, setLoading] = useState<boolean>(false);
|
|
|
|
// 查看历史订单
|
|
const fetchPurchaseHistory = async () => {
|
|
try {
|
|
const purchaseHistories = await getPurchaseHistories();
|
|
console.log('Purchase history fetched:', purchaseHistories);
|
|
return purchaseHistories
|
|
} catch (error) {
|
|
console.error('Failed to fetch purchase history:', error);
|
|
}
|
|
};
|
|
|
|
// 恢复购买
|
|
// const restorePurchases = async () => {
|
|
// try {
|
|
// const purchases = await getAvailablePurchases();
|
|
// console.log('Available purchases:', purchases);
|
|
// // Process and validate restored purchases
|
|
// for (const purchase of purchases) {
|
|
// await validateAndGrantPurchase(purchase);
|
|
// }
|
|
// alert(t('personal:rights.restoreSuccess'));
|
|
// } catch (error) {
|
|
// console.error('Restore failed:', error);
|
|
// }
|
|
// };
|
|
|
|
// 处理购买
|
|
const handlePurchase = async (sku: string, transaction_id: string) => {
|
|
try {
|
|
// 支付中
|
|
await payProcessing(transaction_id, "")
|
|
const res = await requestPurchase({
|
|
request: {
|
|
ios: {
|
|
sku: sku,
|
|
andDangerouslyFinishTransactionAutomaticallyIOS: false,
|
|
},
|
|
},
|
|
});
|
|
// 支付成功
|
|
await paySuccess(transaction_id, res?.transaction_id || "")
|
|
} catch (error: any) {
|
|
console.log('Purchase failed:', error);
|
|
// 支付失败
|
|
payFailure(transaction_id, ErrorCode[error?.code as keyof typeof ErrorCode || "E_UNKNOWN"])
|
|
}
|
|
};
|
|
|
|
// 获取苹果订单信息
|
|
useEffect(() => {
|
|
if (!connected) return;
|
|
|
|
const initializeStore = async () => {
|
|
try {
|
|
await requestProducts({ skus: ["MEMBERSHIP_PRO_QUARTERLY", "MEMBERSHIP_PRO_YEARLY", "MEMBERSHIP_PRO_MONTH"], type: 'subs' });
|
|
} catch (error) {
|
|
console.error('Failed to initialize store:', error);
|
|
}
|
|
};
|
|
|
|
initializeStore();
|
|
}, [connected]);
|
|
|
|
// 初始化获取产品项
|
|
useEffect(() => {
|
|
setLoading(true);
|
|
getPAy().then(({ bestValue, payInfo }) => {
|
|
setPayType(bestValue?.product_code)
|
|
setPremiumPay([bestValue, ...payInfo?.filter((item) => item.product_code !== bestValue?.product_code)]);
|
|
setLoading(false);
|
|
}).catch(() => {
|
|
setLoading(false);
|
|
})
|
|
}, []);
|
|
|
|
// 用户确认购买时,进行 创建订单,创建支付 接口调用
|
|
const confirmPurchase = async () => {
|
|
if (!agree) {
|
|
alert(t('personal:rights.agreementError'));
|
|
return
|
|
}
|
|
setConfirmLoading(true);
|
|
const history = await fetchPurchaseHistory()
|
|
const historyIds = history?.filter((item: any) => isOrderExpired(item?.expirationDateIos))?.map((i: any) => { return i?.id })
|
|
if (historyIds?.includes(payType)) {
|
|
setConfirmLoading(false);
|
|
setTimeout(() => {
|
|
alert(t('personal:rights.againError'));
|
|
}, 0);
|
|
return
|
|
}
|
|
|
|
try {
|
|
// 创建订单
|
|
createOrder(premiumPay?.filter((item) => item.product_code === payType)?.[0]?.id || 1, 1).then((res: CreateOrder) => {
|
|
// 创建支付
|
|
createPayment(res?.id || "", payChoice).then(async (res) => {
|
|
// 苹果支付
|
|
await handlePurchase(payType, res?.transaction_id || "")
|
|
setConfirmLoading(false);
|
|
}).catch((err) => {
|
|
console.log("createPayment", err);
|
|
setConfirmLoading(false);
|
|
})
|
|
}).catch((err) => {
|
|
console.log("createOrder", err);
|
|
setConfirmLoading(false);
|
|
})
|
|
} catch (error) {
|
|
console.log("confirmPurchase", error);
|
|
setConfirmLoading(false);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (pro === "Pro") {
|
|
setUserType('premium')
|
|
} else {
|
|
setUserType('normal')
|
|
}
|
|
}, [pro])
|
|
|
|
useEffect(() => {
|
|
fetchPurchaseHistory()
|
|
}, [])
|
|
|
|
return (
|
|
<View style={{ flex: 1 }}>
|
|
{/* 整个页面的中间添加一个loading */}
|
|
{confirmLoading && (
|
|
<View style={[styles.loadingContainer, { top: insets.top + 60 }]}>
|
|
<View style={styles.loadingContent}>
|
|
<ActivityIndicator size="large" color="#AC7E35" />
|
|
<ThemedText style={{ color: '#AC7E35', fontSize: 14, fontWeight: '700' }}>
|
|
{t('personal:rights.confirmLoading')}
|
|
</ThemedText>
|
|
</View>
|
|
</View>
|
|
)}
|
|
<ScrollView style={[styles.container, { paddingTop: insets.top, paddingBottom: insets.bottom + 80 }]}>
|
|
{/* 导航栏 */}
|
|
<View
|
|
style={styles.header}
|
|
>
|
|
<TouchableOpacity onPress={() => { router.push('/owner'); setConfirmLoading(false) }} style={{ padding: 16 }}>
|
|
<ReturnArrowSvg />
|
|
</TouchableOpacity>
|
|
<ThemedText style={styles.headerTitle}>
|
|
{t('rights.title', { ns: 'personal' })}
|
|
</ThemedText>
|
|
<ThemedText className='opacity-0'>123</ThemedText>
|
|
</View>
|
|
{/* 会员卡 */}
|
|
<View style={styles.card}>
|
|
{userType === 'normal' ? (
|
|
<Image source={require('@/assets/images/png/owner/normal.png')} style={{ height: 150, objectFit: 'cover', width: '100%' }} />
|
|
) : (
|
|
<Image source={require('@/assets/images/png/owner/pro.png')} style={{ height: 150, objectFit: 'cover', width: '100%' }} />
|
|
)}
|
|
<View style={styles.cardContent}>
|
|
<View style={styles.cardinfo}>
|
|
<ThemedText style={styles.cardTitle}>
|
|
{t('rights.purchase', { ns: 'personal' })}
|
|
</ThemedText>
|
|
<View style={styles.cardPoints}>
|
|
<StarSvg />
|
|
<ThemedText style={styles.cardPointsText}>{pro}</ThemedText>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
|
|
{/* 会员信息 */}
|
|
<View style={styles.info}>
|
|
{/* 切换按钮 */}
|
|
<View style={styles.switchButton}>
|
|
<TouchableOpacity
|
|
onPress={() => { setUserType("normal") }}
|
|
style={[styles.switchButtonItem, { backgroundColor: userType === 'normal' ? "#FFB645" : "#fff", borderColor: userType === 'normal' ? "#FFB645" : "#E2793F" }]}
|
|
>
|
|
<ThemedText style={{ color: userType === 'normal' ? "#fff" : "#E2793F" }}> {t('rights.free', { ns: 'personal' })}</ThemedText>
|
|
</TouchableOpacity>
|
|
<TouchableOpacity
|
|
onPress={() => { setUserType("premium") }}
|
|
style={[styles.switchButtonItem, { backgroundColor: userType === 'premium' ? "#E2793F" : "#fff", borderColor: userType === 'premium' ? "#E2793F" : "#E2793F" }]}
|
|
>
|
|
<ThemedText style={{ color: userType === 'premium' ? "#fff" : "#E2793F" }}>{t('rights.premium', { ns: 'personal' })}</ThemedText>
|
|
</TouchableOpacity>
|
|
</View>
|
|
{/* 普通权益 */}
|
|
<Normal setUserType={setUserType} style={{ display: userType === 'normal' ? "flex" : "none" }} />
|
|
{/* 会员权益 */}
|
|
<Premium setPayType={setPayType} setShowTerms={setShowTerms} payType={payType} premiumPay={premiumPay} loading={loading} style={{ display: userType === 'normal' ? "none" : "flex" }} />
|
|
</View>
|
|
{/* 支付方式 */}
|
|
{/* <PayTypeModal setConfirmPay={setConfirmPay} modalVisible={showPayType} setModalVisible={setShowPayType} payChoice={payChoice} setPayChoice={setPayChoice} /> */}
|
|
{/* 会员权益信息 */}
|
|
<View style={{ flex: 1, marginBottom: 120 }}>
|
|
<ProRights style={{ display: userType === 'normal' ? "none" : "flex" }} />
|
|
</View>
|
|
</ScrollView>
|
|
{/* 付费按钮 */}
|
|
<View style={{
|
|
padding: 16,
|
|
paddingBottom: 32,
|
|
backgroundColor: '#fff',
|
|
borderTopWidth: 1,
|
|
borderTopColor: '#eee',
|
|
position: 'absolute',
|
|
bottom: 0,
|
|
left: 0,
|
|
right: 0,
|
|
display: userType === 'normal' ? "none" : "flex"
|
|
}}>
|
|
<View style={{ flexDirection: 'row', gap: 8, alignItems: 'center', marginLeft: 8, marginBottom: 8 }}>
|
|
<TouchableOpacity onPress={() => { setAgree(!agree) }} activeOpacity={0.8}>
|
|
<View style={[styles.agree, { backgroundColor: agree ? '#FFB645' : '#fff', borderColor: agree ? '#FFB645' : '#AC7E35' }]}>
|
|
{agree && <CheckSvg />}
|
|
</View>
|
|
</TouchableOpacity>
|
|
<View style={{ flexDirection: 'row', gap: 4 }}>
|
|
<ThemedText style={{ fontWeight: '400', fontSize: 11 }}>
|
|
{t('personal:rights.agreement')}
|
|
</ThemedText>
|
|
<TouchableOpacity
|
|
onPress={async () => {
|
|
setShowTerms(true);
|
|
}}
|
|
activeOpacity={0.8}
|
|
>
|
|
<ThemedText style={{ color: '#AC7E35', fontWeight: '400', fontSize: 11, textAlign: 'center' }}>
|
|
{t('personal:rights.membership')}
|
|
</ThemedText>
|
|
</TouchableOpacity>
|
|
</View>
|
|
</View>
|
|
<TouchableOpacity
|
|
style={styles.goPay}
|
|
onPress={async () => {
|
|
confirmPurchase()
|
|
}}
|
|
activeOpacity={0.8}
|
|
>
|
|
<ThemedText style={{ color: '#fff', fontWeight: '700', fontSize: 14 }}>
|
|
{t('rights.subscribe', { ns: 'personal' })}
|
|
</ThemedText>
|
|
</TouchableOpacity>
|
|
|
|
</View>
|
|
{/* 协议弹窗 */}
|
|
<PrivacyModal modalVisible={showTerms} setModalVisible={setShowTerms} type={"membership"} />
|
|
</View>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
agree: {
|
|
width: 15,
|
|
height: 15,
|
|
borderRadius: 15,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
borderColor: '#AC7E35',
|
|
borderWidth: 1,
|
|
},
|
|
loadingContent: {
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
backgroundColor: '#fff',
|
|
borderRadius: 12,
|
|
padding: 16,
|
|
},
|
|
loadingContainer: {
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
position: 'absolute',
|
|
left: 0,
|
|
right: 0,
|
|
bottom: 0,
|
|
zIndex: 9,
|
|
backgroundColor: 'rgba(255, 255, 255, 0.5)',
|
|
},
|
|
payChoice: {
|
|
width: 20,
|
|
height: 20,
|
|
borderRadius: 15,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
},
|
|
paymentMethod: {
|
|
marginHorizontal: 16,
|
|
marginVertical: 16,
|
|
borderRadius: 12,
|
|
backgroundColor: '#fff',
|
|
shadowColor: '#000',
|
|
shadowOffset: {
|
|
width: 0,
|
|
height: 2,
|
|
},
|
|
shadowOpacity: 0.25,
|
|
shadowRadius: 5,
|
|
elevation: 5,
|
|
},
|
|
goPay: {
|
|
backgroundColor: '#E2793F',
|
|
borderRadius: 24,
|
|
paddingVertical: 10,
|
|
display: "flex",
|
|
alignItems: "center",
|
|
width: "100%",
|
|
},
|
|
switchButton: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
gap: 16,
|
|
marginBottom: 16
|
|
},
|
|
switchButtonItem: {
|
|
width: "48%",
|
|
borderRadius: 24,
|
|
paddingVertical: 6,
|
|
display: "flex",
|
|
alignItems: "center",
|
|
borderWidth: 1
|
|
},
|
|
info: {
|
|
marginHorizontal: 16,
|
|
marginVertical: 16,
|
|
padding: 16,
|
|
borderRadius: 12,
|
|
backgroundColor: '#fff',
|
|
shadowColor: '#000',
|
|
shadowOffset: {
|
|
width: 0,
|
|
height: 2,
|
|
},
|
|
shadowOpacity: 0.25,
|
|
shadowRadius: 5,
|
|
elevation: 5,
|
|
},
|
|
container: {
|
|
flex: 1,
|
|
backgroundColor: 'white',
|
|
},
|
|
header: {
|
|
display: 'flex',
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
marginVertical: 16,
|
|
},
|
|
headerTitle: {
|
|
fontSize: 20,
|
|
fontWeight: '700',
|
|
color: '#4C320C',
|
|
},
|
|
card: {
|
|
marginHorizontal: 16,
|
|
marginVertical: 16,
|
|
backgroundColor: '#FFB645',
|
|
borderRadius: 12,
|
|
},
|
|
cardContent: {
|
|
position: 'absolute',
|
|
top: 0,
|
|
right: 0,
|
|
bottom: 0,
|
|
left: 0,
|
|
padding: 16,
|
|
justifyContent: 'space-between'
|
|
},
|
|
cardinfo: {
|
|
alignItems: 'flex-end',
|
|
},
|
|
cardTitle: {
|
|
fontSize: 12,
|
|
fontWeight: '700',
|
|
color: '#E2793F',
|
|
backgroundColor: '#fff',
|
|
paddingHorizontal: 8,
|
|
paddingVertical: 2,
|
|
borderRadius: 20,
|
|
textAlign: 'center',
|
|
marginBottom: 24
|
|
},
|
|
cardPoints: {
|
|
display: 'flex',
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: 4
|
|
},
|
|
cardPointsText: {
|
|
fontSize: 32,
|
|
fontWeight: '700',
|
|
color: '#4C320C',
|
|
lineHeight: 32
|
|
}
|
|
}); |