import { Ionicons } from '@expo/vector-icons'; import { useLocalSearchParams, useRouter } from 'expo-router'; import { useCallback, useState } from 'react'; import { ActivityIndicator, Alert, Pressable, ScrollView, StyleSheet, Text, View } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { useLegalData, useLegalMembership } from '../../src/hooks'; import { LoadingState, EmptyState } from '../../src/components/StateView'; import { colors } from '../../src/theme'; import type { LegalPaymentChannel } from '../../src/types'; /* ===================== 付款方式 ===================== */ const CHANNELS: { id: LegalPaymentChannel; label: string; icon: keyof typeof Ionicons.glyphMap; color: string }[] = [ { id: 'wechat', label: '微信支付', icon: 'logo-wechat', color: colors.success }, { id: 'alipay', label: '支付宝', icon: 'wallet-outline', color: colors.primary }, ]; /* ===================== 顶部导航 ===================== */ function Header({ onBack }: { onBack: () => void }) { return ( 确认支付 ); } /* ===================== 页面 ===================== */ export default function LegalCheckoutScreen() { const router = useRouter(); const { planId } = useLocalSearchParams(); const id = Array.isArray(planId) ? planId[0] : planId; const { plans, loading } = useLegalData(); const { createOrder, payOrder, processing } = useLegalMembership(); const [channel, setChannel] = useState('wechat'); const [paid, setPaid] = useState(false); const plan = plans.find((p) => p.id === id); const handlePay = useCallback(async () => { if (!plan) return; try { const order = await createOrder(plan.id, channel); const result = await payOrder(order.id); if (result.status === 'paid') { setPaid(true); } else { Alert.alert('支付失败', '请稍后重试'); } } catch (e) { Alert.alert('支付失败', (e as Error).message || '请稍后重试'); } }, [plan, channel, createOrder, payOrder]); // 加载套餐中 if (loading) { return (
router.back()} /> ); } // 套餐不存在(异常入口) if (!plan) { return (
router.back()} /> ); } // 支付成功 if (paid) { return (
router.replace('/legal')} /> 支付成功 已开通{plan.name},可畅享会员专属法律服务 router.replace('/legal')}> 返回法律服务 ); } return (
router.back()} /> {/* 套餐摘要 */} {plan.name} ¥ {plan.price} {plan.period} {plan.benefits.map((benefit, idx) => ( {benefit} ))} {/* 支付方式 */} 选择支付方式 {CHANNELS.map((c) => { const active = c.id === channel; return ( [ styles.channelRow, active && styles.channelRowActive, pressed && styles.channelPressed, ]} onPress={() => setChannel(c.id)} > {c.label} {active && } ); })} {/* 实付金额 */} 实付金额 ¥ {plan.price} {/* 确认支付(底部固定) */} [styles.payBtn, pressed && styles.payBtnPressed, processing && styles.payBtnDisabled]} onPress={handlePay} disabled={processing} > {processing ? ( ) : ( 确认支付 ¥{plan.price} )} ); } const styles = StyleSheet.create({ safeArea: { flex: 1, backgroundColor: colors.pageBg, }, header: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: 8, height: 48, backgroundColor: colors.white, borderBottomWidth: 1, borderBottomColor: colors.borderLight, }, headerBtn: { width: 40, height: 40, alignItems: 'center', justifyContent: 'center', }, headerTitle: { fontSize: 17, fontWeight: '700', color: colors.textPrimary, }, scroll: { flex: 1, }, content: { padding: 12, paddingBottom: 24, }, summaryCard: { padding: 16, borderRadius: 8, backgroundColor: colors.white, borderWidth: 1, borderColor: colors.border, }, summaryName: { fontSize: 16, fontWeight: '700', color: colors.textPrimary, }, summaryPriceRow: { flexDirection: 'row', alignItems: 'baseline', marginTop: 8, marginBottom: 12, }, summaryCurrency: { fontSize: 15, fontWeight: '700', color: colors.actionText, }, summaryPrice: { fontSize: 30, fontWeight: '700', color: colors.actionText, }, summaryPeriod: { fontSize: 13, color: colors.textSecondary, marginLeft: 2, }, benefits: { gap: 6, }, benefitRow: { flexDirection: 'row', alignItems: 'center', gap: 6, }, benefitText: { fontSize: 12, color: colors.textSecondary, }, sectionTitle: { marginTop: 20, marginBottom: 10, fontSize: 15, fontWeight: '700', color: colors.textPrimary, }, channelGroup: { gap: 8, }, channelRow: { flexDirection: 'row', alignItems: 'center', padding: 14, borderRadius: 8, backgroundColor: colors.white, borderWidth: 1, borderColor: colors.border, gap: 12, }, channelRowActive: { borderColor: colors.action, backgroundColor: colors.actionLight, }, channelPressed: { opacity: 0.85, }, channelLabel: { flex: 1, fontSize: 15, fontWeight: '600', color: colors.textPrimary, }, radio: { width: 20, height: 20, borderRadius: 10, borderWidth: 2, borderColor: colors.border, alignItems: 'center', justifyContent: 'center', }, radioActive: { borderColor: colors.action, }, radioDot: { width: 10, height: 10, borderRadius: 5, backgroundColor: colors.action, }, amountRow: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', marginTop: 20, paddingTop: 16, borderTopWidth: 1, borderTopColor: colors.borderLight, }, amountLabel: { fontSize: 14, color: colors.textSecondary, }, amountValue: { flexDirection: 'row', alignItems: 'baseline', }, amountCurrency: { fontSize: 15, fontWeight: '700', color: colors.actionText, }, amountPrice: { fontSize: 22, fontWeight: '700', color: colors.actionText, }, footer: { padding: 12, backgroundColor: colors.white, borderTopWidth: 1, borderTopColor: colors.borderLight, }, payBtn: { height: 48, borderRadius: 8, backgroundColor: colors.primaryBtn, alignItems: 'center', justifyContent: 'center', }, payBtnPressed: { opacity: 0.9, }, payBtnDisabled: { opacity: 0.6, }, payBtnText: { fontSize: 16, fontWeight: '700', color: colors.white, }, successBox: { flex: 1, alignItems: 'center', justifyContent: 'center', padding: 24, }, successIconWrap: { marginBottom: 16, }, successTitle: { fontSize: 20, fontWeight: '700', color: colors.textPrimary, }, successDesc: { marginTop: 8, fontSize: 14, color: colors.textSecondary, textAlign: 'center', }, successBtn: { marginTop: 28, width: 200, height: 46, borderRadius: 23, backgroundColor: colors.primaryBtn, alignItems: 'center', justifyContent: 'center', }, successBtnText: { fontSize: 15, fontWeight: '700', color: colors.white, }, });